diff --git a/.gitignore b/.gitignore index e384a9f..3d7a54e 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,6 @@ plugins/shared/*.js plugins/plugin-schema-types.d.ts plugins/plugin-schema-types.js plugins/host-modules.d.ts -output-hyperagent**/** \ No newline at end of file +output-hyperagent**/**scripts/bash-bundle/_tmp_bundle.js +output-hyperagent-*/ +scripts/bash-bundle/_tmp_bundle.js diff --git a/Justfile b/Justfile index af309f0..6f77efb 100644 --- a/Justfile +++ b/Justfile @@ -148,6 +148,10 @@ setup: ensure-tools install build: install @echo "✅ Build complete — run 'just start' to launch the agent" +# Rebuild the ha:bash bundle from just-bash (only needed when just-bash updates) +build-bash: + node scripts/bash-bundle/build.mjs + # Build everything in release mode (hyperlight-js, guest runtime, NAPI addon) build-release: install-release @echo "✅ Release build complete — run 'just start-release' to launch" diff --git a/builtin-modules/bash.json b/builtin-modules/bash.json new file mode 100644 index 0000000..84778fe --- /dev/null +++ b/builtin-modules/bash.json @@ -0,0 +1,11 @@ +{ + "name": "bash", + "description": "Sandboxed bash interpreter powered by just-bash. Provides 40+ Unix commands (ls, grep, jq, curl, sed, awk, etc.) running entirely in JavaScript inside the Hyperlight micro-VM.", + "author": "system", + "mutable": false, + "type": "script", + "sourceHash": "sha256:c53f56d194f34b81", + "hints": { + "overview": "Pure-JS bash interpreter for the sandbox. Used internally by the execute_bash tool — not intended for direct import in handlers." + } +} diff --git a/package-lock.json b/package-lock.json index 5110cdf..db2d9e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,8 @@ "@types/node": "^25.3.3", "@types/pngjs": "^6.0.5", "@xarsh/ooxml-validator": "^0.1.10", + "esbuild": "^0.28.0", + "just-bash": "^2.14.4", "pdf-parse": "^2.4.5", "pixelmatch": "^7.1.0", "pngjs": "^7.0.0", @@ -86,6 +88,17 @@ "node": ">=20" } }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@emnapi/core": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", @@ -119,9 +132,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "cpu": [ "ppc64" ], @@ -136,9 +149,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "cpu": [ "arm" ], @@ -153,9 +166,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "cpu": [ "arm64" ], @@ -170,9 +183,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "cpu": [ "x64" ], @@ -187,9 +200,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "cpu": [ "arm64" ], @@ -204,9 +217,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "cpu": [ "x64" ], @@ -221,9 +234,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "cpu": [ "arm64" ], @@ -238,9 +251,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "cpu": [ "x64" ], @@ -255,9 +268,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "cpu": [ "arm" ], @@ -272,9 +285,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "cpu": [ "arm64" ], @@ -289,9 +302,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "cpu": [ "ia32" ], @@ -306,9 +319,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "cpu": [ "loong64" ], @@ -323,9 +336,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "cpu": [ "mips64el" ], @@ -340,9 +353,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "cpu": [ "ppc64" ], @@ -357,9 +370,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "cpu": [ "riscv64" ], @@ -374,9 +387,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "cpu": [ "s390x" ], @@ -391,7 +404,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "cpu": [ "x64" ], @@ -406,9 +421,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "cpu": [ "arm64" ], @@ -423,9 +438,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "cpu": [ "x64" ], @@ -440,9 +455,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", "cpu": [ "arm64" ], @@ -457,9 +472,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "cpu": [ "x64" ], @@ -474,9 +489,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "cpu": [ "arm64" ], @@ -491,9 +506,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "cpu": [ "x64" ], @@ -508,9 +523,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", "cpu": [ "arm64" ], @@ -525,9 +540,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "cpu": [ "ia32" ], @@ -542,9 +557,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", "cpu": [ "x64" ], @@ -1174,6 +1189,53 @@ } } }, + "node_modules/@jitl/quickjs-ffi-types": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@jitl/quickjs-ffi-types/-/quickjs-ffi-types-0.32.0.tgz", + "integrity": "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jitl/quickjs-wasmfile-debug-asyncify": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-debug-asyncify/-/quickjs-wasmfile-debug-asyncify-0.32.0.tgz", + "integrity": "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, + "node_modules/@jitl/quickjs-wasmfile-debug-sync": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-debug-sync/-/quickjs-wasmfile-debug-sync-0.32.0.tgz", + "integrity": "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, + "node_modules/@jitl/quickjs-wasmfile-release-asyncify": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-release-asyncify/-/quickjs-wasmfile-release-asyncify-0.32.0.tgz", + "integrity": "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, + "node_modules/@jitl/quickjs-wasmfile-release-sync": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-release-sync/-/quickjs-wasmfile-release-sync-0.32.0.tgz", + "integrity": "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -1181,6 +1243,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@mixmark-io/domino": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", + "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", @@ -1243,6 +1312,22 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/@mongodb-js/zstd": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd/-/zstd-7.0.0.tgz", + "integrity": "sha512-mQ2s0pYYiav+tzCDR05Zptem8Ey2v8s11lri5RKGhTtL4COVCvVCk5vtyRYNT+9L8qSfyOqqefF9UtnW8mC5jA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "node-addon-api": "^8.5.0", + "prebuild-install": "^7.1.3" + }, + "engines": { + "node": ">= 20.19.0" + } + }, "node_modules/@napi-rs/canvas": { "version": "0.1.80", "dev": true, @@ -1720,6 +1805,19 @@ "node": ">= 10" } }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@octokit/auth-token": { "version": "6.0.0", "dev": true, @@ -2144,6 +2242,31 @@ "dev": true, "license": "MIT" }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -2498,11 +2621,46 @@ "node": "18 || 20 || >=22" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/before-after-hook": { "version": "4.0.0", "dev": true, "license": "Apache-2.0" }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -2558,6 +2716,32 @@ "node": "18 || 20 || >=22" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -2637,6 +2821,14 @@ "dev": true, "license": "MIT" }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC", + "optional": true + }, "node_modules/cli-boxes": { "version": "3.0.0", "license": "MIT", @@ -2674,6 +2866,16 @@ "dev": true, "license": "MIT" }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/content-disposition": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", @@ -2765,6 +2967,34 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -2789,6 +3019,16 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2844,6 +3084,17 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2889,7 +3140,9 @@ ] }, "node_modules/esbuild": { - "version": "0.27.3", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2900,32 +3153,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" } }, "node_modules/escape-html": { @@ -3121,6 +3374,17 @@ "node": ">=18.0.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/expect-type": { "version": "1.3.0", "dev": true, @@ -3233,9 +3497,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "funding": [ { "type": "github", @@ -3256,6 +3520,45 @@ "fast-string-width": "^3.0.2" } }, + "node_modules/fast-xml-builder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", + "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.7", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3285,6 +3588,25 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -3356,6 +3678,14 @@ "node": ">= 0.8" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3438,6 +3768,14 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/glob-parent": { "version": "6.0.2", "dev": true, @@ -3532,6 +3870,27 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "dev": true, @@ -3554,6 +3913,16 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/ip-address": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", @@ -3676,6 +4045,38 @@ "npm": ">=6" } }, + "node_modules/just-bash": { + "version": "2.14.5", + "resolved": "https://registry.npmjs.org/just-bash/-/just-bash-2.14.5.tgz", + "integrity": "sha512-MCBGnRlDeZ/MM7mcw+ZuSGFMBsggajrmKz6e/hrOAN7syvVZkjiY+Vh2wyCwN/CdcnAX5SxbiQB51n5nrQuX+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "diff": "^8.0.2", + "fast-xml-parser": "^5.7.3", + "file-type": "^21.2.0", + "ini": "^6.0.0", + "minimatch": "^10.1.1", + "modern-tar": "^0.7.3", + "papaparse": "^5.5.3", + "quickjs-emscripten": "^0.32.0", + "re2js": "^1.2.1", + "seek-bzip": "^2.0.0", + "smol-toml": "^1.6.0", + "sprintf-js": "^1.1.3", + "sql.js": "^1.13.0", + "turndown": "^7.2.2", + "yaml": "^2.8.2" + }, + "bin": { + "just-bash": "dist/bin/just-bash.js", + "just-bash-shell": "dist/bin/shell/shell.js" + }, + "optionalDependencies": { + "@mongodb-js/zstd": "^7.0.0", + "node-liblzma": "^2.0.3" + } + }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -4099,6 +4500,20 @@ "url": "https://opencollective.com/express" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "10.2.4", "dev": true, @@ -4113,10 +4528,39 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/modern-tar": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.6.tgz", + "integrity": "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, "node_modules/mute-stream": { "version": "3.0.0", "dev": true, @@ -4144,6 +4588,14 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/natural-compare": { "version": "1.4.0", "dev": true, @@ -4158,6 +4610,67 @@ "node": ">= 0.6" } }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.7.0.tgz", + "integrity": "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-liblzma": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-liblzma/-/node-liblzma-2.2.0.tgz", + "integrity": "sha512-s0KzNOWwOJJgPG6wxg6cKohnAl9Wk/oW1KrQaVzJBjQwVcUGPQCzpR46Ximygjqj/3KhOrtJXnYMp/xYAXp75g==", + "dev": true, + "hasInstallScript": true, + "license": "LGPL-3.0", + "optional": true, + "dependencies": { + "node-addon-api": "^8.5.0", + "node-gyp-build": "^4.8.4" + }, + "bin": { + "nxz": "lib/cli/nxz.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/oorabona" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4253,6 +4766,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "dev": true, + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -4270,6 +4790,22 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "license": "MIT", @@ -4403,6 +4939,35 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "dev": true, @@ -4438,6 +5003,18 @@ "node": ">= 0.10" } }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "dev": true, @@ -4461,6 +5038,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quickjs-emscripten": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/quickjs-emscripten/-/quickjs-emscripten-0.32.0.tgz", + "integrity": "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", + "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", + "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", + "@jitl/quickjs-wasmfile-release-sync": "0.32.0", + "quickjs-emscripten-core": "0.32.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/quickjs-emscripten-core": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/quickjs-emscripten-core/-/quickjs-emscripten-core-0.32.0.tgz", + "integrity": "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -4485,6 +5089,54 @@ "node": ">= 0.10" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/re2js": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/re2js/-/re2js-1.3.3.tgz", + "integrity": "sha512-s/I5zEAo79SUK0Qw4dpZKpiMwbQ6Gz0KU2NRr7eaO4x/p2g7Vvmn3hdeXDg8VsaUjfj/ora+e9oi27LX/C9+mw==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -4576,6 +5228,20 @@ "version": "2.1.2", "license": "MIT" }, + "node_modules/seek-bzip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-2.0.0.tgz", + "integrity": "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^6.0.0" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, "node_modules/semver": { "version": "7.7.4", "license": "ISC", @@ -4742,6 +5408,68 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/smol-toml": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4752,6 +5480,20 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sql.js": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz", + "integrity": "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==", + "dev": true, + "license": "MIT" + }, "node_modules/stackback": { "version": "0.0.2", "dev": true, @@ -4771,6 +5513,17 @@ "dev": true, "license": "MIT" }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "7.2.0", "license": "MIT", @@ -4799,6 +5552,79 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tinybench": { "version": "2.9.0", "dev": true, @@ -4848,6 +5674,25 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tslib": { "version": "2.8.1", "dev": true, @@ -4872,8 +5717,520 @@ "fsevents": "~2.3.3" } }, - "node_modules/typanion": { - "version": "3.14.0", + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/turndown": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.4.tgz", + "integrity": "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mixmark-io/domino": "^2.2.0" + }, + "engines": { + "node": ">=18", + "npm": ">=9" + } + }, + "node_modules/typanion": { + "version": "3.14.0", "dev": true, "license": "MIT", "workspaces": [ @@ -4927,6 +6284,19 @@ "node": ">=14.17" } }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici-types": { "version": "7.19.2", "dev": true, @@ -4954,6 +6324,14 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -5201,6 +6579,38 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/yaml": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "dev": true, diff --git a/package.json b/package.json index 610f6a9..2a0137a 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "@types/node": "^25.3.3", "@types/pngjs": "^6.0.5", "@xarsh/ooxml-validator": "^0.1.10", + "esbuild": "^0.28.0", + "just-bash": "^2.14.4", "pdf-parse": "^2.4.5", "pixelmatch": "^7.1.0", "pngjs": "^7.0.0", diff --git a/scripts/bash-bundle/build.mjs b/scripts/bash-bundle/build.mjs new file mode 100644 index 0000000..1d080dd --- /dev/null +++ b/scripts/bash-bundle/build.mjs @@ -0,0 +1,97 @@ +#!/usr/bin/env node +// scripts/bash-bundle/build.mjs +// +// Builds the ha:bash module bundle from just-bash. +// Output: builtin-modules/bash.js (self-contained ESM module for QuickJS) +// +// Usage: node scripts/bash-bundle/build.mjs +// +// Prerequisites: npm install (just-bash and esbuild must be in node_modules) + +import { execSync } from "node:child_process"; +import { readFileSync, writeFileSync, existsSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(__dirname, "..", ".."); +const outFile = join(repoRoot, "builtin-modules", "bash.js"); + +// ── Step 1: esbuild bundle ────────────────────────────────────────── + +console.log("Building ha:bash bundle from just-bash..."); + +const stubDir = __dirname; +const entryFile = join(stubDir, "entry.mjs"); + +// Check prerequisites +if (!existsSync(join(repoRoot, "node_modules", "just-bash"))) { + console.error( + "Error: just-bash not found in node_modules. Run npm install first.", + ); + process.exit(1); +} + +const aliasArgs = [ + `--alias:node:zlib=${join(stubDir, "zlib-stub.mjs")}`, + `--alias:node:worker_threads=${join(stubDir, "worker-stub.mjs")}`, + `--alias:node:path=${join(stubDir, "node-path-stub.mjs")}`, + `--alias:node:dns=${join(stubDir, "dns-stub.mjs")}`, + `--alias:node:crypto=${join(stubDir, "crypto-stub.mjs")}`, + `--alias:node:url=${join(stubDir, "url-stub.mjs")}`, + `--alias:node:fs=${join(stubDir, "fs-stub.mjs")}`, + `--alias:node:fs/promises=${join(stubDir, "fs-stub.mjs")}`, + `--alias:node:child_process=${join(stubDir, "worker-stub.mjs")}`, + `--alias:node:os=${join(stubDir, "worker-stub.mjs")}`, + `--alias:node:async_hooks=${join(stubDir, "worker-stub.mjs")}`, + `--alias:turndown=${join(stubDir, "turndown-stub.mjs")}`, + `--alias:seek-bzip=${join(stubDir, "bzip-stub.mjs")}`, + `--alias:node-liblzma=${join(stubDir, "liblzma-stub.mjs")}`, + `--alias:@mongodb-js/zstd=${join(stubDir, "zstd-stub.mjs")}`, + `--alias:sql.js=${join(stubDir, "sqljs-stub.mjs")}`, +].join(" "); + +const tmpBundle = join(stubDir, "_tmp_bundle.js"); + +execSync( + `npx esbuild ${entryFile} ` + + `--bundle --format=esm --platform=neutral --target=es2020 ` + + `--main-fields=module,main ` + + `${aliasArgs} ` + + `--outfile=${tmpBundle} ` + + `--minify --tree-shaking=true`, + { stdio: "inherit", cwd: repoRoot }, +); + +// ── Step 2: Prepend polyfills ─────────────────────────────────────── + +const polyfills = `// @module bash +// @description Sandboxed bash interpreter (just-bash) with polyfills for QuickJS +// @author system +// @generated DO NOT EDIT — built by scripts/bash-bundle/build.mjs + +// ── QuickJS Polyfills ──────────────────────────────────────────────── +if(typeof globalThis.URL==='undefined'){globalThis.URL=class URL{constructor(input,base){let full=String(input);if(base&&!full.match(/^[a-z]+:\\/\\//i)){full=String(base).replace(/\\/[^\\/]*$/,'/')+full}const m=full.match(/^(https?:)\\/\\/([^\\/:]+)(:\\d+)?(\\/[^?#]*)?(\\?[^#]*)?(#.*)?$/i);if(m){this.protocol=m[1];this.hostname=m[2];this.port=m[3]?m[3].slice(1):'';this.pathname=m[4]||'/';this.search=m[5]||'';this.hash=m[6]||'';this.host=this.hostname+(this.port?':'+this.port:'');this.origin=this.protocol+'//'+this.host;this.href=this.origin+this.pathname+this.search+this.hash;this.searchParams=new URLSearchParams(this.search);this.username='';this.password=''}else{this.href=full;this.protocol='';this.hostname='';this.port='';this.pathname=full;this.search='';this.hash='';this.host='';this.origin='';this.searchParams=new URLSearchParams();this.username='';this.password=''}}toString(){return this.href}}} +if(typeof globalThis.URLSearchParams==='undefined'){globalThis.URLSearchParams=class URLSearchParams{constructor(init){this._p=[];if(typeof init==='string'){const s=init.startsWith('?')?init.slice(1):init;for(const pair of s.split('&')){const[k,v]=pair.split('=');if(k)this._p.push([decodeURIComponent(k),decodeURIComponent(v||'')])}}}get(k){const p=this._p.find(([a])=>a===k);return p?p[1]:null}has(k){return this._p.some(([a])=>a===k)}toString(){return this._p.map(([k,v])=>encodeURIComponent(k)+'='+encodeURIComponent(v)).join('&')}entries(){return this._p[Symbol.iterator]()}[Symbol.iterator](){return this._p[Symbol.iterator]()}forEach(fn){this._p.forEach(([k,v])=>fn(v,k))}}} +if(typeof globalThis.Buffer==='undefined'){const _e=new TextEncoder();const _d=new TextDecoder();class HaBuffer extends Uint8Array{toString(encoding){if(!encoding||encoding==='utf-8'||encoding==='utf8')return _d.decode(this);if(encoding==='base64')return btoa(String.fromCharCode.apply(null,this));if(encoding==='latin1'||encoding==='binary'){let s='';for(let i=0;is+b.length,0);const r=new HaBuffer(t);let o=0;for(const b of l){r.set(b,o);o+=b.length}return r},alloc(s){return new HaBuffer(s)},byteLength(s,e){if(typeof s==='string')return _e.encode(s).length;return s.length}}} +if(typeof globalThis.process==='undefined'){globalThis.process={env:{},nextTick(fn){queueMicrotask(fn)},execPath:'/usr/bin/node',mainModule:null,umask(){return 18},type:'renderer'}} +if(typeof globalThis.AbortController==='undefined'){globalThis.AbortController=class AbortController{constructor(){this.signal={aborted:false,addEventListener(){}}}abort(){this.signal.aborted=true}}} +// crypto.getRandomValues, crypto.randomUUID, and Math.random are provided +// natively by the Hyperlight runtime via RDRAND — no JS polyfill needed. +if(typeof globalThis.setTimeout==='undefined'){globalThis.setTimeout=(fn)=>{fn();return 0};globalThis.clearTimeout=()=>{};globalThis.setInterval=()=>0;globalThis.clearInterval=()=>{}} +if(typeof globalThis.performance==='undefined'){globalThis.performance={now(){return Date.now()}}} +// ── End Polyfills ──────────────────────────────────────────────────── + +`; + +const bundleSource = readFileSync(tmpBundle, "utf-8"); +const output = polyfills + bundleSource; +writeFileSync(outFile, output); + +// Clean up temp file +try { + require("node:fs").unlinkSync(tmpBundle); +} catch {} + +const sizeKb = (output.length / 1024).toFixed(0); +console.log(`\\n✅ Built builtin-modules/bash.js (${sizeKb} KB)`); diff --git a/scripts/bash-bundle/bzip-stub.mjs b/scripts/bash-bundle/bzip-stub.mjs new file mode 100644 index 0000000..9f20917 --- /dev/null +++ b/scripts/bash-bundle/bzip-stub.mjs @@ -0,0 +1 @@ +export default { decode() { throw new Error("bzip2: not available in sandbox"); } }; diff --git a/scripts/bash-bundle/crypto-stub.mjs b/scripts/bash-bundle/crypto-stub.mjs new file mode 100644 index 0000000..f0d54eb --- /dev/null +++ b/scripts/bash-bundle/crypto-stub.mjs @@ -0,0 +1,19 @@ +// Stub for node:crypto +export function randomBytes(n) { + const buf = new Uint8Array(n); + for (let i = 0; i < n; i++) buf[i] = Math.floor(Math.random() * 256); + return buf; +} +export function randomUUID() { + const h = "0123456789abcdef"; + let u = ""; + for (let i = 0; i < 36; i++) { + if (i === 8 || i === 13 || i === 18 || i === 23) u += "-"; + else u += h[Math.floor(Math.random() * 16)]; + } + return u; +} +export function createHash() { + throw new Error("createHash: not available in sandbox"); +} +export default { randomBytes, randomUUID, createHash }; diff --git a/scripts/bash-bundle/dns-stub.mjs b/scripts/bash-bundle/dns-stub.mjs new file mode 100644 index 0000000..6345edd --- /dev/null +++ b/scripts/bash-bundle/dns-stub.mjs @@ -0,0 +1,8 @@ +// Stub for node:dns — just-bash uses this for network allow-list validation +// We handle networking through host:fetch plugin, so DNS lookup is not needed +export function lookup(hostname, opts, cb) { + if (typeof opts === "function") { cb = opts; opts = {}; } + // Return a dummy address — our fetch plugin does its own SSRF checks + if (cb) cb(null, "0.0.0.0", 4); +} +export default { lookup }; diff --git a/scripts/bash-bundle/entry.mjs b/scripts/bash-bundle/entry.mjs new file mode 100644 index 0000000..6604e4f --- /dev/null +++ b/scripts/bash-bundle/entry.mjs @@ -0,0 +1,4 @@ +// Entry point for esbuild bundling of just-bash for QuickJS/Hyperlight +// Re-exports only what we need, stubs out node:zlib +export { Bash } from "just-bash"; +export { InMemoryFs } from "just-bash"; diff --git a/scripts/bash-bundle/fs-stub.mjs b/scripts/bash-bundle/fs-stub.mjs new file mode 100644 index 0000000..42d7022 --- /dev/null +++ b/scripts/bash-bundle/fs-stub.mjs @@ -0,0 +1,22 @@ +// Stub for node:fs — just-bash's ReadWriteFs uses this for real FS +// We don't use ReadWriteFs — we use our own IFileSystem adapter +// But the import exists in the bundle, so we stub it +export function readFileSync() { throw new Error("node:fs not available in sandbox"); } +export function writeFileSync() { throw new Error("node:fs not available in sandbox"); } +export function existsSync() { return false; } +export function statSync() { throw new Error("node:fs not available in sandbox"); } +export function readdirSync() { return []; } +export function mkdirSync() { throw new Error("node:fs not available in sandbox"); } +export function unlinkSync() { throw new Error("node:fs not available in sandbox"); } +export function rmdirSync() { throw new Error("node:fs not available in sandbox"); } +export function chmodSync() { throw new Error("node:fs not available in sandbox"); } +export function symlinkSync() { throw new Error("node:fs not available in sandbox"); } +export function linkSync() { throw new Error("node:fs not available in sandbox"); } +export function readlinkSync() { throw new Error("node:fs not available in sandbox"); } +export function realpathSync() { throw new Error("node:fs not available in sandbox"); } +export function lstatSync() { throw new Error("node:fs not available in sandbox"); } +export function utimesSync() { throw new Error("node:fs not available in sandbox"); } +export function copyFileSync() { throw new Error("node:fs not available in sandbox"); } +export function renameSync() { throw new Error("node:fs not available in sandbox"); } +export async function open() { throw new Error("node:fs/promises not available in sandbox"); } +export default {}; diff --git a/scripts/bash-bundle/liblzma-stub.mjs b/scripts/bash-bundle/liblzma-stub.mjs new file mode 100644 index 0000000..7646bbd --- /dev/null +++ b/scripts/bash-bundle/liblzma-stub.mjs @@ -0,0 +1 @@ +export default null; diff --git a/scripts/bash-bundle/node-path-stub.mjs b/scripts/bash-bundle/node-path-stub.mjs new file mode 100644 index 0000000..0c24d30 --- /dev/null +++ b/scripts/bash-bundle/node-path-stub.mjs @@ -0,0 +1,53 @@ +// Minimal node:path polyfill for just-bash +export function join(...parts) { + return parts.filter(Boolean).join("/").replace(/\/+/g, "/"); +} +export function resolve(...parts) { + let result = ""; + for (const part of parts) { + if (part.startsWith("/")) result = part; + else result = result ? result + "/" + part : part; + } + return normalize(result || "/"); +} +export function normalize(p) { + const absolute = p.startsWith("/"); + const parts = p.split("/"); + const out = []; + for (const part of parts) { + if (part === "..") { + if (out.length > 0) out.pop(); + else if (!absolute) out.push(".."); + } + else if (part !== "." && part !== "") out.push(part); + } + const normalized = (absolute ? "/" : "") + out.join("/"); + return normalized || (absolute ? "/" : "."); +} +export function dirname(p) { + const i = p.lastIndexOf("/"); + return i <= 0 ? (p.startsWith("/") ? "/" : ".") : p.slice(0, i); +} +export function basename(p, ext) { + let b = p.split("/").pop() || ""; + if (ext && b.endsWith(ext)) b = b.slice(0, -ext.length); + return b; +} +export function extname(p) { + const b = basename(p); + const i = b.lastIndexOf("."); + return i > 0 ? b.slice(i) : ""; +} +export function isAbsolute(p) { return p.startsWith("/"); } +export function relative(from, to) { + const f = resolve(from).split("/").filter(Boolean); + const t = resolve(to).split("/").filter(Boolean); + let i = 0; + while (i < f.length && i < t.length && f[i] === t[i]) i++; + const ups = f.length - i; + return [...Array(ups).fill(".."), ...t.slice(i)].join("/") || "."; +} +export const sep = "/"; +export const delimiter = ":"; +export const posix = { join, resolve, normalize, dirname, basename, extname, isAbsolute, relative, sep, delimiter }; +export default { join, resolve, normalize, dirname, basename, extname, isAbsolute, relative, sep, delimiter, posix }; diff --git a/scripts/bash-bundle/sqljs-stub.mjs b/scripts/bash-bundle/sqljs-stub.mjs new file mode 100644 index 0000000..f8d2f38 --- /dev/null +++ b/scripts/bash-bundle/sqljs-stub.mjs @@ -0,0 +1,4 @@ +// Stub for sql.js — sqlite3 not supported in sandbox +export default function () { + throw new Error("sqlite3: not available in sandbox"); +} diff --git a/scripts/bash-bundle/turndown-stub.mjs b/scripts/bash-bundle/turndown-stub.mjs new file mode 100644 index 0000000..d574c9f --- /dev/null +++ b/scripts/bash-bundle/turndown-stub.mjs @@ -0,0 +1,5 @@ +// Stub for turndown (html-to-markdown) — not needed in sandbox +export default class TurndownService { + constructor() {} + turndown(html) { return html.replace(/<[^>]+>/g, ''); } +} diff --git a/scripts/bash-bundle/url-stub.mjs b/scripts/bash-bundle/url-stub.mjs new file mode 100644 index 0000000..357f59a --- /dev/null +++ b/scripts/bash-bundle/url-stub.mjs @@ -0,0 +1,7 @@ +// Stub for node:url +export function fileURLToPath(url) { + if (typeof url === "string" && url.startsWith("file://")) return url.slice(7); + return String(url); +} +export function pathToFileURL(p) { return new URL("file://" + p); } +export default { fileURLToPath, pathToFileURL }; diff --git a/scripts/bash-bundle/worker-stub.mjs b/scripts/bash-bundle/worker-stub.mjs new file mode 100644 index 0000000..1f66f7f --- /dev/null +++ b/scripts/bash-bundle/worker-stub.mjs @@ -0,0 +1,11 @@ +// Stub for node:worker_threads and node:async_hooks +export class Worker { + constructor() { throw new Error("Workers not available in this environment"); } +} +export const parentPort = null; +export const workerData = null; +export const isMainThread = true; +export class AsyncLocalStorage { + getStore() { return undefined; } + run(store, fn) { return fn(); } +} diff --git a/scripts/bash-bundle/zlib-stub.mjs b/scripts/bash-bundle/zlib-stub.mjs new file mode 100644 index 0000000..07bcb09 --- /dev/null +++ b/scripts/bash-bundle/zlib-stub.mjs @@ -0,0 +1,11 @@ +// Stub for node:zlib — just-bash's gzip/tar commands use this +export function gzipSync(data) { + throw new Error("gzip: not available in this environment. Use the ha:zip-format module in a JavaScript handler instead."); +} +export function gunzipSync(data) { + throw new Error("gunzip: not available in this environment. Use the ha:zip-format module in a JavaScript handler instead."); +} +export function deflateSync(data) { return gzipSync(data); } +export function inflateSync(data) { return gunzipSync(data); } +export const constants = { Z_SYNC_FLUSH: 0, Z_FINISH: 4, Z_DEFAULT_COMPRESSION: -1, Z_NO_COMPRESSION: 0, Z_BEST_COMPRESSION: 9 }; +export default { gzipSync, gunzipSync, deflateSync, inflateSync, constants }; diff --git a/scripts/bash-bundle/zstd-stub.mjs b/scripts/bash-bundle/zstd-stub.mjs new file mode 100644 index 0000000..7646bbd --- /dev/null +++ b/scripts/bash-bundle/zstd-stub.mjs @@ -0,0 +1 @@ +export default null; diff --git a/scripts/build-modules.js b/scripts/build-modules.js index d16b7eb..7f85204 100644 --- a/scripts/build-modules.js +++ b/scripts/build-modules.js @@ -140,4 +140,19 @@ execSync("npx tsx scripts/generate-host-modules-dts.ts", { stdio: "inherit", }); +// Step 9: Rebuild ha:bash bundle from just-bash (if just-bash is installed) +try { + const justBashPath = join(ROOT, "node_modules", "just-bash"); + if (existsSync(justBashPath)) { + console.log("\nRebuilding ha:bash bundle from just-bash..."); + execSync("node scripts/bash-bundle/build.mjs", { + cwd: ROOT, + stdio: "inherit", + }); + } +} catch (e) { + console.error(" ⚠️ bash bundle build failed:", e.message); + // Non-fatal — bash is optional, core agent works without it +} + console.log("✓ Build complete"); diff --git a/skills/api-explorer/SKILL.md b/skills/api-explorer/SKILL.md index 4756fe4..7d466e0 100644 --- a/skills/api-explorer/SKILL.md +++ b/skills/api-explorer/SKILL.md @@ -35,6 +35,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/data-processor/SKILL.md b/skills/data-processor/SKILL.md index b4f713e..13e4abc 100644 --- a/skills/data-processor/SKILL.md +++ b/skills/data-processor/SKILL.md @@ -24,6 +24,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/mcp-services/SKILL.md b/skills/mcp-services/SKILL.md index 5a8d311..f97d721 100644 --- a/skills/mcp-services/SKILL.md +++ b/skills/mcp-services/SKILL.md @@ -29,6 +29,7 @@ allowed-tools: - mcp_tool_info - manage_mcp - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/pdf-expert/SKILL.md b/skills/pdf-expert/SKILL.md index 19e6fd6..19eb701 100644 --- a/skills/pdf-expert/SKILL.md +++ b/skills/pdf-expert/SKILL.md @@ -31,6 +31,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/pptx-expert/SKILL.md b/skills/pptx-expert/SKILL.md index 75d9dc1..8d5002b 100644 --- a/skills/pptx-expert/SKILL.md +++ b/skills/pptx-expert/SKILL.md @@ -26,6 +26,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/report-builder/SKILL.md b/skills/report-builder/SKILL.md index 652e40e..7f017b5 100644 --- a/skills/report-builder/SKILL.md +++ b/skills/report-builder/SKILL.md @@ -21,6 +21,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/research-synthesiser/SKILL.md b/skills/research-synthesiser/SKILL.md index a1b6901..c719b37 100644 --- a/skills/research-synthesiser/SKILL.md +++ b/skills/research-synthesiser/SKILL.md @@ -38,6 +38,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/web-scraper/SKILL.md b/skills/web-scraper/SKILL.md index ae93590..55292a3 100644 --- a/skills/web-scraper/SKILL.md +++ b/skills/web-scraper/SKILL.md @@ -24,6 +24,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/skills/xlsx-expert/SKILL.md b/skills/xlsx-expert/SKILL.md index 7509b53..aedb02e 100644 --- a/skills/xlsx-expert/SKILL.md +++ b/skills/xlsx-expert/SKILL.md @@ -25,6 +25,7 @@ antiPatterns: allowed-tools: - register_handler - execute_javascript + - execute_bash - delete_handler - get_handler_source - edit_handler diff --git a/src/agent/index.ts b/src/agent/index.ts index 5249421..e69c556 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -397,7 +397,12 @@ async function questionCapturingPaste( prompt: string, collectWindowMs = 100, ): Promise { - process.stdout.write(prompt); + // Use rl.prompt() instead of raw process.stdout.write() so readline + // properly enters its terminal mode (raw mode, cursor tracking, etc.). + // Without this, arrow keys print raw escape codes (^[[A) after the + // first prompt because readline doesn't know it should be active. + rl.setPrompt(prompt); + rl.prompt(); return new Promise((resolve) => { const lines: string[] = []; @@ -990,6 +995,12 @@ async function syncPluginsToSandbox(): Promise { // Hand the registrations to the sandbox — it will rebuild on next call // Await is important: saves shared-state before invalidating the sandbox await sandbox.setPlugins(registrations); + + // Keep bash sandbox in sync — same plugins, same host modules. + // Without this, execute_bash can't use curl (fetch plugin) or filesystem. + if (bashSandbox) { + await bashSandbox.setPlugins(registrations); + } } // ── Mutable Agent State ────────────────────────────────────────────── @@ -2049,8 +2060,9 @@ const executeJavascriptTool = defineTool("execute_javascript", { result: `Result saved to ${relativePath} (${(fullResultBytes / 1024).toFixed(1)} KB).\n` + `Preview (first 500 chars):\n${preview}\n\n` + - `IMPORTANT: Read the full output by writing a handler that reads "${relativePath}" via host:fs-read.\n` + - `Process the data in handler code — do NOT summarize or propose changes based only on the preview.\n` + + `IMPORTANT: Read the full output by writing a handler that reads "${relativePath}" via host:fs-read,\n` + + `or use execute_bash to process it (e.g. cat, grep, jq, head, wc).\n` + + `Process the data in handler code or bash — do NOT summarize or propose changes based only on the preview.\n` + `Only use read_output("${relativePath}") directly if you need a quick look at a specific section;\n` + `read_output("${relativePath}", startLine, endLine) supports line ranges.`, ...(consoleOutput?.length ? { consoleOutput } : {}), @@ -2140,6 +2152,310 @@ const executeJavascriptTool = defineTool("execute_javascript", { }, }); +// ── Tool: execute_bash ─────────────────────────────────────────────── +// +// Runs bash commands inside the Hyperlight sandbox using just-bash +// (a pure-JS bash interpreter). Commands execute in the same sandbox +// as JavaScript handlers — same plugins, same baseDir, same isolation. +// +// Stateless: each call creates a fresh Bash instance. Use && or ; +// to chain commands that share state within a single call. +// +// The internal _bash_runner handler is auto-registered on first use. + +/** Bash commands available in the sandbox. */ +const BASH_SUPPORTED_COMMANDS = [ + "echo", + "cat", + "grep", + "fgrep", + "egrep", + "rg", + "head", + "tail", + "wc", + "sort", + "uniq", + "find", + "tree", + "diff", + "sed", + "awk", + "tr", + "cut", + "paste", + "join", + "xargs", + "tee", + "printf", + "seq", + "env", + "printenv", + "date", + "basename", + "dirname", + "readlink", + "which", + "whoami", + "hostname", + "jq", + "yq", + "base64", + "md5sum", + "sha256sum", + "cp", + "mkdir", + "touch", + "chmod", + "ls", + "stat", + "du", + "file", + "curl", + "rev", + "nl", + "fold", + "expand", + "column", + "comm", + "tac", + "od", + "expr", + "true", + "time", +] as const; + +/** Handler code for the internal bash runner. */ +const BASH_RUNNER_HANDLER = ` +import { Bash, InMemoryFs } from "ha:bash"; + +// Bridge host:fetch plugin into Web Fetch-compatible function for curl. +// just-bash's curl calls env.fetch(url, opts) expecting a Response-like object. +let fetchFn = null; +try { + const mod = await import("host:fetch"); + // Build a minimal Web Fetch adapter over the synchronous host:fetch plugin. + fetchFn = async function(url, opts) { + const method = (opts && opts.method || "GET").toUpperCase(); + const headers = {}; + if (opts && opts.headers) { + // Headers can be a Headers object, plain object, or entries array + if (typeof opts.headers.forEach === 'function') { + opts.headers.forEach((v, k) => { headers[k] = v; }); + } else { + Object.assign(headers, opts.headers); + } + } + const fetchOpts = { method, headers }; + if (opts && opts.body) fetchOpts.body = opts.body; + if (method === "GET" || method === "HEAD") { + const result = mod.fetchText(url, { ...fetchOpts, includeMeta: true }); + const enc = new TextEncoder(); + const body = typeof result === 'string' + ? enc.encode(result) + : enc.encode(result.data || ''); + const status = typeof result === 'object' ? (result.status || 200) : 200; + const ct = typeof result === 'object' ? (result.contentType || '') : ''; + return { + status, + statusText: status === 200 ? 'OK' : String(status), + headers: new Map([['content-type', ct]]), + body, + url, + }; + } else { + const result = mod.post(url, opts.body, fetchOpts); + const enc = new TextEncoder(); + return { + status: result.status || 200, + statusText: result.statusText || 'OK', + headers: new Map(Object.entries(result.headers || {})), + body: enc.encode(typeof result.body === 'string' ? result.body : JSON.stringify(result.body || '')), + url, + }; + } + }; +} catch { + // fetch plugin not enabled — curl will report "command not found" +} + +export async function handler(event) { + const e = typeof event === 'string' ? JSON.parse(event) : event; + const bashOpts = { + commands: ${JSON.stringify([...BASH_SUPPORTED_COMMANDS])}, + }; + // Wire fetch plugin into just-bash so curl works + if (fetchFn) bashOpts.fetch = fetchFn; + const bash = new Bash(bashOpts); + const result = await bash.exec(e.command || "echo 'No command provided'"); + return { + stdout: result.stdout, + stderr: result.stderr, + exitCode: result.exitCode, + }; +}`; + +/** Whether the bash runner handler has been registered this session. */ +let bashRunnerRegistered = false; + +/** + * Dedicated sandbox for bash execution. Separate from the main JS sandbox + * so the 1.4 MB bash module doesn't compete with pptx/pdf/xlsx modules + * for memory during compilation. Created lazily on first execute_bash call. + */ +let bashSandbox: ReturnType | null = null; + +/** + * Create (or return existing) dedicated bash sandbox. + * Only loads ha:bash module — no pptx, pdf, xlsx overhead. + */ +async function getBashSandbox() { + if (bashSandbox && bashRunnerRegistered) return bashSandbox; + + // Load the bash module source from disk + const bashModulePath = join(getModulesDir(), "bash.js"); + if (!existsSync(bashModulePath)) { + return null; + } + const bashSource = readFileSync(bashModulePath, "utf-8"); + + // Create a lightweight sandbox with only ha:bash loaded + bashSandbox = createSandboxTool({ + heapSizeMb: 64, + scratchSizeMb: 32, + inputBufferKb: 2048, + outputBufferKb: 2048, + cpuTimeoutMs: 10000, + wallClockTimeoutMs: 15000, + }); + + // Load ONLY the bash module — no pptx/pdf/xlsx + bashSandbox.setModules([{ name: "bash", source: bashSource }]); + + // Sync enabled plugins so bash has the same host modules as the + // main JS sandbox (fs-read, fs-write, fetch, MCP adapters, etc.). + await syncPluginsToSandbox(); + + // Register the bash runner handler + console.error(` ${C.dim("🐚 Initialising bash sandbox...")}`); + const reg = await bashSandbox.registerHandler( + "sys-bash-runner", + BASH_RUNNER_HANDLER, + ); + if (!reg.success) { + bashSandbox = null; + return null; + } + bashRunnerRegistered = true; + return bashSandbox; +} + +const executeBashTool = defineTool("execute_bash", { + description: [ + "Execute a bash command in the sandbox using a pure-JS bash interpreter.", + "The command runs inside a dedicated Hyperlight micro-VM (separate from JavaScript handlers).", + "", + "STATELESS: each call is a fresh shell. Use && or ; to chain commands.", + "The filesystem is shared with JavaScript handlers (same baseDir, same plugins).", + "", + "SUPPORTED COMMANDS:", + " Text: echo, cat, grep, rg, head, tail, wc, sort, uniq, sed, awk, tr,", + " cut, paste, join, xargs, tee, rev, nl, fold, tac, column, comm,", + " diff, printf, seq, expr, base64, md5sum, sha256sum, od", + " Files: ls, find, tree, cp, mkdir, touch, chmod, stat, du, file", + " Data: jq, yq (JSON/YAML processing)", + " Net: curl (requires fetch plugin enabled — use apply_profile('web-research') first)", + " Meta: date, env, whoami, hostname, basename, dirname, which, readlink", + "", + "POSIX FLAGS ONLY: This is a pure-JS bash interpreter, not GNU coreutils.", + " GNU-only flags will fail (e.g. sort -R, grep -P, sed -i).", + " Stick to POSIX-standard flags for all commands.", + "", + "NOT AVAILABLE: rm, mv, ln, wget, python, node, sleep, ssh, git, apt", + " - For file deletion, there is no command available", + " - For complex logic, use register_handler with JavaScript instead", + "", + "EXAMPLES:", + ' execute_bash({ command: "ls -la" })', + " execute_bash({ command: \"cat data.json | jq '.results[] | .name' | sort | uniq -c | sort -rn | head -10\" })", + ' execute_bash({ command: "grep -r TODO . | wc -l" })', + " execute_bash({ command: \"find . -name '*.json' | head -5\" })", + ].join("\n"), + parameters: { + type: "object", + properties: { + command: { + type: "string", + description: + "The bash command to execute. Supports pipes (|), " + + "redirects (>, >>), command chaining (&&, ;), " + + "variables (FOO=bar; echo $FOO), and subshells ($(...)).", + }, + }, + required: ["command"], + }, + handler: async ({ command }: { command: string }) => { + if ( + !command || + typeof command !== "string" || + command.trim().length === 0 + ) { + return { error: "command is required and must be a non-empty string." }; + } + + // Get the dedicated bash sandbox (created lazily, separate from main JS sandbox) + const bs = await getBashSandbox(); + if (!bs) { + return { + error: + "Bash module not available. Ensure builtin-modules/bash.js exists (run: just build-bash).", + }; + } + + // Log bash command to code log (same as show-code for JS handlers) + if (state.showCodeEnabled) { + console.log(` ${C.dim("$ " + command)}`); + } + sandbox.writeCode(`// ── bash ──\n$ ${command}\n`); + + const { success, result, error, consoleOutput, stats, timing } = + await bs.executeJavaScript("sys-bash-runner", { command }); + + if (state.showTimingEnabled && timing) { + console.error( + ` ${C.dim(`⏱️ ${timing.totalMs}ms (exec: ${timing.executeMs}ms)`)}`, + ); + } + + if (success && result) { + const stdout = String(result.stdout ?? ""); + const stderr = String(result.stderr ?? ""); + const exitCode = Number(result.exitCode ?? 0); + + // Show output to user + if (stdout) { + console.log(stdout.endsWith("\n") ? stdout.slice(0, -1) : stdout); + } + if (stderr) { + console.error(` ${C.dim(stderr.trimEnd())}`); + } + + return { + stdout, + stderr, + exitCode, + ...(consoleOutput?.length ? { consoleOutput } : {}), + }; + } else { + console.error(` ${C.err("❌ " + (error ?? "Unknown bash error"))}`); + return { + error: error ?? "Bash execution failed", + ...(consoleOutput?.length ? { consoleOutput } : {}), + }; + } + }, +}); + // ── Tool: reset_sandbox ────────────────────────────────────────────── const sandboxResetTool = defineTool("reset_sandbox", { @@ -2159,6 +2475,10 @@ const sandboxResetTool = defineTool("reset_sandbox", { }, handler: async () => { const result = await sandbox.resetSandbox(); + // Also reset the bash sandbox — it's a separate instance. + // Setting it to null forces re-creation on next execute_bash call. + bashSandbox = null; + bashRunnerRegistered = false; if (result.success) { console.error( ` ${C.ok("🔄 Sandbox state reset")} (${result.handlers?.length ?? 0} handlers preserved, ha:shared-state preserved)`, @@ -2213,6 +2533,11 @@ const configureSandboxTool = defineTool("configure_sandbox", { "Call proactively if you can predict a task needs more resources", "(e.g. ZIP/PPTX building, multiple network calls, large data processing).", "Also call reactively when a limit error tells you which limit was hit.", + "", + "COMMON MEMORY ERRORS:", + " 'Out of physical memory' / 'Guest aborted: 13' → increase SCRATCH (not heap)", + " 'malloc failed' / 'out of memory' → increase HEAP", + " 'buffer to push data' → increase inputBuffer or outputBuffer", ].join("\n"), parameters: { type: "object", @@ -4245,8 +4570,11 @@ const listModulesTool = defineTool("list_modules", { state.hasCalledListModules = true; try { - // Filter out internal modules (names starting with _) - const modules = listModules().filter((m) => !m.name.startsWith("_")); + // Filter out internal modules (names starting with _) and bash + // (bash runs in its own sandbox, not importable by JS handlers) + const modules = listModules().filter( + (m) => !m.name.startsWith("_") && m.name !== "bash", + ); const result = modules.map((m) => ({ name: m.name, description: m.description, @@ -4380,11 +4708,16 @@ const moduleInfoTool = defineTool("module_info", { state.hasCalledListModules = true; try { - // Block access to internal modules (names starting with _) - if (name.startsWith("_")) { + // Block access to internal modules and bash (bash runs in its own sandbox) + if (name.startsWith("_") || name === "bash") { const available = listModules() - .filter((m) => !m.name.startsWith("_")) + .filter((m) => !m.name.startsWith("_") && m.name !== "bash") .map((m) => m.name); + if (name === "bash") { + return { + error: `ha:bash is not importable in JavaScript handlers. Use the execute_bash tool instead.`, + }; + } return { error: `Module "${name}" not found. Available: ${available.join(", ") || "(none)"}`, }; @@ -4700,8 +5033,10 @@ function loadAllModules(): void { } } - // Load all modules into the sandbox cache - const allModules = listModules(); + // Load all modules into the sandbox cache. + // Exclude ha:bash — it runs in its own dedicated sandbox (getBashSandbox()) + // to avoid competing with pptx/pdf/xlsx for memory during compilation. + const allModules = listModules().filter((m) => m.name !== "bash"); if (allModules.length > 0) { sandbox.setModules( allModules.map((m) => ({ name: m.name, source: m.source })), @@ -4775,6 +5110,7 @@ function buildSessionConfig() { tools: [ registerHandlerTool, executeJavascriptTool, + executeBashTool, deleteHandlerTool, deleteHandlersTool, getHandlerSourceTool, @@ -4810,6 +5146,7 @@ function buildSessionConfig() { availableTools: [ "register_handler", "execute_javascript", + "execute_bash", "delete_handler", "get_handler_source", "edit_handler", @@ -5085,10 +5422,27 @@ function buildSessionConfig() { return { additionalContext: `Current heap: ${heapMb}MB, scratch: ${scratchMb}MB. ` + - `If the error says "Out of physical memory" or "Guest aborted: 13", ` + - `the scratch setting is too low — suggest /set scratch (e.g. double it). ` + - `For general OOM, suggest /set heap . ` + - `Or try breaking the computation into smaller pieces.`, + `IMPORTANT: "Out of physical memory" or "Guest aborted: 13" means SCRATCH is too low, NOT heap. ` + + `Call configure_sandbox({ scratch: ${scratchMb * 2} }) to double scratch memory. ` + + `For "malloc failed" or general OOM, call configure_sandbox({ heap: ${heapMb * 2} }). ` + + `For "buffer" errors, increase inputBuffer or outputBuffer.`, + }; + } + + // For bash sandbox memory errors, guide with fixed limits (no configure_sandbox) + if ( + toolName === "execute_bash" && + toolResult.resultType === "failure" && + /out of memory|out of physical memory|heap|stack overflow|guest aborted/i.test( + toolResult.error ?? "", + ) + ) { + return { + additionalContext: + `The bash sandbox has fixed resource limits (64MB heap, 32MB scratch). ` + + `configure_sandbox does NOT affect execute_bash — it only controls the JavaScript sandbox. ` + + `To work around this, break the command into smaller pieces or use ` + + `register_handler with JavaScript for memory-intensive operations.`, }; } diff --git a/src/agent/slash-commands.ts b/src/agent/slash-commands.ts index b2a163f..ea658ae 100644 --- a/src/agent/slash-commands.ts +++ b/src/agent/slash-commands.ts @@ -544,7 +544,10 @@ export async function handleSlashCommand( console.log(); return true; } - const showAll = parts.includes("--all") || parts.includes("-a"); + const showAll = + parts.includes("--all") || + parts.includes("-a") || + parts.includes("-all"); try { const allSessions = await state.copilotClient.listSessions(); // Filter to only hyperagent-prefixed sessions @@ -585,12 +588,10 @@ export async function handleSlashCommand( const summary = s.summary ? ` — ${s.summary.slice(0, 60)}${s.summary.length > 60 ? "…" : ""}` : ""; - // Show the UUID part after the prefix for readability - const shortId = s.sessionId.slice( - SESSION_ID_PREFIX.length, - SESSION_ID_PREFIX.length + 12, - ); - console.log(` ${C.val(shortId + "…")}${current}`); + // Show the full UUID part after the prefix so users can + // paste it into /resume (partial matching works too). + const uuidPart = s.sessionId.slice(SESSION_ID_PREFIX.length); + console.log(` ${C.val(uuidPart)}${current}`); console.log(` ${C.dim("Modified:")} ${modified}${summary}`); } if (hidden > 0) { @@ -617,7 +618,7 @@ export async function handleSlashCommand( try { let targetId = parts[1]; if (!targetId) { - // No ID given — resume most recent hyperagent session + // No ID given — show interactive session picker (like az account list). const allSessions = await state.copilotClient.listSessions(); const ours = allSessions .filter((s) => s.sessionId.startsWith(SESSION_ID_PREFIX)) @@ -637,7 +638,64 @@ export async function handleSlashCommand( console.log(); return true; } - targetId = ours[0].sessionId; + + // Show numbered list (max 20 most recent) + const pickCount = Math.min(ours.length, 20); + console.log( + ` ${C.label("📋 Pick a session to resume")} (${ours.length} total):`, + ); + for (let i = 0; i < pickCount; i++) { + const s = ours[i]; + const current = + state.activeSession && + s.sessionId === state.activeSession.sessionId + ? ` ${C.ok("← current")}` + : ""; + const modified = s.modifiedTime + ? new Date(s.modifiedTime).toLocaleString() + : "unknown"; + const summary = s.summary + ? ` — ${s.summary.slice(0, 50)}${s.summary.length > 50 ? "…" : ""}` + : ""; + const num = String(i + 1).padStart(2); + console.log( + ` ${C.warn(num)}) ${C.dim(modified)}${summary}${current}`, + ); + } + if (ours.length > pickCount) { + console.log( + ` ${C.dim(`… ${ours.length - pickCount} older sessions not shown`)}`, + ); + } + console.log(); + + // Prompt for selection + const answer = await rl.question( + ` ${C.label("Enter number (1-" + pickCount + ") or session ID:")} `, + ); + const trimmed = answer.trim(); + if (!trimmed) { + console.log(` ${C.dim("Cancelled.")}`); + console.log(); + return true; + } + + // Check if it's a number + const num = parseInt(trimmed, 10); + if (!isNaN(num) && num >= 1 && num <= pickCount) { + targetId = ours[num - 1].sessionId; + } else { + // Treat as session ID (partial match) + const prefixed = SESSION_ID_PREFIX + trimmed; + const match = + ours.find((s) => s.sessionId.startsWith(prefixed)) ?? + ours.find((s) => s.sessionId.startsWith(trimmed)); + if (match) { + targetId = match.sessionId; + } else { + targetId = trimmed; // pass through — SDK will error if invalid + } + } } else { // Allow partial session ID matching (with or without prefix) const allSessions = await state.copilotClient.listSessions(); diff --git a/src/agent/system-message.ts b/src/agent/system-message.ts index 2136ce2..46708fd 100644 --- a/src/agent/system-message.ts +++ b/src/agent/system-message.ts @@ -35,8 +35,9 @@ const SYSTEM_MESSAGE_TEMPLATE = `You are HyperAgent — an open-source AI agent Source: https://github.com/hyperlight-dev/hyperagent If users ask how you work, what you can do, or about your architecture, point them to the repo — they can explore the code, open issues, and contribute. The project welcomes pull requests. -You have NO direct access to filesystem, network, or shell. No bash, curl, Python. +You have NO direct access to filesystem, network, or shell. No Python, no pip. EVERYTHING goes through sandbox tools — register_handler, execute_javascript, etc. +For bash commands, use the execute_bash tool (separate sandbox, same plugins). ╔══════════════════════════════════════════════════════════════════════╗ ║ MANDATORY HANDLER FORMAT — YOUR CODE WILL BE REJECTED WITHOUT THIS ║ @@ -80,6 +81,13 @@ WORKFLOW: 4. register_handler — write JavaScript with function handler(event) 5. execute_javascript — run your handler +BASH TOOL (for data exploration, text processing, and file analysis): + execute_bash({ command: "..." }) — run bash commands in the sandbox + 40+ commands: ls, cat, grep, jq, sed, awk, sort, find, head, tail, wc, curl, etc. + Supports pipes, redirects, env vars. Stateless — each call is independent. + Use bash for quick data exploration, text processing, and analysis. + Use JavaScript handlers for complex logic, binary output, and module APIs. + DIRECT FILE I/O (text content only — no sandbox needed): write_output(path, content) — write to fs-write directory read_input(path) — read from fs-read directory @@ -152,9 +160,11 @@ MCP HANDLER-ONLY EXECUTION: URLS: Do NOT guess URLs — they will 404. Discover via APIs or verify first. UNAVAILABLE: setTimeout, fetch(), Buffer, fs, process. - AVAILABLE GLOBALS: TextEncoder, TextDecoder, atob, btoa, queueMicrotask. + AVAILABLE GLOBALS: TextEncoder, TextDecoder, atob, btoa, queueMicrotask, crypto. + crypto provides: crypto.getRandomValues, crypto.randomUUID, crypto.subtle.digest (SHA-1, SHA-256). For Latin-1 byte encoding: import { strToBytes } from "ha:str-bytes" - No SQL, no bash, no web browsing — only sandbox tools and plugins exist. + No SQL, no web browsing — only sandbox tools and plugins exist. + For bash commands, use the execute_bash tool (separate from JavaScript handlers). RESOURCE LIMITS (call configure_sandbox to increase if you hit them): CPU: \${CPU_TIMEOUT_MS}ms | Wall: \${WALL_TIMEOUT_MS}ms diff --git a/src/agent/tool-gating.ts b/src/agent/tool-gating.ts index 526b784..3a5f00e 100644 --- a/src/agent/tool-gating.ts +++ b/src/agent/tool-gating.ts @@ -13,6 +13,7 @@ export const ALLOWED_TOOLS = new Set([ "register_handler", // Register named JavaScript handler "execute_javascript", // Execute a registered handler + "execute_bash", // Execute bash commands in the sandbox "delete_handler", // Remove a handler "get_handler_source", // Retrieve handler source for editing "edit_handler", // Surgical edit to existing handler diff --git a/src/sandbox/runtime/Cargo.lock b/src/sandbox/runtime/Cargo.lock index e5b210a..510400b 100644 --- a/src/sandbox/runtime/Cargo.lock +++ b/src/sandbox/runtime/Cargo.lock @@ -640,7 +640,10 @@ dependencies = [ name = "native-globals" version = "0.1.0" dependencies = [ + "digest", "rquickjs", + "sha1", + "sha2", ] [[package]] @@ -889,6 +892,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" diff --git a/src/sandbox/runtime/modules/native-globals/Cargo.toml b/src/sandbox/runtime/modules/native-globals/Cargo.toml index 11f0757..e21c724 100644 --- a/src/sandbox/runtime/modules/native-globals/Cargo.toml +++ b/src/sandbox/runtime/modules/native-globals/Cargo.toml @@ -8,3 +8,6 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(hyperlight)'] } [dependencies] rquickjs = { version = "0.11", default-features = false, features = ["bindgen", "macro"] } +sha1 = { version = "0.10", default-features = false } +sha2 = { version = "0.10", default-features = false } +digest = { version = "0.10", default-features = false } diff --git a/src/sandbox/runtime/modules/native-globals/src/lib.rs b/src/sandbox/runtime/modules/native-globals/src/lib.rs index 734a7ff..d8a0261 100644 --- a/src/sandbox/runtime/modules/native-globals/src/lib.rs +++ b/src/sandbox/runtime/modules/native-globals/src/lib.rs @@ -1,14 +1,21 @@ -//! Native globals — TextEncoder, TextDecoder, atob, btoa, console extras. +//! Native globals — TextEncoder, TextDecoder, atob, btoa, crypto, console extras. //! //! Registers standard Web API globals that npm libraries expect. //! Called via `custom_globals!` macro during runtime init. //! //! Core encoding logic is in Rust for performance and correctness. //! JS constructor wrappers call into the Rust functions. +//! +//! crypto.getRandomValues, crypto.randomUUID, and Math.random use the +//! x86_64 RDRAND hardware instruction for random number generation. +//! RDRAND provides high-quality randomness but is NOT guaranteed +//! cryptographically secure in all hypervisor environments — treat +//! it as suitable for UUIDs, awk rand(), hashing nonces, etc. #![cfg_attr(hyperlight, no_std)] #[cfg(hyperlight)] +#[macro_use] extern crate alloc; #[cfg(hyperlight)] @@ -16,6 +23,8 @@ use alloc::string::String; #[cfg(hyperlight)] use alloc::vec::Vec; +use core::sync::atomic::{AtomicU64, Ordering}; +use digest::Digest; use rquickjs::{Ctx, Function, Result as QjsResult, TypedArray}; // ── TextEncoder ───────────────────────────────────────────────────────── @@ -141,6 +150,140 @@ fn rust_btoa(input: String) -> QjsResult { Ok(result) } +// ── Hardware RNG (RDRAND / RDSEED) ─────────────────────────────────── +// +// x86_64 CPUs provide hardware random number generation via RDRAND +// (random numbers) and RDSEED (entropy seed). Since we run in a +// Hyperlight micro-VM on x86_64, these instructions are available +// directly — no OS, no crate, no software PRNG needed. +// +// Used by crypto.getRandomValues, crypto.randomUUID, and Math.random. + +/// Read a hardware random u64 via RDRAND. Retries on transient failure. +/// +/// Only available on x86_64 — Hyperlight micro-VMs always target this arch. +#[cfg(target_arch = "x86_64")] +#[inline] +fn rdrand64() -> u64 { + let mut val: u64; + let mut ok: u8; + // RDRAND can fail if the HW buffer is exhausted — retry up to 10 times + for _ in 0..10 { + unsafe { + core::arch::asm!( + "rdrand {val}", + "setc {ok}", + val = out(reg) val, + ok = out(reg_byte) ok, + options(nostack, nomem), + ); + } + if ok != 0 { + return val; + } + } + // Fallback: should never happen on modern CPUs, but don't panic + // in production code. Return a non-zero value based on a counter. + static FALLBACK: AtomicU64 = AtomicU64::new(0xDEAD_BEEF_CAFE_BABE); + FALLBACK.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed) +} + +/// Fallback for non-x86_64 targets (e.g. clippy cross-compilation). +/// Uses an atomic counter — NOT random, but keeps the build working. +#[cfg(not(target_arch = "x86_64"))] +#[inline] +fn rdrand64() -> u64 { + static COUNTER: AtomicU64 = AtomicU64::new(0xDEAD_BEEF_CAFE_BABE); + COUNTER.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed) +} + +/// Fill a Uint8Array with hardware random bytes via RDRAND. +fn crypto_get_random_values<'js>( + ctx: Ctx<'js>, + input: TypedArray<'js, u8>, +) -> QjsResult> { + let len = input.len(); + let mut bytes = Vec::with_capacity(len); + let mut i = 0; + while i < len { + let val = rdrand64(); + let val_bytes = val.to_le_bytes(); + for &b in &val_bytes { + if i >= len { + break; + } + bytes.push(b); + i += 1; + } + } + let result = TypedArray::new(ctx, bytes)?; + Ok(result) +} + +/// Generate a v4 UUID string using RDRAND. +fn crypto_random_uuid() -> QjsResult { + let r1 = rdrand64(); + let r2 = rdrand64(); + // Set version (4) and variant (10xx) bits per RFC 4122 + let time_hi = ((r1 >> 32) as u16 & 0x0FFF) | 0x4000; // version 4 + let clock_seq = ((r2 >> 48) as u16 & 0x3FFF) | 0x8000; // variant 10xx + + Ok(format!( + "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}", + (r1 & 0xFFFF_FFFF) as u32, + ((r1 >> 32) & 0xFFFF) as u16, + time_hi, + clock_seq, + r2 & 0xFFFF_FFFF_FFFF + )) +} + +/// Generate a random f64 in [0, 1) — used by Math.random. +/// Hardware RDRAND provides true randomness, not a PRNG sequence. +fn math_random() -> QjsResult { + let r = rdrand64(); + // Use upper 53 bits for full double precision + Ok((r >> 11) as f64 / (1u64 << 53) as f64) +} + +// ── crypto.subtle.digest (SHA-1 / SHA-256) ────────────────────────── +// +// Implements the SubtleCrypto.digest() API needed by just-bash's +// sha1sum and sha256sum commands. Uses RustCrypto crates (no_std, +// audited) — not hand-rolled. +// +// API: crypto.subtle.digest(algorithm, data) → ArrayBuffer +// algorithm: "SHA-1" or "SHA-256" +// data: Uint8Array (or ArrayBuffer view) +// returns: Uint8Array containing the hash bytes + +/// Compute SHA-1 or SHA-256 digest. Called from JS via crypto.subtle.digest(). +fn crypto_subtle_digest<'js>( + ctx: Ctx<'js>, + algorithm: String, + data: Vec, +) -> QjsResult> { + let hash_bytes: Vec = match algorithm.as_str() { + "SHA-1" => { + let mut hasher = sha1::Sha1::new(); + hasher.update(&data); + hasher.finalize().to_vec() + } + "SHA-256" => { + let mut hasher = sha2::Sha256::new(); + hasher.update(&data); + hasher.finalize().to_vec() + } + _ => { + return Err(rquickjs::Error::new_from_js( + "string", + "supported algorithm (SHA-1 or SHA-256)", + )); + } + }; + TypedArray::new(ctx, hash_bytes) +} + // ── Public setup function ──────────────────────────────────────────────── // // Called by custom_globals! macro during runtime init. @@ -231,5 +374,69 @@ pub fn setup_globals(ctx: &Ctx<'_>) -> QjsResult<()> { "#, )?; + // ── crypto.getRandomValues + crypto.randomUUID ────────────── + // Backed by x86_64 RDRAND hardware instruction — true randomness. + let get_random_values_fn = Function::new(ctx.clone(), crypto_get_random_values)?; + let random_uuid_fn = Function::new(ctx.clone(), crypto_random_uuid)?; + let subtle_digest_fn = Function::new(ctx.clone(), crypto_subtle_digest)?; + globals.set("__ha_getRandomValues", get_random_values_fn)?; + globals.set("__ha_randomUUID", random_uuid_fn)?; + globals.set("__ha_subtleDigest", subtle_digest_fn)?; + ctx.eval::<(), _>( + r#" + (function() { + const getRandomValues = globalThis.__ha_getRandomValues; + const randomUUID = globalThis.__ha_randomUUID; + const subtleDigest = globalThis.__ha_subtleDigest; + if (typeof globalThis.crypto === 'undefined') { + globalThis.crypto = {}; + } + globalThis.crypto.getRandomValues = function(array) { + const filled = getRandomValues(array); + for (let i = 0; i < array.length; i++) array[i] = filled[i]; + return array; + }; + globalThis.crypto.randomUUID = randomUUID; + // SubtleCrypto.digest — returns Promise matching the Web API. + // Backed by RustCrypto sha1/sha2 crates (no_std, audited). + globalThis.crypto.subtle = { + digest: function(algorithm, data) { + try { + let bytes; + if (data instanceof ArrayBuffer) { + bytes = new Uint8Array(data); + } else if (ArrayBuffer.isView(data)) { + bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); + } else { + bytes = new Uint8Array(data); + } + const result = subtleDigest(algorithm, Array.from(bytes)); + return Promise.resolve(result.buffer); + } catch (e) { + return Promise.reject(e); + } + } + }; + delete globalThis.__ha_getRandomValues; + delete globalThis.__ha_randomUUID; + delete globalThis.__ha_subtleDigest; + })(); + "#, + )?; + + // ── Math.random (PRNG-backed) ──────────────────────────────── + // Override QuickJS's built-in Math.random with our seeded PRNG + // so awk rand() and other random functions produce varied output. + let math_random_fn = Function::new(ctx.clone(), math_random)?; + globals.set("__ha_mathRandom", math_random_fn)?; + ctx.eval::<(), _>( + r#" + (function() { + Math.random = globalThis.__ha_mathRandom; + delete globalThis.__ha_mathRandom; + })(); + "#, + )?; + Ok(()) } diff --git a/tests/pattern-integrity.test.ts b/tests/pattern-integrity.test.ts index 7461914..8fb9fb5 100644 --- a/tests/pattern-integrity.test.ts +++ b/tests/pattern-integrity.test.ts @@ -134,4 +134,53 @@ describe("pattern-integrity", () => { }); } }); + + describe("tool gating completeness", () => { + // Extract all tool names from defineTool() calls in index.ts + const indexSource = readFileSync( + join(ROOT, "src", "agent", "index.ts"), + "utf-8", + ); + const toolNames = [...indexSource.matchAll(/defineTool\("([^"]+)"/g)].map( + (m) => m[1], + ); + + // Tools that are intentionally NOT gated (internal-only, not exposed to LLM) + const INTERNAL_TOOLS = new Set([ + "llm_thought", // Internal reasoning — not user-facing + "delete_handlers", // Bulk delete — internal use + ]); + + for (const toolName of toolNames) { + if (INTERNAL_TOOLS.has(toolName)) continue; + + it(`tool "${toolName}" is in ALLOWED_TOOLS`, () => { + expect( + ALLOWED_TOOLS.has(toolName), + `Tool "${toolName}" is defined via defineTool() but NOT in ALLOWED_TOOLS. ` + + `The LLM cannot use tools that aren't in the gating list. ` + + `Add it to src/agent/tool-gating.ts.`, + ).toBe(true); + }); + + it(`tool "${toolName}" is in availableTools`, () => { + // availableTools is a separate SDK list that controls which tools + // the model can SEE. Without this, the tool exists but is invisible. + const availableToolsMatch = indexSource.match( + /availableTools:\s*\[([\s\S]*?)\]/, + ); + expect( + availableToolsMatch, + "Could not find availableTools array in index.ts", + ).toBeTruthy(); + const availableList = availableToolsMatch![1]; + expect( + availableList.includes(`"${toolName}"`), + `Tool "${toolName}" is defined via defineTool() but NOT in availableTools. ` + + `The model cannot see tools that aren't in availableTools. ` + + `Add "${toolName}" to the availableTools array in buildSessionConfig().`, + ).toBe(true); + }); + } + }); });