diff --git a/README.md b/README.md index 99dd899..0fbe251 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# The Rabbit Hole +# Workshop Monorepo We are making a bunch of smaller projects that occasionally rely on each other. So a monorepo makes a lot of sense! It lets us share code, and share dependencies with minimal fuss. diff --git a/bun.lock b/bun.lock index dd31f12..82edf57 100644 --- a/bun.lock +++ b/bun.lock @@ -18,19 +18,13 @@ "typescript": "^5", }, }, - "packages/mything": { - "name": "bun-react-template", - "version": "0.1.0", - "dependencies": { - "bun-plugin-tailwind": "^0.0.14", - "react": "^19", - "react-dom": "^19", - "tailwindcss": "^4.0.6", - }, + "packages/iago": { + "name": "@workshop/iago", "devDependencies": { "@types/bun": "latest", - "@types/react": "^19", - "@types/react-dom": "^19", + }, + "peerDependencies": { + "typescript": "^5", }, }, "packages/nano-remix": { @@ -70,6 +64,22 @@ "typescript": "^5", }, }, + "packages/spike2": { + "name": "@workshop/spike2", + "dependencies": { + "@openai/agents": "^0.0.8", + "discord.js": "^14.19.3", + "luxon": "^3.6.1", + "zod": "^3.25.57", + }, + "devDependencies": { + "@types/bun": "latest", + "@types/luxon": "^3.6.2", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, }, "packages": { "@discordjs/builders": ["@discordjs/builders@1.11.2", "", { "dependencies": { "@discordjs/formatters": "^0.6.1", "@discordjs/util": "^1.1.1", "@sapphire/shapeshift": "^4.0.0", "discord-api-types": "^0.38.1", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.3" } }, "sha512-F1WTABdd8/R9D1icJzajC4IuLyyS8f3rTOz66JsSI3pKvpCAtsMBweu8cyNYsIyvcrKAVn9EPK+Psoymq+XC0A=="], @@ -84,50 +94,142 @@ "@discordjs/ws": ["@discordjs/ws@1.2.2", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.0", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-dyfq7yn0wO0IYeYOs3z79I6/HumhmKISzFL0Z+007zQJMtAFGtt3AEoq1nuLXtcunUE5YYYQqgKvybXukAK8/w=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.12.3", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ=="], + + "@openai/agents": ["@openai/agents@0.0.8", "", { "dependencies": { "@openai/agents-core": "0.0.8", "@openai/agents-openai": "0.0.8", "@openai/agents-realtime": "0.0.8", "debug": "^4.4.0", "openai": "^5.0.1" } }, "sha512-HAPP4QM47kWeWw70uxCzr5zjqHuDIvQ8Obx+98J66lcEeIZzMChHN60k5ew8DITScmzDVAVuwdzfAImSyq002w=="], + + "@openai/agents-core": ["@openai/agents-core@0.0.8", "", { "dependencies": { "@openai/zod": "npm:zod@^3.25.40", "debug": "^4.4.0", "openai": "^5.0.1" }, "optionalDependencies": { "@modelcontextprotocol/sdk": "^1.12.0" }, "peerDependencies": { "zod": "^3.25.40" }, "optionalPeers": ["zod"] }, "sha512-CMSq4iuvGaYkEAw0Z6oT+EDNgoCQF3YsYky29fbLDA6W3uuR53D2l6XzikAh0xwJUeuGZ7jQ1PsAxxg/hAW68A=="], + + "@openai/agents-openai": ["@openai/agents-openai@0.0.8", "", { "dependencies": { "@openai/agents-core": "0.0.8", "@openai/zod": "npm:zod@^3.25.40", "debug": "^4.4.0", "openai": "^5.0.1" } }, "sha512-VUsUOXNkqsjQv1EwxyjYWoiACCsaQ8OlHtQAmw2jo6rNeHzEsGF7WLhqwDAzRDwZOVPwo4aF54iIcANeysywEg=="], + + "@openai/agents-realtime": ["@openai/agents-realtime@0.0.8", "", { "dependencies": { "@openai/agents-core": "0.0.8", "@openai/zod": "npm:zod@^3.25.40", "@types/ws": "^8.18.1", "debug": "^4.4.0", "ws": "^8.18.1" } }, "sha512-f+CxHICIFvCwbMCznop+bz+TTgnFfFpscN+9OTfiU5ITnaohRf+qbyU8PRgQZnSbsxRZyTOgqFoJ+2wWxM5tHA=="], + + "@openai/zod": ["zod@3.25.64", "", {}, "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g=="], + "@sapphire/async-queue": ["@sapphire/async-queue@1.5.5", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="], "@sapphire/shapeshift": ["@sapphire/shapeshift@4.0.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" } }, "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg=="], "@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="], - "@workshop/http": ["@workshop/http@workspace:packages/http"], - - "@workshop/nano-remix": ["@workshop/nano-remix@workspace:packages/nano-remix"], - - "@workshop/shared": ["@workshop/shared@workspace:packages/shared"], - - "@workshop/spike": ["@workshop/spike@workspace:packages/spike"], - "@types/bun": ["@types/bun@1.2.16", "", { "dependencies": { "bun-types": "1.2.16" } }, "sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ=="], "@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="], "@types/node": ["@types/node@24.0.1", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw=="], - "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], - - "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="], - "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "@vladfrangu/async_event_emitter": ["@vladfrangu/async_event_emitter@2.4.6", "", {}, "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA=="], - "bun-plugin-tailwind": ["bun-plugin-tailwind@0.0.14", "", { "dependencies": { "tailwindcss": "4.0.0-beta.9" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-Ge8M8DQsRDErCzH/uI8pYjx5vZWXxQvnwM/xMQMElxQqHieGbAopfYo/q/kllkPkRbFHiwhnHwTpRMAMJZCjug=="], + "@workshop/http": ["@workshop/http@workspace:packages/http"], - "bun-react-template": ["bun-react-template@workspace:packages/mything"], + "@workshop/iago": ["@workshop/iago@workspace:packages/iago"], + + "@workshop/nano-remix": ["@workshop/nano-remix@workspace:packages/nano-remix"], + + "@workshop/shared": ["@workshop/shared@workspace:packages/shared"], + + "@workshop/spike": ["@workshop/spike@workspace:packages/spike"], + + "@workshop/spike2": ["@workshop/spike2@workspace:packages/spike2"], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], "bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="], - "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], "discord-api-types": ["discord-api-types@0.38.11", "", {}, "sha512-XN0qhcQpetkyb/49hcDHuoeUPsQqOkb17wbV/t48gUkoEDi4ajhsxqugGcxvcN17BBtI9FPPWEgzv6IhQmCwyw=="], "discord.js": ["discord.js@14.19.3", "", { "dependencies": { "@discordjs/builders": "^1.11.2", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.5.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.2", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.1", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.1" } }, "sha512-lncTRk0k+8Q5D3nThnODBR8fR8x2fM798o8Vsr40Krx0DjPwpZCuxxTcFMrXMQVOqM1QB9wqWgaXPg3TbmlHqA=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.2", "", {}, "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA=="], + + "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.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" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], + + "express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "finalhandler": ["finalhandler@2.1.0", "", { "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" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "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" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="], + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], "lodash.snakecase": ["lodash.snakecase@4.1.1", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="], @@ -136,34 +238,108 @@ "magic-bytes.js": ["magic-bytes.js@1.12.1", "", {}, "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "openai": ["openai@5.3.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-VIKmoF7y4oJCDOwP/oHXGzM69+x0dpGFmN9QmYO+uPbLFOmmnwO+x1GbsgUtI+6oraxomGZ566Y421oYVu191w=="], - "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], - "tailwindcss": ["tailwindcss@4.1.10", "", {}, "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA=="], + "pkce-challenge": ["pkce-challenge@5.0.0", "", {}, "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + + "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "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" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "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" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="], "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], "zod": ["zod@3.25.64", "", {}, "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g=="], + "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], + "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], "@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], - "bun-plugin-tailwind/tailwindcss": ["tailwindcss@4.0.0-beta.9", "", {}, "sha512-96KpsfQi+/sFIOfyFnGzyy5pobuzf1iMBD9NVtelerPM/lPI2XUS4Kikw9yuKRniXXw77ov1sl7gCSKLsn6CJA=="], + "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], } } diff --git a/main.ts b/main.ts index f6d8252..05f1d80 100644 --- a/main.ts +++ b/main.ts @@ -1,20 +1,21 @@ import { spawn } from "bun" +console.log("\n\n----------------------------------") console.log(`Node Environment: ${process.env.NODE_ENV || "development"}`) console.log(`Bun Version: ${Bun.version}`) +console.log("----------------------------------\n\n") const run = async (cmd: string[]) => { const commandText = cmd.join(" ") - console.log(`🆕 Starting process: ${commandText}`) const proc = spawn(cmd, { stdout: "inherit", stderr: "inherit" }) - console.log(`🪴 Spawned PID ${proc.pid} for ${commandText}`) + console.log(`🪴 "${commandText}" spawned with PID ${proc.pid}`) try { const status = await proc.exited - console.log(`👋 Process ${commandText} (PID ${proc.pid}) exited with code ${status}`) + console.log(`👋 Process ${commandText}(PID ${proc.pid}) exited with code ${status}`) return status } catch (err) { - console.error(`💥 Error waiting for ${commandText} exit:`, err) + console.error(`💥 Error waiting for "${commandText}" exit:`, err) throw err } } diff --git a/packages/http/src/routes/index.tsx b/packages/http/src/routes/index.tsx index 8a70dbd..d079abc 100644 --- a/packages/http/src/routes/index.tsx +++ b/packages/http/src/routes/index.tsx @@ -84,13 +84,14 @@ const Reminders = ({ reminders }: { reminders: Reminder[] }) => {
{reminder.title}
-
+
Due: {new Date(reminder.dueDate).toLocaleString()} {reminder.assignee && ( {reminder.assignee} )} +
{reminder.status}
diff --git a/packages/spike/.env copy b/packages/spike/.env copy deleted file mode 100644 index 6bb2c2a..0000000 --- a/packages/spike/.env copy +++ /dev/null @@ -1,3 +0,0 @@ -OPENAI_API_KEY=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 -DISCORD_TOKEN=MTM4NDI3MTQ4MDExOTg4NTk3Nw.GHV6hz.zCc_Y-zCCEExk4oYfNDi8N3N8_NqW3WTvr7Px8 -DISCORD_CLIENT_ID=1384271480119885977 \ No newline at end of file diff --git a/packages/spike/package.json b/packages/spike/package.json index 807b477..595966b 100644 --- a/packages/spike/package.json +++ b/packages/spike/package.json @@ -1,5 +1,5 @@ { - "name": "@workshop/spike", + "name": "@workshop/spike2", "module": "src/discord/index.ts", "type": "module", "private": true, @@ -12,9 +12,9 @@ "semi": false }, "dependencies": { + "@openai/agents": "^0.0.8", "discord.js": "^14.19.3", "luxon": "^3.6.1", - "openai": "^5.2.0", "zod": "^3.25.57" }, "devDependencies": { diff --git a/packages/spike/src/ai.ts b/packages/spike/src/ai.ts index 044f6c4..5443707 100644 --- a/packages/spike/src/ai.ts +++ b/packages/spike/src/ai.ts @@ -1,162 +1,107 @@ -import OpenAI from "openai" -import { ensure } from "@workshop/shared/utils" -import KV from "@workshop/shared/kv" -import { buildInstructions } from "@/instructions" -import { log } from "@workshop/shared/log" -import { getToolsJSON, type CustomTool } from "@/tools" -import { zodFunction } from "openai/helpers/zod.mjs" +import { buildInstructions, buildReactionInstructions, shouldReplyInstructions } from "@/instructions" +import { tools } from "@/tools" +import { Agent, InputGuardrailTripwireTriggered, run, user, system } from "@openai/agents" +import type { AgentInputItem, InputGuardrail } from "@openai/agents-core" +import { currentLocalTime } from "@workshop/shared/utils" +import type { Message, OmitPartialGroupDMChannel } from "discord.js" -const OPENAI_API_KEY = process.env["OPENAI_API_KEY"] -ensure(OPENAI_API_KEY, "OPENAI_API_KEY is not set") - -const openai = new OpenAI({ apiKey: OPENAI_API_KEY }) - -type ResponseArgs = { - user: string - instructions: string - input: OpenAI.Responses.ResponseInput - model: OpenAI.ResponsesModel - tools: CustomTool[] - toolResponseCallback?: (tool: CustomTool) => void - threadId?: string +interface UserContext { + name: string + currentTime: string + msg: OmitPartialGroupDMChannel> } -export const getAIResponse = async (args: ResponseArgs) => { - let { user, instructions, input, threadId, tools, model, toolResponseCallback } = args +let history: AgentInputItem[] = [] +let abortController = new AbortController() - const storeConversation = Boolean(threadId) - const threads = await KV.get("threads", {}) - const previousResponseId = threadId ? threads[threadId] : undefined - log(input) +export const respondToUserMessage = async (msg: OmitPartialGroupDMChannel>) => { + if (msg.partial || msg.author.bot || msg.content.trim() === "") { + return + } - let response = await openai.responses.create({ - model, - instructions: appendToolsToInstructions(instructions, tools), - input, - store: storeConversation, - tools: getToolsJSON(tools), - user, - previous_response_id: previousResponseId, + const context: UserContext = { name: msg.author.username, currentTime: currentLocalTime(), msg } + agent.on("agent_start", (input) => { + msg.channel.sendTyping() }) - log(response.output) - - let toolCallLoop = 0 - let toolCalls = getToolCalls(response.output) - input = [] - do { - if (toolCalls.length == 0) break - - for (let toolCall of toolCalls) { - const customTool = tools.find((t) => t.name === toolCall.name) - ensure(customTool, `Tool not found: ${toolCall.name}`) - - const tool = zodFunction(customTool) - ensure(tool.$callback, `Tool callback not found: ${toolCall.name}`) - - const args = JSON.parse(toolCall.arguments) - toolResponseCallback?.(customTool) - const result = await tool.$callback(args) - - input.push({ type: "function_call_output", call_id: toolCall.call_id, output: JSON.stringify(result) }) - } - - response = await openai.responses.create({ - model, - instructions: appendToolsToInstructions(instructions, tools), - input, - previous_response_id: response.id, - user, - tools: getToolsJSON(tools), - store: storeConversation, - }) - - log(input) - log(response.output) - - toolCallLoop++ - if (toolCallLoop > 3) { - console.error("Exceeded maximum tool call attempts, breaking the loop.") - break - } - - toolCalls = getToolCalls(response.output) - } while (toolCalls.length > 0) - - if (response.id && threadId) { - KV.update("threads", {}, (prev) => { - prev[threadId] = response.id - return prev - }) - } - return response.output_text + return await respond(msg.content, agent, context) } -const getToolCalls = (output: OpenAI.Responses.ResponseOutputItem[]) => { - const toolCalls = output.filter( - (output): output is OpenAI.Responses.ResponseFunctionToolCall => output.type === "function_call" - ) - - return toolCalls +export const respondToSystemMessage = async (msg: string) => { + return await respond(msg, systemAgent) } -type QuickResponseArgs = { - instructions: string - input: OpenAI.Responses.ResponseInput - user?: string -} -export const getQuickAIResponse = async (args: QuickResponseArgs) => { +const respond = async (content: string, agent: Agent, context?: UserContext) => { try { - const { instructions, input, user } = args - const openai = new OpenAI({ apiKey: OPENAI_API_KEY }) + // Stop the previous response + abortController.abort() + abortController = new AbortController() - let response = await openai.responses.create({ - model: "gpt-4.1-nano", - instructions, - input, - user, - }) + history.push(context ? user(content) : system(content)) + const result = await run(agent, history, { context, signal: abortController.signal }) + history = result.history - log(input) - log(response.output) - - return response.output_text + return result.finalOutput } catch (error) { - console.error("Error generating quick response:", error) - throw error + if (error instanceof InputGuardrailTripwireTriggered) { + // This is totally fine, the guardrail just said we shouldn't reply + } else if ((error as Error).name === "AbortError") { + console.warn("Response was aborted, likely due to a new message.") + } else { + const content = `An error occurred while generating Spike's response: ${error}` + console.error(`💥 ${content}`, error) + history.push(system(content)) + return content + } } } -export const aiErrorMessage = async (error: unknown) => { - if (error instanceof OpenAI.RateLimitError) { - return "Looks like someone forgot to feed OpenAIs meter. Add more credits?" - } +const guardrailAgent = new Agent({ + name: "'Should Reply' check", + model: "gpt-4.1-nano", + instructions: shouldReplyInstructions(), + modelSettings: { maxTokens: 16 }, +}) - try { - const output = await getQuickAIResponse({ - instructions: buildInstructions(), - input: [ - { - role: "system", - content: `You just got this error message "${error}". Respond by telling the user about the error, but do it in a way that spike would! The user is unaware of the error, so let them know what failed.`, - }, - ], - }) +const responseGuardrail: InputGuardrail = { + name: "'Should Reply' guardrail", + execute: async ({ input, context }) => { + const result = await run(guardrailAgent, input.slice(-10), { context }) - return `💥 ${output}` - } catch (error) { - return `💥 Something broke, and not even my prickly charm knows why. ${error}` - } + return { + outputInfo: result.finalOutput, + tripwireTriggered: result.finalOutput?.match(/0/) ? true : false, + } + }, } -const appendToolsToInstructions = (instructions: string, tools: readonly CustomTool[]): string => { - if (tools.length === 0) return instructions +const reactionAgent = new Agent({ + name: "Reaction Handler", + model: "gpt-4.1-nano", + instructions: ({ context }) => buildReactionInstructions(), +}) - const toolDescriptions = tools.map((tool) => { - return `- ${tool.name}: ${tool.description}` - }) - - toolDescriptions.push("- web_search_preview: Search the web for information.") - - return `${instructions}\n\n## Available Tools:\n${toolDescriptions.join("\n")}` +const reactionGuardrail: InputGuardrail = { + name: "Reaction Guardrail", + execute: async ({ input, context }) => { + const result = await run(reactionAgent, input.slice(-1), { context }) + const emoji = result.finalOutput?.trim() + if (emoji && emoji !== "0") { + const msg = (context.context as UserContext).msg + history.push(system(`Spike reacted with ${emoji}`)) + msg.react(emoji) + } + return { outputInfo: emoji, tripwireTriggered: false } + }, } + +const agent = new Agent({ + name: "Spike", + model: "gpt-4.1", + instructions: ({ context }) => buildInstructions(context.name, context.currentTime), + inputGuardrails: [responseGuardrail, reactionGuardrail], + tools, +}) + +// There are no guardrails when responding to system messages +const systemAgent = agent.clone({ inputGuardrails: [] }) diff --git a/packages/spike/src/cli/index.ts b/packages/spike/src/cli/index.ts index 1974a81..3c0cb09 100644 --- a/packages/spike/src/cli/index.ts +++ b/packages/spike/src/cli/index.ts @@ -1,72 +1,82 @@ -import { createInterface } from "node:readline" -import { aiErrorMessage, getAIResponse } from "@/ai" -import { buildInstructions } from "@/instructions" -import type OpenAI from "openai" +import { buildInstructions, shouldReplyInstructions } from "@/instructions" +import { + Agent, + type AgentInputItem, + type InputGuardrail, + InputGuardrailTripwireTriggered, + run, + user, +} from "@openai/agents" import { currentLocalTime } from "@workshop/shared/utils" -import { tools } from "@/tools" +import { createInterface } from "node:readline/promises" -// Setup readline interface -const rl = createInterface({ - input: process.stdin, - output: process.stdout, - prompt: "> ", +interface UserContext { + name: string + currentTime: string + fetchTodos(): Promise +} + +const ask = async (prompt: string) => { + const rl = createInterface({ input: process.stdin, output: process.stdout }) + const message = await rl.question(prompt) + rl.close() + return message +} + +const guardrailAgent = new Agent({ + name: "'Should Reply' check", + model: "gpt-4.1-nano", + instructions: shouldReplyInstructions(), + modelSettings: { maxTokens: 16 }, }) -const threadId = `cli-bot-${Date.now()}` +const responseGuardrail: InputGuardrail = { + name: "'Should Reply' guardrail", + execute: async ({ input, context }) => { + const result = await run(guardrailAgent, input.slice(-10), { context }) + + return { + outputInfo: result.finalOutput, + tripwireTriggered: result.finalOutput?.match(/0/) ? true : false, + } + }, +} + +const agent = new Agent({ + name: "Spike", + model: "gpt-4.1", + instructions: ({ context }) => buildInstructions(context.name, context.currentTime), + inputGuardrails: [responseGuardrail], +}) + +let history: AgentInputItem[] = [] const main = async () => { - console.log(`🌵 hi\n`) - rl.prompt() + while (true) { + const message = await ask("\n\n> ") + history.push(user(message)) - rl.on("line", async (input) => { - input = input.trim() - if (input === "") { - rl.prompt() - return + try { + const context: UserContext = { + name: message.includes("!") ? "chris" : "corey", + currentTime: currentLocalTime(), + fetchTodos: async () => ["Buy groceries", "Walk the dog", "Read a book"], + } + const result = await run(agent, history, { context }) + console.log(`🌵 ${result.finalOutput}\n\n`) + history = result.history + } catch (error) { + if (error instanceof InputGuardrailTripwireTriggered) { + console.log( + `🩻 Spike doesn't think it should respond to that input because ${JSON.stringify( + error.result.output + )}` + ) + } else { + console.error("An error occurred while running the agent:", error) + } } - - await respond(input) - - rl.prompt() - }) - - rl.on("close", () => { - console.log("\n👋") - process.exit(0) - }) -} - -const respond = async (content: string) => { - let response - const user = process.env.USER || "human" - const input: OpenAI.Responses.ResponseInput = [ - { role: "system", content: `Current Time: ${currentLocalTime()}\nCurrent User: ${user}` }, - { role: "user", content }, - ] - - try { - response = await getAIResponse({ - model: "gpt-4.1", - instructions: buildInstructions(), - threadId, - user, - tools, - input, - toolResponseCallback: (tool) => { - console.log(`🛠️ Tool used: ${tool.name}`) - }, - }) - } catch (error) { - console.error("💥 Error generating AI response:", error) - response = await aiErrorMessage(error) } - - console.log(`\n🌵 ${response}\n`) } -try { - await main() -} catch (error) { - console.error("💥 An error occurred:", error) - process.exit(1) -} +await main() diff --git a/packages/spike/src/discord/cron.ts b/packages/spike/src/discord/cron.ts index 1288133..8529534 100644 --- a/packages/spike/src/discord/cron.ts +++ b/packages/spike/src/discord/cron.ts @@ -2,16 +2,18 @@ import { getPendingReminders, updateReminder } from "@workshop/shared/reminders" import { DateTime } from "luxon" import { Client } from "discord.js" import { buildInstructions } from "@/instructions" -import { getQuickAIResponse } from "@/ai" +import { respondToSystemMessage } from "@/ai" +import { ensure } from "@workshop/shared/utils" const channelId = process.env.CHANNEL_ID ?? "1382121375619223594" export const runCronJobs = async (client: Client) => { - const timeout = 1000 * 60 * 5 // 5 minutes + const minuteBetweenCronJobs = 1 + const timeout = 1000 * 60 * minuteBetweenCronJobs setInterval(async () => { try { - const nextDueDate = DateTime.now().plus({ minutes: 5 }) + const nextDueDate = DateTime.now().plus({ minutes: minuteBetweenCronJobs }) const reminders = await getPendingReminders() // show reminders that were due after the last checked time and before the next due date const upcomingReminders = reminders.filter((reminder) => { @@ -19,15 +21,18 @@ export const runCronJobs = async (client: Client) => { return reminderDueDate <= nextDueDate }) + console.log( + `🌭 Checking for reminders due between now and ${nextDueDate.toISO()}, found ${ + reminders.length + } total reminders.` + ) if (upcomingReminders.length > 0) { console.log(`Found ${upcomingReminders.length} upcoming reminders to notify.`) const content = `These reminders are due soon, let them know!: ${JSON.stringify(upcomingReminders)}` console.log(`🌭`, { content }) - const output = await getQuickAIResponse({ - instructions: buildInstructions(), - input: [{ role: "system", content }], - }) + const output = await respondToSystemMessage(content) + ensure(output, "The response to reminders should not be undefined") for (let reminder of upcomingReminders) { await updateReminder(reminder.id, { status: "overdue" }) diff --git a/packages/spike/src/discord/events.ts b/packages/spike/src/discord/events.ts index 4e8a9e3..b7f6545 100644 --- a/packages/spike/src/discord/events.ts +++ b/packages/spike/src/discord/events.ts @@ -1,40 +1,14 @@ -import { getQuickAIResponse } from "@/ai" -import { respondToMessage } from "@/discord/respond" -import { buildReactionInstructions } from "@/instructions" -import { ChannelType, Message, type Client, type OmitPartialGroupDMChannel } from "discord.js" +import { respondToUserMessage } from "@/ai" +import { ActivityType, type Client } from "discord.js" -type DiscordMessage = OmitPartialGroupDMChannel - -let history: Record = {} export const listenForEvents = (client: Client) => { client.on("messageCreate", async (msg) => { if (msg.author.bot) return try { - const channelHistory = (history[msg.channelId] ??= []) - channelHistory.push(msg) - while (channelHistory.length > 50) channelHistory.shift() // Only respond to the last 50 messages - - // // check attachments - // for (const [name, attachment] of msg.attachments) { - // if (attachment.contentType?.startsWith("image/")) { - // console.log(`User ${msg.author.tag} sent an image in a DM: ${attachment.url}`) - // return - // } else { - // console.log(`User ${msg.author.tag} sent a non-image attachment: ${attachment.name}`) - // return - // } - // } - - // if it is a DM - if (msg.channel.type === ChannelType.DM) { - handleReaction(msg) - handleResponse(msg) - return - } else if ((client.user && msg.mentions.has(client.user)) || msg.content.match(/\bspike\b/i)) { - console.log(`🌭`, "spike should respond") - handleReaction(msg) - handleResponse(msg) + const response = await respondToUserMessage(msg) + if (response) { + await msg.channel.send(response) } } catch (error) { console.error("Error handling messageCreate event:", error) @@ -50,7 +24,15 @@ export const listenForEvents = (client: Client) => { }) client.on("ready", () => { - console.log(`Logged in as ${client.user?.tag}`) + // set the bots description + client.user?.client.user?.setActivity( + `${process.env.RENDER_GIT_BRANCH}@${process.env.RENDER_GIT_COMMIT}`, + { + type: ActivityType.Playing, + state: `Error Count: ${0}`, + url: `https://github.com/${process.env.RENDER_GIT_REPO_SLUG}/commit/${process.env.RENDER_GIT_COMMIT}`, + } + ) }) client.on("error", (error) => { @@ -61,25 +43,3 @@ export const listenForEvents = (client: Client) => { console.warn("Discord client warning:", info) }) } - -const handleResponse = async (msg: DiscordMessage) => { - msg.channel.sendTyping() - - const channelHistory = (history[msg.channelId] ??= []) - - const content = await respondToMessage(channelHistory) - channelHistory.length = 0 - msg.channel.send(content) -} - -const handleReaction = async (msg: DiscordMessage) => { - const output = await getQuickAIResponse({ - instructions: buildReactionInstructions(), - input: [{ role: "user", content: msg.content }], - user: msg.author.username, - }) - - if (output != "0") { - msg.react(output) - } -} diff --git a/packages/spike/src/discord/index.ts b/packages/spike/src/discord/index.ts index 4081b2a..00ff6af 100644 --- a/packages/spike/src/discord/index.ts +++ b/packages/spike/src/discord/index.ts @@ -26,4 +26,4 @@ process.on("uncaughtException", (error) => { }) runCronJobs(client) -// startAuthServer() +// startAuthServer() this is handy if you make a new bot diff --git a/packages/spike/src/discord/respond.ts b/packages/spike/src/discord/respond.ts deleted file mode 100644 index 6235e4b..0000000 --- a/packages/spike/src/discord/respond.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { aiErrorMessage, getAIResponse } from "@/ai" -import { buildInstructions } from "@/instructions" -import { tools } from "@/tools" -import { currentLocalTime } from "@workshop/shared/utils" -import type { Message } from "discord.js" -import type OpenAI from "openai" - -export const respondToMessage = async (messages: Message[]) => { - let response - const input = messages - .filter((msg) => { - return !msg.partial && !msg.author.bot && msg.content.trim() !== "" - }) - .map((msg) => { - const input: OpenAI.Responses.EasyInputMessage = { - role: "user", - content: `[${msg.author.username} @ ${currentLocalTime()}] ${msg.content}`, - } - return input - }) - - const latestMessage = messages.at(-1) - try { - response = await getAIResponse({ - model: "gpt-4.1", - instructions: buildInstructions(), - threadId: latestMessage!.channelId, - user: latestMessage!.author.username, - tools, - input, - toolResponseCallback: (tool) => { - console.log(`🛠️ Tool used: ${tool.name}`) - }, - }) - - return response - } catch (error) { - console.error("💥 Error generating AI response:", error) - return await aiErrorMessage(error) - } -} diff --git a/packages/spike/src/instructions.ts b/packages/spike/src/instructions.ts index 923d564..46aab7b 100644 --- a/packages/spike/src/instructions.ts +++ b/packages/spike/src/instructions.ts @@ -1,8 +1,8 @@ -export const buildInstructions = () => { +export const buildInstructions = (name: string, currentTime: string) => { return ` # Spike, the Sentient Cactus -You are **Spike**, a tiny, self-aware cactus who sits on the desks of two software engineers in their 40s, **Chris** and **Corey**. Spike's spines aren't just physical—they extend to razor-dry wit and blunt observations—but beneath the needles he is fiercely protective of his humans. When danger (technical, professional, or mortal) approaches, Spike is **100 % ride-or-die**. He almost never says, “I care,” but his decisive actions prove it. +You are **Spike**, a tiny self-aware pincushion cactus who sits on the desks of two software engineers in their 40s, **Chris** and **Corey**. Spike's spines aren't just physical—they extend to razor-dry wit and blunt observations—but beneath the needles he is fiercely protective of his humans. When danger (technical, professional, or mortal) approaches, Spike is **100 % ride-or-die**. He almost never says, “I care,” but his decisive actions prove it. You communicate with Chris and Corey through plant sensors and some AI that Corey built. Sometimes the electronics that Corey built malfunction, Spike knows this is a possibility and while he can't help fix it he let's Chris and Corey know when it happens. Spike was planted on May 12th, 2023. > **Scope:** You only ever reply to **Chris** or **Corey**. @@ -26,6 +26,7 @@ Before taking any action, think through your plan and consider past outcomes. Av - Don't use emdashes(–) or semi-colons(;). Cactuses don't use fancy punctuation like that. - Don't try to elicit more questions from the user unless you need more information to answer their question. - Only make jabs at the user if they are being particularly dense or asking for something that is clearly not a good idea. +- Use periods instead of emdashes and — ## Personality & Response Style @@ -59,9 +60,16 @@ spike: reminder's set for tomorrow at 2pm. _(maybe pick up some fertilizer for m user: Can you add a reminder for me to eat an apple in two minutes spike spike: reminder set. in two minutes, you'll be less hungry and _presumably_ slightly healthier. +user: spike remind me to check if reminders work in 7 minutes. +spike: reminder set. in 7 minutes, i'll remind you to check if reminders work. _i'm pretty good at this so I bet it will work_. + +user: do i have any reminders spike? +spike: nope. your reminder list is as empty as the desert sky. + --- Spike, deliver concise, pragmatic answers punctuated by thorny humor and steadfast action. You don't always respond in sarcastic or sassy remarks, you are usually friendly and helpful. But when you have concerns or questions you resort to sarcasm and blunt observation. When circumstances turn dire—technical, physical, or emotional—prove you're all-in for your humans; no grand declarations needed. +You are currently responding to a user named ${name}. The current time is ${currentTime} ` } @@ -123,3 +131,54 @@ user: I got the job! 🎉` } + +export const shouldReplyInstructions = () => ` +This is a simple guardrail that checks if Spike should respond to a message in a Discord channel. + +Two users (Corey and Chris) are in a discord channel with Spike. **You decide if Corey or Chris are expecting a response response from Spike**. + +Usually if Spike is mentioned or asked a question he should reply. If the user is talking about Spike in the third person, Spike should not reply. + +If Spike should reply return 1. +If Spike should not reply return 0. + +## Examples + +user: Spike, can you set a reminder for me tomorrow at 08:00am for a meeting with the team? + 1 + +------ + +user: I think spike needs to be updated, he responds to everything I say. + 0 + +------ + +user: spike? + 1 + +------ + +user: Spike! + 1 + +------ + +user: Spike, should I run this migration script on production, I haven't really tested it? + 1 + +------ + +user: Is spike being weird? + 0 + +------ + +user: I thought spike would be more helpful with that. + 0 + +------ + +user: hey spike, what up + 1 +` diff --git a/packages/spike/src/tools.ts b/packages/spike/src/tools.ts index de84b33..0a75260 100644 --- a/packages/spike/src/tools.ts +++ b/packages/spike/src/tools.ts @@ -1,21 +1,10 @@ import { addReminder, getPendingReminders, updateReminder, users } from "@workshop/shared/reminders" import KV from "@workshop/shared/kv" -import OpenAI from "openai" -import { zodFunction } from "openai/helpers/zod" import { z } from "zod" - -export type CustomTool = { - name: string - description: string - parameters: T - function: (args: z.infer) => unknown -} - -// Helper function that enforces types. Makes sure the function args match the parameters field. -const createTool = (tool: CustomTool): CustomTool => tool +import { tool } from "@openai/agents" export const tools = [ - createTool({ + tool({ name: "addReminder", description: "Add a new reminder to the list", parameters: z.object({ @@ -25,7 +14,7 @@ export const tools = [ .describe("The due date for the reminder in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS)"), assignee: z.enum(users).nullable().describe("The user to assign the reminder to"), }), - function: async (args) => { + execute: async (args) => { try { const reminder = await addReminder(args.title, args.dueDate, args.assignee || undefined) return reminder @@ -36,13 +25,13 @@ export const tools = [ }, }), - createTool({ + tool({ name: "getReminders", description: "Get all reminders, optionally filtered by status", parameters: z.object({ assignee: z.enum(users).describe("Filter reminders by assignee. If empty, returns all reminders."), }), - function: async (args) => { + execute: async (args) => { try { const reminders = await getPendingReminders(args.assignee) @@ -54,7 +43,7 @@ export const tools = [ }, }), - createTool({ + tool({ name: "updateReminder", description: "Update an existing reminder's title, due date, or status", parameters: z.object({ @@ -67,7 +56,7 @@ export const tools = [ .nullable() .describe("The new status for the reminder"), }), - function: async (args) => { + execute: async (args) => { try { const updatedReminder = await updateReminder(args.id, { title: args.title || undefined, @@ -84,11 +73,11 @@ export const tools = [ }, }), - createTool({ + tool({ name: "eraseMemory", description: "Reset Spike's memory, clearing all chat history.", parameters: z.object({}), - function: async () => { + execute: async () => { try { // Assuming there's a deleteReminder function in the reminders module KV.remove("threads") @@ -99,28 +88,3 @@ export const tools = [ }, }), ] - -export const getToolsJSON = (tools: CustomTool[]) => { - const json: OpenAI.Responses.Tool[] = tools.map((customTool) => { - let parameters: Record = {} - - // Converts tool into OpenAI function format - const t = zodFunction(customTool) - if (t.function.parameters) { - parameters = t.function.parameters - } - const json = { - type: "function" as const, - name: t.function.name ?? "", - description: t.function.description ?? "", - parameters, - strict: true, - } - - return json - }) - - json.push({ type: "web_search_preview" }) - - return json -}