diff --git a/prompt-gateway/.gitignore b/prompt-gateway/.gitignore new file mode 100644 index 0000000..24c4607 --- /dev/null +++ b/prompt-gateway/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +bin/agent +bin/prompt-gateway +.prompt-gateway/ +*.db +*.db-wal +*.db-shm diff --git a/prompt-gateway/esbuild.config.mjs b/prompt-gateway/esbuild.config.mjs new file mode 100644 index 0000000..95c0578 --- /dev/null +++ b/prompt-gateway/esbuild.config.mjs @@ -0,0 +1,39 @@ +import * as esbuild from "esbuild"; + +const shared = { + bundle: true, + platform: "node", + target: "node20", + format: "esm", + sourcemap: true, + // better-sqlite3 is a native addon — must stay external + external: ["better-sqlite3"], + banner: { + // ESM needs createRequire for native modules + js: ` +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +const require = createRequire(import.meta.url); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +`.trim(), + }, +}; + +// CLI: agent command +await esbuild.build({ + ...shared, + entryPoints: ["src/bin/agent.ts"], + outfile: "dist/agent.mjs", +}); + +// Daemon: prompt-gateway server +await esbuild.build({ + ...shared, + entryPoints: ["src/index.ts"], + outfile: "dist/prompt-gateway.mjs", +}); + +console.log("✓ Built dist/agent.mjs"); +console.log("✓ Built dist/prompt-gateway.mjs"); diff --git a/prompt-gateway/package-lock.json b/prompt-gateway/package-lock.json new file mode 100644 index 0000000..e79d1c7 --- /dev/null +++ b/prompt-gateway/package-lock.json @@ -0,0 +1,2641 @@ +{ + "name": "prompt-gateway", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "prompt-gateway", + "version": "0.1.0", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "better-sqlite3": "^11.7.0", + "express": "^4.21.0", + "uuid": "^10.0.0", + "zod": "^3.24.0" + }, + "bin": { + "agent": "dist/agent.mjs", + "prompt-gateway": "dist/prompt-gateway.mjs" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.12", + "@types/express": "^5.0.0", + "@types/node": "^22.10.0", + "@types/uuid": "^10.0.0", + "esbuild": "^0.27.5", + "tsx": "^4.19.0", + "typescript": "^5.7.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.5.tgz", + "integrity": "sha512-nGsF/4C7uzUj+Nj/4J+Zt0bYQ6bz33Phz8Lb2N80Mti1HjGclTJdXZ+9APC4kLvONbjxN1zfvYNd8FEcbBK/MQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.5.tgz", + "integrity": "sha512-Cv781jd0Rfj/paoNrul1/r4G0HLvuFKYh7C9uHZ2Pl8YXstzvCyyeWENTFR9qFnRzNMCjXmsulZuvosDg10Mog==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.5.tgz", + "integrity": "sha512-Oeghq+XFgh1pUGd1YKs4DDoxzxkoUkvko+T/IVKwlghKLvvjbGFB3ek8VEDBmNvqhwuL0CQS3cExdzpmUyIrgA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.5.tgz", + "integrity": "sha512-nQD7lspbzerlmtNOxYMFAGmhxgzn8Z7m9jgFkh6kpkjsAhZee1w8tJW3ZlW+N9iRePz0oPUDrYrXidCPSImD0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.5.tgz", + "integrity": "sha512-I+Ya/MgC6rr8oRWGRDF3BXDfP8K1BVUggHqN6VI2lUZLdDi1IM1v2cy0e3lCPbP+pVcK3Tv8cgUhHse1kaNZZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.5.tgz", + "integrity": "sha512-MCjQUtC8wWJn/pIPM7vQaO69BFgwPD1jriEdqwTCKzWjGgkMbcg+M5HzrOhPhuYe1AJjXlHmD142KQf+jnYj8A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.5.tgz", + "integrity": "sha512-X6xVS+goSH0UelYXnuf4GHLwpOdc8rgK/zai+dKzBMnncw7BTQIwquOodE7EKvY2UVUetSqyAfyZC1D+oqLQtg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.5.tgz", + "integrity": "sha512-233X1FGo3a8x1ekLB6XT69LfZ83vqz+9z3TSEQCTYfMNY880A97nr81KbPcAMl9rmOFp11wO0dP+eB18KU/Ucg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.5.tgz", + "integrity": "sha512-0wkVrYHG4sdCCN/bcwQ7yYMXACkaHc3UFeaEOwSVW6e5RycMageYAFv+JS2bKLwHyeKVUvtoVH+5/RHq0fgeFw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.5.tgz", + "integrity": "sha512-euKkilsNOv7x/M1NKsx5znyprbpsRFIzTV6lWziqJch7yWYayfLtZzDxDTl+LSQDJYAjd9TVb/Kt5UKIrj2e4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.5.tgz", + "integrity": "sha512-hVRQX4+P3MS36NxOy24v/Cdsimy/5HYePw+tmPqnNN1fxV0bPrFWR6TMqwXPwoTM2VzbkA+4lbHWUKDd5ZDA/w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.5.tgz", + "integrity": "sha512-mKqqRuOPALI8nDzhOBmIS0INvZOOFGGg5n1osGIXAx8oersceEbKd4t1ACNTHM3sJBXGFAlEgqM+svzjPot+ZQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.5.tgz", + "integrity": "sha512-EE/QXH9IyaAj1qeuIV5+/GZkBTipgGO782Ff7Um3vPS9cvLhJJeATy4Ggxikz2inZ46KByamMn6GqtqyVjhenA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.5.tgz", + "integrity": "sha512-0V2iF1RGxBf1b7/BjurA5jfkl7PtySjom1r6xOK2q9KWw/XCpAdtB6KNMO+9xx69yYfSCRR9FE0TyKfHA2eQMw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.5.tgz", + "integrity": "sha512-rYxThBx6G9HN6tFNuvB/vykeLi4VDsm5hE5pVwzqbAjZEARQrWu3noZSfbEnPZ/CRXP3271GyFk/49up2W190g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.5.tgz", + "integrity": "sha512-uEP2q/4qgd8goEUc4QIdU/1P2NmEtZ/zX5u3OpLlCGhJIuBIv0s0wr7TB2nBrd3/A5XIdEkkS5ZLF0ULuvaaYQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.5.tgz", + "integrity": "sha512-+Gq47Wqq6PLOOZuBzVSII2//9yyHNKZLuwfzCemqexqOQCSz0zy0O26kIzyp9EMNMK+nZ0tFHBZrCeVUuMs/ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.5.tgz", + "integrity": "sha512-3F/5EG8VHfN/I+W5cO1/SV2H9Q/5r7vcHabMnBqhHK2lTWOh3F8vixNzo8lqxrlmBtZVFpW8pmITHnq54+Tq4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.5.tgz", + "integrity": "sha512-28t+Sj3CPN8vkMOlZotOmDgilQwVvxWZl7b8rxpn73Tt/gCnvrHxQUMng4uu3itdFvrtba/1nHejvxqz8xgEMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.5.tgz", + "integrity": "sha512-Doz/hKtiuVAi9hMsBMpwBANhIZc8l238U2Onko3t2xUp8xtM0ZKdDYHMnm/qPFVthY8KtxkXaocwmMh6VolzMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.5.tgz", + "integrity": "sha512-WfGVaa1oz5A7+ZFPkERIbIhKT4olvGl1tyzTRaB5yoZRLqC0KwaO95FeZtOdQj/oKkjW57KcVF944m62/0GYtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.5.tgz", + "integrity": "sha512-Xh+VRuh6OMh3uJ0JkCjI57l+DVe7VRGBYymen8rFPnTVgATBwA6nmToxM2OwTlSvrnWpPKkrQUj93+K9huYC6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.5.tgz", + "integrity": "sha512-aC1gpJkkaUADHuAdQfuVTnqVUTLqqUNhAvEwHwVWcnVVZvNlDPGA0UveZsfXJJ9T6k9Po4eHi3c02gbdwO3g6w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.5.tgz", + "integrity": "sha512-0UNx2aavV0fk6UpZcwXFLztA2r/k9jTUa7OW7SAea1VYUhkug99MW1uZeXEnPn5+cHOd0n8myQay6TlFnBR07w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.5.tgz", + "integrity": "sha512-5nlJ3AeJWCTSzR7AEqVjT/faWyqKU86kCi1lLmxVqmNR+j4HrYdns+eTGjS/vmrzCIe8inGQckUadvS0+JkKdQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.5.tgz", + "integrity": "sha512-PWypQR+d4FLfkhBIV+/kHsUELAnMpx1bRvvsn3p+/sAERbnCzFrtDRG2Xw5n+2zPxBK2+iaP+vetsRl4Ti7WgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.12", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz", + "integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "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==", + "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" + }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "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==", + "license": "MIT", + "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", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.5.tgz", + "integrity": "sha512-zdQoHBjuDqKsvV5OPaWansOwfSQ0Js+Uj9J85TBvj3bFW1JjWTSULMRwdQAc8qMeIScbClxeMK0jlrtB9linhA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.5", + "@esbuild/android-arm": "0.27.5", + "@esbuild/android-arm64": "0.27.5", + "@esbuild/android-x64": "0.27.5", + "@esbuild/darwin-arm64": "0.27.5", + "@esbuild/darwin-x64": "0.27.5", + "@esbuild/freebsd-arm64": "0.27.5", + "@esbuild/freebsd-x64": "0.27.5", + "@esbuild/linux-arm": "0.27.5", + "@esbuild/linux-arm64": "0.27.5", + "@esbuild/linux-ia32": "0.27.5", + "@esbuild/linux-loong64": "0.27.5", + "@esbuild/linux-mips64el": "0.27.5", + "@esbuild/linux-ppc64": "0.27.5", + "@esbuild/linux-riscv64": "0.27.5", + "@esbuild/linux-s390x": "0.27.5", + "@esbuild/linux-x64": "0.27.5", + "@esbuild/netbsd-arm64": "0.27.5", + "@esbuild/netbsd-x64": "0.27.5", + "@esbuild/openbsd-arm64": "0.27.5", + "@esbuild/openbsd-x64": "0.27.5", + "@esbuild/openharmony-arm64": "0.27.5", + "@esbuild/sunos-x64": "0.27.5", + "@esbuild/win32-arm64": "0.27.5", + "@esbuild/win32-ia32": "0.27.5", + "@esbuild/win32-x64": "0.27.5" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "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==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "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==", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.9", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz", + "integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "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.", + "license": "MIT", + "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/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "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==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "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/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "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", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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" + }, + "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==", + "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", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "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==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/prompt-gateway/package.json b/prompt-gateway/package.json new file mode 100644 index 0000000..47f806b --- /dev/null +++ b/prompt-gateway/package.json @@ -0,0 +1,37 @@ +{ + "name": "prompt-gateway", + "version": "0.1.0", + "description": "Prompt compiler and agent gateway — converts raw human intent into structured task contracts via MCP", + "type": "module", + "main": "dist/index.js", + "bin": { + "agent": "dist/agent.mjs", + "prompt-gateway": "dist/prompt-gateway.mjs" + }, + "scripts": { + "build": "node esbuild.config.mjs", + "build:ts": "tsc", + "start": "node dist/prompt-gateway.mjs --http", + "start:mcp": "node dist/prompt-gateway.mjs --mcp", + "agent": "node dist/agent.mjs", + "dev": "tsx src/index.ts --http", + "dev:agent": "tsx src/bin/agent.ts", + "setup": "npm install && npm run build" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "better-sqlite3": "^11.7.0", + "express": "^4.21.0", + "uuid": "^10.0.0", + "zod": "^3.24.0" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.12", + "@types/express": "^5.0.0", + "@types/node": "^22.10.0", + "@types/uuid": "^10.0.0", + "esbuild": "^0.27.5", + "tsx": "^4.19.0", + "typescript": "^5.7.0" + } +} diff --git a/prompt-gateway/setup.bat b/prompt-gateway/setup.bat new file mode 100644 index 0000000..96c5241 --- /dev/null +++ b/prompt-gateway/setup.bat @@ -0,0 +1,6 @@ +@echo off +REM One-click installer — double-click this file or run from cmd. +REM Launches setup.ps1 with execution policy bypass. +cd /d "%~dp0" +powershell -ExecutionPolicy Bypass -File "%~dp0setup.ps1" +if %ERRORLEVEL% NEQ 0 pause diff --git a/prompt-gateway/setup.ps1 b/prompt-gateway/setup.ps1 new file mode 100644 index 0000000..b2f0b66 --- /dev/null +++ b/prompt-gateway/setup.ps1 @@ -0,0 +1,273 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + prompt-gateway setup for Windows. + Installs, builds, auto-configures all MCP clients, adds to PATH. + +.DESCRIPTION + Run directly: .\setup.ps1 + Or double-click: setup.bat +#> + +$ErrorActionPreference = "Stop" +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +Set-Location $ScriptDir + +$GatewayBin = Join-Path $ScriptDir "dist\prompt-gateway.mjs" +$AgentBin = Join-Path $ScriptDir "dist\agent.mjs" +$BinDir = Join-Path $ScriptDir "bin" + +Write-Host "" +Write-Host "================================================" -ForegroundColor Cyan +Write-Host " prompt-gateway setup (Windows)" -ForegroundColor Cyan +Write-Host "================================================" -ForegroundColor Cyan +Write-Host "" + +# ── Check Node ────────────────────────────────────── +$nodeCmd = Get-Command node -ErrorAction SilentlyContinue +if (-not $nodeCmd) { + Write-Host "[X] Node.js not found. Install Node 20+ first:" -ForegroundColor Red + Write-Host " https://nodejs.org/" -ForegroundColor Yellow + Write-Host "" + Read-Host "Press Enter to exit" + exit 1 +} +$NodeExe = $nodeCmd.Source +$nodeVer = (node -v) -replace '^v', '' +$nodeMajor = [int]($nodeVer.Split('.')[0]) +if ($nodeMajor -lt 20) { + Write-Host "[X] Node $nodeVer found, need 20+." -ForegroundColor Red + exit 1 +} +Write-Host "[OK] Node v$nodeVer" -ForegroundColor Green + +# ── Install deps ──────────────────────────────────── +Write-Host " Installing dependencies..." +$npmOutput = npm install 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Host "[X] npm install failed:" -ForegroundColor Red + Write-Host $npmOutput + exit 1 +} +Write-Host "[OK] Dependencies installed" -ForegroundColor Green + +# ── Build ─────────────────────────────────────────── +Write-Host " Building bundles..." +node esbuild.config.mjs +if ($LASTEXITCODE -ne 0) { + Write-Host "[X] Build failed" -ForegroundColor Red + exit 1 +} +Write-Host "[OK] Built" -ForegroundColor Green + +# ── CLI shims ─────────────────────────────────────── +if (-not (Test-Path $BinDir)) { New-Item -ItemType Directory -Path $BinDir | Out-Null } + +# .cmd for cmd.exe and Windows Terminal +@" +@echo off +"$NodeExe" "$AgentBin" %* +"@ | Set-Content (Join-Path $BinDir "agent.cmd") -Encoding ASCII + +@" +@echo off +"$NodeExe" "$GatewayBin" %* +"@ | Set-Content (Join-Path $BinDir "prompt-gateway.cmd") -Encoding ASCII + +# .ps1 for PowerShell terminals +@" +#!/usr/bin/env pwsh +& "$NodeExe" "$AgentBin" @args +"@ | Set-Content (Join-Path $BinDir "agent.ps1") -Encoding UTF8 + +@" +#!/usr/bin/env pwsh +& "$NodeExe" "$GatewayBin" @args +"@ | Set-Content (Join-Path $BinDir "prompt-gateway.ps1") -Encoding UTF8 + +Write-Host "[OK] CLI shims created (cmd + PowerShell)" -ForegroundColor Green + +# ── MCP config helper ────────────────────────────── +# Windows paths have backslashes that must be escaped in JSON. +# Use forward slashes which Node.js handles fine on Windows. +$GatewayBinJson = $GatewayBin -replace '\\', '/' +$NodeExeJson = $NodeExe -replace '\\', '/' + +function Set-McpConfig { + param( + [string]$FilePath, + [string]$Label, + [string]$WrapperKey = "mcpServers" + ) + + $dir = Split-Path -Parent $FilePath + if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } + + # Read or create + if (Test-Path $FilePath) { + try { + $raw = Get-Content $FilePath -Raw -ErrorAction Stop + if ([string]::IsNullOrWhiteSpace($raw)) { $raw = '{}' } + $cfg = $raw | ConvertFrom-Json + } catch { + Write-Host " [!] $Label — could not parse $(Split-Path -Leaf $FilePath), skipping" -ForegroundColor Yellow + return + } + } else { + $cfg = [PSCustomObject]@{} + } + + # Ensure wrapper key + if (-not ($cfg.PSObject.Properties.Name -contains $WrapperKey)) { + $cfg | Add-Member -NotePropertyName $WrapperKey -NotePropertyValue ([PSCustomObject]@{}) + } + + # Already there? + if ($cfg.$WrapperKey.PSObject.Properties.Name -contains "prompt-gateway") { + Write-Host "[OK] $Label — already configured" -ForegroundColor Green + return + } + + # Add entry with forward-slash paths + $entry = [PSCustomObject]@{ + command = $NodeExeJson + args = @($GatewayBinJson, "--mcp") + } + $cfg.$WrapperKey | Add-Member -NotePropertyName "prompt-gateway" -NotePropertyValue $entry + $cfg | ConvertTo-Json -Depth 10 | Set-Content $FilePath -Encoding UTF8 + Write-Host "[OK] $Label — configured" -ForegroundColor Green +} + +Write-Host "" +Write-Host " Configuring MCP clients..." -ForegroundColor Cyan + +$configured = 0 + +# ── Cursor ───────────────────────────────────────── +$cursorConfig = Join-Path $env:USERPROFILE ".cursor\mcp.json" +Set-McpConfig -FilePath $cursorConfig -Label "Cursor" +$configured++ + +# ── Claude Desktop ───────────────────────────────── +$claudeDesktop = Join-Path $env:APPDATA "Claude\claude_desktop_config.json" +Set-McpConfig -FilePath $claudeDesktop -Label "Claude Desktop" +$configured++ + +# ── Claude Code ──────────────────────────────────── +$claudeCode = Join-Path $env:USERPROFILE ".claude\settings.json" +Set-McpConfig -FilePath $claudeCode -Label "Claude Code" +$configured++ + +# ── VS Code ─────────────────────────────────────── +$vscodeDir = Join-Path $env:APPDATA "Code\User" +if (Test-Path $vscodeDir) { + $vscodePath = Join-Path $vscodeDir "settings.json" + if (-not (Test-Path $vscodePath)) { + '{}' | Set-Content $vscodePath -Encoding UTF8 + } + + try { + $vsCfg = Get-Content $vscodePath -Raw | ConvertFrom-Json + $vsKey = "mcp.servers" + + if (-not ($vsCfg.PSObject.Properties.Name -contains $vsKey)) { + $vsCfg | Add-Member -NotePropertyName $vsKey -NotePropertyValue ([PSCustomObject]@{}) + } + + if (-not ($vsCfg.$vsKey.PSObject.Properties.Name -contains "prompt-gateway")) { + $vsCfg.$vsKey | Add-Member -NotePropertyName "prompt-gateway" -NotePropertyValue ([PSCustomObject]@{ + command = $NodeExeJson + args = @($GatewayBinJson, "--mcp") + }) + $vsCfg | ConvertTo-Json -Depth 10 | Set-Content $vscodePath -Encoding UTF8 + Write-Host "[OK] VS Code — configured" -ForegroundColor Green + } else { + Write-Host "[OK] VS Code — already configured" -ForegroundColor Green + } + $configured++ + } catch { + Write-Host " [!] VS Code — could not parse settings.json, skipping" -ForegroundColor Yellow + } +} + +# ── Windsurf ─────────────────────────────────────── +$windsurfDir = Join-Path $env:USERPROFILE ".windsurf" +if (Test-Path $windsurfDir) { + $windsurfConfig = Join-Path $windsurfDir "mcp.json" + Set-McpConfig -FilePath $windsurfConfig -Label "Windsurf" + $configured++ +} + +# ── JetBrains IDEs ───────────────────────────────── +# JetBrains stores MCP config per-project or globally. +# We write to the global location if any JetBrains IDE is detected. +$jbConfigDir = Join-Path $env:APPDATA "JetBrains" +if (Test-Path $jbConfigDir) { + # Find the most recent IDE config dir + $latestIde = Get-ChildItem $jbConfigDir -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + if ($latestIde) { + $jbMcpDir = Join-Path $latestIde.FullName "options" + $jbMcpFile = Join-Path $jbMcpDir "mcp.json" + Set-McpConfig -FilePath $jbMcpFile -Label "JetBrains ($($latestIde.Name))" + $configured++ + } +} + +# ── PATH ─────────────────────────────────────────── +Write-Host "" +$userPath = [Environment]::GetEnvironmentVariable("Path", "User") +if (-not $userPath) { $userPath = "" } + +if ($userPath -notlike "*$BinDir*") { + [Environment]::SetEnvironmentVariable("Path", "$BinDir;$userPath", "User") + $env:Path = "$BinDir;$env:Path" + Write-Host "[OK] Added to user PATH" -ForegroundColor Green + $pathChanged = $true +} else { + Write-Host "[OK] Already in PATH" -ForegroundColor Green + $pathChanged = $false +} + +# ── Quick test ───────────────────────────────────── +Write-Host "" +Write-Host " Running quick test..." -ForegroundColor Cyan +$testOutput = & $NodeExe $AgentBin --json "test" 2>&1 +$testParsed = $testOutput | ConvertFrom-Json -ErrorAction SilentlyContinue +if ($testParsed.contract.request_id) { + Write-Host "[OK] Gateway works" -ForegroundColor Green +} else { + Write-Host " [!] Quick test returned unexpected output" -ForegroundColor Yellow +} + +# ── Done ─────────────────────────────────────────── +Write-Host "" +Write-Host "================================================" -ForegroundColor Cyan +Write-Host " All done! $configured MCP clients configured." -ForegroundColor Cyan +Write-Host "================================================" -ForegroundColor Cyan +Write-Host "" +Write-Host " Usage:" -ForegroundColor White +Write-Host "" +Write-Host ' agent "fix the auth race and keep changes minimal"' -ForegroundColor Yellow +Write-Host ' agent --json "add a health endpoint"' -ForegroundColor Yellow +Write-Host ' prompt-gateway --http' -ForegroundColor Yellow +Write-Host "" + +if ($pathChanged) { + Write-Host " >> Open a NEW terminal for PATH changes. <<" -ForegroundColor Magenta + Write-Host "" +} + +Write-Host " MCP clients will see prompt-gateway tools on next restart:" -ForegroundColor White +Write-Host " Cursor, Claude Desktop, Claude Code, VS Code" -ForegroundColor Gray +if (Test-Path (Join-Path $env:USERPROFILE ".windsurf")) { + Write-Host " Windsurf" -ForegroundColor Gray +} +if (Test-Path (Join-Path $env:APPDATA "JetBrains")) { + Write-Host " JetBrains" -ForegroundColor Gray +} +Write-Host "" + +# Keep window open if launched via double-click +if ($Host.Name -eq 'ConsoleHost' -and [Environment]::GetCommandLineArgs().Count -le 1) { + Read-Host "Press Enter to close" +} diff --git a/prompt-gateway/setup.sh b/prompt-gateway/setup.sh new file mode 100755 index 0000000..d19aac2 --- /dev/null +++ b/prompt-gateway/setup.sh @@ -0,0 +1,272 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +GATEWAY_BIN="$SCRIPT_DIR/dist/prompt-gateway.mjs" +AGENT_BIN="$SCRIPT_DIR/dist/agent.mjs" + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " prompt-gateway setup" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo + +# ── Check node ────────────────────────────────────── +if ! command -v node &>/dev/null; then + echo "✗ Node.js not found. Install Node 20+ first." + exit 1 +fi + +NODE_VERSION=$(node -v | sed 's/v//' | cut -d. -f1) +if [ "$NODE_VERSION" -lt 20 ]; then + echo "✗ Node $NODE_VERSION found, need 20+." + exit 1 +fi +echo "✓ Node $(node -v)" + +# ── Install deps ──────────────────────────────────── +echo " Installing dependencies..." +npm install --silent 2>/dev/null +echo "✓ Dependencies installed" + +# ── Build ─────────────────────────────────────────── +echo " Building bundles..." +node esbuild.config.mjs +echo "✓ Built" + +# ── CLI shims ─────────────────────────────────────── +mkdir -p "$SCRIPT_DIR/bin" + +cat > "$SCRIPT_DIR/bin/agent" << SHIM +#!/usr/bin/env bash +exec node "$AGENT_BIN" "\$@" +SHIM +chmod +x "$SCRIPT_DIR/bin/agent" + +cat > "$SCRIPT_DIR/bin/prompt-gateway" << SHIM +#!/usr/bin/env bash +exec node "$GATEWAY_BIN" "\$@" +SHIM +chmod +x "$SCRIPT_DIR/bin/prompt-gateway" + +echo "✓ CLI shims created" + +# ── Auto-configure MCP clients ───────────────────── + +NODE_BIN="$(which node)" +MCP_ENTRY='{"command":"'"$NODE_BIN"'","args":["'"$GATEWAY_BIN"'","--mcp"]}' + +# Helper: merge prompt-gateway into an MCP config JSON file. +# Creates the file if it doesn't exist. Preserves existing servers. +configure_mcp_file() { + local file="$1" + local label="$2" + local wrapper_key="${3:-}" # optional top-level key (e.g. "mcpServers" vs nested) + + mkdir -p "$(dirname "$file")" + + if [ ! -f "$file" ]; then + # Create fresh + if [ -n "$wrapper_key" ]; then + echo "{\"$wrapper_key\":{\"prompt-gateway\":$MCP_ENTRY}}" | node -e " + process.stdin.setEncoding('utf8'); + let d=''; process.stdin.on('data',c=>d+=c); + process.stdin.on('end',()=>process.stdout.write(JSON.stringify(JSON.parse(d),null,2)+'\n')); + " > "$file" + else + echo "{\"prompt-gateway\":$MCP_ENTRY}" | node -e " + process.stdin.setEncoding('utf8'); + let d=''; process.stdin.on('data',c=>d+=c); + process.stdin.on('end',()=>process.stdout.write(JSON.stringify(JSON.parse(d),null,2)+'\n')); + " > "$file" + fi + echo "✓ $label — created $file" + return + fi + + # File exists — merge without clobbering + local tmp="${file}.tmp.$$" + local merge_result + merge_result=$(node -e " + const fs = require('fs'); + const cfg = JSON.parse(fs.readFileSync('$file','utf8')); + const entry = $MCP_ENTRY; + const key = '$wrapper_key'; + if (key) { + if (!cfg[key]) cfg[key] = {}; + if (cfg[key]['prompt-gateway']) { process.exit(0); } + cfg[key]['prompt-gateway'] = entry; + } else { + if (cfg['prompt-gateway']) { process.exit(0); } + cfg['prompt-gateway'] = entry; + } + fs.writeFileSync('$tmp', JSON.stringify(cfg, null, 2) + '\n'); + process.stdout.write('wrote'); + " 2>/dev/null) || true + + if [ -f "$tmp" ]; then + mv "$tmp" "$file" + echo "✓ $label — updated $file" + else + echo "✓ $label — already configured" + fi +} + +echo +echo " Configuring MCP clients..." + +CONFIGURED=0 + +# ── Cursor ────────────────────────────────────────── +# Global: ~/.cursor/mcp.json +CURSOR_GLOBAL="$HOME/.cursor/mcp.json" +configure_mcp_file "$CURSOR_GLOBAL" "Cursor (global)" "mcpServers" +CONFIGURED=$((CONFIGURED + 1)) + +# ── Claude Desktop ────────────────────────────────── +if [ "$(uname)" = "Darwin" ]; then + CLAUDE_DESKTOP="$HOME/Library/Application Support/Claude/claude_desktop_config.json" +else + # Linux (XDG) + CLAUDE_DESKTOP="${XDG_CONFIG_HOME:-$HOME/.config}/Claude/claude_desktop_config.json" +fi +configure_mcp_file "$CLAUDE_DESKTOP" "Claude Desktop" "mcpServers" +CONFIGURED=$((CONFIGURED + 1)) + +# ── Claude Code ───────────────────────────────────── +CLAUDE_CODE="$HOME/.claude/settings.json" +if [ -f "$CLAUDE_CODE" ]; then + RESULT=$(node -e " + const fs = require('fs'); + const cfg = JSON.parse(fs.readFileSync('$CLAUDE_CODE','utf8')); + if (!cfg.mcpServers) cfg.mcpServers = {}; + if (cfg.mcpServers['prompt-gateway']) { process.exit(0); } + cfg.mcpServers['prompt-gateway'] = $MCP_ENTRY; + fs.writeFileSync('$CLAUDE_CODE', JSON.stringify(cfg, null, 2) + '\n'); + process.stdout.write('wrote'); + " 2>/dev/null) + if [ "$RESULT" = "wrote" ]; then + echo "✓ Claude Code — updated $CLAUDE_CODE" + else + echo "✓ Claude Code — already configured" + fi + CONFIGURED=$((CONFIGURED + 1)) +else + configure_mcp_file "$CLAUDE_CODE" "Claude Code" "mcpServers" + CONFIGURED=$((CONFIGURED + 1)) +fi + +# ── VS Code (Copilot MCP) ────────────────────────── +VSCODE_SETTINGS="$HOME/.vscode/settings.json" +if [ -d "$HOME/.vscode" ] || command -v code &>/dev/null; then + if [ ! -f "$VSCODE_SETTINGS" ]; then + mkdir -p "$HOME/.vscode" + echo '{}' > "$VSCODE_SETTINGS" + fi + VS_RESULT=$(node -e " + const fs = require('fs'); + const cfg = JSON.parse(fs.readFileSync('$VSCODE_SETTINGS','utf8')); + const key = 'mcp.servers'; + if (!cfg[key]) cfg[key] = {}; + if (cfg[key]['prompt-gateway']) { process.exit(0); } + cfg[key]['prompt-gateway'] = { command: '$NODE_BIN', args: ['$GATEWAY_BIN', '--mcp'] }; + fs.writeFileSync('$VSCODE_SETTINGS', JSON.stringify(cfg, null, 2) + '\n'); + process.stdout.write('wrote'); + " 2>/dev/null) + if [ "$VS_RESULT" = "wrote" ]; then + echo "✓ VS Code — updated $VSCODE_SETTINGS" + else + echo "✓ VS Code — already configured" + fi + CONFIGURED=$((CONFIGURED + 1)) +fi + +# ── Windsurf ──────────────────────────────────────── +if [ -d "$HOME/.windsurf" ]; then + WINDSURF_CONFIG="$HOME/.windsurf/mcp.json" + configure_mcp_file "$WINDSURF_CONFIG" "Windsurf" "mcpServers" + CONFIGURED=$((CONFIGURED + 1)) +fi + +# ── JetBrains IDEs ───────────────────────────────── +JB_CONFIG_BASE="" +if [ "$(uname)" = "Darwin" ]; then + JB_CONFIG_BASE="$HOME/Library/Application Support/JetBrains" +else + JB_CONFIG_BASE="${XDG_CONFIG_HOME:-$HOME/.config}/JetBrains" +fi +if [ -d "$JB_CONFIG_BASE" ]; then + # Find the most recent IDE config + JB_LATEST=$(ls -dt "$JB_CONFIG_BASE"/*/ 2>/dev/null | head -1) + if [ -n "$JB_LATEST" ]; then + JB_MCP_FILE="$JB_LATEST/options/mcp.json" + JB_NAME=$(basename "$JB_LATEST") + configure_mcp_file "$JB_MCP_FILE" "JetBrains ($JB_NAME)" "mcpServers" + CONFIGURED=$((CONFIGURED + 1)) + fi +fi + +# ── PATH setup ────────────────────────────────────── +echo +AGENT_PATH="$SCRIPT_DIR/bin" +SHELL_RC="" +PATH_ALREADY=0 + +if echo "$PATH" | tr ':' '\n' | grep -qx "$AGENT_PATH"; then + PATH_ALREADY=1 +fi + +if [ "$PATH_ALREADY" -eq 0 ]; then + # Detect shell config file + if [ -n "${ZSH_VERSION:-}" ] || [ "$(basename "${SHELL:-}")" = "zsh" ]; then + SHELL_RC="$HOME/.zshrc" + elif [ -f "$HOME/.bashrc" ]; then + SHELL_RC="$HOME/.bashrc" + elif [ -f "$HOME/.bash_profile" ]; then + SHELL_RC="$HOME/.bash_profile" + elif [ -f "$HOME/.profile" ]; then + SHELL_RC="$HOME/.profile" + fi + + if [ -n "$SHELL_RC" ]; then + MARKER="# prompt-gateway PATH" + if ! grep -q "$MARKER" "$SHELL_RC" 2>/dev/null; then + echo "" >> "$SHELL_RC" + echo "$MARKER" >> "$SHELL_RC" + echo "export PATH=\"$AGENT_PATH:\$PATH\"" >> "$SHELL_RC" + echo "✓ Added to PATH in $SHELL_RC" + else + echo "✓ PATH already in $SHELL_RC" + fi + fi +fi + +# ── Quick test ────────────────────────────────────── +echo " Running quick test..." +TEST_OUTPUT=$(node "$SCRIPT_DIR/dist/agent.mjs" --json "test" 2>/dev/null) +if echo "$TEST_OUTPUT" | node -e "const d=require('fs').readFileSync(0,'utf8');const j=JSON.parse(d);process.exit(j.contract?.request_id?0:1)" 2>/dev/null; then + echo "✓ Gateway works" +else + echo "⚠ Quick test returned unexpected output" +fi + +# ── Done ──────────────────────────────────────────── +echo +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " All done! $CONFIGURED MCP clients configured." +echo +echo " Usage:" +echo +echo " agent \"fix the auth race and keep changes minimal\"" +echo " agent --json \"add a health endpoint\"" +echo " prompt-gateway --http" +echo +if [ "$PATH_ALREADY" -eq 0 ] && [ -n "$SHELL_RC" ]; then + echo " Restart your shell or run:" + echo " source $SHELL_RC" + echo +fi +echo " Your MCP clients (Cursor, Claude, VS Code) will" +echo " see prompt-gateway tools on next restart." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/prompt-gateway/src/adapters/cli.ts b/prompt-gateway/src/adapters/cli.ts new file mode 100644 index 0000000..44df1ae --- /dev/null +++ b/prompt-gateway/src/adapters/cli.ts @@ -0,0 +1,36 @@ +import * as child_process from "node:child_process"; +import type { InputAdapter, CompileRequest, Surface } from "../types.js"; + +// --------------------------------------------------------------------------- +// CLI adapter — captures cwd, git state, env profile, and wraps raw input +// into a CompileRequest. +// --------------------------------------------------------------------------- + +export class CliAdapter implements InputAdapter { + readonly surface: Surface = "cli"; + + parse(raw: unknown): CompileRequest { + const input = typeof raw === "string" ? raw : String(raw); + + return { + raw_input: input, + surface: "cli", + cwd: process.cwd(), + git_ref: getGitRef(), + env_profile: process.env.NODE_ENV ?? "development", + }; + } +} + +function getGitRef(): string | undefined { + try { + return child_process + .execSync("git rev-parse --short HEAD 2>/dev/null", { + encoding: "utf-8", + timeout: 3000, + }) + .trim() || undefined; + } catch { + return undefined; + } +} diff --git a/prompt-gateway/src/adapters/cursor.ts b/prompt-gateway/src/adapters/cursor.ts new file mode 100644 index 0000000..4bfa18c --- /dev/null +++ b/prompt-gateway/src/adapters/cursor.ts @@ -0,0 +1,42 @@ +import type { InputAdapter, CompileRequest, Surface } from "../types.js"; + +// --------------------------------------------------------------------------- +// Cursor / IDE adapter — parses requests from Cursor's MCP client, +// extracting workspace info and IDE-specific metadata. +// +// In Cursor, this adapter is consumed indirectly: Cursor connects to the +// MCP server, and the MCP tools handle the compilation. This adapter is +// for when the gateway receives HTTP requests from a Cursor extension. +// --------------------------------------------------------------------------- + +export interface CursorPayload { + prompt: string; + workspace_root?: string; + active_file?: string; + selected_text?: string; + git_ref?: string; + session_id?: string; +} + +export class CursorAdapter implements InputAdapter { + readonly surface: Surface = "cursor"; + + parse(raw: unknown): CompileRequest { + const payload = raw as CursorPayload; + + // Build raw input from available context + let input = payload.prompt ?? String(raw); + if (payload.selected_text) { + input += `\n\n[Selected code]:\n${payload.selected_text}`; + } + + return { + raw_input: input, + surface: "cursor", + cwd: payload.workspace_root, + git_ref: payload.git_ref, + session_id: payload.session_id, + files: payload.active_file ? [payload.active_file] : undefined, + }; + } +} diff --git a/prompt-gateway/src/adapters/desktop.ts b/prompt-gateway/src/adapters/desktop.ts new file mode 100644 index 0000000..42611f9 --- /dev/null +++ b/prompt-gateway/src/adapters/desktop.ts @@ -0,0 +1,35 @@ +import type { InputAdapter, CompileRequest, Surface } from "../types.js"; + +// --------------------------------------------------------------------------- +// Desktop app adapter — thin transport layer for the desktop UI surface. +// +// The desktop app is a UI/approval surface over the same gateway. It should +// NOT have its own prompt logic — just transport, identity, and session. +// --------------------------------------------------------------------------- + +export interface DesktopPayload { + prompt: string; + session_id?: string; + workspace_root?: string; + user_id?: string; + preferences?: { + verbosity?: "low" | "medium" | "high"; + autonomy?: "suggest" | "confirm" | "auto"; + }; +} + +export class DesktopAdapter implements InputAdapter { + readonly surface: Surface = "desktop"; + + parse(raw: unknown): CompileRequest { + const payload = raw as DesktopPayload; + + return { + raw_input: payload.prompt ?? String(raw), + surface: "desktop", + cwd: payload.workspace_root, + session_id: payload.session_id, + preferences: payload.preferences, + }; + } +} diff --git a/prompt-gateway/src/adapters/index.ts b/prompt-gateway/src/adapters/index.ts new file mode 100644 index 0000000..226c0a4 --- /dev/null +++ b/prompt-gateway/src/adapters/index.ts @@ -0,0 +1,22 @@ +export { CliAdapter } from "./cli.js"; +export { CursorAdapter } from "./cursor.js"; +export { DesktopAdapter } from "./desktop.js"; +export { LocalAgentAdapter } from "./local-agent.js"; + +import type { InputAdapter, Surface } from "../types.js"; +import { CliAdapter } from "./cli.js"; +import { CursorAdapter } from "./cursor.js"; +import { DesktopAdapter } from "./desktop.js"; +import { LocalAgentAdapter } from "./local-agent.js"; + +const ADAPTERS: Record = { + cli: new CliAdapter(), + cursor: new CursorAdapter(), + desktop: new DesktopAdapter(), + "local-agent": new LocalAgentAdapter(), + api: new CliAdapter(), // API uses same parsing as CLI +}; + +export function getAdapter(surface: Surface): InputAdapter { + return ADAPTERS[surface]; +} diff --git a/prompt-gateway/src/adapters/local-agent.ts b/prompt-gateway/src/adapters/local-agent.ts new file mode 100644 index 0000000..558de99 --- /dev/null +++ b/prompt-gateway/src/adapters/local-agent.ts @@ -0,0 +1,38 @@ +import type { InputAdapter, CompileRequest, Surface } from "../types.js"; + +// --------------------------------------------------------------------------- +// Local agent adapter — for local agents that consume the same contract +// and MCP tool layer. Agents should NOT invent their own operating +// instructions — they consume the canonical contract. +// --------------------------------------------------------------------------- + +export interface LocalAgentPayload { + prompt: string; + agent_id: string; + agent_type?: string; + session_id?: string; + cwd?: string; + git_ref?: string; + capabilities?: string[]; + preferences?: { + verbosity?: "low" | "medium" | "high"; + autonomy?: "suggest" | "confirm" | "auto"; + }; +} + +export class LocalAgentAdapter implements InputAdapter { + readonly surface: Surface = "local-agent"; + + parse(raw: unknown): CompileRequest { + const payload = raw as LocalAgentPayload; + + return { + raw_input: payload.prompt ?? String(raw), + surface: "local-agent", + cwd: payload.cwd, + git_ref: payload.git_ref, + session_id: payload.session_id, + preferences: payload.preferences, + }; + } +} diff --git a/prompt-gateway/src/bin/agent.ts b/prompt-gateway/src/bin/agent.ts new file mode 100644 index 0000000..21ef7b6 --- /dev/null +++ b/prompt-gateway/src/bin/agent.ts @@ -0,0 +1,149 @@ +#!/usr/bin/env node + +import { CliAdapter } from "../adapters/cli.js"; +import { compileContract } from "../compiler/index.js"; +import { PolicyEngine } from "../policy/engine.js"; +import { route, checkCapabilities } from "../router/index.js"; + +// --------------------------------------------------------------------------- +// CLI entry point — the "agent" command +// +// Usage: +// agent "fix the auth race and keep changes minimal" +// agent --json "add a health check endpoint" +// echo "refactor the router" | agent +// +// Captures cwd, git state, env, compiles to contract, evaluates policy, +// routes, and outputs the result. +// --------------------------------------------------------------------------- + +async function main() { + const args = process.argv.slice(2); + const jsonOutput = args.includes("--json"); + const filteredArgs = args.filter((a) => !a.startsWith("--")); + + // Get input from args or stdin + let input: string; + if (filteredArgs.length > 0) { + input = filteredArgs.join(" "); + } else if (!process.stdin.isTTY) { + input = await readStdin(); + } else { + console.error("Usage: agent "); + console.error(' agent "fix the auth race and keep changes minimal"'); + console.error(' agent --json "add a health check endpoint"'); + console.error(' echo "refactor the router" | agent'); + process.exit(1); + } + + // Adapt + const adapter = new CliAdapter(); + const req = adapter.parse(input); + + // Compile + const contract = compileContract(req); + + // Evaluate policy + const policyEngine = new PolicyEngine(); + const evaluation = policyEngine.evaluate(contract); + + // Route + const target = route(contract); + const caps = checkCapabilities(target.surface, contract.tools.allow); + + if (jsonOutput) { + // Full JSON output + console.log( + JSON.stringify( + { contract, policy: evaluation, routing: { target, capabilities: caps } }, + null, + 2 + ) + ); + } else { + // Human-readable output + console.log("━".repeat(60)); + console.log(" PROMPT GATEWAY — Task Contract"); + console.log("━".repeat(60)); + console.log(); + console.log(` Request: ${contract.request_id}`); + console.log(` Task type: ${contract.intent.task_type}`); + console.log(` Intent: ${contract.intent.primary}`); + console.log(` Objective: ${contract.objective}`); + console.log(` Risk: ${contract.risk.level}${contract.risk.approval_required ? " (approval required)" : ""}`); + console.log(` Exec mode: ${contract.execution.mode}`); + console.log(` Output: ${contract.output.type}`); + console.log(` Timeout: ${contract.execution.timeout_sec}s`); + console.log(); + + if (contract.constraints.length > 0) { + console.log(" Constraints:"); + for (const c of contract.constraints) { + console.log(` • ${c}`); + } + console.log(); + } + + if (contract.assumptions.length > 0) { + console.log(" Assumptions:"); + for (const a of contract.assumptions) { + console.log(` • ${a}`); + } + console.log(); + } + + console.log(" Plan:"); + for (let i = 0; i < contract.plan.steps.length; i++) { + console.log(` ${i + 1}. ${contract.plan.steps[i]}`); + } + console.log(); + + console.log(` Tools allowed: ${contract.tools.allow.join(", ")}`); + console.log(` Tools denied: ${contract.tools.deny.join(", ")}`); + console.log(); + + console.log(" Routing:"); + console.log(` Target: ${target.surface}`); + console.log(` Reason: ${target.reason}`); + if (target.fallback) { + console.log(` Fallback: ${target.fallback}`); + } + if (caps.missing.length > 0) { + console.log(` ⚠ Missing: ${caps.missing.join(", ")}`); + } + console.log(); + + console.log(" Policy:"); + console.log(` Allowed: ${evaluation.allowed ? "yes" : "NO"}`); + console.log(` Approval: ${evaluation.approval_required ? "REQUIRED" : "not required"}`); + console.log(` Summary: ${evaluation.summary}`); + console.log(); + + console.log(" Validation checks:"); + for (const check of contract.validation.checks) { + console.log(` • ${check}`); + } + console.log(); + + console.log(" Success criteria:"); + for (const sc of contract.validation.success_criteria) { + console.log(` ✓ ${sc}`); + } + console.log(); + console.log("━".repeat(60)); + } +} + +function readStdin(): Promise { + return new Promise((resolve) => { + let data = ""; + process.stdin.setEncoding("utf-8"); + process.stdin.on("data", (chunk) => (data += chunk)); + process.stdin.on("end", () => resolve(data.trim())); + }); +} + +main().catch((err) => { + console.error("Error:", err); + process.exit(1); +}); diff --git a/prompt-gateway/src/compiler/classifier.ts b/prompt-gateway/src/compiler/classifier.ts new file mode 100644 index 0000000..0f1ceab --- /dev/null +++ b/prompt-gateway/src/compiler/classifier.ts @@ -0,0 +1,130 @@ +import type { TaskType } from "../types.js"; + +// --------------------------------------------------------------------------- +// Intent classifier — maps raw user input to a TaskType + primary intent. +// +// This is a rule-based classifier. In production you'd wire this to an LLM +// call, but the contract output is the same regardless of the backend. +// --------------------------------------------------------------------------- + +export interface Classification { + task_type: TaskType; + primary_intent: string; + confidence: number; +} + +interface PatternRule { + pattern: RegExp; + task_type: TaskType; + intent_template: string; +} + +const RULES: PatternRule[] = [ + // Code editing + { + pattern: /\b(add|create|implement|write|build)\b.*\b(function|method|class|component|feature|endpoint|api|route|module)\b/i, + task_type: "code_edit", + intent_template: "Create new code", + }, + { + pattern: /\b(change|modify|update|edit|replace|rename|move)\b/i, + task_type: "code_edit", + intent_template: "Modify existing code", + }, + + // Debugging + { + pattern: /\b(fix|bug|error|crash|broken|issue|fail|exception|wrong|not working)\b/i, + task_type: "debug", + intent_template: "Fix a bug or error", + }, + + // Refactoring + { + pattern: /\b(refactor|restructure|reorganize|clean\s*up|simplify|extract|dedup|split)\b/i, + task_type: "refactor", + intent_template: "Refactor code", + }, + + // Testing + { + pattern: /\b(test|spec|coverage|assert|expect|mock|stub|unit test|integration test)\b/i, + task_type: "test", + intent_template: "Write or run tests", + }, + + // Code review + { + pattern: /\b(review|audit|check|inspect|look at|assess|evaluate)\b.*\b(code|pr|pull request|changes|diff)\b/i, + task_type: "code_review", + intent_template: "Review code", + }, + + // Shell + { + pattern: /\b(run|execute|shell|command|terminal|bash|script)\b/i, + task_type: "shell", + intent_template: "Execute shell command", + }, + + // Deploy + { + pattern: /\b(deploy|release|publish|ship|push to prod|rollout)\b/i, + task_type: "deploy", + intent_template: "Deploy or release", + }, + + // Documentation + { + pattern: /\b(doc|document|readme|jsdoc|comment|explain|describe)\b/i, + task_type: "docs", + intent_template: "Write documentation", + }, + + // Architecture / design + { + pattern: /\b(architect|design|spec|specification|plan|blueprint|proposal|rfc)\b/i, + task_type: "design_spec", + intent_template: "Create design specification", + }, + { + pattern: /\b(agent|gateway|pipeline|workflow|system|infrastructure)\b.*\b(architect|design|build|create)\b/i, + task_type: "agent_architecture", + intent_template: "Design agent architecture", + }, + + // Query / question + { + pattern: /\b(what|how|why|where|when|who|which|can you|tell me|explain)\b/i, + task_type: "query", + intent_template: "Answer a question", + }, +]; + +export function classify(input: string): Classification { + const trimmed = input.trim(); + if (!trimmed) { + return { + task_type: "unknown", + primary_intent: "Unable to classify empty input", + confidence: 0, + }; + } + + // Score each rule — first match wins, but we accumulate confidence + for (const rule of RULES) { + if (rule.pattern.test(trimmed)) { + return { + task_type: rule.task_type, + primary_intent: rule.intent_template, + confidence: 0.8, + }; + } + } + + return { + task_type: "unknown", + primary_intent: "Unclassified task", + confidence: 0.3, + }; +} diff --git a/prompt-gateway/src/compiler/constraint-extractor.ts b/prompt-gateway/src/compiler/constraint-extractor.ts new file mode 100644 index 0000000..74e033a --- /dev/null +++ b/prompt-gateway/src/compiler/constraint-extractor.ts @@ -0,0 +1,98 @@ +// --------------------------------------------------------------------------- +// Constraint extractor — pulls constraints, assumptions, referenced files, +// and tools from raw user input. +// +// Rule-based with pattern matching. Swap for LLM-backed extraction in prod. +// --------------------------------------------------------------------------- + +export interface Extraction { + constraints: string[]; + assumptions: string[]; + files_mentioned: string[]; + tools_mentioned: string[]; +} + +// File path patterns +const FILE_PATTERN = /(?:^|\s)((?:\.{0,2}\/)?[\w./-]+\.[\w]+)/g; +const QUOTED_PATH = /["'`]((?:\.{0,2}\/)?[\w./-]+\.[\w]+)["'`]/g; + +// Constraint signal phrases +const CONSTRAINT_SIGNALS: Array<{ pattern: RegExp; prefix: string }> = [ + { pattern: /\bmust\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "" }, + { pattern: /\bshould\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "" }, + { pattern: /\bdon'?t\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Do not " }, + { pattern: /\bdo\s+not\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Do not " }, + { pattern: /\bnever\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Never " }, + { pattern: /\bonly\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Only " }, + { pattern: /\bno\s+more\s+than\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Limit: " }, + { pattern: /\bkeep\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Keep " }, + { pattern: /\bwithout\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Without " }, + { pattern: /\bpreserve\b\s+(.+?)(?:[.!,;]|$)/gi, prefix: "Preserve " }, + { pattern: /\bminimal\b/gi, prefix: "Keep changes minimal" }, +]; + +// Tool / capability signals +const TOOL_SIGNALS: Array<{ pattern: RegExp; tool: string }> = [ + { pattern: /\bgit\b/i, tool: "git" }, + { pattern: /\bdocker\b/i, tool: "docker" }, + { pattern: /\bnpm|yarn|pnpm|bun\b/i, tool: "package_manager" }, + { pattern: /\bshell|bash|terminal|command\b/i, tool: "shell" }, + { pattern: /\bdatabase|sql|postgres|mysql|sqlite\b/i, tool: "database" }, + { pattern: /\bapi|http|fetch|curl\b/i, tool: "http" }, + { pattern: /\beditor|vim|vscode\b/i, tool: "editor" }, + { pattern: /\bfilesystem|file|directory|folder\b/i, tool: "filesystem" }, +]; + +export function extractConstraints(input: string): Extraction { + const constraints: string[] = []; + const assumptions: string[] = []; + const filesSet = new Set(); + const toolsSet = new Set(); + + // Extract files + for (const regex of [FILE_PATTERN, QUOTED_PATH]) { + let m: RegExpExecArray | null; + const r = new RegExp(regex.source, regex.flags); + while ((m = r.exec(input)) !== null) { + const path = m[1]; + // Filter out obvious non-paths + if (path.length > 3 && !path.startsWith("http")) { + filesSet.add(path); + } + } + } + + // Extract constraints + for (const signal of CONSTRAINT_SIGNALS) { + let m: RegExpExecArray | null; + const r = new RegExp(signal.pattern.source, signal.pattern.flags); + while ((m = r.exec(input)) !== null) { + const constraint = m[1] ? signal.prefix + m[1].trim() : signal.prefix; + if (constraint.length > 3) { + constraints.push(constraint); + } + } + } + + // Extract tools + for (const signal of TOOL_SIGNALS) { + if (signal.pattern.test(input)) { + toolsSet.add(signal.tool); + } + } + + // Infer assumptions from lack of specificity + if (filesSet.size === 0) { + assumptions.push("No specific files mentioned — will scan workspace"); + } + if (constraints.length === 0) { + assumptions.push("No explicit constraints — applying safe defaults"); + } + + return { + constraints: [...new Set(constraints)], + assumptions, + files_mentioned: [...filesSet], + tools_mentioned: [...toolsSet], + }; +} diff --git a/prompt-gateway/src/compiler/context-selector.ts b/prompt-gateway/src/compiler/context-selector.ts new file mode 100644 index 0000000..cc5e2d6 --- /dev/null +++ b/prompt-gateway/src/compiler/context-selector.ts @@ -0,0 +1,87 @@ +import type { TaskType } from "../types.js"; + +// --------------------------------------------------------------------------- +// Context selector — assembles only the relevant context for a task. +// +// Anti-stuffing: we do NOT dump the whole workspace. We pick context sources +// based on task type, mentioned files, and available tools. +// --------------------------------------------------------------------------- + +export interface ContextQuery { + task_type: TaskType; + cwd?: string; + files_mentioned: string[]; + tools_mentioned: string[]; +} + +export interface SelectedContext { + workspace_roots: string[]; + files: string[]; + memory_keys: string[]; + environment: Record; +} + +// Task types that need repo context +const REPO_AWARE: Set = new Set([ + "code_edit", + "code_review", + "debug", + "refactor", + "test", + "deploy", +]); + +// Task types that need environment context +const ENV_AWARE: Set = new Set([ + "shell", + "deploy", + "debug", +]); + +// Memory keys by task type +const MEMORY_KEYS: Partial> = { + code_edit: ["recent_edits", "project_conventions"], + debug: ["recent_errors", "recent_edits", "known_issues"], + refactor: ["project_conventions", "architecture_notes"], + test: ["test_patterns", "coverage_gaps"], + deploy: ["deploy_history", "deploy_config"], + code_review: ["project_conventions", "review_checklist"], +}; + +export function selectContext(query: ContextQuery): SelectedContext { + const result: SelectedContext = { + workspace_roots: [], + files: [], + memory_keys: [], + environment: {}, + }; + + // Workspace roots + if (query.cwd) { + result.workspace_roots.push(query.cwd); + } + + // Files — start with explicitly mentioned ones + result.files.push(...query.files_mentioned); + + // If repo-aware task but no files mentioned, note the workspace root + if (REPO_AWARE.has(query.task_type) && result.files.length === 0 && query.cwd) { + // Don't add the whole repo, just mark the root for later scanning + result.workspace_roots.push(query.cwd); + } + + // Memory keys + const keys = MEMORY_KEYS[query.task_type]; + if (keys) { + result.memory_keys.push(...keys); + } + + // Environment context + if (ENV_AWARE.has(query.task_type)) { + result.environment["NODE_ENV"] = process.env.NODE_ENV ?? "development"; + result.environment["SHELL"] = process.env.SHELL ?? "unknown"; + // Do NOT leak sensitive env vars + } + + return result; +} diff --git a/prompt-gateway/src/compiler/contract.ts b/prompt-gateway/src/compiler/contract.ts new file mode 100644 index 0000000..699828f --- /dev/null +++ b/prompt-gateway/src/compiler/contract.ts @@ -0,0 +1,334 @@ +import { v4 as uuid } from "uuid"; +import { + TaskContract, + type CompileRequest, + type ExecutionMode, + type OutputType, + type RiskLevel, + type TaskType, +} from "../types.js"; +import { classify } from "./classifier.js"; +import { extractConstraints } from "./constraint-extractor.js"; +import { selectContext } from "./context-selector.js"; + +// --------------------------------------------------------------------------- +// Contract builder — the core compilation pipeline. +// +// raw input -> classify -> extract constraints -> select context +// -> assess risk -> choose execution mode -> emit contract +// --------------------------------------------------------------------------- + +export function compileContract(req: CompileRequest): TaskContract { + // 1. Classify intent + const classification = classify(req.raw_input); + + // 2. Extract constraints + const extraction = extractConstraints(req.raw_input); + + // 3. Select context + const context = selectContext({ + task_type: classification.task_type, + cwd: req.cwd, + files_mentioned: extraction.files_mentioned, + tools_mentioned: extraction.tools_mentioned, + }); + + // Merge adapter-provided files + if (req.files?.length) { + for (const f of req.files) { + if (!context.files.includes(f)) context.files.push(f); + } + } + + // 4. Assess risk + const risk = assessRisk(classification.task_type, extraction.constraints); + + // 5. Determine execution mode + const mode = chooseExecutionMode(classification.task_type, risk.level); + + // 6. Choose output type + const outputType = chooseOutputType(classification.task_type); + + // 7. Determine allowed / denied tools + const tools = resolveTools(extraction.tools_mentioned, classification.task_type); + + // 8. Generate plan steps + const steps = generatePlanSteps(classification.task_type, req.raw_input); + + // 9. Generate validation checks + const checks = generateValidationChecks(classification.task_type); + + // 10. Build contract + const now = new Date().toISOString(); + + const contract: TaskContract = TaskContract.parse({ + request_id: uuid(), + session_id: req.session_id ?? uuid(), + timestamp: now, + + origin: { + surface: req.surface, + cwd: req.cwd, + git_ref: req.git_ref, + env_profile: req.env_profile, + }, + + intent: { + primary: classification.primary_intent, + task_type: classification.task_type, + }, + + objective: req.raw_input, + constraints: extraction.constraints, + assumptions: extraction.assumptions, + + preferences: { + verbosity: req.preferences?.verbosity ?? "medium", + autonomy: req.preferences?.autonomy ?? "confirm", + }, + + context: { + workspace_roots: context.workspace_roots, + files: context.files, + memory_keys: context.memory_keys, + environment: context.environment, + }, + + tools, + risk, + + plan: { steps }, + + output: { + type: outputType, + }, + + execution: { + mode, + timeout_sec: risk.level === "high" ? 300 : 900, + target_surface: req.surface, + }, + + validation: { + checks, + success_criteria: generateSuccessCriteria(classification.task_type), + }, + }); + + return contract; +} + +// --------------------------------------------------------------------------- +// Risk assessment +// --------------------------------------------------------------------------- + +function assessRisk( + taskType: TaskType, + constraints: string[] +): { level: RiskLevel; approval_required: boolean; reasons: string[] } { + const reasons: string[] = []; + + const HIGH_RISK_TYPES = new Set(["deploy", "shell"]); + const MEDIUM_RISK_TYPES = new Set(["code_edit", "refactor", "debug"]); + + let level: RiskLevel = "low"; + + if (HIGH_RISK_TYPES.has(taskType)) { + level = "high"; + reasons.push(`Task type '${taskType}' is inherently high-risk`); + } else if (MEDIUM_RISK_TYPES.has(taskType)) { + level = "medium"; + reasons.push(`Task type '${taskType}' modifies code`); + } + + // Escalate if constraints mention destructive operations + const destructiveSignals = /\b(delete|remove|drop|force|reset|overwrite|destroy)\b/i; + for (const c of constraints) { + if (destructiveSignals.test(c)) { + level = "high"; + reasons.push(`Constraint mentions destructive operation: "${c}"`); + } + } + + return { + level, + approval_required: level === "high", + reasons, + }; +} + +// --------------------------------------------------------------------------- +// Execution mode +// --------------------------------------------------------------------------- + +function chooseExecutionMode( + taskType: TaskType, + riskLevel: RiskLevel +): ExecutionMode { + if (riskLevel === "high") return "plan_then_act"; + if (taskType === "query" || taskType === "docs") return "think"; + if (taskType === "design_spec" || taskType === "agent_architecture") + return "plan_then_act"; + return "act"; +} + +// --------------------------------------------------------------------------- +// Output type +// --------------------------------------------------------------------------- + +function chooseOutputType(taskType: TaskType): OutputType { + const map: Partial> = { + code_edit: "patch", + refactor: "patch", + debug: "patch", + shell: "command-list", + deploy: "command-list", + query: "text", + docs: "text", + design_spec: "report", + agent_architecture: "report", + code_review: "report", + test: "patch", + }; + return map[taskType] ?? "text"; +} + +// --------------------------------------------------------------------------- +// Tool resolution +// --------------------------------------------------------------------------- + +function resolveTools( + mentioned: string[], + taskType: TaskType +): { allow: string[]; deny: string[] } { + const BASE_TOOLS = ["filesystem", "shell", "git", "editor"]; + const TASK_TOOLS: Partial> = { + code_edit: ["filesystem", "editor", "git"], + debug: ["filesystem", "editor", "git", "shell"], + shell: ["shell", "filesystem"], + deploy: ["shell", "git", "docker"], + test: ["filesystem", "shell", "git"], + code_review: ["filesystem", "git"], + docs: ["filesystem", "editor"], + }; + + const allowed = new Set(TASK_TOOLS[taskType] ?? BASE_TOOLS); + for (const t of mentioned) { + allowed.add(t); + } + + // Never allow dangerous tools by default + const deny = ["database_admin", "cloud_admin", "secrets_manager"]; + + return { + allow: [...allowed], + deny, + }; +} + +// --------------------------------------------------------------------------- +// Plan generation +// --------------------------------------------------------------------------- + +function generatePlanSteps(taskType: TaskType, _input: string): string[] { + const BASE_STEPS: Partial> = { + code_edit: [ + "Identify files to modify", + "Read current code", + "Apply changes", + "Verify changes compile/lint", + ], + debug: [ + "Reproduce the issue", + "Identify root cause", + "Implement fix", + "Verify fix", + "Check for regressions", + ], + refactor: [ + "Understand current structure", + "Plan refactoring approach", + "Apply changes incrementally", + "Run tests", + "Verify behavior preservation", + ], + test: [ + "Identify test targets", + "Write test cases", + "Run tests", + "Verify coverage", + ], + shell: [ + "Validate command safety", + "Execute command", + "Verify output", + ], + deploy: [ + "Check deployment prerequisites", + "Run pre-deploy checks", + "Execute deployment", + "Verify deployment", + "Run smoke tests", + ], + code_review: [ + "Read changed files", + "Check for issues", + "Compile findings", + "Produce report", + ], + docs: [ + "Identify documentation targets", + "Write documentation", + "Review for accuracy", + ], + design_spec: [ + "Analyze requirements", + "Research existing patterns", + "Draft specification", + "Review for completeness", + ], + query: ["Research question", "Compile answer"], + }; + + return BASE_STEPS[taskType] ?? ["Analyze task", "Execute", "Validate"]; +} + +// --------------------------------------------------------------------------- +// Validation checks +// --------------------------------------------------------------------------- + +function generateValidationChecks(taskType: TaskType): string[] { + const checks: string[] = ["output_matches_contract"]; + + if (["code_edit", "refactor", "debug", "test"].includes(taskType)) { + checks.push("code_compiles", "tests_pass", "no_new_lint_errors"); + } + if (taskType === "deploy") { + checks.push("deploy_health_check", "rollback_plan_exists"); + } + if (taskType === "shell") { + checks.push("exit_code_zero", "no_destructive_side_effects"); + } + + return checks; +} + +// --------------------------------------------------------------------------- +// Success criteria +// --------------------------------------------------------------------------- + +function generateSuccessCriteria(taskType: TaskType): string[] { + const criteria: Record = { + code_edit: ["Changes applied correctly", "Code compiles", "Tests pass"], + debug: ["Bug is fixed", "Root cause identified", "No regressions"], + refactor: ["Behavior preserved", "Code improved", "Tests pass"], + test: ["Tests written", "Tests pass", "Coverage adequate"], + shell: ["Command executed successfully", "Expected output produced"], + deploy: ["Deployment successful", "Health checks pass"], + code_review: ["All files reviewed", "Issues documented"], + docs: ["Documentation complete", "Accurate"], + design_spec: ["Spec complete", "Requirements covered"], + query: ["Question answered", "Answer accurate"], + }; + return criteria[taskType] ?? ["Task completed successfully"]; +} diff --git a/prompt-gateway/src/compiler/index.ts b/prompt-gateway/src/compiler/index.ts new file mode 100644 index 0000000..0aac605 --- /dev/null +++ b/prompt-gateway/src/compiler/index.ts @@ -0,0 +1,4 @@ +export { classify } from "./classifier.js"; +export { extractConstraints } from "./constraint-extractor.js"; +export { selectContext } from "./context-selector.js"; +export { compileContract } from "./contract.js"; diff --git a/prompt-gateway/src/index.ts b/prompt-gateway/src/index.ts new file mode 100644 index 0000000..43a06be --- /dev/null +++ b/prompt-gateway/src/index.ts @@ -0,0 +1,92 @@ +import * as path from "node:path"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { createMcpServer } from "./mcp/server.js"; +import { createHttpServer } from "./server/http.js"; +import { Storage } from "./storage/db.js"; + +// --------------------------------------------------------------------------- +// prompt-gateway daemon entry point +// +// Runs two servers: +// 1. MCP server on stdio (for Cursor, Claude, Codex, etc.) +// 2. HTTP server on port 4840 (for CLI, desktop, API clients) +// +// Usage: +// npx prompt-gateway # starts both servers +// npx prompt-gateway --http # HTTP only +// npx prompt-gateway --mcp # MCP stdio only +// npx prompt-gateway --mcp-config # print MCP client config +// --------------------------------------------------------------------------- + +function printMcpConfig(): void { + const gatewayPath = path.resolve( + path.dirname(new URL(import.meta.url).pathname), + "..", + "dist", + "prompt-gateway.mjs" + ); + + const config = { + mcpServers: { + "prompt-gateway": { + command: "node", + args: [gatewayPath, "--mcp"], + }, + }, + }; + + console.log("Add this to your MCP client config:\n"); + console.log(" Cursor: .cursor/mcp.json"); + console.log(" Claude Desktop: claude_desktop_config.json"); + console.log(" Claude Code: .claude/settings.json (mcpServers key)\n"); + console.log(JSON.stringify(config, null, 2)); +} + +async function main() { + const args = process.argv.slice(2); + + if (args.includes("--mcp-config")) { + printMcpConfig(); + return; + } + + const httpOnly = args.includes("--http"); + const mcpOnly = args.includes("--mcp"); + const port = parseInt(args.find((a) => a.startsWith("--port="))?.split("=")[1] ?? "4840"); + + const storage = new Storage(); + + if (!httpOnly) { + // Start MCP server on stdio + const mcpServer = createMcpServer(storage); + const transport = new StdioServerTransport(); + await mcpServer.connect(transport); + + if (mcpOnly) { + console.error("[prompt-gateway] MCP server running on stdio"); + return; + } + } + + if (!mcpOnly) { + // Start HTTP server + const { app } = createHttpServer(storage, port); + app.listen(port, () => { + console.error(`[prompt-gateway] HTTP server listening on http://localhost:${port}`); + console.error(`[prompt-gateway] Endpoints:`); + console.error(` POST /compile — compile raw input to task contract`); + console.error(` POST /execute — execute a compiled contract`); + console.error(` POST /approve — approve/deny pending runs`); + console.error(` POST /validate — validate execution results`); + console.error(` GET /runs/:id — get run details`); + console.error(` GET /runs — list recent runs`); + console.error(` GET /capabilities — list gateway capabilities`); + console.error(` GET /health — health check`); + }); + } +} + +main().catch((err) => { + console.error("[prompt-gateway] Fatal:", err); + process.exit(1); +}); diff --git a/prompt-gateway/src/mcp/server.ts b/prompt-gateway/src/mcp/server.ts new file mode 100644 index 0000000..f4aa1f7 --- /dev/null +++ b/prompt-gateway/src/mcp/server.ts @@ -0,0 +1,389 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { compileContract } from "../compiler/index.js"; +import { PolicyEngine } from "../policy/engine.js"; +import { route, checkCapabilities } from "../router/index.js"; +import { validate, formatResponse } from "../validator/index.js"; +import { Storage } from "../storage/db.js"; +import type { Surface, CompileRequest } from "../types.js"; + +// --------------------------------------------------------------------------- +// MCP Server — exposes the prompt gateway as MCP tools, resources, and prompts +// so every MCP-compatible client (Cursor, Claude, Codex, etc.) consumes the +// same capability layer. +// --------------------------------------------------------------------------- + +export function createMcpServer(storage: Storage): McpServer { + const server = new McpServer({ + name: "prompt-gateway", + version: "0.1.0", + }); + + const policyEngine = new PolicyEngine(); + + // ----------------------------------------------------------------------- + // Tools + // ----------------------------------------------------------------------- + + // compile — convert raw input to a task contract + server.tool( + "compile", + "Compile raw human input into a structured task contract", + { + raw_input: z.string().describe("Raw user prompt / instruction"), + surface: z + .enum(["cursor", "cli", "desktop", "local-agent", "api"]) + .default("cli") + .describe("Originating surface"), + cwd: z.string().optional().describe("Current working directory"), + git_ref: z.string().optional().describe("Current git ref"), + session_id: z.string().optional().describe("Session identifier"), + }, + async (args) => { + const req: CompileRequest = { + raw_input: args.raw_input, + surface: args.surface as Surface, + cwd: args.cwd, + git_ref: args.git_ref, + session_id: args.session_id, + }; + + const contract = compileContract(req); + const run = storage.createRun(contract); + storage.log(run.id, "info", "Contract compiled", { contract }); + + return { + content: [ + { + type: "text" as const, + text: JSON.stringify(contract, null, 2), + }, + ], + }; + } + ); + + // evaluate — run policy checks on a contract + server.tool( + "evaluate", + "Evaluate a compiled contract against policy rules", + { + request_id: z.string().describe("The request_id from a compiled contract"), + }, + async (args) => { + const run = storage.getRun(args.request_id); + if (!run) { + return { + content: [{ type: "text" as const, text: `Run ${args.request_id} not found` }], + isError: true, + }; + } + + storage.updateRunStatus(run.id, "compiling"); + const evaluation = policyEngine.evaluate(run.contract); + storage.log(run.id, "info", "Policy evaluated", { evaluation }); + + if (!evaluation.allowed) { + storage.updateRunStatus(run.id, "failed", null, evaluation.summary); + } else if (evaluation.approval_required) { + storage.updateRunStatus(run.id, "awaiting_approval"); + } + + return { + content: [ + { + type: "text" as const, + text: JSON.stringify(evaluation, null, 2), + }, + ], + }; + } + ); + + // route — determine the best execution surface + server.tool( + "route", + "Route a task contract to the best execution surface", + { + request_id: z.string().describe("The request_id from a compiled contract"), + }, + async (args) => { + const run = storage.getRun(args.request_id); + if (!run) { + return { + content: [{ type: "text" as const, text: `Run ${args.request_id} not found` }], + isError: true, + }; + } + + const target = route(run.contract); + const caps = checkCapabilities(target.surface, run.contract.tools.allow); + + storage.log(run.id, "info", "Routed", { target, capabilities: caps }); + + return { + content: [ + { + type: "text" as const, + text: JSON.stringify({ target, capabilities: caps }, null, 2), + }, + ], + }; + } + ); + + // approve — approve or deny a pending run + server.tool( + "approve", + "Approve or deny a run that requires approval", + { + request_id: z.string().describe("The run to approve/deny"), + approved: z.boolean().describe("Whether to approve"), + reason: z.string().optional().describe("Reason for decision"), + }, + async (args) => { + const run = storage.getRun(args.request_id); + if (!run) { + return { + content: [{ type: "text" as const, text: `Run ${args.request_id} not found` }], + isError: true, + }; + } + + storage.createApproval(run.id, args.approved, "mcp-client", args.reason); + storage.log(run.id, "info", args.approved ? "Approved" : "Denied", { + reason: args.reason, + }); + + return { + content: [ + { + type: "text" as const, + text: args.approved + ? `Run ${run.id} approved and ready for execution` + : `Run ${run.id} denied: ${args.reason ?? "no reason given"}`, + }, + ], + }; + } + ); + + // validate — validate execution results against contract + server.tool( + "validate", + "Validate execution results against the task contract", + { + request_id: z.string().describe("The run to validate"), + result: z.string().describe("Execution result (as JSON string)"), + }, + async (args) => { + const run = storage.getRun(args.request_id); + if (!run) { + return { + content: [{ type: "text" as const, text: `Run ${args.request_id} not found` }], + isError: true, + }; + } + + let parsedResult: unknown; + try { + parsedResult = JSON.parse(args.result); + } catch { + parsedResult = args.result; + } + + const validation = validate(run.contract, parsedResult); + const status = validation.passed ? "completed" : "failed"; + const response = formatResponse(run.contract, parsedResult, validation, status); + + storage.updateRunStatus(run.id, status, parsedResult); + storage.log(run.id, "info", "Validated", { validation }); + + return { + content: [ + { + type: "text" as const, + text: JSON.stringify(response, null, 2), + }, + ], + }; + } + ); + + // get-run — retrieve a run by ID + server.tool( + "get-run", + "Get details of a specific run", + { + request_id: z.string().describe("The run ID"), + }, + async (args) => { + const run = storage.getRun(args.request_id); + if (!run) { + return { + content: [{ type: "text" as const, text: `Run ${args.request_id} not found` }], + isError: true, + }; + } + return { + content: [{ type: "text" as const, text: JSON.stringify(run, null, 2) }], + }; + } + ); + + // list-runs — list recent runs + server.tool( + "list-runs", + "List recent runs, optionally filtered by session", + { + session_id: z.string().optional().describe("Filter by session"), + limit: z.number().default(20).describe("Max results"), + }, + async (args) => { + const runs = storage.listRuns(args.session_id, args.limit); + return { + content: [ + { + type: "text" as const, + text: JSON.stringify( + runs.map((r) => ({ + id: r.id, + session_id: r.session_id, + status: r.status, + task_type: r.contract.intent.task_type, + objective: r.contract.objective.slice(0, 100), + created_at: r.created_at, + })), + null, + 2 + ), + }, + ], + }; + } + ); + + // capabilities — list available tools and surfaces + server.tool( + "capabilities", + "List gateway capabilities, surfaces, and tool registry", + {}, + async () => { + const capabilities = { + surfaces: ["cursor", "cli", "desktop", "local-agent", "api"], + task_types: [ + "code_edit", "code_review", "debug", "refactor", "test", + "docs", "shell", "agent_architecture", "design_spec", + "query", "deploy", + ], + execution_modes: ["think", "plan_then_act", "act"], + risk_levels: ["low", "medium", "high"], + output_types: ["text", "json", "patch", "command-list", "report"], + tools: [ + "filesystem", "shell", "git", "editor", "docker", + "package_manager", "database", "http", + ], + }; + return { + content: [{ type: "text" as const, text: JSON.stringify(capabilities, null, 2) }], + }; + } + ); + + // ----------------------------------------------------------------------- + // Resources + // ----------------------------------------------------------------------- + + server.resource( + "contract-schema", + "gateway://schema/task-contract", + async () => ({ + contents: [ + { + uri: "gateway://schema/task-contract", + mimeType: "application/json", + text: JSON.stringify( + { + description: "Canonical task contract schema", + fields: { + request_id: "uuid", + session_id: "string", + timestamp: "ISO 8601 datetime", + origin: "{ surface, cwd?, git_ref?, env_profile? }", + intent: "{ primary: string, task_type: TaskType }", + objective: "string — raw user input", + constraints: "string[] — extracted constraints", + assumptions: "string[] — inferred assumptions", + preferences: "{ verbosity, autonomy }", + context: "{ workspace_roots, files, memory_keys, environment }", + tools: "{ allow: string[], deny: string[] }", + risk: "{ level, approval_required, reasons }", + plan: "{ steps: string[] }", + output: "{ type, format? }", + execution: "{ mode, timeout_sec, target_surface? }", + validation: "{ checks, success_criteria }", + }, + }, + null, + 2 + ), + }, + ], + }) + ); + + // ----------------------------------------------------------------------- + // Prompts + // ----------------------------------------------------------------------- + + server.prompt( + "task-compiler", + "System prompt for the task compiler agent", + () => ({ + messages: [ + { + role: "user" as const, + content: { + type: "text" as const, + text: `You are the task compiler between a human operator and downstream AI agents. + +Do not solve the task directly unless requested. +Convert raw user intent into a precise execution contract. + +Always: +1. Infer the primary objective +2. Extract explicit and implicit constraints +3. Identify required context +4. Estimate task risk +5. Determine whether approval is required +6. Choose the best execution mode +7. Define success criteria +8. Produce structured output only + +Preserve real user intent. +Do not hallucinate requirements. +Do not expand tool permissions beyond necessity. +Prefer minimal viable plans. + +Output a JSON task contract with: +- objective +- task_type +- constraints +- context_needed +- recommended_tools +- disallowed_tools +- execution_mode +- risk_level +- approval_required +- plan_steps +- success_criteria +- output_format +- assumptions`, + }, + }, + ], + }) + ); + + return server; +} diff --git a/prompt-gateway/src/policy/engine.ts b/prompt-gateway/src/policy/engine.ts new file mode 100644 index 0000000..91d498a --- /dev/null +++ b/prompt-gateway/src/policy/engine.ts @@ -0,0 +1,152 @@ +import type { PolicyRule, PolicyDecision, TaskContract } from "../types.js"; + +// --------------------------------------------------------------------------- +// Policy engine — evaluates a compiled contract against a set of rules +// before allowing execution. +// +// Rules answer: +// - Can this agent do it? +// - Does this surface have permission? +// - Is approval required? +// - Should this run locally or remotely? +// - Should it dry-run first? +// --------------------------------------------------------------------------- + +export class PolicyEngine { + private rules: PolicyRule[] = []; + + constructor() { + this.registerDefaults(); + } + + addRule(rule: PolicyRule): void { + this.rules.push(rule); + } + + evaluate(contract: TaskContract): PolicyEvaluation { + const decisions: PolicyDecision[] = []; + let blocked = false; + let approvalRequired = contract.risk.approval_required; + + for (const rule of this.rules) { + const decision = rule.evaluate(contract); + decisions.push(decision); + + if (!decision.allowed) { + blocked = true; + } + if (decision.approval_required) { + approvalRequired = true; + } + } + + return { + allowed: !blocked, + approval_required: approvalRequired, + decisions, + summary: blocked + ? `Blocked by: ${decisions + .filter((d) => !d.allowed) + .map((d) => d.reason) + .join("; ")}` + : approvalRequired + ? "Allowed but requires approval" + : "Allowed", + }; + } + + private registerDefaults(): void { + // Rule: high-risk tasks require approval + this.addRule({ + id: "high-risk-approval", + description: "High-risk tasks always require approval", + evaluate(contract: TaskContract): PolicyDecision { + if (contract.risk.level === "high") { + return { + allowed: true, + approval_required: true, + reason: "High-risk task requires approval before execution", + }; + } + return { allowed: true, approval_required: false, reason: "OK" }; + }, + }); + + // Rule: deploy from non-CLI surfaces requires approval + this.addRule({ + id: "deploy-surface-check", + description: "Deploy tasks must originate from CLI or API", + evaluate(contract: TaskContract): PolicyDecision { + if ( + contract.intent.task_type === "deploy" && + !["cli", "api"].includes(contract.origin.surface) + ) { + return { + allowed: true, + approval_required: true, + reason: `Deploy from '${contract.origin.surface}' requires explicit approval`, + }; + } + return { allowed: true, approval_required: false, reason: "OK" }; + }, + }); + + // Rule: deny dangerous tool combinations + this.addRule({ + id: "tool-scope-guard", + description: "Block requests that ask for denied tools", + evaluate(contract: TaskContract): PolicyDecision { + const denied = contract.tools.deny; + const requested = contract.tools.allow; + const violations = requested.filter((t) => denied.includes(t)); + if (violations.length > 0) { + return { + allowed: false, + approval_required: false, + reason: `Denied tools requested: ${violations.join(", ")}`, + }; + } + return { allowed: true, approval_required: false, reason: "OK" }; + }, + }); + + // Rule: timeout sanity check + this.addRule({ + id: "timeout-guard", + description: "Tasks must have reasonable timeouts", + evaluate(contract: TaskContract): PolicyDecision { + if (contract.execution.timeout_sec > 3600) { + return { + allowed: false, + approval_required: false, + reason: "Timeout exceeds 1 hour maximum", + }; + } + return { allowed: true, approval_required: false, reason: "OK" }; + }, + }); + + // Rule: unknown task types get elevated scrutiny + this.addRule({ + id: "unknown-task-guard", + description: "Unknown task types require approval", + evaluate(contract: TaskContract): PolicyDecision { + if (contract.intent.task_type === "unknown") { + return { + allowed: true, + approval_required: true, + reason: "Unclassified task — requesting human review", + }; + } + return { allowed: true, approval_required: false, reason: "OK" }; + }, + }); + } +} + +export interface PolicyEvaluation { + allowed: boolean; + approval_required: boolean; + decisions: PolicyDecision[]; + summary: string; +} diff --git a/prompt-gateway/src/router/index.ts b/prompt-gateway/src/router/index.ts new file mode 100644 index 0000000..b397739 --- /dev/null +++ b/prompt-gateway/src/router/index.ts @@ -0,0 +1,132 @@ +import type { TaskContract, Surface, RouteTarget } from "../types.js"; + +// --------------------------------------------------------------------------- +// Runtime router — decides which execution surface handles a compiled task. +// +// Route by task type: +// - Cursor for repo-aware coding +// - CLI/local agent for shell-heavy work +// - Desktop app for human-in-the-loop workflows +// - Specialized local agent for domain tasks +// --------------------------------------------------------------------------- + +interface RoutingRule { + match: (contract: TaskContract) => boolean; + target: Surface; + reason: string; + fallback: Surface; +} + +const ROUTING_TABLE: RoutingRule[] = [ + // Shell-heavy work -> CLI + { + match: (c) => c.intent.task_type === "shell", + target: "cli", + reason: "Shell tasks route to CLI for direct execution", + fallback: "local-agent", + }, + + // Deploy -> CLI (with approval gate) + { + match: (c) => c.intent.task_type === "deploy", + target: "cli", + reason: "Deploy tasks route to CLI with approval gate", + fallback: "api", + }, + + // Repo-aware coding -> Cursor + { + match: (c) => + ["code_edit", "refactor", "debug", "code_review"].includes( + c.intent.task_type + ), + target: "cursor", + reason: "Code tasks route to Cursor for repo-aware editing", + fallback: "cli", + }, + + // Testing -> CLI (runs shell commands) + { + match: (c) => c.intent.task_type === "test", + target: "cli", + reason: "Test tasks route to CLI for test runner execution", + fallback: "cursor", + }, + + // Documentation -> Cursor (file editing) + { + match: (c) => c.intent.task_type === "docs", + target: "cursor", + reason: "Documentation routes to Cursor for file editing", + fallback: "cli", + }, + + // Design / architecture -> desktop (needs human review) + { + match: (c) => + ["design_spec", "agent_architecture"].includes(c.intent.task_type), + target: "desktop", + reason: "Design specs route to desktop for human-in-the-loop review", + fallback: "cursor", + }, + + // Queries -> local agent (lightweight) + { + match: (c) => c.intent.task_type === "query", + target: "local-agent", + reason: "Queries route to local agent for fast resolution", + fallback: "cli", + }, +]; + +export function route(contract: TaskContract): RouteTarget { + // If the contract specifies a target surface, honor it + if (contract.execution.target_surface) { + return { + surface: contract.execution.target_surface, + reason: "Explicitly specified by contract", + }; + } + + // Match routing rules + for (const rule of ROUTING_TABLE) { + if (rule.match(contract)) { + return { + surface: rule.target, + reason: rule.reason, + fallback: rule.fallback, + }; + } + } + + // Default fallback + return { + surface: "cli", + reason: "No matching routing rule — defaulting to CLI", + fallback: "local-agent", + }; +} + +// --------------------------------------------------------------------------- +// Capability check — verifies a surface can handle the required tools +// --------------------------------------------------------------------------- + +const SURFACE_CAPABILITIES: Record> = { + cursor: new Set(["filesystem", "editor", "git", "shell", "package_manager"]), + cli: new Set(["filesystem", "shell", "git", "docker", "package_manager", "database", "http"]), + desktop: new Set(["filesystem", "editor"]), + "local-agent": new Set(["filesystem", "shell", "git", "http", "editor"]), + api: new Set(["http", "shell", "filesystem", "git"]), +}; + +export function checkCapabilities( + surface: Surface, + requiredTools: string[] +): { capable: boolean; missing: string[] } { + const caps = SURFACE_CAPABILITIES[surface]; + const missing = requiredTools.filter((t) => !caps.has(t)); + return { + capable: missing.length === 0, + missing, + }; +} diff --git a/prompt-gateway/src/server/http.ts b/prompt-gateway/src/server/http.ts new file mode 100644 index 0000000..d711452 --- /dev/null +++ b/prompt-gateway/src/server/http.ts @@ -0,0 +1,225 @@ +import express, { type Request, type Response } from "express"; +import { compileContract } from "../compiler/index.js"; +import { PolicyEngine } from "../policy/engine.js"; +import { route, checkCapabilities } from "../router/index.js"; +import { validate, formatResponse } from "../validator/index.js"; +import { Storage } from "../storage/db.js"; +import { CompileRequest } from "../types.js"; + +// --------------------------------------------------------------------------- +// HTTP gateway server — REST endpoints for the prompt gateway daemon. +// +// Endpoints: +// POST /compile — compile raw input into a task contract +// POST /execute — (stub) execute a compiled contract +// POST /approve — approve or deny a pending run +// GET /runs/:id — get run details +// GET /runs — list recent runs +// GET /capabilities — list gateway capabilities +// --------------------------------------------------------------------------- + +export function createHttpServer(storage: Storage, port = 4840) { + const app = express(); + app.use(express.json()); + + const policyEngine = new PolicyEngine(); + + // ----------------------------------------------------------------------- + // POST /compile + // ----------------------------------------------------------------------- + app.post("/compile", (req: Request, res: Response) => { + try { + const parsed = CompileRequest.safeParse(req.body); + if (!parsed.success) { + res.status(400).json({ + error: "Invalid compile request", + details: parsed.error.issues, + }); + return; + } + + const contract = compileContract(parsed.data); + const run = storage.createRun(contract); + + // Auto-evaluate policy + const evaluation = policyEngine.evaluate(contract); + storage.log(run.id, "info", "Compiled and evaluated", { evaluation }); + + if (!evaluation.allowed) { + storage.updateRunStatus(run.id, "failed", null, evaluation.summary); + } else if (evaluation.approval_required) { + storage.updateRunStatus(run.id, "awaiting_approval"); + } + + // Auto-route + const target = route(contract); + const caps = checkCapabilities(target.surface, contract.tools.allow); + + res.json({ + contract, + policy: evaluation, + routing: { target, capabilities: caps }, + run_id: run.id, + }); + } catch (err) { + storage.log(null, "error", "Compile failed", { error: String(err) }); + res.status(500).json({ error: String(err) }); + } + }); + + // ----------------------------------------------------------------------- + // POST /execute (stub — actual execution is surface-specific) + // ----------------------------------------------------------------------- + app.post("/execute", (req: Request, res: Response) => { + const { request_id } = req.body; + if (!request_id) { + res.status(400).json({ error: "request_id required" }); + return; + } + + const run = storage.getRun(request_id); + if (!run) { + res.status(404).json({ error: `Run ${request_id} not found` }); + return; + } + + if (run.status === "awaiting_approval") { + res.status(403).json({ + error: "Run requires approval before execution", + run_id: run.id, + status: run.status, + }); + return; + } + + // In a real implementation, this dispatches to the target surface. + // For the MVP, we mark as executing and return the contract for the + // downstream agent to consume. + storage.updateRunStatus(run.id, "executing"); + storage.log(run.id, "info", "Execution started"); + + res.json({ + run_id: run.id, + status: "executing", + contract: run.contract, + message: "Contract dispatched to execution surface. Poll GET /runs/:id for status.", + }); + }); + + // ----------------------------------------------------------------------- + // POST /approve + // ----------------------------------------------------------------------- + app.post("/approve", (req: Request, res: Response) => { + const { request_id, approved, reason } = req.body; + if (!request_id || approved === undefined) { + res.status(400).json({ error: "request_id and approved required" }); + return; + } + + const run = storage.getRun(request_id); + if (!run) { + res.status(404).json({ error: `Run ${request_id} not found` }); + return; + } + + storage.createApproval(run.id, approved, "http-client", reason); + storage.log(run.id, "info", approved ? "Approved" : "Denied", { reason }); + + res.json({ + run_id: run.id, + approved, + status: approved ? "executing" : "cancelled", + }); + }); + + // ----------------------------------------------------------------------- + // POST /validate + // ----------------------------------------------------------------------- + app.post("/validate", (req: Request, res: Response) => { + const { request_id, result } = req.body; + if (!request_id) { + res.status(400).json({ error: "request_id required" }); + return; + } + + const run = storage.getRun(request_id); + if (!run) { + res.status(404).json({ error: `Run ${request_id} not found` }); + return; + } + + const validation = validate(run.contract, result); + const status = validation.passed ? "completed" : "failed"; + const response = formatResponse(run.contract, result, validation, status); + + storage.updateRunStatus(run.id, status, result); + storage.log(run.id, "info", "Validated", { validation }); + + res.json(response); + }); + + // ----------------------------------------------------------------------- + // GET /runs/:id + // ----------------------------------------------------------------------- + app.get("/runs/:id", (req: Request, res: Response) => { + const id = String(req.params.id); + const run = storage.getRun(id); + if (!run) { + res.status(404).json({ error: `Run ${id} not found` }); + return; + } + res.json(run); + }); + + // ----------------------------------------------------------------------- + // GET /runs + // ----------------------------------------------------------------------- + app.get("/runs", (req: Request, res: Response) => { + const sessionId = typeof req.query.session_id === "string" ? req.query.session_id : undefined; + const limit = parseInt(String(req.query.limit ?? "50")) || 50; + const runs = storage.listRuns(sessionId, limit); + res.json(runs); + }); + + // ----------------------------------------------------------------------- + // GET /capabilities + // ----------------------------------------------------------------------- + app.get("/capabilities", (_req: Request, res: Response) => { + res.json({ + version: "0.1.0", + surfaces: ["cursor", "cli", "desktop", "local-agent", "api"], + task_types: [ + "code_edit", "code_review", "debug", "refactor", "test", + "docs", "shell", "agent_architecture", "design_spec", + "query", "deploy", + ], + execution_modes: ["think", "plan_then_act", "act"], + endpoints: { + compile: "POST /compile", + execute: "POST /execute", + approve: "POST /approve", + validate: "POST /validate", + runs: "GET /runs", + run: "GET /runs/:id", + capabilities: "GET /capabilities", + }, + mcp: { + tools: [ + "compile", "evaluate", "route", "approve", + "validate", "get-run", "list-runs", "capabilities", + ], + resources: ["contract-schema"], + prompts: ["task-compiler"], + }, + }); + }); + + // ----------------------------------------------------------------------- + // Health check + // ----------------------------------------------------------------------- + app.get("/health", (_req: Request, res: Response) => { + res.json({ status: "ok", timestamp: new Date().toISOString() }); + }); + + return { app, port }; +} diff --git a/prompt-gateway/src/storage/db.ts b/prompt-gateway/src/storage/db.ts new file mode 100644 index 0000000..03ebf43 --- /dev/null +++ b/prompt-gateway/src/storage/db.ts @@ -0,0 +1,236 @@ +import Database from "better-sqlite3"; +import type { TaskContract, RunRecord, RunStatus } from "../types.js"; +import { v4 as uuid } from "uuid"; +import * as path from "node:path"; +import * as fs from "node:fs"; + +// --------------------------------------------------------------------------- +// Storage layer — SQLite for runs, approvals, memory, and policies. +// --------------------------------------------------------------------------- + +export class Storage { + private db: Database.Database; + + constructor(dbPath?: string) { + const resolvedPath = dbPath ?? path.join(process.cwd(), ".prompt-gateway", "gateway.db"); + const dir = path.dirname(resolvedPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + this.db = new Database(resolvedPath); + this.db.pragma("journal_mode = WAL"); + this.db.pragma("foreign_keys = ON"); + this.migrate(); + } + + // ------------------------------------------------------------------------- + // Migrations + // ------------------------------------------------------------------------- + + private migrate(): void { + this.db.exec(` + CREATE TABLE IF NOT EXISTS runs ( + id TEXT PRIMARY KEY, + session_id TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'pending', + contract TEXT NOT NULL, + result TEXT, + error TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE INDEX IF NOT EXISTS idx_runs_session ON runs(session_id); + CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status); + + CREATE TABLE IF NOT EXISTS approvals ( + id TEXT PRIMARY KEY, + run_id TEXT NOT NULL REFERENCES runs(id), + approved INTEGER NOT NULL DEFAULT 0, + approved_by TEXT, + reason TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE INDEX IF NOT EXISTS idx_approvals_run ON approvals(run_id); + + CREATE TABLE IF NOT EXISTS memory ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + category TEXT NOT NULL DEFAULT 'general', + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE INDEX IF NOT EXISTS idx_memory_category ON memory(category); + + CREATE TABLE IF NOT EXISTS policies ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL UNIQUE, + rule TEXT NOT NULL, + enabled INTEGER NOT NULL DEFAULT 1, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE IF NOT EXISTS logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + run_id TEXT REFERENCES runs(id), + level TEXT NOT NULL DEFAULT 'info', + message TEXT NOT NULL, + data TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE INDEX IF NOT EXISTS idx_logs_run ON logs(run_id); + `); + } + + // ------------------------------------------------------------------------- + // Runs + // ------------------------------------------------------------------------- + + createRun(contract: TaskContract): RunRecord { + const now = new Date().toISOString(); + const record: RunRecord = { + id: contract.request_id, + session_id: contract.session_id, + status: "pending", + contract, + created_at: now, + updated_at: now, + }; + + this.db + .prepare( + `INSERT INTO runs (id, session_id, status, contract, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?)` + ) + .run( + record.id, + record.session_id, + record.status, + JSON.stringify(record.contract), + record.created_at, + record.updated_at + ); + + return record; + } + + getRun(id: string): RunRecord | undefined { + const row = this.db + .prepare("SELECT * FROM runs WHERE id = ?") + .get(id) as RawRunRow | undefined; + + if (!row) return undefined; + return this.parseRunRow(row); + } + + updateRunStatus(id: string, status: RunStatus, result?: unknown, error?: string): void { + const now = new Date().toISOString(); + this.db + .prepare( + `UPDATE runs SET status = ?, result = ?, error = ?, updated_at = ? WHERE id = ?` + ) + .run(status, result ? JSON.stringify(result) : null, error ?? null, now, id); + } + + listRuns(sessionId?: string, limit = 50): RunRecord[] { + let rows: RawRunRow[]; + if (sessionId) { + rows = this.db + .prepare("SELECT * FROM runs WHERE session_id = ? ORDER BY created_at DESC LIMIT ?") + .all(sessionId, limit) as RawRunRow[]; + } else { + rows = this.db + .prepare("SELECT * FROM runs ORDER BY created_at DESC LIMIT ?") + .all(limit) as RawRunRow[]; + } + return rows.map((r) => this.parseRunRow(r)); + } + + // ------------------------------------------------------------------------- + // Approvals + // ------------------------------------------------------------------------- + + createApproval(runId: string, approved: boolean, approvedBy?: string, reason?: string): void { + this.db + .prepare( + `INSERT INTO approvals (id, run_id, approved, approved_by, reason) + VALUES (?, ?, ?, ?, ?)` + ) + .run(uuid(), runId, approved ? 1 : 0, approvedBy ?? null, reason ?? null); + + this.updateRunStatus(runId, approved ? "executing" : "cancelled"); + } + + // ------------------------------------------------------------------------- + // Memory + // ------------------------------------------------------------------------- + + setMemory(key: string, value: string, category = "general"): void { + this.db + .prepare( + `INSERT OR REPLACE INTO memory (key, value, category, updated_at) + VALUES (?, ?, ?, datetime('now'))` + ) + .run(key, value, category); + } + + getMemory(key: string): string | undefined { + const row = this.db + .prepare("SELECT value FROM memory WHERE key = ?") + .get(key) as { value: string } | undefined; + return row?.value; + } + + getMemoryByCategory(category: string): Array<{ key: string; value: string }> { + return this.db + .prepare("SELECT key, value FROM memory WHERE category = ?") + .all(category) as Array<{ key: string; value: string }>; + } + + // ------------------------------------------------------------------------- + // Logs + // ------------------------------------------------------------------------- + + log(runId: string | null, level: string, message: string, data?: unknown): void { + this.db + .prepare( + `INSERT INTO logs (run_id, level, message, data) VALUES (?, ?, ?, ?)` + ) + .run(runId, level, message, data ? JSON.stringify(data) : null); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private parseRunRow(row: RawRunRow): RunRecord { + return { + id: row.id, + session_id: row.session_id, + status: row.status as RunStatus, + contract: JSON.parse(row.contract) as TaskContract, + result: row.result ? JSON.parse(row.result) : undefined, + error: row.error ?? undefined, + created_at: row.created_at, + updated_at: row.updated_at, + }; + } + + close(): void { + this.db.close(); + } +} + +interface RawRunRow { + id: string; + session_id: string; + status: string; + contract: string; + result: string | null; + error: string | null; + created_at: string; + updated_at: string; +} diff --git a/prompt-gateway/src/types.ts b/prompt-gateway/src/types.ts new file mode 100644 index 0000000..fc0e003 --- /dev/null +++ b/prompt-gateway/src/types.ts @@ -0,0 +1,213 @@ +import { z } from "zod"; + +// --------------------------------------------------------------------------- +// Surface / origin +// --------------------------------------------------------------------------- + +export const Surface = z.enum([ + "cursor", + "cli", + "desktop", + "local-agent", + "api", +]); +export type Surface = z.infer; + +// --------------------------------------------------------------------------- +// Enums used across the contract +// --------------------------------------------------------------------------- + +export const Verbosity = z.enum(["low", "medium", "high"]); +export type Verbosity = z.infer; + +export const Autonomy = z.enum(["suggest", "confirm", "auto"]); +export type Autonomy = z.infer; + +export const ExecutionMode = z.enum(["think", "plan_then_act", "act"]); +export type ExecutionMode = z.infer; + +export const RiskLevel = z.enum(["low", "medium", "high"]); +export type RiskLevel = z.infer; + +export const OutputType = z.enum([ + "text", + "json", + "patch", + "command-list", + "report", +]); +export type OutputType = z.infer; + +export const TaskType = z.enum([ + "code_edit", + "code_review", + "debug", + "refactor", + "test", + "docs", + "shell", + "agent_architecture", + "design_spec", + "query", + "deploy", + "unknown", +]); +export type TaskType = z.infer; + +export const RunStatus = z.enum([ + "pending", + "compiling", + "awaiting_approval", + "executing", + "validating", + "completed", + "failed", + "cancelled", +]); +export type RunStatus = z.infer; + +// --------------------------------------------------------------------------- +// Canonical Task Contract — the lingua franca +// --------------------------------------------------------------------------- + +export const TaskContract = z.object({ + request_id: z.string().uuid(), + session_id: z.string(), + timestamp: z.string().datetime(), + + origin: z.object({ + surface: Surface, + cwd: z.string().optional(), + git_ref: z.string().optional(), + env_profile: z.string().optional(), + }), + + intent: z.object({ + primary: z.string(), + task_type: TaskType, + }), + + objective: z.string(), + constraints: z.array(z.string()), + assumptions: z.array(z.string()).default([]), + + preferences: z.object({ + verbosity: Verbosity.default("medium"), + autonomy: Autonomy.default("confirm"), + }), + + context: z.object({ + workspace_roots: z.array(z.string()).default([]), + files: z.array(z.string()).default([]), + memory_keys: z.array(z.string()).default([]), + environment: z.record(z.string()).default({}), + }), + + tools: z.object({ + allow: z.array(z.string()).default([]), + deny: z.array(z.string()).default([]), + }), + + risk: z.object({ + level: RiskLevel, + approval_required: z.boolean(), + reasons: z.array(z.string()).default([]), + }), + + plan: z.object({ + steps: z.array(z.string()).default([]), + }), + + output: z.object({ + type: OutputType, + format: z.string().optional(), + }), + + execution: z.object({ + mode: ExecutionMode, + timeout_sec: z.number().default(900), + target_surface: Surface.optional(), + }), + + validation: z.object({ + checks: z.array(z.string()).default([]), + success_criteria: z.array(z.string()).default([]), + }), +}); + +export type TaskContract = z.infer; + +// --------------------------------------------------------------------------- +// Compile request (raw input from any adapter) +// --------------------------------------------------------------------------- + +export const CompileRequest = z.object({ + raw_input: z.string(), + session_id: z.string().optional(), + surface: Surface.default("cli"), + cwd: z.string().optional(), + git_ref: z.string().optional(), + env_profile: z.string().optional(), + files: z.array(z.string()).optional(), + preferences: z + .object({ + verbosity: Verbosity.optional(), + autonomy: Autonomy.optional(), + }) + .optional(), +}); + +export type CompileRequest = z.infer; + +// --------------------------------------------------------------------------- +// Run record (persisted) +// --------------------------------------------------------------------------- + +export const RunRecord = z.object({ + id: z.string().uuid(), + session_id: z.string(), + status: RunStatus, + contract: TaskContract, + result: z.any().optional(), + error: z.string().optional(), + created_at: z.string().datetime(), + updated_at: z.string().datetime(), +}); + +export type RunRecord = z.infer; + +// --------------------------------------------------------------------------- +// Policy rule +// --------------------------------------------------------------------------- + +export interface PolicyRule { + id: string; + description: string; + evaluate(contract: TaskContract): PolicyDecision; +} + +export interface PolicyDecision { + allowed: boolean; + approval_required: boolean; + reason: string; + modified_contract?: Partial; +} + +// --------------------------------------------------------------------------- +// Adapter interface +// --------------------------------------------------------------------------- + +export interface InputAdapter { + readonly surface: Surface; + parse(raw: unknown): CompileRequest; +} + +// --------------------------------------------------------------------------- +// Router target +// --------------------------------------------------------------------------- + +export interface RouteTarget { + surface: Surface; + reason: string; + fallback?: Surface; +} diff --git a/prompt-gateway/src/validator/index.ts b/prompt-gateway/src/validator/index.ts new file mode 100644 index 0000000..b9bd85b --- /dev/null +++ b/prompt-gateway/src/validator/index.ts @@ -0,0 +1,196 @@ +import type { TaskContract, RunStatus } from "../types.js"; + +// --------------------------------------------------------------------------- +// Validator — checks execution results against the contract's validation +// criteria before returning results to the user. +// --------------------------------------------------------------------------- + +export interface ValidationResult { + passed: boolean; + checks: CheckResult[]; + summary: string; +} + +export interface CheckResult { + name: string; + passed: boolean; + detail: string; +} + +// Built-in check implementations +type CheckFn = (contract: TaskContract, result: unknown) => CheckResult; + +const BUILT_IN_CHECKS: Record = { + output_matches_contract: (contract, result) => { + const hasResult = result !== null && result !== undefined; + return { + name: "output_matches_contract", + passed: hasResult, + detail: hasResult + ? "Output present and non-null" + : "No output produced", + }; + }, + + code_compiles: (_contract, result) => { + // Check if result contains compilation errors + const resultStr = String(result ?? ""); + const hasCompileError = + /\b(error|SyntaxError|TypeError|ReferenceError)\b/i.test(resultStr) && + /\b(compile|compilation|parse)\b/i.test(resultStr); + return { + name: "code_compiles", + passed: !hasCompileError, + detail: hasCompileError + ? "Compilation errors detected in output" + : "No compilation errors detected", + }; + }, + + tests_pass: (_contract, result) => { + const resultStr = String(result ?? ""); + const hasTestFailure = + /\b(FAIL|failed|failing)\b/.test(resultStr) && + /\b(test|spec|suite)\b/i.test(resultStr); + return { + name: "tests_pass", + passed: !hasTestFailure, + detail: hasTestFailure + ? "Test failures detected in output" + : "No test failures detected", + }; + }, + + no_new_lint_errors: (_contract, result) => { + const resultStr = String(result ?? ""); + const hasLintError = /\b(lint|eslint|warning)\b/i.test(resultStr) && + /\b(error|violation)\b/i.test(resultStr); + return { + name: "no_new_lint_errors", + passed: !hasLintError, + detail: hasLintError + ? "Lint errors detected" + : "No lint errors detected", + }; + }, + + exit_code_zero: (_contract, result) => { + const resultStr = String(result ?? ""); + const hasNonZeroExit = /exit\s*(code|status)\s*[1-9]/i.test(resultStr); + return { + name: "exit_code_zero", + passed: !hasNonZeroExit, + detail: hasNonZeroExit + ? "Non-zero exit code detected" + : "Exit code OK", + }; + }, + + no_destructive_side_effects: (contract, _result) => { + // Check contract constraints for destructive signals + const destructive = contract.constraints.some((c) => + /\b(delete|drop|remove|destroy|force)\b/i.test(c) + ); + return { + name: "no_destructive_side_effects", + passed: !destructive, + detail: destructive + ? "Destructive operations present in constraints" + : "No destructive side effects", + }; + }, + + deploy_health_check: (_contract, result) => { + const resultStr = String(result ?? ""); + const healthy = + /\b(healthy|success|ok|up|running)\b/i.test(resultStr) || + !resultStr.includes("error"); + return { + name: "deploy_health_check", + passed: healthy, + detail: healthy ? "Deployment appears healthy" : "Deployment may have issues", + }; + }, + + rollback_plan_exists: (contract, _result) => { + const hasRollback = contract.plan.steps.some((s) => + /\b(rollback|revert|undo)\b/i.test(s) + ); + return { + name: "rollback_plan_exists", + passed: hasRollback, + detail: hasRollback + ? "Rollback step found in plan" + : "No rollback step in plan — consider adding one", + }; + }, +}; + +export function validate( + contract: TaskContract, + result: unknown +): ValidationResult { + const checks: CheckResult[] = []; + + for (const checkName of contract.validation.checks) { + const checkFn = BUILT_IN_CHECKS[checkName]; + if (checkFn) { + checks.push(checkFn(contract, result)); + } else { + // Unknown check — pass with warning + checks.push({ + name: checkName, + passed: true, + detail: `Check '${checkName}' has no built-in implementation — skipped`, + }); + } + } + + const allPassed = checks.every((c) => c.passed); + + return { + passed: allPassed, + checks, + summary: allPassed + ? `All ${checks.length} validation checks passed` + : `${checks.filter((c) => !c.passed).length}/${checks.length} checks failed`, + }; +} + +// --------------------------------------------------------------------------- +// Response formatter — shapes output for the originating surface +// --------------------------------------------------------------------------- + +export interface FormattedResponse { + request_id: string; + status: RunStatus; + result: unknown; + validation: ValidationResult; + metadata: { + task_type: string; + execution_mode: string; + risk_level: string; + elapsed_ms?: number; + }; +} + +export function formatResponse( + contract: TaskContract, + result: unknown, + validation: ValidationResult, + status: RunStatus, + elapsedMs?: number +): FormattedResponse { + return { + request_id: contract.request_id, + status, + result, + validation, + metadata: { + task_type: contract.intent.task_type, + execution_mode: contract.execution.mode, + risk_level: contract.risk.level, + elapsed_ms: elapsedMs, + }, + }; +} diff --git a/prompt-gateway/tsconfig.json b/prompt-gateway/tsconfig.json new file mode 100644 index 0000000..cc487cc --- /dev/null +++ b/prompt-gateway/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}