├── .DS_Store ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets └── output.png ├── example-plugin ├── .assets │ └── example.png ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── index.ts │ └── search.ts └── wrangler.toml ├── example-retrieval-plugin ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── plugin │ ├── src │ │ ├── index.js │ │ └── manifests │ │ │ ├── ai-plugin.json │ │ │ ├── openapi.d.ts │ │ │ └── openapi.json │ ├── worker-configuration.d.ts │ └── wrangler.toml ├── scheduler │ ├── src │ │ └── index.js │ ├── worker-configuration.d.ts │ └── wrangler.toml ├── scripts │ ├── chunk.js │ ├── embeddings.js │ └── refresh.sh ├── shared │ ├── docs.js │ ├── github.js │ ├── openai.js │ ├── types.d.ts │ └── vector.js └── tsconfig.json ├── example-weather-plugin ├── .assets │ └── example.png ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── forecast.ts │ └── index.ts └── wrangler.toml └── package-lock.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyAI-ID/cloudflare-ai-example-chatgpt-plugin/41183a0a32d048f84fe8936498cd09f9b709c0ec/.DS_Store -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ TERMS 6 | AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | 11 | 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, and 14 | distribution as defined by Sections 1 through 9 of this document. 15 | 16 | 17 | 18 | 19 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 20 | owner that is granting the License. 21 | 22 | 23 | 24 | "Legal Entity" shall mean the 25 | union of the acting entity and all other entities that control, are controlled 26 | by, or are under common control with that entity. For the purposes of this 27 | definition, "control" means (i) the power, direct or indirect, to cause the 28 | direction or management of such entity, whether by contract or otherwise, or (ii) 29 | ownership of fifty percent (50%) or more of the outstanding shares, or (iii) 30 | beneficial ownership of such entity. 31 | 32 | 33 | 34 | "You" (or "Your") shall mean 35 | an individual or Legal Entity exercising permissions granted by this License. 36 | 37 | 38 | 39 | 40 | "Source" form shall mean the preferred form for making modifications, 41 | including but not limited to software source code, documentation source, and 42 | configuration files. 43 | 44 | 45 | 46 | "Object" form shall mean any form resulting 47 | from mechanical transformation or translation of a Source form, including but not 48 | limited to compiled object code, generated documentation, and conversions to 49 | other media types. 50 | 51 | 52 | 53 | "Work" shall mean the work of authorship, 54 | whether in Source or Object form, made available under the License, as indicated 55 | by a copyright notice that is included in or attached to the work (an example is 56 | provided in the Appendix below). 57 | 58 | 59 | 60 | "Derivative Works" shall mean any 61 | work, whether in Source or Object form, that is based on (or derived from) the 62 | Work and for which the editorial revisions, annotations, elaborations, or other 63 | modifications represent, as a whole, an original work of authorship. For the 64 | purposes of this License, Derivative Works shall not include works that remain 65 | separable from, or merely link (or bind by name) to the interfaces of, the Work 66 | and Derivative Works thereof. 67 | 68 | 69 | 70 | "Contribution" shall mean any work 71 | of authorship, including the original version of the Work and any modifications 72 | or additions to that Work or Derivative Works thereof, that is intentionally 73 | submitted to Licensor for inclusion in the Work by the copyright owner or by an 74 | individual or Legal Entity authorized to submit on behalf of the copyright owner. 75 | For the purposes of this definition, "submitted" means any form of electronic, 76 | verbal, or written communication sent to the Licensor or its representatives, 77 | including but not limited to communication on electronic mailing lists, source 78 | code control systems, and issue tracking systems that are managed by, or on 79 | behalf of, the Licensor for the purpose of discussing and improving the Work, but 80 | excluding communication that is conspicuously marked or otherwise designated in 81 | writing by the copyright owner as "Not a Contribution." 82 | 83 | 84 | 85 | 86 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of 87 | whom a Contribution has been received by Licensor and subsequently incorporated 88 | within the Work. 89 | 90 | 2. Grant of Copyright License. Subject to the terms and 91 | conditions of this License, each Contributor hereby grants to You a perpetual, 92 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license 93 | to reproduce, prepare Derivative Works of, publicly display, publicly perform, 94 | sublicense, and distribute the Work and such Derivative Works in Source or Object 95 | form. 96 | 97 | 3. Grant of Patent License. Subject to the terms and conditions of this 98 | License, each Contributor hereby grants to You a perpetual, worldwide, 99 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this 100 | section) patent license to make, have made, use, offer to sell, sell, import, and 101 | otherwise transfer the Work, where such license applies only to those patent 102 | claims licensable by such Contributor that are necessarily infringed by their 103 | Contribution(s) alone or by combination of their Contribution(s) with the Work to 104 | which such Contribution(s) was submitted. If You institute patent litigation 105 | against any entity (including a cross-claim or counterclaim in a lawsuit) 106 | alleging that the Work or a Contribution incorporated within the Work constitutes 107 | direct or contributory patent infringement, then any patent licenses granted to 108 | You under this License for that Work shall terminate as of the date such 109 | litigation is filed. 110 | 111 | 4. Redistribution. You may reproduce and distribute 112 | copies of the Work or Derivative Works thereof in any medium, with or without 113 | modifications, and in Source or Object form, provided that You meet the following 114 | conditions: 115 | 116 | (a) You must give any other recipients of the Work or 117 | Derivative Works a copy of this License; and 118 | 119 | (b) You must cause any 120 | modified files to carry prominent notices stating that You changed the files; 121 | and 122 | 123 | (c) You must retain, in the Source form of any Derivative Works that 124 | You distribute, all copyright, patent, trademark, and attribution notices from 125 | the Source form of the Work, excluding those notices that do not pertain to any 126 | part of the Derivative Works; and 127 | 128 | (d) If the Work includes a "NOTICE" text 129 | file as part of its distribution, then any Derivative Works that You distribute 130 | must include a readable copy of the attribution notices contained within such 131 | NOTICE file, excluding those notices that do not pertain to any part of the 132 | Derivative Works, in at least one of the following places: within a NOTICE text 133 | file distributed as part of the Derivative Works; within the Source form or 134 | documentation, if provided along with the Derivative Works; or, within a display 135 | generated by the Derivative Works, if and wherever such third-party notices 136 | normally appear. The contents of the NOTICE file are for informational purposes 137 | only and do not modify the License. You may add Your own attribution notices 138 | within Derivative Works that You distribute, alongside or as an addendum to the 139 | NOTICE text from the Work, provided that such additional attribution notices 140 | cannot be construed as modifying the License. 141 | 142 | You may add Your own 143 | copyright statement to Your modifications and may provide additional or different 144 | license terms and conditions for use, reproduction, or distribution of Your 145 | modifications, or for any such Derivative Works as a whole, provided Your use, 146 | reproduction, and distribution of the Work otherwise complies with the conditions 147 | stated in this License. 148 | 149 | 5. Submission of Contributions. Unless You explicitly 150 | state otherwise, any Contribution intentionally submitted for inclusion in the 151 | Work by You to the Licensor shall be under the terms and conditions of this 152 | License, without any additional terms or conditions. Notwithstanding the above, 153 | nothing herein shall supersede or modify the terms of any separate license 154 | agreement you may have executed with Licensor regarding such Contributions. 155 | 156 | 157 | 6. Trademarks. This License does not grant permission to use the trade names, 158 | trademarks, service marks, or product names of the Licensor, except as required 159 | for reasonable and customary use in describing the origin of the Work and 160 | reproducing the content of the NOTICE file. 161 | 162 | 7. Disclaimer of Warranty. Unless 163 | required by applicable law or agreed to in writing, Licensor provides the Work 164 | (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT 165 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, 166 | without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, 167 | MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible 168 | for determining the appropriateness of using or redistributing the Work and 169 | assume any risks associated with Your exercise of permissions under this 170 | License. 171 | 172 | 8. Limitation of Liability. In no event and under no legal theory, 173 | whether in tort (including negligence), contract, or otherwise, unless required 174 | by applicable law (such as deliberate and grossly negligent acts) or agreed to in 175 | writing, shall any Contributor be liable to You for damages, including any 176 | direct, indirect, special, incidental, or consequential damages of any character 177 | arising as a result of this License or out of the use or inability to use the 178 | Work (including but not limited to damages for loss of goodwill, work stoppage, 179 | computer failure or malfunction, or any and all other commercial damages or 180 | losses), even if such Contributor has been advised of the possibility of such 181 | damages. 182 | 183 | 9. Accepting Warranty or Additional Liability. While redistributing 184 | the Work or Derivative Works thereof, You may choose to offer, and charge a fee 185 | for, acceptance of support, warranty, indemnity, or other liability obligations 186 | and/or rights consistent with this License. However, in accepting such 187 | obligations, You may act only on Your own behalf and on Your sole responsibility, 188 | not on behalf of any other Contributor, and only if You agree to indemnify, 189 | defend, and hold each Contributor harmless for any liability incurred by, or 190 | claims asserted against, such Contributor by reason of your accepting any such 191 | warranty or additional liability. END OF TERMS AND CONDITIONS 192 | 193 | APPENDIX: How to 194 | apply the Apache License to your work. 195 | 196 | To apply the Apache License to your work, 197 | attach the following boilerplate notice, with the fields enclosed by brackets 198 | "[]" replaced with your own identifying information. (Don't include the 199 | brackets!) The text should be enclosed in the appropriate comment syntax for the 200 | file format. We also recommend that a file or class name and description of 201 | purpose be included on the same "printed page" as the copyright notice for easier 202 | identification within third-party archives. 203 | 204 | Copyright 2023 Cloudflare 205 | 206 | Licensed under the Apache License, Version 2.0 (the "License"); 207 | 208 | you may 209 | not use this file except in compliance with the License. 210 | 211 | You may obtain a copy 212 | of the License at 213 | 214 | http://www.apache.org/licenses/LICENSE-2.0 215 | 216 | Unless required by 217 | applicable law or agreed to in writing, software 218 | 219 | distributed under the License 220 | is distributed on an "AS IS" BASIS, 221 | 222 | WITHOUT WARRANTIES OR CONDITIONS OF ANY 223 | KIND, either express or implied. 224 | 225 | See the License for the specific language 226 | governing permissions and 227 | 228 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2023 Cloudflare 2 | 3 | Permission is hereby granted, 4 | free of charge, to any person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | The above copyright notice and this permission notice 12 | (including the next paragraph) shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatGPT Plugins on Cloudflare 2 | 3 | This repo contains Cloudflare's work with ChatGPT plugins. The plugins are built using the [Cloudflare Workers](https://workers.cloudflare.com/) platform. 4 | 5 | - [`example-plugin`](https://github.com/cloudflare/chatgpt-plugin/tree/main/example-plugin): An example plugin that can be deployed to Cloudflare Workers in 5 minutes, allowing search and retrieval of GitHub repositories. 6 | - [`example-weather-plugin`](https://github.com/cloudflare/chatgpt-plugin/tree/main/example-weather-plugin): An example plugin that retrieves weather information from the [Pirate Weather API](https://pirate-weather.apiable.io/). 7 | 8 | ## Extended with AI example 9 | 10 | In this version of ChatGPT plugin template, we're experimenting Cloudflare AI solution on workers. 11 | 12 | ## Output: 13 | 14 | Screenshot from Swagger interface. 15 | 16 | ![Screenshot from Swagger Interface](./assets/output.png) 17 | 18 | ## Additions: 19 | 20 | `./example-plugin` modified. 21 | 22 | 23 | ## How To Use: 24 | 25 | ```bash 26 | cd example-plugin; 27 | wrangler deploy; # npm i -g wrangler 28 | ``` 29 | 30 | ## Demo: 31 | 32 | Check out the demo: 33 | [https://cloudflare-ai-workers-chatgpt-plugin-example.cagatay.workers.dev](https://cloudflare-ai-workers-chatgpt-plugin-example.cagatay.workers.dev) 34 | 35 | > // TODO @cagataycali: Remove the .cagatay.workers.dev when merged into main. 36 | 37 | 38 | ```typescript 39 | async handle(request: Request, env, ctx, data: Record) { 40 | // Initiate the AI 41 | const ai = new Ai(env.AI) 42 | 43 | const url = `https://api.github.com/search/repositories?q=${data.q}` 44 | 45 | const resp = await fetch(url, { 46 | headers: { 47 | 'Accept': 'application/vnd.github.v3+json', 48 | 'User-Agent': 'RepoAI - Cloudflare Workers ChatGPT Plugin Example' 49 | } 50 | }) 51 | 52 | if (!resp.ok) { 53 | return new Response(await resp.text(), { status: 400 }) 54 | } 55 | 56 | const json = await resp.json() 57 | 58 | // @ts-ignore 59 | const repos = json.items.map((item: any) => ({ 60 | name: item.name, 61 | description: item.description, 62 | stars: item.stargazers_count, 63 | url: item.html_url 64 | })) 65 | // Generate a response with Cloudflare AI: 66 | const messages = [ 67 | { role: 'system', content: "I'm going to help you to find the best GitHub repository based on your search." }, 68 | { role: 'user', content: 'My search is: ' + data.q }, 69 | ]; 70 | const response = await ai.run('@cf/meta/llama-2-7b-chat-int8', { messages }); 71 | 72 | return { 73 | repos: repos, 74 | ai: response 75 | } 76 | } 77 | ``` -------------------------------------------------------------------------------- /assets/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyAI-ID/cloudflare-ai-example-chatgpt-plugin/41183a0a32d048f84fe8936498cd09f9b709c0ec/assets/output.png -------------------------------------------------------------------------------- /example-plugin/.assets/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyAI-ID/cloudflare-ai-example-chatgpt-plugin/41183a0a32d048f84fe8936498cd09f9b709c0ec/example-plugin/.assets/example.png -------------------------------------------------------------------------------- /example-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /example-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Example ChatGPT Plugin for Cloudflare Workers with Cloudflare AI 2 | 3 | This is an example plugin showing how to build [ChatGPT plugins](https://platform.openai.com/docs/plugins/introduction) using [Cloudflare Workers](https://workers.dev). Using this example, you can deploy a plugin to Cloudflare Workers in just a few minutes. 4 | 5 | The sample plugin allows ChatGPT users to search for repositories using GitHub's search API. The plugin is implemented in TypeScript and uses the [OpenAPI](https://www.openapis.org/) specification to define the plugin's API. 6 | 7 | ![Example conversation in ChatGPT showing the plugin in use](./.assets/example.png) 8 | 9 | ## Get started 10 | 11 | 0. Sign up for [Cloudflare Workers](https://workers.dev). The free tier is more than enough for most use cases. 12 | 1. Install [wrangler](https://developers.cloudflare.com/workers/cli-wrangler/install-update), the Cloudflare Workers CLI 13 | 2. Clone this project and install dependencies with `npm install` 14 | 3. Run `wrangler login` to login to your Cloudflare account in wrangler 15 | 4. Run `wrangler publish` to publish the plugin to Cloudflare Workers 16 | 17 | ## Usage 18 | 19 | 1. You can configure the `.well-known/ai-plugin.json` route in `index.ts`. 20 | 2. Update the OpenAPI schema in `openapi.ts`. 21 | 3. You can set up any new routes and the associated OpenAPI schema by defining new routes. See `search.ts` for an example. 22 | 23 | ## Deploying to OpenAI's API 24 | 25 | Follow the instructions [in the ChatGPT documentation](https://platform.openai.com/docs/plugins/introduction/plugin-flow) to deploy your plugin and begin using it in ChatGPT. -------------------------------------------------------------------------------- /example-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-workers-chatgpt-plugin-example", 3 | "version": "0.0.1", 4 | "devDependencies": { 5 | "@cloudflare/workers-types": "^4.20230404.0", 6 | "wrangler": "^2.15.1" 7 | }, 8 | "private": true, 9 | "scripts": { 10 | "start": "wrangler dev", 11 | "deploy": "wrangler publish" 12 | }, 13 | "dependencies": { 14 | "@cloudflare/ai": "^1.0.14", 15 | "@cloudflare/itty-router-openapi": "^0.1.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example-plugin/src/index.ts: -------------------------------------------------------------------------------- 1 | import { OpenAPIRouter } from "@cloudflare/itty-router-openapi"; 2 | import { GetSearch } from "./search"; 3 | 4 | export const router = OpenAPIRouter({ 5 | schema: { 6 | info: { 7 | title: 'GitHub Repositories Search API', 8 | description: 'A plugin that allows the user to search for GitHub repositories using ChatGPT', 9 | version: 'v0.0.1', 10 | }, 11 | }, 12 | docs_url: '/', 13 | aiPlugin: { 14 | name_for_human: 'GitHub Repositories Search', 15 | name_for_model: 'github_repositories_search', 16 | description_for_human: "GitHub Repositories Search plugin for ChatGPT.", 17 | description_for_model: "GitHub Repositories Search plugin for ChatGPT. You can search for GitHub repositories using this plugin.", 18 | contact_email: 'support@example.com', 19 | legal_info_url: 'http://www.example.com/legal', 20 | logo_url: 'https://workers.cloudflare.com/resources/logo/logo.svg', 21 | }, 22 | }) 23 | 24 | router.get('/search', GetSearch) 25 | 26 | // 404 for everything else 27 | router.all('*', () => new Response('Not Found.', { status: 404 })) 28 | 29 | export default { 30 | fetch: router.handle 31 | } 32 | -------------------------------------------------------------------------------- /example-plugin/src/search.ts: -------------------------------------------------------------------------------- 1 | import { ApiException, OpenAPIRoute, Query, ValidationError } from "@cloudflare/itty-router-openapi"; 2 | import { Ai } from '@cloudflare/ai'; 3 | 4 | export class GetSearch extends OpenAPIRoute { 5 | static schema = { 6 | tags: ['Search'], 7 | summary: 'Search repositories by a query parameter', 8 | parameters: { 9 | q: Query(String, { 10 | description: 'The query to search for', 11 | default: 'cloudflare workers' 12 | }), 13 | }, 14 | responses: { 15 | '200': { 16 | schema: { 17 | repos: [ 18 | { 19 | name: 'itty-router-openapi', 20 | description: 'OpenAPI 3 schema generator and validator for Cloudflare Workers', 21 | stars: '80', 22 | url: 'https://github.com/cloudflare/itty-router-openapi', 23 | } 24 | ] 25 | }, 26 | }, 27 | }, 28 | } 29 | 30 | async handle(request: Request, env, ctx, data: Record) { 31 | // Initiate the AI 32 | const ai = new Ai(env.AI) 33 | 34 | const url = `https://api.github.com/search/repositories?q=${data.q}` 35 | 36 | const resp = await fetch(url, { 37 | headers: { 38 | 'Accept': 'application/vnd.github.v3+json', 39 | 'User-Agent': 'RepoAI - Cloudflare Workers ChatGPT Plugin Example' 40 | } 41 | }) 42 | 43 | if (!resp.ok) { 44 | return new Response(await resp.text(), { status: 400 }) 45 | } 46 | 47 | const json = await resp.json() 48 | 49 | // @ts-ignore 50 | const repos = json.items.map((item: any) => ({ 51 | name: item.name, 52 | description: item.description, 53 | stars: item.stargazers_count, 54 | url: item.html_url 55 | })) 56 | // Generate a response with Cloudflare AI: 57 | const messages = [ 58 | { role: 'system', content: "I'm going to help you to find the best GitHub repository based on your search." }, 59 | { role: 'user', content: 'My search is: ' + data.q }, 60 | ]; 61 | const response = await ai.run('@cf/meta/llama-2-7b-chat-int8', { messages }); 62 | 63 | return { 64 | repos: repos, 65 | ai: response 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example-plugin/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "cloudflare-ai-workers-chatgpt-plugin-example" 2 | main = "src/index.ts" 3 | compatibility_date = "2023-04-07" 4 | 5 | [ai] 6 | binding = "AI" # i.e. available in your Worker on env.AI -------------------------------------------------------------------------------- /example-retrieval-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .mf/ 3 | .dev.vars 4 | scripts/*.bulk.json 5 | scripts/*.ndjson 6 | scripts/*.bin 7 | -------------------------------------------------------------------------------- /example-retrieval-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Cloudflare Docs Retrieval Plugin 2 | 3 | A [ChatGPT Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin) 4 | for querying the [public Cloudflare Documentation](https://developers.cloudflare.com/), 5 | running as a [Worker](https://developers.cloudflare.com/workers/). 6 | 7 | Also includes a [scheduled Worker](https://developers.cloudflare.com/workers/platform/triggers/cron-triggers/) 8 | to periodically index the documentation, so the plugin always retrieves up-to-date data. 9 | 10 | The documents (markdown files) are converted into [embeddings](https://platform.openai.com/docs/guides/embeddings) 11 | so that relevant text can be retrieved quickly when a query comes through. These embedding vectors are stored in 12 | [KV](https://developers.cloudflare.com/workers/learning/how-kv-works/), Cloudflare's low-latency, key-value data store. 13 | 14 | ## Overview 15 | 16 | You'll need an [OpenAI API Key](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety) to run this. 17 | The (optional) scheduler Worker also needs a [token to access GitHub's GraphQL API](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic), 18 | and [Queues enabled on the Cloudflare account](https://developers.cloudflare.com/queues/). 19 | 20 | The plugin Worker responds to the `/query` endpoint – as well as returning the JSON manifests needed for the plugin. 21 | 22 | When a query comes in, the Worker will use the OpenAI API to convert the text into an embedding vector, and then 23 | compare that with the stored embeddings created from the documentation, to find the most relevant documents to return. 24 | ChatGPT will use these documents to inform its answer to the user's question. 25 | 26 | ## Plugin Worker 27 | 28 | Configuration can be found in `plugin/wrangler.toml` 29 | 30 | ### Running locally 31 | 32 | Create a preview KV namespace: 33 | 34 | ``` 35 | npx wrangler kv:namespace create retrieval --preview 36 | ``` 37 | 38 | Then update the `preview_id` in `plugin/wrangler.toml` – leave the `binding` name as-is 39 | 40 | Create a `.dev.vars` file in the `plugin` directory containing: 41 | 42 | ``` 43 | OPENAI_API_KEY="sk-..." 44 | ``` 45 | 46 | Then: 47 | 48 | ``` 49 | npm run dev 50 | ``` 51 | 52 | ### Deploying 53 | 54 | ``` 55 | cd plugin 56 | 57 | npx wrangler kv:namespace create retrieval 58 | ``` 59 | 60 | Then update the KV `id` (under `[[kv_namespaces]]`) in `plugin/wrangler.toml` – leave the `binding` name as-is 61 | 62 | Then you'll need to enter your `OPENAI_API_KEY` (you'll be prompted to paste it) 63 | 64 | ``` 65 | npx wrangler secret put OPENAI_API_KEY 66 | ``` 67 | 68 | And finally: 69 | 70 | ``` 71 | npx wrangler publish 72 | ``` 73 | 74 | (or `npm run deploy` from the same directory as this README) 75 | 76 | ## Refreshing the embeddings manually 77 | 78 | Before you can query anything, you'll need to populate the embeddings vector store, which you can do manually with: 79 | 80 | ``` 81 | OPENAI_API_KEY="sk-..." KV_NAMESPACE_ID="d03..." npm run refresh 82 | ``` 83 | 84 | ## Scheduler Worker (optional) 85 | 86 | Configuration can be found in `scheduler/wrangler.toml` 87 | 88 | ### Running locally 89 | 90 | If you haven't already: 91 | 92 | ``` 93 | npx wrangler kv:namespace create retrieval --preview 94 | ``` 95 | 96 | Then update the `preview_id` in `scheduler/wrangler.toml` – leave the `binding` name as-is 97 | 98 | Create a `.dev.vars` file in the `scheduler` directory containing: 99 | 100 | ``` 101 | OPENAI_API_KEY="sk-..." 102 | GITHUB_API_KEY="ghp_..." 103 | ``` 104 | 105 | Then: 106 | 107 | ``` 108 | npm run dev:scheduler 109 | ``` 110 | 111 | ### Deploying 112 | 113 | Update the KV `id` (under `[[kv_namespaces]]`) in `scheduler/wrangler.toml` 114 | to match the same as the Plugin Worker – leave the `binding` name as-is 115 | 116 | You'll need an [OpenAI API Key](https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety), a 117 | [token to access GitHub's GraphQL API](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic) 118 | (it needs a "classic" token, but you won't need any specific privileges if you're just reading from a public repo, 119 | you can leave them all unchecked), and [Queues enabled on the Cloudflare account](https://developers.cloudflare.com/queues/). 120 | 121 | Then you can create the resources needed for the scheduler worker: 122 | 123 | ``` 124 | cd scheduler 125 | 126 | npx wrangler queues create embeddings-resolver 127 | npx wrangler secret put OPENAI_API_KEY 128 | npx wrangler secret put GITHUB_API_KEY 129 | ``` 130 | 131 | And finally: 132 | 133 | ``` 134 | npx wrangler publish 135 | ``` 136 | 137 | (or `npm run deploy:scheduler` from the same directory as this README) 138 | -------------------------------------------------------------------------------- /example-retrieval-plugin/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-docs-retrieval-plugin", 3 | "version": "0.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cloudflare-docs-retrieval-plugin", 9 | "version": "0.0.0", 10 | "devDependencies": { 11 | "@cloudflare/workers-types": "^4.20230419.0", 12 | "@types/node": "^18.16.4", 13 | "wrangler": "^2.19.0" 14 | } 15 | }, 16 | "node_modules/@cloudflare/kv-asset-handler": { 17 | "version": "0.2.0", 18 | "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", 19 | "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", 20 | "dev": true, 21 | "dependencies": { 22 | "mime": "^3.0.0" 23 | } 24 | }, 25 | "node_modules/@cloudflare/workers-types": { 26 | "version": "4.20230419.0", 27 | "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20230419.0.tgz", 28 | "integrity": "sha512-MfNBlHrI/ekRkbLtdAo23D4hkXF+3QD92OCwuXxCUK73HtMHuBqkMp9T/8KFbKNRCnz7PzUderc7Jr5m3eeW3g==", 29 | "dev": true 30 | }, 31 | "node_modules/@esbuild-plugins/node-globals-polyfill": { 32 | "version": "0.1.1", 33 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", 34 | "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", 35 | "dev": true, 36 | "peerDependencies": { 37 | "esbuild": "*" 38 | } 39 | }, 40 | "node_modules/@esbuild-plugins/node-modules-polyfill": { 41 | "version": "0.1.4", 42 | "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", 43 | "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", 44 | "dev": true, 45 | "dependencies": { 46 | "escape-string-regexp": "^4.0.0", 47 | "rollup-plugin-node-polyfills": "^0.2.1" 48 | }, 49 | "peerDependencies": { 50 | "esbuild": "*" 51 | } 52 | }, 53 | "node_modules/@esbuild/android-arm": { 54 | "version": "0.16.3", 55 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.3.tgz", 56 | "integrity": "sha512-mueuEoh+s1eRbSJqq9KNBQwI4QhQV6sRXIfTyLXSHGMpyew61rOK4qY21uKbXl1iBoMb0AdL1deWFCQVlN2qHA==", 57 | "cpu": [ 58 | "arm" 59 | ], 60 | "dev": true, 61 | "optional": true, 62 | "os": [ 63 | "android" 64 | ], 65 | "engines": { 66 | "node": ">=12" 67 | } 68 | }, 69 | "node_modules/@esbuild/android-arm64": { 70 | "version": "0.16.3", 71 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.3.tgz", 72 | "integrity": "sha512-RolFVeinkeraDvN/OoRf1F/lP0KUfGNb5jxy/vkIMeRRChkrX/HTYN6TYZosRJs3a1+8wqpxAo5PI5hFmxyPRg==", 73 | "cpu": [ 74 | "arm64" 75 | ], 76 | "dev": true, 77 | "optional": true, 78 | "os": [ 79 | "android" 80 | ], 81 | "engines": { 82 | "node": ">=12" 83 | } 84 | }, 85 | "node_modules/@esbuild/android-x64": { 86 | "version": "0.16.3", 87 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.3.tgz", 88 | "integrity": "sha512-SFpTUcIT1bIJuCCBMCQWq1bL2gPTjWoLZdjmIhjdcQHaUfV41OQfho6Ici5uvvkMmZRXIUGpM3GxysP/EU7ifQ==", 89 | "cpu": [ 90 | "x64" 91 | ], 92 | "dev": true, 93 | "optional": true, 94 | "os": [ 95 | "android" 96 | ], 97 | "engines": { 98 | "node": ">=12" 99 | } 100 | }, 101 | "node_modules/@esbuild/darwin-arm64": { 102 | "version": "0.16.3", 103 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.3.tgz", 104 | "integrity": "sha512-DO8WykMyB+N9mIDfI/Hug70Dk1KipavlGAecxS3jDUwAbTpDXj0Lcwzw9svkhxfpCagDmpaTMgxWK8/C/XcXvw==", 105 | "cpu": [ 106 | "arm64" 107 | ], 108 | "dev": true, 109 | "optional": true, 110 | "os": [ 111 | "darwin" 112 | ], 113 | "engines": { 114 | "node": ">=12" 115 | } 116 | }, 117 | "node_modules/@esbuild/darwin-x64": { 118 | "version": "0.16.3", 119 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.3.tgz", 120 | "integrity": "sha512-uEqZQ2omc6BvWqdCiyZ5+XmxuHEi1SPzpVxXCSSV2+Sh7sbXbpeNhHIeFrIpRjAs0lI1FmA1iIOxFozKBhKgRQ==", 121 | "cpu": [ 122 | "x64" 123 | ], 124 | "dev": true, 125 | "optional": true, 126 | "os": [ 127 | "darwin" 128 | ], 129 | "engines": { 130 | "node": ">=12" 131 | } 132 | }, 133 | "node_modules/@esbuild/freebsd-arm64": { 134 | "version": "0.16.3", 135 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.3.tgz", 136 | "integrity": "sha512-nJansp3sSXakNkOD5i5mIz2Is/HjzIhFs49b1tjrPrpCmwgBmH9SSzhC/Z1UqlkivqMYkhfPwMw1dGFUuwmXhw==", 137 | "cpu": [ 138 | "arm64" 139 | ], 140 | "dev": true, 141 | "optional": true, 142 | "os": [ 143 | "freebsd" 144 | ], 145 | "engines": { 146 | "node": ">=12" 147 | } 148 | }, 149 | "node_modules/@esbuild/freebsd-x64": { 150 | "version": "0.16.3", 151 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.3.tgz", 152 | "integrity": "sha512-TfoDzLw+QHfc4a8aKtGSQ96Wa+6eimljjkq9HKR0rHlU83vw8aldMOUSJTUDxbcUdcgnJzPaX8/vGWm7vyV7ug==", 153 | "cpu": [ 154 | "x64" 155 | ], 156 | "dev": true, 157 | "optional": true, 158 | "os": [ 159 | "freebsd" 160 | ], 161 | "engines": { 162 | "node": ">=12" 163 | } 164 | }, 165 | "node_modules/@esbuild/linux-arm": { 166 | "version": "0.16.3", 167 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.3.tgz", 168 | "integrity": "sha512-VwswmSYwVAAq6LysV59Fyqk3UIjbhuc6wb3vEcJ7HEJUtFuLK9uXWuFoH1lulEbE4+5GjtHi3MHX+w1gNHdOWQ==", 169 | "cpu": [ 170 | "arm" 171 | ], 172 | "dev": true, 173 | "optional": true, 174 | "os": [ 175 | "linux" 176 | ], 177 | "engines": { 178 | "node": ">=12" 179 | } 180 | }, 181 | "node_modules/@esbuild/linux-arm64": { 182 | "version": "0.16.3", 183 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.3.tgz", 184 | "integrity": "sha512-7I3RlsnxEFCHVZNBLb2w7unamgZ5sVwO0/ikE2GaYvYuUQs9Qte/w7TqWcXHtCwxvZx/2+F97ndiUQAWs47ZfQ==", 185 | "cpu": [ 186 | "arm64" 187 | ], 188 | "dev": true, 189 | "optional": true, 190 | "os": [ 191 | "linux" 192 | ], 193 | "engines": { 194 | "node": ">=12" 195 | } 196 | }, 197 | "node_modules/@esbuild/linux-ia32": { 198 | "version": "0.16.3", 199 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.3.tgz", 200 | "integrity": "sha512-X8FDDxM9cqda2rJE+iblQhIMYY49LfvW4kaEjoFbTTQ4Go8G96Smj2w3BRTwA8IHGoi9dPOPGAX63dhuv19UqA==", 201 | "cpu": [ 202 | "ia32" 203 | ], 204 | "dev": true, 205 | "optional": true, 206 | "os": [ 207 | "linux" 208 | ], 209 | "engines": { 210 | "node": ">=12" 211 | } 212 | }, 213 | "node_modules/@esbuild/linux-loong64": { 214 | "version": "0.16.3", 215 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.3.tgz", 216 | "integrity": "sha512-hIbeejCOyO0X9ujfIIOKjBjNAs9XD/YdJ9JXAy1lHA+8UXuOqbFe4ErMCqMr8dhlMGBuvcQYGF7+kO7waj2KHw==", 217 | "cpu": [ 218 | "loong64" 219 | ], 220 | "dev": true, 221 | "optional": true, 222 | "os": [ 223 | "linux" 224 | ], 225 | "engines": { 226 | "node": ">=12" 227 | } 228 | }, 229 | "node_modules/@esbuild/linux-mips64el": { 230 | "version": "0.16.3", 231 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.3.tgz", 232 | "integrity": "sha512-znFRzICT/V8VZQMt6rjb21MtAVJv/3dmKRMlohlShrbVXdBuOdDrGb+C2cZGQAR8RFyRe7HS6klmHq103WpmVw==", 233 | "cpu": [ 234 | "mips64el" 235 | ], 236 | "dev": true, 237 | "optional": true, 238 | "os": [ 239 | "linux" 240 | ], 241 | "engines": { 242 | "node": ">=12" 243 | } 244 | }, 245 | "node_modules/@esbuild/linux-ppc64": { 246 | "version": "0.16.3", 247 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.3.tgz", 248 | "integrity": "sha512-EV7LuEybxhXrVTDpbqWF2yehYRNz5e5p+u3oQUS2+ZFpknyi1NXxr8URk4ykR8Efm7iu04//4sBg249yNOwy5Q==", 249 | "cpu": [ 250 | "ppc64" 251 | ], 252 | "dev": true, 253 | "optional": true, 254 | "os": [ 255 | "linux" 256 | ], 257 | "engines": { 258 | "node": ">=12" 259 | } 260 | }, 261 | "node_modules/@esbuild/linux-riscv64": { 262 | "version": "0.16.3", 263 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.3.tgz", 264 | "integrity": "sha512-uDxqFOcLzFIJ+r/pkTTSE9lsCEaV/Y6rMlQjUI9BkzASEChYL/aSQjZjchtEmdnVxDKETnUAmsaZ4pqK1eE5BQ==", 265 | "cpu": [ 266 | "riscv64" 267 | ], 268 | "dev": true, 269 | "optional": true, 270 | "os": [ 271 | "linux" 272 | ], 273 | "engines": { 274 | "node": ">=12" 275 | } 276 | }, 277 | "node_modules/@esbuild/linux-s390x": { 278 | "version": "0.16.3", 279 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.3.tgz", 280 | "integrity": "sha512-NbeREhzSxYwFhnCAQOQZmajsPYtX71Ufej3IQ8W2Gxskfz9DK58ENEju4SbpIj48VenktRASC52N5Fhyf/aliQ==", 281 | "cpu": [ 282 | "s390x" 283 | ], 284 | "dev": true, 285 | "optional": true, 286 | "os": [ 287 | "linux" 288 | ], 289 | "engines": { 290 | "node": ">=12" 291 | } 292 | }, 293 | "node_modules/@esbuild/linux-x64": { 294 | "version": "0.16.3", 295 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.3.tgz", 296 | "integrity": "sha512-SDiG0nCixYO9JgpehoKgScwic7vXXndfasjnD5DLbp1xltANzqZ425l7LSdHynt19UWOcDjG9wJJzSElsPvk0w==", 297 | "cpu": [ 298 | "x64" 299 | ], 300 | "dev": true, 301 | "optional": true, 302 | "os": [ 303 | "linux" 304 | ], 305 | "engines": { 306 | "node": ">=12" 307 | } 308 | }, 309 | "node_modules/@esbuild/netbsd-x64": { 310 | "version": "0.16.3", 311 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.3.tgz", 312 | "integrity": "sha512-AzbsJqiHEq1I/tUvOfAzCY15h4/7Ivp3ff/o1GpP16n48JMNAtbW0qui2WCgoIZArEHD0SUQ95gvR0oSO7ZbdA==", 313 | "cpu": [ 314 | "x64" 315 | ], 316 | "dev": true, 317 | "optional": true, 318 | "os": [ 319 | "netbsd" 320 | ], 321 | "engines": { 322 | "node": ">=12" 323 | } 324 | }, 325 | "node_modules/@esbuild/openbsd-x64": { 326 | "version": "0.16.3", 327 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.3.tgz", 328 | "integrity": "sha512-gSABi8qHl8k3Cbi/4toAzHiykuBuWLZs43JomTcXkjMZVkp0gj3gg9mO+9HJW/8GB5H89RX/V0QP4JGL7YEEVg==", 329 | "cpu": [ 330 | "x64" 331 | ], 332 | "dev": true, 333 | "optional": true, 334 | "os": [ 335 | "openbsd" 336 | ], 337 | "engines": { 338 | "node": ">=12" 339 | } 340 | }, 341 | "node_modules/@esbuild/sunos-x64": { 342 | "version": "0.16.3", 343 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.3.tgz", 344 | "integrity": "sha512-SF9Kch5Ete4reovvRO6yNjMxrvlfT0F0Flm+NPoUw5Z4Q3r1d23LFTgaLwm3Cp0iGbrU/MoUI+ZqwCv5XJijCw==", 345 | "cpu": [ 346 | "x64" 347 | ], 348 | "dev": true, 349 | "optional": true, 350 | "os": [ 351 | "sunos" 352 | ], 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/@esbuild/win32-arm64": { 358 | "version": "0.16.3", 359 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.3.tgz", 360 | "integrity": "sha512-u5aBonZIyGopAZyOnoPAA6fGsDeHByZ9CnEzyML9NqntK6D/xl5jteZUKm/p6nD09+v3pTM6TuUIqSPcChk5gg==", 361 | "cpu": [ 362 | "arm64" 363 | ], 364 | "dev": true, 365 | "optional": true, 366 | "os": [ 367 | "win32" 368 | ], 369 | "engines": { 370 | "node": ">=12" 371 | } 372 | }, 373 | "node_modules/@esbuild/win32-ia32": { 374 | "version": "0.16.3", 375 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.3.tgz", 376 | "integrity": "sha512-GlgVq1WpvOEhNioh74TKelwla9KDuAaLZrdxuuUgsP2vayxeLgVc+rbpIv0IYF4+tlIzq2vRhofV+KGLD+37EQ==", 377 | "cpu": [ 378 | "ia32" 379 | ], 380 | "dev": true, 381 | "optional": true, 382 | "os": [ 383 | "win32" 384 | ], 385 | "engines": { 386 | "node": ">=12" 387 | } 388 | }, 389 | "node_modules/@esbuild/win32-x64": { 390 | "version": "0.16.3", 391 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.3.tgz", 392 | "integrity": "sha512-5/JuTd8OWW8UzEtyf19fbrtMJENza+C9JoPIkvItgTBQ1FO2ZLvjbPO6Xs54vk0s5JB5QsfieUEshRQfu7ZHow==", 393 | "cpu": [ 394 | "x64" 395 | ], 396 | "dev": true, 397 | "optional": true, 398 | "os": [ 399 | "win32" 400 | ], 401 | "engines": { 402 | "node": ">=12" 403 | } 404 | }, 405 | "node_modules/@iarna/toml": { 406 | "version": "2.2.5", 407 | "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", 408 | "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", 409 | "dev": true 410 | }, 411 | "node_modules/@miniflare/cache": { 412 | "version": "2.13.0", 413 | "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.13.0.tgz", 414 | "integrity": "sha512-y3SdN3SVyPECWmLAEGkkrv0RB+LugEPs/FeXn8QtN9aE1vyj69clOAgmsDzoh1DpFfFsLKRiv05aWs4m79P8Xw==", 415 | "dev": true, 416 | "dependencies": { 417 | "@miniflare/core": "2.13.0", 418 | "@miniflare/shared": "2.13.0", 419 | "http-cache-semantics": "^4.1.0", 420 | "undici": "5.20.0" 421 | }, 422 | "engines": { 423 | "node": ">=16.13" 424 | } 425 | }, 426 | "node_modules/@miniflare/cli-parser": { 427 | "version": "2.13.0", 428 | "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.13.0.tgz", 429 | "integrity": "sha512-Nx1PIfuMZ3mK9Dg/JojWZAjHR16h1pcdCFSqYln/ME7y5ifx+P1E5UkShWUQ1cBlibNaltjbJ2n/7stSAsIGPQ==", 430 | "dev": true, 431 | "dependencies": { 432 | "@miniflare/shared": "2.13.0", 433 | "kleur": "^4.1.4" 434 | }, 435 | "engines": { 436 | "node": ">=16.13" 437 | } 438 | }, 439 | "node_modules/@miniflare/core": { 440 | "version": "2.13.0", 441 | "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.13.0.tgz", 442 | "integrity": "sha512-YJ/C0J3k+7xn4gvlMpvePnM3xC8nOnkweW96cc0IA8kJ1JSmScOO2tZ7rrU1RyDgp6StkAtQBw4yC0wYeFycBw==", 443 | "dev": true, 444 | "dependencies": { 445 | "@iarna/toml": "^2.2.5", 446 | "@miniflare/queues": "2.13.0", 447 | "@miniflare/shared": "2.13.0", 448 | "@miniflare/watcher": "2.13.0", 449 | "busboy": "^1.6.0", 450 | "dotenv": "^10.0.0", 451 | "kleur": "^4.1.4", 452 | "set-cookie-parser": "^2.4.8", 453 | "undici": "5.20.0", 454 | "urlpattern-polyfill": "^4.0.3" 455 | }, 456 | "engines": { 457 | "node": ">=16.13" 458 | } 459 | }, 460 | "node_modules/@miniflare/d1": { 461 | "version": "2.13.0", 462 | "resolved": "https://registry.npmjs.org/@miniflare/d1/-/d1-2.13.0.tgz", 463 | "integrity": "sha512-OslqjO8iTcvzyrC0spByftMboRmHJEyHyTHnlKkjWDGdQQztEOjso2Xj+3I4SZIeUYvbzDRhKLS2QXI9a8LS5A==", 464 | "dev": true, 465 | "dependencies": { 466 | "@miniflare/core": "2.13.0", 467 | "@miniflare/shared": "2.13.0" 468 | }, 469 | "engines": { 470 | "node": ">=16.7" 471 | } 472 | }, 473 | "node_modules/@miniflare/durable-objects": { 474 | "version": "2.13.0", 475 | "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.13.0.tgz", 476 | "integrity": "sha512-CRGVBPO9vY4Fc3aV+pdPRVVeYIt64vQqvw+BJbyW+TQtqVP2CGQeziJGnCfcONNNKyooZxGyUkHewUypyH+Qhg==", 477 | "dev": true, 478 | "dependencies": { 479 | "@miniflare/core": "2.13.0", 480 | "@miniflare/shared": "2.13.0", 481 | "@miniflare/storage-memory": "2.13.0", 482 | "undici": "5.20.0" 483 | }, 484 | "engines": { 485 | "node": ">=16.13" 486 | } 487 | }, 488 | "node_modules/@miniflare/html-rewriter": { 489 | "version": "2.13.0", 490 | "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.13.0.tgz", 491 | "integrity": "sha512-XhN7Icyzvtvu+o/A0hrnSiSmla78seCaNwQ9M1TDHxt352I/ahPX4wtPXs6GbKqY0/i+V6yoG2KGFRQ/j59cQQ==", 492 | "dev": true, 493 | "dependencies": { 494 | "@miniflare/core": "2.13.0", 495 | "@miniflare/shared": "2.13.0", 496 | "html-rewriter-wasm": "^0.4.1", 497 | "undici": "5.20.0" 498 | }, 499 | "engines": { 500 | "node": ">=16.13" 501 | } 502 | }, 503 | "node_modules/@miniflare/http-server": { 504 | "version": "2.13.0", 505 | "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.13.0.tgz", 506 | "integrity": "sha512-aMS/nUMTKP15hKnyZboeuWCiqmNrrCu+XRBY/TxDDl07iXcLpiHGf3oVv+yXxXkWlJHJVCbK7i/nXSNPllRMSw==", 507 | "dev": true, 508 | "dependencies": { 509 | "@miniflare/core": "2.13.0", 510 | "@miniflare/shared": "2.13.0", 511 | "@miniflare/web-sockets": "2.13.0", 512 | "kleur": "^4.1.4", 513 | "selfsigned": "^2.0.0", 514 | "undici": "5.20.0", 515 | "ws": "^8.2.2", 516 | "youch": "^2.2.2" 517 | }, 518 | "engines": { 519 | "node": ">=16.13" 520 | } 521 | }, 522 | "node_modules/@miniflare/kv": { 523 | "version": "2.13.0", 524 | "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.13.0.tgz", 525 | "integrity": "sha512-J0AS5x3g/YVOmHMxMAZs07nRXRvSo9jyuC0eikTBf+4AABvBIyvVYmdTjYNjCmr8O5smcfWBX5S27HelD3aAAQ==", 526 | "dev": true, 527 | "dependencies": { 528 | "@miniflare/shared": "2.13.0" 529 | }, 530 | "engines": { 531 | "node": ">=16.13" 532 | } 533 | }, 534 | "node_modules/@miniflare/queues": { 535 | "version": "2.13.0", 536 | "resolved": "https://registry.npmjs.org/@miniflare/queues/-/queues-2.13.0.tgz", 537 | "integrity": "sha512-Gf/a6M1mJL03iOvNqh3JNahcBfvEMPHnO28n0gkCoyYWGvddIr9lwCdFIa0qwNJsC1fIDRxhPg8PZ5cQLBMwRA==", 538 | "dev": true, 539 | "dependencies": { 540 | "@miniflare/shared": "2.13.0" 541 | }, 542 | "engines": { 543 | "node": ">=16.7" 544 | } 545 | }, 546 | "node_modules/@miniflare/r2": { 547 | "version": "2.13.0", 548 | "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.13.0.tgz", 549 | "integrity": "sha512-/5k6GHOYMNV/oBtilV9HDXBkJUrx8oXVigG5vxbnzEGRXyVRmR+Glzu7mFT8JiE94XiEbXHk9Qvu1S5Dej3wBw==", 550 | "dev": true, 551 | "dependencies": { 552 | "@miniflare/shared": "2.13.0", 553 | "undici": "5.20.0" 554 | }, 555 | "engines": { 556 | "node": ">=16.13" 557 | } 558 | }, 559 | "node_modules/@miniflare/runner-vm": { 560 | "version": "2.13.0", 561 | "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.13.0.tgz", 562 | "integrity": "sha512-VmKtF2cA8HmTuLXor1THWY0v+DmaobPct63iLcgWIaUdP3MIvL+9X8HDXFAviCR7bCTe6MKxckHkaOj0IE0aJQ==", 563 | "dev": true, 564 | "dependencies": { 565 | "@miniflare/shared": "2.13.0" 566 | }, 567 | "engines": { 568 | "node": ">=16.13" 569 | } 570 | }, 571 | "node_modules/@miniflare/scheduler": { 572 | "version": "2.13.0", 573 | "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.13.0.tgz", 574 | "integrity": "sha512-AOaQanoR4NjVEzVGWHnrL15A7aMx+d9AKLJhSDF7KaP+4NrT2Wo2BQuXCpn5oStx3itOdlQpMfqQ139e/I8WhQ==", 575 | "dev": true, 576 | "dependencies": { 577 | "@miniflare/core": "2.13.0", 578 | "@miniflare/shared": "2.13.0", 579 | "cron-schedule": "^3.0.4" 580 | }, 581 | "engines": { 582 | "node": ">=16.13" 583 | } 584 | }, 585 | "node_modules/@miniflare/shared": { 586 | "version": "2.13.0", 587 | "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.13.0.tgz", 588 | "integrity": "sha512-m8YFQzKmbjberrV9hPzNcQjNCXxjTjXUpuNrIGjAJO7g+BDztUHaZbdd26H9maBDlkeiWxA3hf0mDyCT/6MCMA==", 589 | "dev": true, 590 | "dependencies": { 591 | "@types/better-sqlite3": "^7.6.0", 592 | "kleur": "^4.1.4", 593 | "npx-import": "^1.1.4", 594 | "picomatch": "^2.3.1" 595 | }, 596 | "engines": { 597 | "node": ">=16.13" 598 | } 599 | }, 600 | "node_modules/@miniflare/sites": { 601 | "version": "2.13.0", 602 | "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.13.0.tgz", 603 | "integrity": "sha512-/tuzIu00o6CF2tkSv01q02MgEShXBSKx85h9jwWvc+6u7prGacAOer0FA1YNRFbE+t9QIfutAkoPGMA9zYf8+Q==", 604 | "dev": true, 605 | "dependencies": { 606 | "@miniflare/kv": "2.13.0", 607 | "@miniflare/shared": "2.13.0", 608 | "@miniflare/storage-file": "2.13.0" 609 | }, 610 | "engines": { 611 | "node": ">=16.13" 612 | } 613 | }, 614 | "node_modules/@miniflare/storage-file": { 615 | "version": "2.13.0", 616 | "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.13.0.tgz", 617 | "integrity": "sha512-LuAeAAY5046rq5U1eFLVkz+ppiFEWytWacpkQw92DvVKFFquZcXSj6WPxZF4rSs23WDk+rdcwuLekbb52aDR7A==", 618 | "dev": true, 619 | "dependencies": { 620 | "@miniflare/shared": "2.13.0", 621 | "@miniflare/storage-memory": "2.13.0" 622 | }, 623 | "engines": { 624 | "node": ">=16.13" 625 | } 626 | }, 627 | "node_modules/@miniflare/storage-memory": { 628 | "version": "2.13.0", 629 | "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.13.0.tgz", 630 | "integrity": "sha512-FnkYcBNXa/ym1ksNilNZycg9WYYKo6cWKplVBeSthRon3e8QY6t3n7/XRseBUo7O6mhDybVTy4wNCP1R2nBiEw==", 631 | "dev": true, 632 | "dependencies": { 633 | "@miniflare/shared": "2.13.0" 634 | }, 635 | "engines": { 636 | "node": ">=16.13" 637 | } 638 | }, 639 | "node_modules/@miniflare/watcher": { 640 | "version": "2.13.0", 641 | "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.13.0.tgz", 642 | "integrity": "sha512-teAacWcpMStoBLbLae95IUaL5lPzjPlXa9lhK9CbRaio/KRMibTMRGWrYos3IVGQRZvklvLwcms/nTvgcdb6yw==", 643 | "dev": true, 644 | "dependencies": { 645 | "@miniflare/shared": "2.13.0" 646 | }, 647 | "engines": { 648 | "node": ">=16.13" 649 | } 650 | }, 651 | "node_modules/@miniflare/web-sockets": { 652 | "version": "2.13.0", 653 | "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.13.0.tgz", 654 | "integrity": "sha512-+U2/HCf+BetRIgjAnNQjkuN6UeAjQmXifhQC+7CCaX834XJhrKXoR6z2xr2xkg1qj0qQs4D2jWG0KzrO5OUpug==", 655 | "dev": true, 656 | "dependencies": { 657 | "@miniflare/core": "2.13.0", 658 | "@miniflare/shared": "2.13.0", 659 | "undici": "5.20.0", 660 | "ws": "^8.2.2" 661 | }, 662 | "engines": { 663 | "node": ">=16.13" 664 | } 665 | }, 666 | "node_modules/@types/better-sqlite3": { 667 | "version": "7.6.4", 668 | "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.4.tgz", 669 | "integrity": "sha512-dzrRZCYPXIXfSR1/surNbJ/grU3scTaygS0OMzjlGf71i9sc2fGyHPXXiXmEvNIoE0cGwsanEFMVJxPXmco9Eg==", 670 | "dev": true, 671 | "dependencies": { 672 | "@types/node": "*" 673 | } 674 | }, 675 | "node_modules/@types/node": { 676 | "version": "18.16.4", 677 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.4.tgz", 678 | "integrity": "sha512-LUhvPmAKAbgm+p/K11IWszLZVoZDlMF4NRmqbhEzDz/CnCuehPkZXwZbBCKGJsgjnuVejotBwM7B3Scrq4EqDw==", 679 | "dev": true 680 | }, 681 | "node_modules/@types/stack-trace": { 682 | "version": "0.0.29", 683 | "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", 684 | "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", 685 | "dev": true 686 | }, 687 | "node_modules/anymatch": { 688 | "version": "3.1.3", 689 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 690 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 691 | "dev": true, 692 | "dependencies": { 693 | "normalize-path": "^3.0.0", 694 | "picomatch": "^2.0.4" 695 | }, 696 | "engines": { 697 | "node": ">= 8" 698 | } 699 | }, 700 | "node_modules/binary-extensions": { 701 | "version": "2.2.0", 702 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 703 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 704 | "dev": true, 705 | "engines": { 706 | "node": ">=8" 707 | } 708 | }, 709 | "node_modules/blake3-wasm": { 710 | "version": "2.1.5", 711 | "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 712 | "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 713 | "dev": true 714 | }, 715 | "node_modules/braces": { 716 | "version": "3.0.2", 717 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 718 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 719 | "dev": true, 720 | "dependencies": { 721 | "fill-range": "^7.0.1" 722 | }, 723 | "engines": { 724 | "node": ">=8" 725 | } 726 | }, 727 | "node_modules/buffer-from": { 728 | "version": "1.1.2", 729 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 730 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 731 | "dev": true 732 | }, 733 | "node_modules/builtins": { 734 | "version": "5.0.1", 735 | "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", 736 | "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", 737 | "dev": true, 738 | "dependencies": { 739 | "semver": "^7.0.0" 740 | } 741 | }, 742 | "node_modules/busboy": { 743 | "version": "1.6.0", 744 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 745 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 746 | "dev": true, 747 | "dependencies": { 748 | "streamsearch": "^1.1.0" 749 | }, 750 | "engines": { 751 | "node": ">=10.16.0" 752 | } 753 | }, 754 | "node_modules/chokidar": { 755 | "version": "3.5.3", 756 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 757 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 758 | "dev": true, 759 | "funding": [ 760 | { 761 | "type": "individual", 762 | "url": "https://paulmillr.com/funding/" 763 | } 764 | ], 765 | "dependencies": { 766 | "anymatch": "~3.1.2", 767 | "braces": "~3.0.2", 768 | "glob-parent": "~5.1.2", 769 | "is-binary-path": "~2.1.0", 770 | "is-glob": "~4.0.1", 771 | "normalize-path": "~3.0.0", 772 | "readdirp": "~3.6.0" 773 | }, 774 | "engines": { 775 | "node": ">= 8.10.0" 776 | }, 777 | "optionalDependencies": { 778 | "fsevents": "~2.3.2" 779 | } 780 | }, 781 | "node_modules/cookie": { 782 | "version": "0.4.2", 783 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 784 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 785 | "dev": true, 786 | "engines": { 787 | "node": ">= 0.6" 788 | } 789 | }, 790 | "node_modules/cron-schedule": { 791 | "version": "3.0.6", 792 | "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", 793 | "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==", 794 | "dev": true 795 | }, 796 | "node_modules/cross-spawn": { 797 | "version": "7.0.3", 798 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 799 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 800 | "dev": true, 801 | "dependencies": { 802 | "path-key": "^3.1.0", 803 | "shebang-command": "^2.0.0", 804 | "which": "^2.0.1" 805 | }, 806 | "engines": { 807 | "node": ">= 8" 808 | } 809 | }, 810 | "node_modules/dotenv": { 811 | "version": "10.0.0", 812 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 813 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 814 | "dev": true, 815 | "engines": { 816 | "node": ">=10" 817 | } 818 | }, 819 | "node_modules/esbuild": { 820 | "version": "0.16.3", 821 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.3.tgz", 822 | "integrity": "sha512-71f7EjPWTiSguen8X/kxEpkAS7BFHwtQKisCDDV3Y4GLGWBaoSCyD5uXkaUew6JDzA9FEN1W23mdnSwW9kqCeg==", 823 | "dev": true, 824 | "hasInstallScript": true, 825 | "bin": { 826 | "esbuild": "bin/esbuild" 827 | }, 828 | "engines": { 829 | "node": ">=12" 830 | }, 831 | "optionalDependencies": { 832 | "@esbuild/android-arm": "0.16.3", 833 | "@esbuild/android-arm64": "0.16.3", 834 | "@esbuild/android-x64": "0.16.3", 835 | "@esbuild/darwin-arm64": "0.16.3", 836 | "@esbuild/darwin-x64": "0.16.3", 837 | "@esbuild/freebsd-arm64": "0.16.3", 838 | "@esbuild/freebsd-x64": "0.16.3", 839 | "@esbuild/linux-arm": "0.16.3", 840 | "@esbuild/linux-arm64": "0.16.3", 841 | "@esbuild/linux-ia32": "0.16.3", 842 | "@esbuild/linux-loong64": "0.16.3", 843 | "@esbuild/linux-mips64el": "0.16.3", 844 | "@esbuild/linux-ppc64": "0.16.3", 845 | "@esbuild/linux-riscv64": "0.16.3", 846 | "@esbuild/linux-s390x": "0.16.3", 847 | "@esbuild/linux-x64": "0.16.3", 848 | "@esbuild/netbsd-x64": "0.16.3", 849 | "@esbuild/openbsd-x64": "0.16.3", 850 | "@esbuild/sunos-x64": "0.16.3", 851 | "@esbuild/win32-arm64": "0.16.3", 852 | "@esbuild/win32-ia32": "0.16.3", 853 | "@esbuild/win32-x64": "0.16.3" 854 | } 855 | }, 856 | "node_modules/escape-string-regexp": { 857 | "version": "4.0.0", 858 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 859 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 860 | "dev": true, 861 | "engines": { 862 | "node": ">=10" 863 | }, 864 | "funding": { 865 | "url": "https://github.com/sponsors/sindresorhus" 866 | } 867 | }, 868 | "node_modules/estree-walker": { 869 | "version": "0.6.1", 870 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 871 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 872 | "dev": true 873 | }, 874 | "node_modules/execa": { 875 | "version": "6.1.0", 876 | "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", 877 | "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", 878 | "dev": true, 879 | "dependencies": { 880 | "cross-spawn": "^7.0.3", 881 | "get-stream": "^6.0.1", 882 | "human-signals": "^3.0.1", 883 | "is-stream": "^3.0.0", 884 | "merge-stream": "^2.0.0", 885 | "npm-run-path": "^5.1.0", 886 | "onetime": "^6.0.0", 887 | "signal-exit": "^3.0.7", 888 | "strip-final-newline": "^3.0.0" 889 | }, 890 | "engines": { 891 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 892 | }, 893 | "funding": { 894 | "url": "https://github.com/sindresorhus/execa?sponsor=1" 895 | } 896 | }, 897 | "node_modules/fill-range": { 898 | "version": "7.0.1", 899 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 900 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 901 | "dev": true, 902 | "dependencies": { 903 | "to-regex-range": "^5.0.1" 904 | }, 905 | "engines": { 906 | "node": ">=8" 907 | } 908 | }, 909 | "node_modules/fsevents": { 910 | "version": "2.3.2", 911 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 912 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 913 | "dev": true, 914 | "hasInstallScript": true, 915 | "optional": true, 916 | "os": [ 917 | "darwin" 918 | ], 919 | "engines": { 920 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 921 | } 922 | }, 923 | "node_modules/get-stream": { 924 | "version": "6.0.1", 925 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", 926 | "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", 927 | "dev": true, 928 | "engines": { 929 | "node": ">=10" 930 | }, 931 | "funding": { 932 | "url": "https://github.com/sponsors/sindresorhus" 933 | } 934 | }, 935 | "node_modules/glob-parent": { 936 | "version": "5.1.2", 937 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 938 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 939 | "dev": true, 940 | "dependencies": { 941 | "is-glob": "^4.0.1" 942 | }, 943 | "engines": { 944 | "node": ">= 6" 945 | } 946 | }, 947 | "node_modules/html-rewriter-wasm": { 948 | "version": "0.4.1", 949 | "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", 950 | "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==", 951 | "dev": true 952 | }, 953 | "node_modules/http-cache-semantics": { 954 | "version": "4.1.1", 955 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", 956 | "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", 957 | "dev": true 958 | }, 959 | "node_modules/human-signals": { 960 | "version": "3.0.1", 961 | "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", 962 | "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", 963 | "dev": true, 964 | "engines": { 965 | "node": ">=12.20.0" 966 | } 967 | }, 968 | "node_modules/is-binary-path": { 969 | "version": "2.1.0", 970 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 971 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 972 | "dev": true, 973 | "dependencies": { 974 | "binary-extensions": "^2.0.0" 975 | }, 976 | "engines": { 977 | "node": ">=8" 978 | } 979 | }, 980 | "node_modules/is-extglob": { 981 | "version": "2.1.1", 982 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 983 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 984 | "dev": true, 985 | "engines": { 986 | "node": ">=0.10.0" 987 | } 988 | }, 989 | "node_modules/is-glob": { 990 | "version": "4.0.3", 991 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 992 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 993 | "dev": true, 994 | "dependencies": { 995 | "is-extglob": "^2.1.1" 996 | }, 997 | "engines": { 998 | "node": ">=0.10.0" 999 | } 1000 | }, 1001 | "node_modules/is-number": { 1002 | "version": "7.0.0", 1003 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1004 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1005 | "dev": true, 1006 | "engines": { 1007 | "node": ">=0.12.0" 1008 | } 1009 | }, 1010 | "node_modules/is-stream": { 1011 | "version": "3.0.0", 1012 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", 1013 | "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", 1014 | "dev": true, 1015 | "engines": { 1016 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1017 | }, 1018 | "funding": { 1019 | "url": "https://github.com/sponsors/sindresorhus" 1020 | } 1021 | }, 1022 | "node_modules/isexe": { 1023 | "version": "2.0.0", 1024 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1025 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1026 | "dev": true 1027 | }, 1028 | "node_modules/kleur": { 1029 | "version": "4.1.5", 1030 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 1031 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 1032 | "dev": true, 1033 | "engines": { 1034 | "node": ">=6" 1035 | } 1036 | }, 1037 | "node_modules/lru-cache": { 1038 | "version": "6.0.0", 1039 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1040 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1041 | "dev": true, 1042 | "dependencies": { 1043 | "yallist": "^4.0.0" 1044 | }, 1045 | "engines": { 1046 | "node": ">=10" 1047 | } 1048 | }, 1049 | "node_modules/magic-string": { 1050 | "version": "0.25.9", 1051 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", 1052 | "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", 1053 | "dev": true, 1054 | "dependencies": { 1055 | "sourcemap-codec": "^1.4.8" 1056 | } 1057 | }, 1058 | "node_modules/merge-stream": { 1059 | "version": "2.0.0", 1060 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 1061 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 1062 | "dev": true 1063 | }, 1064 | "node_modules/mime": { 1065 | "version": "3.0.0", 1066 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 1067 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 1068 | "dev": true, 1069 | "bin": { 1070 | "mime": "cli.js" 1071 | }, 1072 | "engines": { 1073 | "node": ">=10.0.0" 1074 | } 1075 | }, 1076 | "node_modules/mimic-fn": { 1077 | "version": "4.0.0", 1078 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", 1079 | "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", 1080 | "dev": true, 1081 | "engines": { 1082 | "node": ">=12" 1083 | }, 1084 | "funding": { 1085 | "url": "https://github.com/sponsors/sindresorhus" 1086 | } 1087 | }, 1088 | "node_modules/miniflare": { 1089 | "version": "2.13.0", 1090 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.13.0.tgz", 1091 | "integrity": "sha512-ayNhVa4a6bZiOuHtrPmOt4BCYcmW1fBQ/+qGL85smq1m2OBBm3aUs6f4ISf38xH8tk+qewgmAywetyVtn6KHPw==", 1092 | "dev": true, 1093 | "dependencies": { 1094 | "@miniflare/cache": "2.13.0", 1095 | "@miniflare/cli-parser": "2.13.0", 1096 | "@miniflare/core": "2.13.0", 1097 | "@miniflare/d1": "2.13.0", 1098 | "@miniflare/durable-objects": "2.13.0", 1099 | "@miniflare/html-rewriter": "2.13.0", 1100 | "@miniflare/http-server": "2.13.0", 1101 | "@miniflare/kv": "2.13.0", 1102 | "@miniflare/queues": "2.13.0", 1103 | "@miniflare/r2": "2.13.0", 1104 | "@miniflare/runner-vm": "2.13.0", 1105 | "@miniflare/scheduler": "2.13.0", 1106 | "@miniflare/shared": "2.13.0", 1107 | "@miniflare/sites": "2.13.0", 1108 | "@miniflare/storage-file": "2.13.0", 1109 | "@miniflare/storage-memory": "2.13.0", 1110 | "@miniflare/web-sockets": "2.13.0", 1111 | "kleur": "^4.1.4", 1112 | "semiver": "^1.1.0", 1113 | "source-map-support": "^0.5.20", 1114 | "undici": "5.20.0" 1115 | }, 1116 | "bin": { 1117 | "miniflare": "bootstrap.js" 1118 | }, 1119 | "engines": { 1120 | "node": ">=16.13" 1121 | }, 1122 | "peerDependencies": { 1123 | "@miniflare/storage-redis": "2.13.0", 1124 | "cron-schedule": "^3.0.4", 1125 | "ioredis": "^4.27.9" 1126 | }, 1127 | "peerDependenciesMeta": { 1128 | "@miniflare/storage-redis": { 1129 | "optional": true 1130 | }, 1131 | "cron-schedule": { 1132 | "optional": true 1133 | }, 1134 | "ioredis": { 1135 | "optional": true 1136 | } 1137 | } 1138 | }, 1139 | "node_modules/mustache": { 1140 | "version": "4.2.0", 1141 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1142 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1143 | "dev": true, 1144 | "bin": { 1145 | "mustache": "bin/mustache" 1146 | } 1147 | }, 1148 | "node_modules/nanoid": { 1149 | "version": "3.3.6", 1150 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 1151 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 1152 | "dev": true, 1153 | "funding": [ 1154 | { 1155 | "type": "github", 1156 | "url": "https://github.com/sponsors/ai" 1157 | } 1158 | ], 1159 | "bin": { 1160 | "nanoid": "bin/nanoid.cjs" 1161 | }, 1162 | "engines": { 1163 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1164 | } 1165 | }, 1166 | "node_modules/node-forge": { 1167 | "version": "1.3.1", 1168 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", 1169 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", 1170 | "dev": true, 1171 | "engines": { 1172 | "node": ">= 6.13.0" 1173 | } 1174 | }, 1175 | "node_modules/normalize-path": { 1176 | "version": "3.0.0", 1177 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1178 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1179 | "dev": true, 1180 | "engines": { 1181 | "node": ">=0.10.0" 1182 | } 1183 | }, 1184 | "node_modules/npm-run-path": { 1185 | "version": "5.1.0", 1186 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", 1187 | "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", 1188 | "dev": true, 1189 | "dependencies": { 1190 | "path-key": "^4.0.0" 1191 | }, 1192 | "engines": { 1193 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1194 | }, 1195 | "funding": { 1196 | "url": "https://github.com/sponsors/sindresorhus" 1197 | } 1198 | }, 1199 | "node_modules/npm-run-path/node_modules/path-key": { 1200 | "version": "4.0.0", 1201 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", 1202 | "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", 1203 | "dev": true, 1204 | "engines": { 1205 | "node": ">=12" 1206 | }, 1207 | "funding": { 1208 | "url": "https://github.com/sponsors/sindresorhus" 1209 | } 1210 | }, 1211 | "node_modules/npx-import": { 1212 | "version": "1.1.4", 1213 | "resolved": "https://registry.npmjs.org/npx-import/-/npx-import-1.1.4.tgz", 1214 | "integrity": "sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==", 1215 | "dev": true, 1216 | "dependencies": { 1217 | "execa": "^6.1.0", 1218 | "parse-package-name": "^1.0.0", 1219 | "semver": "^7.3.7", 1220 | "validate-npm-package-name": "^4.0.0" 1221 | } 1222 | }, 1223 | "node_modules/onetime": { 1224 | "version": "6.0.0", 1225 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", 1226 | "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", 1227 | "dev": true, 1228 | "dependencies": { 1229 | "mimic-fn": "^4.0.0" 1230 | }, 1231 | "engines": { 1232 | "node": ">=12" 1233 | }, 1234 | "funding": { 1235 | "url": "https://github.com/sponsors/sindresorhus" 1236 | } 1237 | }, 1238 | "node_modules/parse-package-name": { 1239 | "version": "1.0.0", 1240 | "resolved": "https://registry.npmjs.org/parse-package-name/-/parse-package-name-1.0.0.tgz", 1241 | "integrity": "sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==", 1242 | "dev": true 1243 | }, 1244 | "node_modules/path-key": { 1245 | "version": "3.1.1", 1246 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1247 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1248 | "dev": true, 1249 | "engines": { 1250 | "node": ">=8" 1251 | } 1252 | }, 1253 | "node_modules/path-to-regexp": { 1254 | "version": "6.2.1", 1255 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", 1256 | "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", 1257 | "dev": true 1258 | }, 1259 | "node_modules/picomatch": { 1260 | "version": "2.3.1", 1261 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1262 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1263 | "dev": true, 1264 | "engines": { 1265 | "node": ">=8.6" 1266 | }, 1267 | "funding": { 1268 | "url": "https://github.com/sponsors/jonschlinkert" 1269 | } 1270 | }, 1271 | "node_modules/readdirp": { 1272 | "version": "3.6.0", 1273 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1274 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1275 | "dev": true, 1276 | "dependencies": { 1277 | "picomatch": "^2.2.1" 1278 | }, 1279 | "engines": { 1280 | "node": ">=8.10.0" 1281 | } 1282 | }, 1283 | "node_modules/rollup-plugin-inject": { 1284 | "version": "3.0.2", 1285 | "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", 1286 | "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", 1287 | "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", 1288 | "dev": true, 1289 | "dependencies": { 1290 | "estree-walker": "^0.6.1", 1291 | "magic-string": "^0.25.3", 1292 | "rollup-pluginutils": "^2.8.1" 1293 | } 1294 | }, 1295 | "node_modules/rollup-plugin-node-polyfills": { 1296 | "version": "0.2.1", 1297 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", 1298 | "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", 1299 | "dev": true, 1300 | "dependencies": { 1301 | "rollup-plugin-inject": "^3.0.0" 1302 | } 1303 | }, 1304 | "node_modules/rollup-pluginutils": { 1305 | "version": "2.8.2", 1306 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 1307 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 1308 | "dev": true, 1309 | "dependencies": { 1310 | "estree-walker": "^0.6.1" 1311 | } 1312 | }, 1313 | "node_modules/selfsigned": { 1314 | "version": "2.1.1", 1315 | "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", 1316 | "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", 1317 | "dev": true, 1318 | "dependencies": { 1319 | "node-forge": "^1" 1320 | }, 1321 | "engines": { 1322 | "node": ">=10" 1323 | } 1324 | }, 1325 | "node_modules/semiver": { 1326 | "version": "1.1.0", 1327 | "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", 1328 | "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", 1329 | "dev": true, 1330 | "engines": { 1331 | "node": ">=6" 1332 | } 1333 | }, 1334 | "node_modules/semver": { 1335 | "version": "7.3.8", 1336 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", 1337 | "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", 1338 | "dev": true, 1339 | "dependencies": { 1340 | "lru-cache": "^6.0.0" 1341 | }, 1342 | "bin": { 1343 | "semver": "bin/semver.js" 1344 | }, 1345 | "engines": { 1346 | "node": ">=10" 1347 | } 1348 | }, 1349 | "node_modules/set-cookie-parser": { 1350 | "version": "2.6.0", 1351 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", 1352 | "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", 1353 | "dev": true 1354 | }, 1355 | "node_modules/shebang-command": { 1356 | "version": "2.0.0", 1357 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1358 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1359 | "dev": true, 1360 | "dependencies": { 1361 | "shebang-regex": "^3.0.0" 1362 | }, 1363 | "engines": { 1364 | "node": ">=8" 1365 | } 1366 | }, 1367 | "node_modules/shebang-regex": { 1368 | "version": "3.0.0", 1369 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1370 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1371 | "dev": true, 1372 | "engines": { 1373 | "node": ">=8" 1374 | } 1375 | }, 1376 | "node_modules/signal-exit": { 1377 | "version": "3.0.7", 1378 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1379 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", 1380 | "dev": true 1381 | }, 1382 | "node_modules/source-map": { 1383 | "version": "0.7.4", 1384 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", 1385 | "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", 1386 | "dev": true, 1387 | "engines": { 1388 | "node": ">= 8" 1389 | } 1390 | }, 1391 | "node_modules/source-map-support": { 1392 | "version": "0.5.21", 1393 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1394 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1395 | "dev": true, 1396 | "dependencies": { 1397 | "buffer-from": "^1.0.0", 1398 | "source-map": "^0.6.0" 1399 | } 1400 | }, 1401 | "node_modules/source-map-support/node_modules/source-map": { 1402 | "version": "0.6.1", 1403 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1404 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1405 | "dev": true, 1406 | "engines": { 1407 | "node": ">=0.10.0" 1408 | } 1409 | }, 1410 | "node_modules/sourcemap-codec": { 1411 | "version": "1.4.8", 1412 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 1413 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 1414 | "deprecated": "Please use @jridgewell/sourcemap-codec instead", 1415 | "dev": true 1416 | }, 1417 | "node_modules/stack-trace": { 1418 | "version": "0.0.10", 1419 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1420 | "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", 1421 | "dev": true, 1422 | "engines": { 1423 | "node": "*" 1424 | } 1425 | }, 1426 | "node_modules/streamsearch": { 1427 | "version": "1.1.0", 1428 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1429 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1430 | "dev": true, 1431 | "engines": { 1432 | "node": ">=10.0.0" 1433 | } 1434 | }, 1435 | "node_modules/strip-final-newline": { 1436 | "version": "3.0.0", 1437 | "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", 1438 | "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", 1439 | "dev": true, 1440 | "engines": { 1441 | "node": ">=12" 1442 | }, 1443 | "funding": { 1444 | "url": "https://github.com/sponsors/sindresorhus" 1445 | } 1446 | }, 1447 | "node_modules/to-regex-range": { 1448 | "version": "5.0.1", 1449 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1450 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1451 | "dev": true, 1452 | "dependencies": { 1453 | "is-number": "^7.0.0" 1454 | }, 1455 | "engines": { 1456 | "node": ">=8.0" 1457 | } 1458 | }, 1459 | "node_modules/undici": { 1460 | "version": "5.20.0", 1461 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", 1462 | "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", 1463 | "dev": true, 1464 | "dependencies": { 1465 | "busboy": "^1.6.0" 1466 | }, 1467 | "engines": { 1468 | "node": ">=12.18" 1469 | } 1470 | }, 1471 | "node_modules/urlpattern-polyfill": { 1472 | "version": "4.0.3", 1473 | "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", 1474 | "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==", 1475 | "dev": true 1476 | }, 1477 | "node_modules/validate-npm-package-name": { 1478 | "version": "4.0.0", 1479 | "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", 1480 | "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", 1481 | "dev": true, 1482 | "dependencies": { 1483 | "builtins": "^5.0.0" 1484 | }, 1485 | "engines": { 1486 | "node": "^12.13.0 || ^14.15.0 || >=16.0.0" 1487 | } 1488 | }, 1489 | "node_modules/which": { 1490 | "version": "2.0.2", 1491 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1492 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1493 | "dev": true, 1494 | "dependencies": { 1495 | "isexe": "^2.0.0" 1496 | }, 1497 | "bin": { 1498 | "node-which": "bin/node-which" 1499 | }, 1500 | "engines": { 1501 | "node": ">= 8" 1502 | } 1503 | }, 1504 | "node_modules/wrangler": { 1505 | "version": "2.19.0", 1506 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.19.0.tgz", 1507 | "integrity": "sha512-/XykXkD7dtvBMWq2p84V/aHhkifZA15+HBD7nm/pV62s6MJrJ+DWd1QJB7CF2D7DqknBLxdWpmmZB6YCo0BtMw==", 1508 | "dev": true, 1509 | "dependencies": { 1510 | "@cloudflare/kv-asset-handler": "^0.2.0", 1511 | "@esbuild-plugins/node-globals-polyfill": "^0.1.1", 1512 | "@esbuild-plugins/node-modules-polyfill": "^0.1.4", 1513 | "@miniflare/core": "2.13.0", 1514 | "@miniflare/d1": "2.13.0", 1515 | "@miniflare/durable-objects": "2.13.0", 1516 | "blake3-wasm": "^2.1.5", 1517 | "chokidar": "^3.5.3", 1518 | "esbuild": "0.16.3", 1519 | "miniflare": "2.13.0", 1520 | "nanoid": "^3.3.3", 1521 | "path-to-regexp": "^6.2.0", 1522 | "selfsigned": "^2.0.1", 1523 | "source-map": "^0.7.4", 1524 | "xxhash-wasm": "^1.0.1" 1525 | }, 1526 | "bin": { 1527 | "wrangler": "bin/wrangler.js", 1528 | "wrangler2": "bin/wrangler.js" 1529 | }, 1530 | "engines": { 1531 | "node": ">=16.13.0" 1532 | }, 1533 | "optionalDependencies": { 1534 | "fsevents": "~2.3.2" 1535 | } 1536 | }, 1537 | "node_modules/ws": { 1538 | "version": "8.13.0", 1539 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", 1540 | "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", 1541 | "dev": true, 1542 | "engines": { 1543 | "node": ">=10.0.0" 1544 | }, 1545 | "peerDependencies": { 1546 | "bufferutil": "^4.0.1", 1547 | "utf-8-validate": ">=5.0.2" 1548 | }, 1549 | "peerDependenciesMeta": { 1550 | "bufferutil": { 1551 | "optional": true 1552 | }, 1553 | "utf-8-validate": { 1554 | "optional": true 1555 | } 1556 | } 1557 | }, 1558 | "node_modules/xxhash-wasm": { 1559 | "version": "1.0.2", 1560 | "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz", 1561 | "integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==", 1562 | "dev": true 1563 | }, 1564 | "node_modules/yallist": { 1565 | "version": "4.0.0", 1566 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1567 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1568 | "dev": true 1569 | }, 1570 | "node_modules/youch": { 1571 | "version": "2.2.2", 1572 | "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", 1573 | "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", 1574 | "dev": true, 1575 | "dependencies": { 1576 | "@types/stack-trace": "0.0.29", 1577 | "cookie": "^0.4.1", 1578 | "mustache": "^4.2.0", 1579 | "stack-trace": "0.0.10" 1580 | } 1581 | } 1582 | } 1583 | } 1584 | -------------------------------------------------------------------------------- /example-retrieval-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-docs-retrieval-plugin", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "private": true, 6 | "scripts": { 7 | "dev": "cd plugin && wrangler dev --local", 8 | "deploy": "cd plugin && wrangler publish", 9 | "dev:scheduler": "cd scheduler && wrangler dev --local", 10 | "deploy:scheduler": "cd scheduler && wrangler publish", 11 | "refresh": "cd scripts && ./refresh.sh", 12 | "schema": "cd plugin/src/manifests && npx openapi-typescript openapi.json > openapi.d.ts" 13 | }, 14 | "devDependencies": { 15 | "@cloudflare/workers-types": "^4.20230419.0", 16 | "@types/node": "^18.16.4", 17 | "wrangler": "^2.19.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example-retrieval-plugin/plugin/src/index.js: -------------------------------------------------------------------------------- 1 | import { fetchEmbeddings } from "../../shared/openai.js"; 2 | import { VectorCollection } from "../../shared/vector.js"; 3 | import { pathToUrl } from "../../shared/docs.js"; 4 | import aiPluginJson from "./manifests/ai-plugin.json"; 5 | import openaiJson from "./manifests/openapi.json"; 6 | 7 | /** 8 | * @typedef { import("./manifests/openapi.js").components["schemas"]["QueryRequest"] } QueryRequest 9 | * @typedef { import("./manifests/openapi.js").components["schemas"]["QueryResponse"] } QueryResponse 10 | */ 11 | 12 | /** @type {ExportedHandler} */ 13 | export default { 14 | async fetch(request, env, ctx) { 15 | try { 16 | return await handleRequest(request, env, ctx); 17 | } catch (e) { 18 | return Response.json( 19 | { error: /** @type {Error} */ (e).message }, 20 | { status: 500 } 21 | ); 22 | } 23 | }, 24 | }; 25 | 26 | /** @type {ExportedHandlerFetchHandler} */ 27 | async function handleRequest(request, env, ctx) { 28 | const url = new URL(request.url); 29 | const host = request.headers.get("host") ?? "localhost"; 30 | 31 | // Retrieval Plugin manifest 32 | if ( 33 | request.method === "GET" && 34 | url.pathname === "/.well-known/ai-plugin.json" 35 | ) { 36 | aiPluginJson.api.url = `https://${host}/.well-known/openapi.json`; 37 | return Response.json(aiPluginJson); 38 | } 39 | 40 | // OpenAPI spec 41 | if ( 42 | request.method === "GET" && 43 | url.pathname === "/.well-known/openapi.json" 44 | ) { 45 | openaiJson.servers[0].url = `https://${host}`; 46 | return Response.json(openaiJson); 47 | } 48 | 49 | // Endpoint that OpenAI hits for the Retrieval Plugin 50 | if (request.method === "POST" && url.pathname === "/query") { 51 | const pluginResponse = await retrievalPluginQuery(request, env, ctx); 52 | return Response.json(pluginResponse); 53 | } 54 | 55 | return Response.json({ error: "not found" }, { status: 404 }); 56 | } 57 | 58 | /** 59 | * @param {Request} request 60 | * @param {Env} env 61 | * @param {ExecutionContext} ctx 62 | * @returns {Promise} 63 | */ 64 | async function retrievalPluginQuery(request, env, ctx) { 65 | /** @type {QueryRequest} */ 66 | const { queries } = await request.json(); 67 | 68 | if (queries == null || !Array.isArray(queries)) { 69 | throw new Error("unexpected json payload"); 70 | } 71 | 72 | let topKs = await Promise.all( 73 | queries.map(({ query, top_k: topK }) => queryVectorStore(env, query, topK)) 74 | ); 75 | 76 | return { 77 | results: topKs.map((topKResults, ix) => ({ 78 | query: queries[ix].query, 79 | results: topKResults.map((result) => ({ 80 | id: result.id, 81 | text: result.text ?? "", 82 | metadata: { 83 | source: "file", 84 | source_id: result.fileId, 85 | document_id: result.fileId, 86 | url: pathToUrl(result.filePath), 87 | // created_at: "2022-10-10", 88 | // author: "doctor jones", 89 | }, 90 | // embedding: [1.1, 2.2, 3.3], 91 | score: result.similarity, 92 | })), 93 | })), 94 | }; 95 | } 96 | 97 | const EMBEDDINGS_KEY = "embeddings:v1"; 98 | const EMBEDDINGS_TTL = 10 * 60; // 10 minutes 99 | const CHUNK_TTL = 24 * 60 * 60; // 24 hours 100 | 101 | /** 102 | * @param {Env} env 103 | * @param {string} query 104 | * @param {number} topK 105 | * @returns {Promise<(Chunk & {similarity: number})[]>} 106 | */ 107 | async function queryVectorStore(env, query, topK = 3) { 108 | if (!query) { 109 | throw new Error("query is empty"); 110 | } 111 | if (!env.OPENAI_API_KEY) { 112 | throw new Error("OPENAI_API_KEY is not set"); 113 | } 114 | const res = await fetchEmbeddings([query], env.OPENAI_API_KEY); 115 | if (res.error != null) { 116 | const err = new Error(res.error.message || String(res.error)); 117 | err.cause = res.error; 118 | throw err; 119 | } 120 | const queryEmbedding = res.data[0].embedding; 121 | 122 | const vectors = new VectorCollection( 123 | await env.KV.get(EMBEDDINGS_KEY, { 124 | type: "arrayBuffer", 125 | cacheTtl: EMBEDDINGS_TTL, 126 | }) 127 | ); 128 | 129 | const topKSimilarities = vectors.topK(queryEmbedding, topK); 130 | 131 | const topKChunks = await Promise.all( 132 | topKSimilarities.map( 133 | ({ id }) => 134 | /** @type {Promise} */ ( 135 | env.KV.get("chunk:" + id, { 136 | type: "json", 137 | cacheTtl: CHUNK_TTL, 138 | }) 139 | ) 140 | ) 141 | ); 142 | 143 | return topKChunks.map((chunk, i) => ({ 144 | similarity: Math.min(topKSimilarities[i].similarity, 1), 145 | ...(chunk ?? { id: "", filePath: "", fileId: "", title: "", text: "" }), 146 | })); 147 | } 148 | -------------------------------------------------------------------------------- /example-retrieval-plugin/plugin/src/manifests/ai-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": "v1", 3 | "name_for_model": "cloudflare_docs_retrieval", 4 | "name_for_human": "Cloudflare Docs", 5 | "description_for_model": "Plugin for searching through Cloudflare's documentation to find answers to questions and retrieve relevant information. Use it whenever a user asks something that might be related to Cloudflare.", 6 | "description_for_human": "Search through Cloudflare's documentation.", 7 | "auth": { 8 | "type": "none" 9 | }, 10 | "api": { 11 | "type": "openapi", 12 | "url": "https:///.well-known/openapi.json", 13 | "has_user_authentication": false 14 | }, 15 | "logo_url": "https://developers.cloudflare.com/assets/icon-256x256-870aa2b4.png", 16 | "contact_email": "mhart@cloudflare.com", 17 | "legal_info_url": "https://www.cloudflare.com/website-terms/" 18 | } 19 | -------------------------------------------------------------------------------- /example-retrieval-plugin/plugin/src/manifests/openapi.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by openapi-typescript. 3 | * Do not make direct changes to the file. 4 | */ 5 | 6 | 7 | export interface paths { 8 | "/query": { 9 | /** 10 | * Query 11 | * @description Accepts search query objects array each with query and optional filter. Break down complex questions into sub-questions. Refine results by criteria, e.g. time / source, don't do this often. Split queries if ResponseTooLargeError occurs. 12 | */ 13 | post: operations["query_query_post"]; 14 | }; 15 | } 16 | 17 | export type webhooks = Record; 18 | 19 | export interface components { 20 | schemas: { 21 | /** DocumentChunkMetadata */ 22 | DocumentChunkMetadata: { 23 | source?: components["schemas"]["Source"]; 24 | /** Source Id */ 25 | source_id?: string; 26 | /** Url */ 27 | url?: string; 28 | /** Created At */ 29 | created_at?: string; 30 | /** Author */ 31 | author?: string; 32 | /** Document Id */ 33 | document_id?: string; 34 | }; 35 | /** DocumentChunkWithScore */ 36 | DocumentChunkWithScore: { 37 | /** Id */ 38 | id?: string; 39 | /** Text */ 40 | text: string; 41 | metadata: components["schemas"]["DocumentChunkMetadata"]; 42 | /** Embedding */ 43 | embedding?: (number)[]; 44 | /** Score */ 45 | score: number; 46 | }; 47 | /** DocumentMetadataFilter */ 48 | DocumentMetadataFilter: { 49 | /** Document Id */ 50 | document_id?: string; 51 | source?: components["schemas"]["Source"]; 52 | /** Source Id */ 53 | source_id?: string; 54 | /** Author */ 55 | author?: string; 56 | /** Start Date */ 57 | start_date?: string; 58 | /** End Date */ 59 | end_date?: string; 60 | }; 61 | /** HTTPValidationError */ 62 | HTTPValidationError: { 63 | /** Detail */ 64 | detail?: (components["schemas"]["ValidationError"])[]; 65 | }; 66 | /** Query */ 67 | Query: { 68 | /** Query */ 69 | query: string; 70 | filter?: components["schemas"]["DocumentMetadataFilter"]; 71 | /** 72 | * Top K 73 | * @default 3 74 | */ 75 | top_k?: number; 76 | }; 77 | /** QueryRequest */ 78 | QueryRequest: { 79 | /** Queries */ 80 | queries: (components["schemas"]["Query"])[]; 81 | }; 82 | /** QueryResponse */ 83 | QueryResponse: { 84 | /** Results */ 85 | results: (components["schemas"]["QueryResult"])[]; 86 | }; 87 | /** QueryResult */ 88 | QueryResult: { 89 | /** Query */ 90 | query: string; 91 | /** Results */ 92 | results: (components["schemas"]["DocumentChunkWithScore"])[]; 93 | }; 94 | /** 95 | * Source 96 | * @description An enumeration. 97 | * @enum {string} 98 | */ 99 | Source: "email" | "file" | "chat"; 100 | /** ValidationError */ 101 | ValidationError: { 102 | /** Location */ 103 | loc: (string | number)[]; 104 | /** Message */ 105 | msg: string; 106 | /** Error Type */ 107 | type: string; 108 | }; 109 | }; 110 | responses: never; 111 | parameters: never; 112 | requestBodies: never; 113 | headers: never; 114 | pathItems: never; 115 | } 116 | 117 | export type external = Record; 118 | 119 | export interface operations { 120 | 121 | /** 122 | * Query 123 | * @description Accepts search query objects array each with query and optional filter. Break down complex questions into sub-questions. Refine results by criteria, e.g. time / source, don't do this often. Split queries if ResponseTooLargeError occurs. 124 | */ 125 | query_query_post: { 126 | requestBody: { 127 | content: { 128 | "application/json": components["schemas"]["QueryRequest"]; 129 | }; 130 | }; 131 | responses: { 132 | /** @description Successful Response */ 133 | 200: { 134 | content: { 135 | "application/json": components["schemas"]["QueryResponse"]; 136 | }; 137 | }; 138 | /** @description Validation Error */ 139 | 422: { 140 | content: { 141 | "application/json": components["schemas"]["HTTPValidationError"]; 142 | }; 143 | }; 144 | }; 145 | }; 146 | } 147 | -------------------------------------------------------------------------------- /example-retrieval-plugin/plugin/src/manifests/openapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.2", 3 | "info": { 4 | "title": "Retrieval Plugin API", 5 | "description": "A retrieval API for querying and filtering documents based on natural language queries and metadata", 6 | "version": "1.0.0" 7 | }, 8 | "servers": [ 9 | { 10 | "url": "https://" 11 | } 12 | ], 13 | "paths": { 14 | "/query": { 15 | "post": { 16 | "summary": "Query", 17 | "description": "Accepts search query objects array each with query and optional filter. Break down complex questions into sub-questions. Refine results by criteria, e.g. time / source, don't do this often. Split queries if ResponseTooLargeError occurs.", 18 | "operationId": "query_query_post", 19 | "requestBody": { 20 | "content": { 21 | "application/json": { 22 | "schema": { 23 | "$ref": "#/components/schemas/QueryRequest" 24 | } 25 | } 26 | }, 27 | "required": true 28 | }, 29 | "responses": { 30 | "200": { 31 | "description": "Successful Response", 32 | "content": { 33 | "application/json": { 34 | "schema": { 35 | "$ref": "#/components/schemas/QueryResponse" 36 | } 37 | } 38 | } 39 | }, 40 | "422": { 41 | "description": "Validation Error", 42 | "content": { 43 | "application/json": { 44 | "schema": { 45 | "$ref": "#/components/schemas/HTTPValidationError" 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | }, 54 | "components": { 55 | "schemas": { 56 | "DocumentChunkMetadata": { 57 | "title": "DocumentChunkMetadata", 58 | "type": "object", 59 | "properties": { 60 | "source": { 61 | "$ref": "#/components/schemas/Source" 62 | }, 63 | "source_id": { 64 | "title": "Source Id", 65 | "type": "string" 66 | }, 67 | "url": { 68 | "title": "Url", 69 | "type": "string" 70 | }, 71 | "created_at": { 72 | "title": "Created At", 73 | "type": "string" 74 | }, 75 | "author": { 76 | "title": "Author", 77 | "type": "string" 78 | }, 79 | "document_id": { 80 | "title": "Document Id", 81 | "type": "string" 82 | } 83 | } 84 | }, 85 | "DocumentChunkWithScore": { 86 | "title": "DocumentChunkWithScore", 87 | "required": ["text", "metadata", "score"], 88 | "type": "object", 89 | "properties": { 90 | "id": { 91 | "title": "Id", 92 | "type": "string" 93 | }, 94 | "text": { 95 | "title": "Text", 96 | "type": "string" 97 | }, 98 | "metadata": { 99 | "$ref": "#/components/schemas/DocumentChunkMetadata" 100 | }, 101 | "embedding": { 102 | "title": "Embedding", 103 | "type": "array", 104 | "items": { 105 | "type": "number" 106 | } 107 | }, 108 | "score": { 109 | "title": "Score", 110 | "type": "number" 111 | } 112 | } 113 | }, 114 | "DocumentMetadataFilter": { 115 | "title": "DocumentMetadataFilter", 116 | "type": "object", 117 | "properties": { 118 | "document_id": { 119 | "title": "Document Id", 120 | "type": "string" 121 | }, 122 | "source": { 123 | "$ref": "#/components/schemas/Source" 124 | }, 125 | "source_id": { 126 | "title": "Source Id", 127 | "type": "string" 128 | }, 129 | "author": { 130 | "title": "Author", 131 | "type": "string" 132 | }, 133 | "start_date": { 134 | "title": "Start Date", 135 | "type": "string" 136 | }, 137 | "end_date": { 138 | "title": "End Date", 139 | "type": "string" 140 | } 141 | } 142 | }, 143 | "HTTPValidationError": { 144 | "title": "HTTPValidationError", 145 | "type": "object", 146 | "properties": { 147 | "detail": { 148 | "title": "Detail", 149 | "type": "array", 150 | "items": { 151 | "$ref": "#/components/schemas/ValidationError" 152 | } 153 | } 154 | } 155 | }, 156 | "Query": { 157 | "title": "Query", 158 | "required": ["query"], 159 | "type": "object", 160 | "properties": { 161 | "query": { 162 | "title": "Query", 163 | "type": "string" 164 | }, 165 | "filter": { 166 | "$ref": "#/components/schemas/DocumentMetadataFilter" 167 | }, 168 | "top_k": { 169 | "title": "Top K", 170 | "type": "integer", 171 | "default": 3 172 | } 173 | } 174 | }, 175 | "QueryRequest": { 176 | "title": "QueryRequest", 177 | "required": ["queries"], 178 | "type": "object", 179 | "properties": { 180 | "queries": { 181 | "title": "Queries", 182 | "type": "array", 183 | "items": { 184 | "$ref": "#/components/schemas/Query" 185 | } 186 | } 187 | } 188 | }, 189 | "QueryResponse": { 190 | "title": "QueryResponse", 191 | "required": ["results"], 192 | "type": "object", 193 | "properties": { 194 | "results": { 195 | "title": "Results", 196 | "type": "array", 197 | "items": { 198 | "$ref": "#/components/schemas/QueryResult" 199 | } 200 | } 201 | } 202 | }, 203 | "QueryResult": { 204 | "title": "QueryResult", 205 | "required": ["query", "results"], 206 | "type": "object", 207 | "properties": { 208 | "query": { 209 | "title": "Query", 210 | "type": "string" 211 | }, 212 | "results": { 213 | "title": "Results", 214 | "type": "array", 215 | "items": { 216 | "$ref": "#/components/schemas/DocumentChunkWithScore" 217 | } 218 | } 219 | } 220 | }, 221 | "Source": { 222 | "title": "Source", 223 | "enum": ["email", "file", "chat"], 224 | "type": "string", 225 | "description": "An enumeration." 226 | }, 227 | "ValidationError": { 228 | "title": "ValidationError", 229 | "required": ["loc", "msg", "type"], 230 | "type": "object", 231 | "properties": { 232 | "loc": { 233 | "title": "Location", 234 | "type": "array", 235 | "items": { 236 | "anyOf": [ 237 | { 238 | "type": "string" 239 | }, 240 | { 241 | "type": "integer" 242 | } 243 | ] 244 | } 245 | }, 246 | "msg": { 247 | "title": "Message", 248 | "type": "string" 249 | }, 250 | "type": { 251 | "title": "Error Type", 252 | "type": "string" 253 | } 254 | } 255 | } 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /example-retrieval-plugin/plugin/worker-configuration.d.ts: -------------------------------------------------------------------------------- 1 | interface Env { 2 | readonly OPENAI_API_KEY: string; // secret 3 | readonly KV: KVNamespace; 4 | } 5 | -------------------------------------------------------------------------------- /example-retrieval-plugin/plugin/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "retrieval" 2 | main = "src/index.js" 3 | compatibility_date = "2023-04-07" 4 | usage_model = "bundled" 5 | 6 | [[kv_namespaces]] 7 | binding = "KV" 8 | id = "d03..." 9 | preview_id = "6ab..." 10 | -------------------------------------------------------------------------------- /example-retrieval-plugin/scheduler/src/index.js: -------------------------------------------------------------------------------- 1 | import { VectorCollection } from "../../shared/vector.js"; 2 | import { 3 | OWNER, 4 | REPO_NAME, 5 | FULL_DIRS, 6 | textToChunks, 7 | fetchChunkEmbeddings, 8 | oidPrefix, 9 | } from "../../shared/docs.js"; 10 | import { fetchMdFiles, fetchFileContents } from "../../shared/github.js"; 11 | 12 | const EMBEDDINGS_KEY = "embeddings:v1"; 13 | 14 | /** @type {ExportedHandler} */ 15 | export default { 16 | async scheduled(controller, env, ctx) { 17 | if (!env.GITHUB_API_KEY) { 18 | throw new Error("GITHUB_API_KEY is not set"); 19 | } 20 | if (!env.OPENAI_API_KEY) { 21 | throw new Error("OPENAI_API_KEY is not set"); 22 | } 23 | ctx.waitUntil(scheduleEmbeddings(env, ctx)); 24 | console.log("scheduled ok"); 25 | }, 26 | 27 | async queue(batch, env, ctx) { 28 | const files = batch.messages.map((m) => m.body); 29 | 30 | const jobId = files[0]?.jobId; 31 | const batchId = files[0]?.oid; 32 | 33 | if (jobId == null || batchId == null) { 34 | return; 35 | } 36 | 37 | try { 38 | console.log("fetching file contents"); 39 | 40 | const filesWithContents = await fetchFileContents( 41 | env.GITHUB_API_KEY, 42 | OWNER, 43 | REPO_NAME, 44 | files 45 | ); 46 | 47 | const chunks = filesWithContents 48 | .map(({ path, oid, text }) => textToChunks(path, oid, text)) 49 | .flat(); 50 | 51 | const kvPromises = chunks.map((chunk) => 52 | env.KV.put(`chunk:${chunk.id}`, JSON.stringify(chunk)) 53 | ); 54 | 55 | console.log("fetching chunk embeddings"); 56 | 57 | const chunkEmbeddings = await fetchChunkEmbeddings( 58 | env.OPENAI_API_KEY, 59 | chunks 60 | ); 61 | 62 | const vectorCollection = VectorCollection.from(chunkEmbeddings); 63 | 64 | console.log( 65 | "batch embeddings byteLength:", 66 | vectorCollection.buffer.byteLength 67 | ); 68 | 69 | kvPromises.push( 70 | env.KV.put( 71 | `job:${jobId}:embeddings:${batchId}`, 72 | vectorCollection.buffer 73 | ) 74 | ); 75 | 76 | await Promise.all(kvPromises); 77 | } catch (e) { 78 | console.error(e); 79 | } 80 | 81 | await postJobMessage(env.JOB_STORE, { 82 | action: "batchProcessed", 83 | jobId, 84 | batchId, 85 | batchSize: files.length, 86 | }); 87 | 88 | console.log("queue processed ok:", files.length); 89 | }, 90 | }; 91 | 92 | /** 93 | * @param {Env} env 94 | * @param {ExecutionContext} ctx 95 | */ 96 | async function scheduleEmbeddings(env, ctx) { 97 | console.log("fetching existing and md files"); 98 | 99 | const [existingEmbeddings, mdFiles] = await Promise.all([ 100 | env.KV.get(EMBEDDINGS_KEY, "arrayBuffer"), 101 | fetchMdFiles(env.GITHUB_API_KEY, OWNER, REPO_NAME, FULL_DIRS), 102 | ]); 103 | 104 | const vectors = new VectorCollection(existingEmbeddings); 105 | 106 | const { newMdFiles, deletedChunkIds } = filterOutExisting( 107 | mdFiles, 108 | vectors.idStrs() 109 | ); 110 | 111 | const jobId = Date.now(); 112 | 113 | await postJobMessage(env.JOB_STORE, { 114 | action: "initJob", 115 | jobId, 116 | numFiles: newMdFiles.length, 117 | deletedChunkIds, 118 | }); 119 | 120 | const BATCH_SIZE = 100; 121 | for (let i = 0; i < newMdFiles.length; i += 100) { 122 | const mdFilesBatch = newMdFiles.slice(i, i + BATCH_SIZE); 123 | const queueMessages = mdFilesBatch.map((entry) => ({ 124 | body: { jobId, ...entry }, 125 | })); 126 | console.log("sending batch"); 127 | await env.RESOLVER_QUEUE.sendBatch(queueMessages); 128 | } 129 | } 130 | 131 | /** 132 | * @param {DurableObjectNamespace} jobStore 133 | * @param {JobMessage} message 134 | */ 135 | async function postJobMessage(jobStore, message) { 136 | const stub = jobStore.get(jobStore.idFromName("v1")); 137 | const res = await stub.fetch("http://do", { 138 | method: "POST", 139 | body: JSON.stringify(message), 140 | }); 141 | if (!res.ok) { 142 | console.error(res.status); 143 | console.error(res.headers); 144 | console.error(await res.text()); 145 | } 146 | } 147 | 148 | /** @implements {DurableObject} */ 149 | export class JobStore { 150 | /** 151 | * @param {DurableObjectState} state 152 | * @param {Env} env 153 | */ 154 | constructor(state, env) { 155 | this.state = state; 156 | this.kv = env.KV; 157 | } 158 | 159 | /** 160 | * @param {Request} request 161 | * @returns {Promise} 162 | */ 163 | async fetch(request) { 164 | const { storage } = this.state; 165 | 166 | /** @type {JobMessage} */ 167 | const message = await request.json(); 168 | const { action, jobId } = message; 169 | const remainingKey = `job:${jobId}:remaining`; 170 | const deletedKey = `job:${jobId}:deletedIds`; 171 | const batchesKey = `job:${jobId}:batchIds`; 172 | 173 | if (action === "initJob") { 174 | storage.put(remainingKey, message.numFiles); 175 | storage.put(deletedKey, new Set(message.deletedChunkIds)); 176 | storage.put(batchesKey, []); 177 | } else if (action === "batchProcessed") { 178 | /** @type {Map} */ 179 | const records = await storage.get([remainingKey, batchesKey]); 180 | 181 | let remaining = Number(records.get(remainingKey)); 182 | remaining = Math.max(remaining - message.batchSize, 0); 183 | storage.put(remainingKey, remaining); 184 | 185 | /** @type {string[]} */ 186 | const batchIds = records.get(batchesKey) ?? []; 187 | batchIds.push(message.batchId); 188 | storage.put(batchesKey, batchIds); 189 | 190 | if (remaining === 0) { 191 | await this.finishJob(jobId, batchIds); 192 | } 193 | } 194 | return new Response(null, { status: 204 }); 195 | } 196 | 197 | /** 198 | * @param {number} jobId 199 | * @param {string[]} batchIds 200 | */ 201 | async finishJob(jobId, batchIds) { 202 | const { storage } = this.state; 203 | const deletedKey = `job:${jobId}:deletedIds`; 204 | 205 | // Just a precaution to ensure all KV writes have finished 206 | await scheduler.wait(1000); 207 | 208 | console.log("fetching existing"); 209 | 210 | const [existingEmbeddingsBuffer, newEmbeddingBuffers, deletedIdsOrNull] = 211 | await Promise.all([ 212 | this.kv.get(EMBEDDINGS_KEY, "arrayBuffer"), 213 | Promise.all( 214 | batchIds.map((batchId) => 215 | this.kv.get(`job:${jobId}:embeddings:${batchId}`, "arrayBuffer") 216 | ) 217 | ), 218 | /** @type {Promise | null>} */ 219 | (storage.get(deletedKey)), 220 | ]); 221 | 222 | const existingEmbeddings = new VectorCollection(existingEmbeddingsBuffer); 223 | 224 | const batchVectors = newEmbeddingBuffers.map( 225 | (buffer) => new VectorCollection(buffer) 226 | ); 227 | 228 | const deletedIds = deletedIdsOrNull ?? new Set(); 229 | 230 | console.log("merging"); 231 | 232 | const joinedVectors = VectorCollection.merge( 233 | batchVectors, 234 | existingEmbeddings, 235 | deletedIds 236 | ); 237 | 238 | console.log( 239 | "saving embeddings, byteLength:", 240 | joinedVectors.buffer.byteLength 241 | ); 242 | 243 | await this.kv.put(EMBEDDINGS_KEY, joinedVectors.buffer); 244 | 245 | console.log("cleaning up"); 246 | 247 | try { 248 | await this.cleanupJob(jobId, batchIds, deletedIds); 249 | } catch (e) { 250 | console.error(e); 251 | } 252 | } 253 | 254 | /** 255 | * @param {number} jobId 256 | * @param {string[]} batchIds 257 | * @param {Set} deletedIds 258 | */ 259 | async cleanupJob(jobId, batchIds, deletedIds) { 260 | const { storage } = this.state; 261 | const remainingKey = `job:${jobId}:remaining`; 262 | const deletedKey = `job:${jobId}:deletedIds`; 263 | const batchesKey = `job:${jobId}:batchIds`; 264 | 265 | // Clean up job storage 266 | await storage.delete([remainingKey, deletedKey, batchesKey]); 267 | await Promise.all( 268 | batchIds.map((batchId) => 269 | this.kv.delete(`job:${jobId}:embeddings:${batchId}`) 270 | ) 271 | ); 272 | 273 | await Promise.all( 274 | [...deletedIds].map((id) => this.kvExpire(`chunk:${id}`, 10 * 60)) // keep old chunks around for ten minutes 275 | ); 276 | } 277 | 278 | /** 279 | * @param {string} key 280 | * @param {number} expirationTtl 281 | */ 282 | async kvExpire(key, expirationTtl) { 283 | const val = await this.kv.get(key, "arrayBuffer"); 284 | return val && this.kv.put(key, val, { expirationTtl }); 285 | } 286 | } 287 | 288 | /** 289 | * @param {{path: string, oid: string}[]} mdFiles 290 | * @param {Iterable} existingIds 291 | */ 292 | function filterOutExisting(mdFiles, existingIds) { 293 | const existingOidPrefixes = new Map( 294 | [...existingIds].map((oid) => [oidPrefix(oid), oid]) 295 | ); 296 | 297 | const newMdFiles = mdFiles.filter( 298 | ({ oid }) => !existingOidPrefixes.delete(oidPrefix(oid)) 299 | ); 300 | 301 | const deletedChunkIds = [...existingOidPrefixes.values()]; 302 | 303 | return { newMdFiles, deletedChunkIds }; 304 | } 305 | -------------------------------------------------------------------------------- /example-retrieval-plugin/scheduler/worker-configuration.d.ts: -------------------------------------------------------------------------------- 1 | interface Env { 2 | readonly OPENAI_API_KEY: string; // secret 3 | readonly GITHUB_API_KEY: string; // secret 4 | readonly KV: KVNamespace; 5 | readonly JOB_STORE: DurableObjectNamespace; 6 | readonly RESOLVER_QUEUE: Queue; 7 | } 8 | 9 | interface EmbeddingMessage { 10 | jobId: number; 11 | path: string; // file path 12 | oid: string; // git object id (40 char hex hash) 13 | } 14 | 15 | interface InitJobMessage { 16 | action: "initJob"; 17 | jobId: number; 18 | numFiles: number; 19 | deletedChunkIds: string[]; 20 | } 21 | 22 | interface BatchProcessedMessage { 23 | action: "batchProcessed"; 24 | jobId: number; 25 | batchId: string; 26 | batchSize: number; 27 | } 28 | 29 | type JobMessage = InitJobMessage | BatchProcessedMessage; 30 | -------------------------------------------------------------------------------- /example-retrieval-plugin/scheduler/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "scheduled-retrieval" 2 | main = "src/index.js" 3 | compatibility_date = "2023-04-07" 4 | usage_model = "unbound" 5 | workers_dev = false 6 | 7 | [triggers] 8 | crons = [ "1 * * * *" ] # Every hour at 1 minute past 9 | 10 | [[kv_namespaces]] 11 | binding = "KV" 12 | id = "d03..." 13 | preview_id = "6ab..." 14 | 15 | [[durable_objects.bindings]] 16 | name = "JOB_STORE" 17 | class_name = "JobStore" 18 | 19 | [[migrations]] 20 | tag = "v1" 21 | new_classes = ["JobStore"] 22 | 23 | [[queues.producers]] 24 | binding = "RESOLVER_QUEUE" 25 | queue = "embeddings-resolver" 26 | 27 | [[queues.consumers]] 28 | queue = "embeddings-resolver" 29 | max_batch_size = 100 30 | max_batch_timeout = 30 31 | -------------------------------------------------------------------------------- /example-retrieval-plugin/scripts/chunk.js: -------------------------------------------------------------------------------- 1 | import { mkdtempSync, readFileSync, writeFileSync } from "node:fs"; 2 | import { execSync } from "node:child_process"; 3 | import { join } from "node:path"; 4 | import { tmpdir } from "node:os"; 5 | import { OWNER, REPO_NAME, FULL_DIRS, textToChunks } from "../shared/docs.js"; 6 | 7 | const repo = `https://github.com/${OWNER}/${REPO_NAME}`; 8 | 9 | const cloneDir = mkdtempSync(join(tmpdir(), "chunk-")); 10 | 11 | execSync( 12 | [ 13 | `git clone --depth=1 --filter=blob:none --sparse ${repo} ${cloneDir}`, 14 | `git sparse-checkout set ${FULL_DIRS.join(" ")}`, 15 | ].join(" && "), 16 | { cwd: cloneDir, stdio: "inherit" } 17 | ); 18 | 19 | const objects = execSync( 20 | `git rev-list --all --objects ${FULL_DIRS.join(" ")}`, 21 | { cwd: cloneDir, encoding: "utf8" } 22 | ); 23 | 24 | const filesWithContents = objects 25 | .trim() 26 | .split("\n") 27 | .filter((line) => line.endsWith(".md")) 28 | .map((line) => ({ 29 | oid: line.slice(0, 40), 30 | path: line.slice(41), 31 | text: readFileSync(join(cloneDir, line.slice(41)), "utf8"), 32 | })); 33 | 34 | const chunks = filesWithContents 35 | .map(({ path, oid, text }) => textToChunks(path, oid, text)) 36 | .flat(); 37 | 38 | console.warn("Number of files: ", filesWithContents.length); 39 | console.warn("Number of chunks: ", chunks.length); 40 | 41 | writeFileSync( 42 | "chunks.bulk.json", 43 | JSON.stringify( 44 | chunks.map((chunk) => ({ 45 | key: "chunk:" + chunk.id, 46 | value: JSON.stringify(chunk), 47 | })) 48 | ) 49 | ); 50 | 51 | // Not really necessary, but easier to debug than the bulk json 52 | writeFileSync( 53 | "chunks.ndjson", 54 | chunks.map((chunk) => JSON.stringify(chunk)).join("\n") + "\n" 55 | ); 56 | -------------------------------------------------------------------------------- /example-retrieval-plugin/scripts/embeddings.js: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "node:fs"; 2 | import { fetchChunkEmbeddings } from "../shared/docs.js"; 3 | import { VectorCollection } from "../shared/vector.js"; 4 | 5 | const { OPENAI_API_KEY } = process.env; 6 | 7 | if (OPENAI_API_KEY == null) { 8 | console.error("OPENAI_API_KEY must be set"); 9 | process.exit(1); 10 | } 11 | 12 | /** @type {Chunk[]} */ 13 | const chunks = readFileSync("./chunks.ndjson", "utf8") 14 | .trim() 15 | .split("\n") 16 | .map((line) => JSON.parse(line)); 17 | 18 | main().catch(console.error); 19 | 20 | async function main() { 21 | const chunkEmbeddings = await fetchChunkEmbeddings( 22 | OPENAI_API_KEY ?? "", 23 | chunks 24 | ); 25 | 26 | const vectorCollection = VectorCollection.from(chunkEmbeddings); 27 | 28 | console.warn("Number of embeddings:", vectorCollection.length); 29 | console.warn("Embedding length:", vectorCollection.embeddingLength); 30 | 31 | writeFileSync("embeddings.bin", new Uint8Array(vectorCollection.buffer)); 32 | } 33 | -------------------------------------------------------------------------------- /example-retrieval-plugin/scripts/refresh.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | # export CLOUDFLARE_ACCOUNT_ID=3b2... 4 | # export KV_NAMESPACE_ID=d03... 5 | # export OPENAI_API_KEY=sk-... 6 | 7 | # Crawl .md docs and create chunks 8 | node chunk.js 9 | 10 | # Generate embeddings json/bin 11 | node embeddings.js 12 | 13 | # Uploading chunks to KV 14 | npx wrangler kv:bulk put chunks.bulk.json --namespace-id=$KV_NAMESPACE_ID 15 | 16 | # Uploading embeddings.bin to KV 17 | npx wrangler kv:key put embeddings:v1 --path embeddings.bin --namespace-id=$KV_NAMESPACE_ID 18 | -------------------------------------------------------------------------------- /example-retrieval-plugin/shared/docs.js: -------------------------------------------------------------------------------- 1 | import { fetchEmbeddings } from "./openai.js"; 2 | 3 | const DOCS_BASE_URL = "https://developers.cloudflare.com"; 4 | 5 | export const OWNER = "cloudflare"; 6 | export const REPO_NAME = "cloudflare-docs"; 7 | 8 | export const TOP_DIR = "content"; 9 | export const SUB_DIRS = [ 10 | "analytics/analytics-engine", 11 | "cloudflare-for-platforms/workers-for-platforms", 12 | "d1", 13 | "images", 14 | "pages", 15 | "pub-sub", 16 | "queues", 17 | "r2", 18 | "stream", 19 | "workers", 20 | ]; 21 | export const FULL_DIRS = SUB_DIRS.map((d) => `${TOP_DIR}/${d}`); 22 | 23 | /** 24 | * @param {string} filePath 25 | * @param {string} fileId 26 | * @param {string} markdownText 27 | * @returns {Chunk[]} 28 | */ 29 | export function textToChunks(filePath, fileId, markdownText) { 30 | let chunks = []; 31 | 32 | if (!markdownText) { 33 | console.error("no text!", filePath, fileId); 34 | return []; 35 | } 36 | 37 | let title = (markdownText.match(/^title: (.+)$/m) ?? [])[1]; 38 | 39 | if (title == null) { 40 | console.warn("No title:", filePath); 41 | return []; 42 | } 43 | 44 | markdownText = markdownText 45 | .replace(/---.+?---/ms, "") 46 | .replace(//ms, "") 47 | .replace(/{{.+?}}/g, "") 48 | .replace(/\[(.+?)\]\(.+\)/g, "$1") 49 | .replace(/\n\n+/g, "\n\n") 50 | // .replace(/(? chunk.text), 104 | apiKey 105 | ) 106 | ); 107 | } 108 | const responses = await Promise.all(fetches); 109 | 110 | return responses 111 | .map(({ data, error }, fetchIx) => { 112 | if (error != null) { 113 | console.error({ fetchIx, error }); 114 | return []; 115 | } 116 | return data.map(({ embedding }, embeddingIx) => ({ 117 | id: chunks[fetchIx * BATCH_SIZE + embeddingIx].id, 118 | embedding, 119 | })); 120 | }) 121 | .flat(); 122 | } 123 | 124 | /** 125 | * @param {string} path 126 | */ 127 | export function pathToUrl(path) { 128 | const prefix = `${TOP_DIR}/`; 129 | path = (path.startsWith(prefix) ? path.slice(prefix.length) : path) 130 | .replace(/_index\.md$/, "") 131 | .replace(/\.md$/, ""); 132 | return `${DOCS_BASE_URL}/${path}`; 133 | } 134 | 135 | const OID_PREFIX_CHARS = 12; 136 | const CHUNK_IX_CHARS = 4; 137 | 138 | /** 139 | * @param {string} oid 140 | */ 141 | export function oidPrefix(oid) { 142 | return oid.slice(0, OID_PREFIX_CHARS); 143 | } 144 | 145 | // ID is first 12 hex characters of git object id, plus 4 hex chars for chunk index 146 | // So, 16 / 2 = 8 bytes = 64 bits 147 | /** 148 | * @param {string} oid 149 | * @param {number} chunkIx 150 | */ 151 | function chunkId(oid, chunkIx) { 152 | return oidPrefix(oid) + chunkIx.toString(16).padStart(CHUNK_IX_CHARS, "0"); 153 | } 154 | -------------------------------------------------------------------------------- /example-retrieval-plugin/shared/github.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} apiKey 3 | * @param {string} owner 4 | * @param {string} name 5 | * @param {string[]} dirs 6 | */ 7 | export async function fetchMdFiles(apiKey, owner, name, dirs) { 8 | const maxDepth = 5; 9 | const dirQueries = dirs 10 | .map((dir, ix) => { 11 | let treeExpr = "...on Tree{entries{name oid}}"; 12 | for (let i = 0; i < maxDepth; i++) { 13 | treeExpr = `...on Tree{entries{name oid object{${treeExpr}}}}`; 14 | } 15 | return `obj_${ix}:object(expression:"HEAD:${dir}"){${treeExpr}}`; 16 | }) 17 | .join(""); 18 | const query = `query($owner:String!,$name:String!){repository(owner:$owner,name:$name){${dirQueries}}}`; 19 | 20 | const { 21 | data: { repository }, 22 | } = await fetchGithub(apiKey, { 23 | query, 24 | variables: { owner, name }, 25 | }); 26 | 27 | return dirs 28 | .map((dir, ix) => recurseMdEntries(dir, repository[`obj_${ix}`].entries)) 29 | .flat(); 30 | } 31 | 32 | /** 33 | * @param {string} dir 34 | * @param {TreeEntry[]} entries 35 | * @return {{path: string, oid: string}[]} 36 | */ 37 | function recurseMdEntries(dir, entries) { 38 | return entries 39 | .filter( 40 | ({ name, object: { entries } }) => entries != null || name.endsWith(".md") 41 | ) 42 | .map(({ name, oid, object: { entries } }) => 43 | entries != null 44 | ? recurseMdEntries(`${dir}/${name}`, entries) 45 | : { path: `${dir}/${name}`, oid } 46 | ) 47 | .flat(); 48 | } 49 | 50 | /** 51 | * @param {string} apiKey 52 | * @param {string} owner 53 | * @param {string} name 54 | * @param {{path: string, oid: string}[]} files 55 | */ 56 | export async function fetchFileContents(apiKey, owner, name, files) { 57 | const fileQueries = files 58 | .map( 59 | ({ oid }, ix) => 60 | `obj_${ix}:object(oid:${JSON.stringify(oid)}){...on Blob{text}}` 61 | ) 62 | .join(""); 63 | const query = `query($owner:String!,$name:String!){repository(owner:$owner,name:$name){${fileQueries}}}`; 64 | 65 | const { 66 | data: { repository }, 67 | } = await fetchGithub(apiKey, { 68 | query, 69 | variables: { owner, name }, 70 | }); 71 | 72 | return files.map((file, ix) => ({ 73 | text: repository[`obj_${ix}`].text, 74 | ...file, 75 | })); 76 | } 77 | 78 | const GITHUB_GRAPHQL_API_BASE = "https://api.github.com/graphql"; 79 | 80 | /** 81 | * @param {string} apiKey 82 | * @param {{operationName?: string, query: string, variables?: any}} graphqlPayload 83 | */ 84 | async function fetchGithub(apiKey, graphqlPayload) { 85 | const NUM_RETRIES = 100; 86 | const INIT_RETRY_MS = 50; 87 | for (let i = 0; i <= NUM_RETRIES; i++) { 88 | const res = await fetch(GITHUB_GRAPHQL_API_BASE, { 89 | method: "POST", 90 | headers: { 91 | "User-Agent": "graphql-fetcher", 92 | Authorization: `Bearer ${apiKey}`, 93 | "Content-Type": "application/json", 94 | Accept: "gzip", 95 | }, 96 | body: JSON.stringify(graphqlPayload), 97 | }); 98 | if ((res.status < 500 && res.status !== 429) || i === NUM_RETRIES) { 99 | return res.json(); 100 | } 101 | // Exponential backoff with full jitter 102 | await new Promise((resolve) => 103 | setTimeout(resolve, Math.random() * INIT_RETRY_MS * Math.pow(2, i)) 104 | ); 105 | } 106 | // Should never reach here – last loop iteration should return 107 | throw new Error("An unknown error occurred"); 108 | } 109 | -------------------------------------------------------------------------------- /example-retrieval-plugin/shared/openai.js: -------------------------------------------------------------------------------- 1 | const OPENAI_BASE = "https://api.openai.com/v1"; 2 | 3 | /** 4 | * @param {string[]} chunks 5 | * @param {string} apiKey 6 | * @returns {Promise<{data: {embedding: number[]}[], error?: {message: string, type: string}}>} 7 | */ 8 | export async function fetchEmbeddings(chunks, apiKey) { 9 | const NUM_RETRIES = 100; 10 | const INIT_RETRY_MS = 50; 11 | for (let i = 0; i <= NUM_RETRIES; i++) { 12 | const res = await fetch(`${OPENAI_BASE}/embeddings`, { 13 | method: "POST", 14 | headers: { 15 | "content-type": "application/json", 16 | authorization: `Bearer ${apiKey}`, 17 | }, 18 | body: JSON.stringify({ 19 | model: "text-embedding-ada-002", 20 | input: chunks, 21 | }), 22 | }); 23 | if ((res.status < 500 && res.status !== 429) || i === NUM_RETRIES) { 24 | return res.json(); 25 | } 26 | // Exponential backoff with full jitter 27 | await new Promise((resolve) => 28 | setTimeout(resolve, Math.random() * INIT_RETRY_MS * Math.pow(2, i)) 29 | ); 30 | } 31 | // Should never reach here – last loop iteration should return 32 | throw new Error("An unknown error occurred"); 33 | } 34 | -------------------------------------------------------------------------------- /example-retrieval-plugin/shared/types.d.ts: -------------------------------------------------------------------------------- 1 | interface Chunk { 2 | id: string; 3 | filePath: string; 4 | fileId: string; 5 | title: string; 6 | text: string; 7 | } 8 | 9 | interface TreeEntry { 10 | name: string; 11 | oid: string; 12 | object: { entries?: TreeEntry[] }; 13 | } 14 | 15 | type Vector = 16 | | number[] 17 | | Float32Array 18 | | Uint8Array 19 | | Uint16Array 20 | | Uint32Array 21 | | Int8Array 22 | | Int16Array 23 | | Int32Array; 24 | -------------------------------------------------------------------------------- /example-retrieval-plugin/shared/vector.js: -------------------------------------------------------------------------------- 1 | const ID_BYTES = 8; // 64 bits 2 | const HEADER_BYTE_LENGTH = 2 * Uint16Array.BYTES_PER_ELEMENT; 3 | 4 | // OpenAI embeddings are [-1, 1), so we can 5 | // quantize to Int16 by multiplying by 2^15 6 | const QUANTIZE_MAX = Math.pow(2, 15); 7 | 8 | export class VectorCollection { 9 | static VERSION = 1; 10 | 11 | /** 12 | * @param {ArrayBuffer | number | null} [buffer] 13 | * @param {Uint16Array | number} [header] 14 | * @param {Uint8Array} [ids] 15 | * @param {Int16Array} [embeddings] 16 | */ 17 | constructor(buffer, header, ids, embeddings) { 18 | if (typeof buffer === "number" || typeof header === "number") { 19 | const length = Number(buffer); 20 | const embeddingLength = Number(header); 21 | buffer = new ArrayBuffer( 22 | HEADER_BYTE_LENGTH + 23 | length * (ID_BYTES + embeddingLength * Int16Array.BYTES_PER_ELEMENT) 24 | ); 25 | header = new Uint16Array(buffer, 0, 2); 26 | header[0] = VectorCollection.VERSION; 27 | header[1] = length; 28 | } 29 | this.buffer = 30 | buffer && buffer.byteLength >= HEADER_BYTE_LENGTH 31 | ? buffer 32 | : new Uint16Array([VectorCollection.VERSION, 0]).buffer; 33 | this.header = header ?? new Uint16Array(this.buffer, 0, 2); 34 | if (this.header[0] > VectorCollection.VERSION) { 35 | throw new Error("Unsupported version: " + this.header[0]); 36 | } 37 | this.length = this.header[1]; 38 | this.ids = 39 | ids ?? 40 | new Uint8Array(this.buffer, HEADER_BYTE_LENGTH, this.length * ID_BYTES); 41 | this.embeddings = 42 | embeddings ?? 43 | new Int16Array(this.buffer, HEADER_BYTE_LENGTH + this.length * ID_BYTES); 44 | this.embeddingLength = this.embeddings.length / this.length; 45 | } 46 | 47 | /** 48 | * NB: This assumes `embedding` is an array of floats in the range [-1, 1) 49 | * 50 | * @param {{id: string, embedding: number[]}[]} embeddingsWithIds 51 | * @returns {VectorCollection} 52 | */ 53 | static from(embeddingsWithIds) { 54 | const numEmbeddings = embeddingsWithIds.length; 55 | const embeddingLength = embeddingsWithIds[0]?.embedding.length ?? 0; 56 | 57 | const collection = new VectorCollection(numEmbeddings, embeddingLength); 58 | 59 | for (let i = 0; i < embeddingsWithIds.length; i++) { 60 | const { id, embedding } = embeddingsWithIds[i]; 61 | const idBytes = (id.match(/../g) ?? []).map((b) => parseInt(b, 16)); 62 | const quantizedEmbedding = embedding.map((x) => 63 | Math.min(Math.round(x * QUANTIZE_MAX), QUANTIZE_MAX - 1) 64 | ); 65 | collection.ids.set(idBytes, i * ID_BYTES); 66 | collection.embeddings.set(quantizedEmbedding, i * embeddingLength); 67 | } 68 | 69 | return collection; 70 | } 71 | 72 | /** 73 | * @param {VectorCollection[]} newCollections 74 | * @param {VectorCollection} existingCollection 75 | * @param {Set} deletedIds 76 | * @returns {VectorCollection} 77 | */ 78 | static merge(newCollections, existingCollection, deletedIds) { 79 | let totalEmbeddings = 0; 80 | let embeddingLength = 0; 81 | let idArrays = []; 82 | let embeddingArrays = []; 83 | 84 | for (const collection of newCollections) { 85 | totalEmbeddings += collection.length; 86 | embeddingLength ||= collection.embeddingLength; 87 | idArrays.push(collection.ids); 88 | embeddingArrays.push(collection.embeddings); 89 | } 90 | 91 | const existingIds = [...existingCollection.idStrs()]; 92 | 93 | for (let ix = 0; ix < existingIds.length; ix++) { 94 | if (deletedIds.has(existingIds[ix])) { 95 | continue; 96 | } 97 | totalEmbeddings++; 98 | embeddingLength ||= existingCollection.embeddingLength; 99 | idArrays.push(existingCollection.idAt(ix)); 100 | embeddingArrays.push(existingCollection.vectorAt(ix)); 101 | } 102 | 103 | const collection = new VectorCollection(totalEmbeddings, embeddingLength); 104 | 105 | let offset = 0; 106 | for (const idArray of idArrays) { 107 | collection.ids.set(idArray, offset); 108 | offset += idArray.length; 109 | } 110 | offset = 0; 111 | for (const embeddingArray of embeddingArrays) { 112 | collection.embeddings.set(embeddingArray, offset); 113 | offset += embeddingArray.length; 114 | } 115 | 116 | return collection; 117 | } 118 | 119 | /** @param {number} ix */ 120 | idAt(ix) { 121 | return new Uint8Array( 122 | this.ids.buffer, 123 | this.ids.byteOffset + ix * ID_BYTES * this.ids.BYTES_PER_ELEMENT, 124 | ID_BYTES 125 | ); 126 | } 127 | 128 | /** @param {number} ix */ 129 | idStrAt(ix) { 130 | return [...this.idAt(ix)] 131 | .map((b) => b.toString(16).padStart(2, "0")) 132 | .join(""); 133 | } 134 | 135 | /** @param {number} ix */ 136 | vectorAt(ix) { 137 | return new Int16Array( 138 | this.embeddings.buffer, 139 | this.embeddings.byteOffset + 140 | ix * this.embeddingLength * this.embeddings.BYTES_PER_ELEMENT, 141 | this.embeddingLength 142 | ); 143 | } 144 | 145 | *idStrs() { 146 | for (let ix = 0; ix < this.length; ix++) { 147 | yield this.idStrAt(ix); 148 | } 149 | } 150 | 151 | /** 152 | * NB: This assumes `query` is an array of floats in the range [-1, 1) 153 | * 154 | * @param {number[]} query 155 | * @param {number} numK 156 | */ 157 | topK(query, numK) { 158 | const { embeddings, embeddingLength } = this; 159 | query = query.map((x) => x * QUANTIZE_MAX); 160 | 161 | // Minor optimization, we use euclidian distance to compare, 162 | // and then only calculate cosine similarity on the top K. 163 | // For millions of vectors, a max-heap would work better here, 164 | // rather that allocating and sorting the entire array. 165 | return Array.from({ length: this.length }, (_, ix) => ({ 166 | ix, 167 | distance: sqeuclidian(query, embeddings, ix * embeddingLength), 168 | })) 169 | .sort((a, b) => a.distance - b.distance) 170 | .slice(0, numK) 171 | .map(({ ix }) => ({ 172 | id: this.idStrAt(ix), 173 | similarity: cosine(query, embeddings, ix * embeddingLength), 174 | })); 175 | } 176 | } 177 | 178 | /** 179 | * @param {Vector} v1 180 | * @param {Vector} v2 181 | * @param {number} [v2StartIx] 182 | */ 183 | function sqeuclidian(v1, v2, v2StartIx = 0) { 184 | let sum = 0; 185 | for (let i = 0; i < v1.length; i++) { 186 | const diff = v1[i] - v2[v2StartIx + i]; 187 | sum += diff * diff; 188 | } 189 | return sum; 190 | } 191 | 192 | /** 193 | * @param {Vector} v1 194 | * @param {Vector} v2 195 | * @param {number} [v2StartIx] 196 | */ 197 | function cosine(v1, v2, v2StartIx = 0) { 198 | let dotproduct = 0; 199 | let mA = 0; 200 | let mB = 0; 201 | for (let i = 0; i < v1.length; i++) { 202 | const j = i + v2StartIx; 203 | dotproduct += v1[i] * v2[j]; 204 | mA += v1[i] * v1[i]; 205 | mB += v2[j] * v2[j]; 206 | } 207 | return dotproduct / (Math.sqrt(mA) * Math.sqrt(mB)); 208 | } 209 | -------------------------------------------------------------------------------- /example-retrieval-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "lib": ["esnext"], 6 | "types": ["@cloudflare/workers-types", "@types/node"], 7 | "allowJs": true, 8 | "checkJs": true, 9 | "noEmit": true, 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "noUnusedLocals": true, 14 | "resolveJsonModule": true, 15 | "moduleResolution": "node", 16 | "allowSyntheticDefaultImports": true 17 | }, 18 | "include": [ 19 | "./plugin/**/*", 20 | "./scheduler/**/*", 21 | "./shared/**/*", 22 | "./scripts/**/*" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /example-weather-plugin/.assets/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyAI-ID/cloudflare-ai-example-chatgpt-plugin/41183a0a32d048f84fe8936498cd09f9b709c0ec/example-weather-plugin/.assets/example.png -------------------------------------------------------------------------------- /example-weather-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | -------------------------------------------------------------------------------- /example-weather-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Example ChatGPT Weather Plugin for Cloudflare Workers 2 | 3 | This is an example plugin showing how to build a weather [ChatGPT plugin](https://platform.openai.com/docs/plugins/introduction) using the [Pirate Weather API](https://pirate-weather.apiable.io/) and [Cloudflare Workers](https://workers.dev). Using this example, you can deploy a plugin to Cloudflare Workers in just a few minutes. 4 | 5 | The sample plugin allows ChatGPT users get weather information using the Pirate Weather API. The plugin is implemented in TypeScript and uses the [OpenAPI](https://www.openapis.org/) specification to define the plugin's API. 6 | 7 | ![Example conversation in ChatGPT showing the plugin in use](./.assets/example.png) 8 | 9 | ## Get started 10 | 11 | 0. Sign up for [Cloudflare Workers](https://workers.dev). The free tier is more than enough for most use cases. 12 | 1. Generate an API key from the [Pirate Weather API](https://pirate-weather.apiable.io/) 13 | 2. Install [wrangler](https://developers.cloudflare.com/workers/cli-wrangler/install-update), the Cloudflare Workers CLI 14 | 3. Clone this project and install dependencies with `npm install` 15 | 4. Run `wrangler login` to login to your Cloudflare account in wrangler 16 | 5. Run `wrangler publish` to publish the plugin to Cloudflare Workers 17 | 6. Set the `PIRATE_WEATHER_API_KEY` using `wrangler secret put`: `wrangler secret put PIRATE_WEATHER_API_KEY`. 18 | 19 | ## Usage 20 | 21 | 1. You can configure the `.well-known/ai-plugin.json` route in `index.ts`. 22 | 2. Update the OpenAPI schema in `openapi.ts`. 23 | 3. You can set up any new routes and the associated OpenAPI schema by defining new routes. See `forecast.ts` for an example. 24 | 25 | ## Deploying to OpenAI's API 26 | 27 | Follow the instructions [in the ChatGPT documentation](https://platform.openai.com/docs/plugins/introduction/plugin-flow) to deploy your plugin and begin using it in ChatGPT. -------------------------------------------------------------------------------- /example-weather-plugin/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-workers-chatgpt-plugin-example", 3 | "version": "0.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cloudflare-workers-chatgpt-plugin-example", 9 | "version": "0.0.1", 10 | "dependencies": { 11 | "@cloudflare/itty-router-openapi": "^0.1.2" 12 | }, 13 | "devDependencies": { 14 | "@cloudflare/workers-types": "^4.20230404.0", 15 | "wrangler": "^2.15.1" 16 | } 17 | }, 18 | "node_modules/@cloudflare/itty-router-openapi": { 19 | "version": "0.1.2", 20 | "resolved": "https://registry.npmjs.org/@cloudflare/itty-router-openapi/-/itty-router-openapi-0.1.2.tgz", 21 | "integrity": "sha512-BYILT4M2Tj7fd0pVPr5M8YwSYmuAxOI10FMcbc6nzgTT9XYY2qIVW0FFyo+BNG4zUi9adrFJQRhlfvISuogGsA==", 22 | "dependencies": { 23 | "itty-router": "^3.0.12" 24 | } 25 | }, 26 | "node_modules/@cloudflare/kv-asset-handler": { 27 | "version": "0.2.0", 28 | "dev": true, 29 | "license": "MIT OR Apache-2.0", 30 | "dependencies": { 31 | "mime": "^3.0.0" 32 | } 33 | }, 34 | "node_modules/@cloudflare/workers-types": { 35 | "version": "4.20230404.0", 36 | "dev": true, 37 | "license": "MIT OR Apache-2.0" 38 | }, 39 | "node_modules/@esbuild-plugins/node-globals-polyfill": { 40 | "version": "0.1.1", 41 | "dev": true, 42 | "license": "ISC", 43 | "peerDependencies": { 44 | "esbuild": "*" 45 | } 46 | }, 47 | "node_modules/@esbuild-plugins/node-modules-polyfill": { 48 | "version": "0.1.4", 49 | "dev": true, 50 | "license": "ISC", 51 | "dependencies": { 52 | "escape-string-regexp": "^4.0.0", 53 | "rollup-plugin-node-polyfills": "^0.2.1" 54 | }, 55 | "peerDependencies": { 56 | "esbuild": "*" 57 | } 58 | }, 59 | "node_modules/@esbuild/android-arm": { 60 | "version": "0.16.3", 61 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.3.tgz", 62 | "integrity": "sha512-mueuEoh+s1eRbSJqq9KNBQwI4QhQV6sRXIfTyLXSHGMpyew61rOK4qY21uKbXl1iBoMb0AdL1deWFCQVlN2qHA==", 63 | "cpu": [ 64 | "arm" 65 | ], 66 | "dev": true, 67 | "optional": true, 68 | "os": [ 69 | "android" 70 | ], 71 | "engines": { 72 | "node": ">=12" 73 | } 74 | }, 75 | "node_modules/@esbuild/android-arm64": { 76 | "version": "0.16.3", 77 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.3.tgz", 78 | "integrity": "sha512-RolFVeinkeraDvN/OoRf1F/lP0KUfGNb5jxy/vkIMeRRChkrX/HTYN6TYZosRJs3a1+8wqpxAo5PI5hFmxyPRg==", 79 | "cpu": [ 80 | "arm64" 81 | ], 82 | "dev": true, 83 | "optional": true, 84 | "os": [ 85 | "android" 86 | ], 87 | "engines": { 88 | "node": ">=12" 89 | } 90 | }, 91 | "node_modules/@esbuild/android-x64": { 92 | "version": "0.16.3", 93 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.3.tgz", 94 | "integrity": "sha512-SFpTUcIT1bIJuCCBMCQWq1bL2gPTjWoLZdjmIhjdcQHaUfV41OQfho6Ici5uvvkMmZRXIUGpM3GxysP/EU7ifQ==", 95 | "cpu": [ 96 | "x64" 97 | ], 98 | "dev": true, 99 | "optional": true, 100 | "os": [ 101 | "android" 102 | ], 103 | "engines": { 104 | "node": ">=12" 105 | } 106 | }, 107 | "node_modules/@esbuild/darwin-arm64": { 108 | "version": "0.17.15", 109 | "cpu": [ 110 | "arm64" 111 | ], 112 | "dev": true, 113 | "license": "MIT", 114 | "optional": true, 115 | "os": [ 116 | "darwin" 117 | ], 118 | "peer": true, 119 | "engines": { 120 | "node": ">=12" 121 | } 122 | }, 123 | "node_modules/@esbuild/darwin-x64": { 124 | "version": "0.16.3", 125 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.3.tgz", 126 | "integrity": "sha512-uEqZQ2omc6BvWqdCiyZ5+XmxuHEi1SPzpVxXCSSV2+Sh7sbXbpeNhHIeFrIpRjAs0lI1FmA1iIOxFozKBhKgRQ==", 127 | "cpu": [ 128 | "x64" 129 | ], 130 | "dev": true, 131 | "optional": true, 132 | "os": [ 133 | "darwin" 134 | ], 135 | "engines": { 136 | "node": ">=12" 137 | } 138 | }, 139 | "node_modules/@esbuild/freebsd-arm64": { 140 | "version": "0.16.3", 141 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.3.tgz", 142 | "integrity": "sha512-nJansp3sSXakNkOD5i5mIz2Is/HjzIhFs49b1tjrPrpCmwgBmH9SSzhC/Z1UqlkivqMYkhfPwMw1dGFUuwmXhw==", 143 | "cpu": [ 144 | "arm64" 145 | ], 146 | "dev": true, 147 | "optional": true, 148 | "os": [ 149 | "freebsd" 150 | ], 151 | "engines": { 152 | "node": ">=12" 153 | } 154 | }, 155 | "node_modules/@esbuild/freebsd-x64": { 156 | "version": "0.16.3", 157 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.3.tgz", 158 | "integrity": "sha512-TfoDzLw+QHfc4a8aKtGSQ96Wa+6eimljjkq9HKR0rHlU83vw8aldMOUSJTUDxbcUdcgnJzPaX8/vGWm7vyV7ug==", 159 | "cpu": [ 160 | "x64" 161 | ], 162 | "dev": true, 163 | "optional": true, 164 | "os": [ 165 | "freebsd" 166 | ], 167 | "engines": { 168 | "node": ">=12" 169 | } 170 | }, 171 | "node_modules/@esbuild/linux-arm": { 172 | "version": "0.16.3", 173 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.3.tgz", 174 | "integrity": "sha512-VwswmSYwVAAq6LysV59Fyqk3UIjbhuc6wb3vEcJ7HEJUtFuLK9uXWuFoH1lulEbE4+5GjtHi3MHX+w1gNHdOWQ==", 175 | "cpu": [ 176 | "arm" 177 | ], 178 | "dev": true, 179 | "optional": true, 180 | "os": [ 181 | "linux" 182 | ], 183 | "engines": { 184 | "node": ">=12" 185 | } 186 | }, 187 | "node_modules/@esbuild/linux-arm64": { 188 | "version": "0.16.3", 189 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.3.tgz", 190 | "integrity": "sha512-7I3RlsnxEFCHVZNBLb2w7unamgZ5sVwO0/ikE2GaYvYuUQs9Qte/w7TqWcXHtCwxvZx/2+F97ndiUQAWs47ZfQ==", 191 | "cpu": [ 192 | "arm64" 193 | ], 194 | "dev": true, 195 | "optional": true, 196 | "os": [ 197 | "linux" 198 | ], 199 | "engines": { 200 | "node": ">=12" 201 | } 202 | }, 203 | "node_modules/@esbuild/linux-ia32": { 204 | "version": "0.16.3", 205 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.3.tgz", 206 | "integrity": "sha512-X8FDDxM9cqda2rJE+iblQhIMYY49LfvW4kaEjoFbTTQ4Go8G96Smj2w3BRTwA8IHGoi9dPOPGAX63dhuv19UqA==", 207 | "cpu": [ 208 | "ia32" 209 | ], 210 | "dev": true, 211 | "optional": true, 212 | "os": [ 213 | "linux" 214 | ], 215 | "engines": { 216 | "node": ">=12" 217 | } 218 | }, 219 | "node_modules/@esbuild/linux-loong64": { 220 | "version": "0.16.3", 221 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.3.tgz", 222 | "integrity": "sha512-hIbeejCOyO0X9ujfIIOKjBjNAs9XD/YdJ9JXAy1lHA+8UXuOqbFe4ErMCqMr8dhlMGBuvcQYGF7+kO7waj2KHw==", 223 | "cpu": [ 224 | "loong64" 225 | ], 226 | "dev": true, 227 | "optional": true, 228 | "os": [ 229 | "linux" 230 | ], 231 | "engines": { 232 | "node": ">=12" 233 | } 234 | }, 235 | "node_modules/@esbuild/linux-mips64el": { 236 | "version": "0.16.3", 237 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.3.tgz", 238 | "integrity": "sha512-znFRzICT/V8VZQMt6rjb21MtAVJv/3dmKRMlohlShrbVXdBuOdDrGb+C2cZGQAR8RFyRe7HS6klmHq103WpmVw==", 239 | "cpu": [ 240 | "mips64el" 241 | ], 242 | "dev": true, 243 | "optional": true, 244 | "os": [ 245 | "linux" 246 | ], 247 | "engines": { 248 | "node": ">=12" 249 | } 250 | }, 251 | "node_modules/@esbuild/linux-ppc64": { 252 | "version": "0.16.3", 253 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.3.tgz", 254 | "integrity": "sha512-EV7LuEybxhXrVTDpbqWF2yehYRNz5e5p+u3oQUS2+ZFpknyi1NXxr8URk4ykR8Efm7iu04//4sBg249yNOwy5Q==", 255 | "cpu": [ 256 | "ppc64" 257 | ], 258 | "dev": true, 259 | "optional": true, 260 | "os": [ 261 | "linux" 262 | ], 263 | "engines": { 264 | "node": ">=12" 265 | } 266 | }, 267 | "node_modules/@esbuild/linux-riscv64": { 268 | "version": "0.16.3", 269 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.3.tgz", 270 | "integrity": "sha512-uDxqFOcLzFIJ+r/pkTTSE9lsCEaV/Y6rMlQjUI9BkzASEChYL/aSQjZjchtEmdnVxDKETnUAmsaZ4pqK1eE5BQ==", 271 | "cpu": [ 272 | "riscv64" 273 | ], 274 | "dev": true, 275 | "optional": true, 276 | "os": [ 277 | "linux" 278 | ], 279 | "engines": { 280 | "node": ">=12" 281 | } 282 | }, 283 | "node_modules/@esbuild/linux-s390x": { 284 | "version": "0.16.3", 285 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.3.tgz", 286 | "integrity": "sha512-NbeREhzSxYwFhnCAQOQZmajsPYtX71Ufej3IQ8W2Gxskfz9DK58ENEju4SbpIj48VenktRASC52N5Fhyf/aliQ==", 287 | "cpu": [ 288 | "s390x" 289 | ], 290 | "dev": true, 291 | "optional": true, 292 | "os": [ 293 | "linux" 294 | ], 295 | "engines": { 296 | "node": ">=12" 297 | } 298 | }, 299 | "node_modules/@esbuild/linux-x64": { 300 | "version": "0.16.3", 301 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.3.tgz", 302 | "integrity": "sha512-SDiG0nCixYO9JgpehoKgScwic7vXXndfasjnD5DLbp1xltANzqZ425l7LSdHynt19UWOcDjG9wJJzSElsPvk0w==", 303 | "cpu": [ 304 | "x64" 305 | ], 306 | "dev": true, 307 | "optional": true, 308 | "os": [ 309 | "linux" 310 | ], 311 | "engines": { 312 | "node": ">=12" 313 | } 314 | }, 315 | "node_modules/@esbuild/netbsd-x64": { 316 | "version": "0.16.3", 317 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.3.tgz", 318 | "integrity": "sha512-AzbsJqiHEq1I/tUvOfAzCY15h4/7Ivp3ff/o1GpP16n48JMNAtbW0qui2WCgoIZArEHD0SUQ95gvR0oSO7ZbdA==", 319 | "cpu": [ 320 | "x64" 321 | ], 322 | "dev": true, 323 | "optional": true, 324 | "os": [ 325 | "netbsd" 326 | ], 327 | "engines": { 328 | "node": ">=12" 329 | } 330 | }, 331 | "node_modules/@esbuild/openbsd-x64": { 332 | "version": "0.16.3", 333 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.3.tgz", 334 | "integrity": "sha512-gSABi8qHl8k3Cbi/4toAzHiykuBuWLZs43JomTcXkjMZVkp0gj3gg9mO+9HJW/8GB5H89RX/V0QP4JGL7YEEVg==", 335 | "cpu": [ 336 | "x64" 337 | ], 338 | "dev": true, 339 | "optional": true, 340 | "os": [ 341 | "openbsd" 342 | ], 343 | "engines": { 344 | "node": ">=12" 345 | } 346 | }, 347 | "node_modules/@esbuild/sunos-x64": { 348 | "version": "0.16.3", 349 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.3.tgz", 350 | "integrity": "sha512-SF9Kch5Ete4reovvRO6yNjMxrvlfT0F0Flm+NPoUw5Z4Q3r1d23LFTgaLwm3Cp0iGbrU/MoUI+ZqwCv5XJijCw==", 351 | "cpu": [ 352 | "x64" 353 | ], 354 | "dev": true, 355 | "optional": true, 356 | "os": [ 357 | "sunos" 358 | ], 359 | "engines": { 360 | "node": ">=12" 361 | } 362 | }, 363 | "node_modules/@esbuild/win32-arm64": { 364 | "version": "0.16.3", 365 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.3.tgz", 366 | "integrity": "sha512-u5aBonZIyGopAZyOnoPAA6fGsDeHByZ9CnEzyML9NqntK6D/xl5jteZUKm/p6nD09+v3pTM6TuUIqSPcChk5gg==", 367 | "cpu": [ 368 | "arm64" 369 | ], 370 | "dev": true, 371 | "optional": true, 372 | "os": [ 373 | "win32" 374 | ], 375 | "engines": { 376 | "node": ">=12" 377 | } 378 | }, 379 | "node_modules/@esbuild/win32-ia32": { 380 | "version": "0.16.3", 381 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.3.tgz", 382 | "integrity": "sha512-GlgVq1WpvOEhNioh74TKelwla9KDuAaLZrdxuuUgsP2vayxeLgVc+rbpIv0IYF4+tlIzq2vRhofV+KGLD+37EQ==", 383 | "cpu": [ 384 | "ia32" 385 | ], 386 | "dev": true, 387 | "optional": true, 388 | "os": [ 389 | "win32" 390 | ], 391 | "engines": { 392 | "node": ">=12" 393 | } 394 | }, 395 | "node_modules/@esbuild/win32-x64": { 396 | "version": "0.16.3", 397 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.3.tgz", 398 | "integrity": "sha512-5/JuTd8OWW8UzEtyf19fbrtMJENza+C9JoPIkvItgTBQ1FO2ZLvjbPO6Xs54vk0s5JB5QsfieUEshRQfu7ZHow==", 399 | "cpu": [ 400 | "x64" 401 | ], 402 | "dev": true, 403 | "optional": true, 404 | "os": [ 405 | "win32" 406 | ], 407 | "engines": { 408 | "node": ">=12" 409 | } 410 | }, 411 | "node_modules/@iarna/toml": { 412 | "version": "2.2.5", 413 | "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", 414 | "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", 415 | "dev": true 416 | }, 417 | "node_modules/@miniflare/cache": { 418 | "version": "2.13.0", 419 | "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.13.0.tgz", 420 | "integrity": "sha512-y3SdN3SVyPECWmLAEGkkrv0RB+LugEPs/FeXn8QtN9aE1vyj69clOAgmsDzoh1DpFfFsLKRiv05aWs4m79P8Xw==", 421 | "dev": true, 422 | "dependencies": { 423 | "@miniflare/core": "2.13.0", 424 | "@miniflare/shared": "2.13.0", 425 | "http-cache-semantics": "^4.1.0", 426 | "undici": "5.20.0" 427 | }, 428 | "engines": { 429 | "node": ">=16.13" 430 | } 431 | }, 432 | "node_modules/@miniflare/cli-parser": { 433 | "version": "2.13.0", 434 | "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.13.0.tgz", 435 | "integrity": "sha512-Nx1PIfuMZ3mK9Dg/JojWZAjHR16h1pcdCFSqYln/ME7y5ifx+P1E5UkShWUQ1cBlibNaltjbJ2n/7stSAsIGPQ==", 436 | "dev": true, 437 | "dependencies": { 438 | "@miniflare/shared": "2.13.0", 439 | "kleur": "^4.1.4" 440 | }, 441 | "engines": { 442 | "node": ">=16.13" 443 | } 444 | }, 445 | "node_modules/@miniflare/core": { 446 | "version": "2.13.0", 447 | "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.13.0.tgz", 448 | "integrity": "sha512-YJ/C0J3k+7xn4gvlMpvePnM3xC8nOnkweW96cc0IA8kJ1JSmScOO2tZ7rrU1RyDgp6StkAtQBw4yC0wYeFycBw==", 449 | "dev": true, 450 | "dependencies": { 451 | "@iarna/toml": "^2.2.5", 452 | "@miniflare/queues": "2.13.0", 453 | "@miniflare/shared": "2.13.0", 454 | "@miniflare/watcher": "2.13.0", 455 | "busboy": "^1.6.0", 456 | "dotenv": "^10.0.0", 457 | "kleur": "^4.1.4", 458 | "set-cookie-parser": "^2.4.8", 459 | "undici": "5.20.0", 460 | "urlpattern-polyfill": "^4.0.3" 461 | }, 462 | "engines": { 463 | "node": ">=16.13" 464 | } 465 | }, 466 | "node_modules/@miniflare/d1": { 467 | "version": "2.13.0", 468 | "resolved": "https://registry.npmjs.org/@miniflare/d1/-/d1-2.13.0.tgz", 469 | "integrity": "sha512-OslqjO8iTcvzyrC0spByftMboRmHJEyHyTHnlKkjWDGdQQztEOjso2Xj+3I4SZIeUYvbzDRhKLS2QXI9a8LS5A==", 470 | "dev": true, 471 | "dependencies": { 472 | "@miniflare/core": "2.13.0", 473 | "@miniflare/shared": "2.13.0" 474 | }, 475 | "engines": { 476 | "node": ">=16.7" 477 | } 478 | }, 479 | "node_modules/@miniflare/durable-objects": { 480 | "version": "2.13.0", 481 | "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.13.0.tgz", 482 | "integrity": "sha512-CRGVBPO9vY4Fc3aV+pdPRVVeYIt64vQqvw+BJbyW+TQtqVP2CGQeziJGnCfcONNNKyooZxGyUkHewUypyH+Qhg==", 483 | "dev": true, 484 | "dependencies": { 485 | "@miniflare/core": "2.13.0", 486 | "@miniflare/shared": "2.13.0", 487 | "@miniflare/storage-memory": "2.13.0", 488 | "undici": "5.20.0" 489 | }, 490 | "engines": { 491 | "node": ">=16.13" 492 | } 493 | }, 494 | "node_modules/@miniflare/html-rewriter": { 495 | "version": "2.13.0", 496 | "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.13.0.tgz", 497 | "integrity": "sha512-XhN7Icyzvtvu+o/A0hrnSiSmla78seCaNwQ9M1TDHxt352I/ahPX4wtPXs6GbKqY0/i+V6yoG2KGFRQ/j59cQQ==", 498 | "dev": true, 499 | "dependencies": { 500 | "@miniflare/core": "2.13.0", 501 | "@miniflare/shared": "2.13.0", 502 | "html-rewriter-wasm": "^0.4.1", 503 | "undici": "5.20.0" 504 | }, 505 | "engines": { 506 | "node": ">=16.13" 507 | } 508 | }, 509 | "node_modules/@miniflare/http-server": { 510 | "version": "2.13.0", 511 | "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.13.0.tgz", 512 | "integrity": "sha512-aMS/nUMTKP15hKnyZboeuWCiqmNrrCu+XRBY/TxDDl07iXcLpiHGf3oVv+yXxXkWlJHJVCbK7i/nXSNPllRMSw==", 513 | "dev": true, 514 | "dependencies": { 515 | "@miniflare/core": "2.13.0", 516 | "@miniflare/shared": "2.13.0", 517 | "@miniflare/web-sockets": "2.13.0", 518 | "kleur": "^4.1.4", 519 | "selfsigned": "^2.0.0", 520 | "undici": "5.20.0", 521 | "ws": "^8.2.2", 522 | "youch": "^2.2.2" 523 | }, 524 | "engines": { 525 | "node": ">=16.13" 526 | } 527 | }, 528 | "node_modules/@miniflare/kv": { 529 | "version": "2.13.0", 530 | "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.13.0.tgz", 531 | "integrity": "sha512-J0AS5x3g/YVOmHMxMAZs07nRXRvSo9jyuC0eikTBf+4AABvBIyvVYmdTjYNjCmr8O5smcfWBX5S27HelD3aAAQ==", 532 | "dev": true, 533 | "dependencies": { 534 | "@miniflare/shared": "2.13.0" 535 | }, 536 | "engines": { 537 | "node": ">=16.13" 538 | } 539 | }, 540 | "node_modules/@miniflare/queues": { 541 | "version": "2.13.0", 542 | "resolved": "https://registry.npmjs.org/@miniflare/queues/-/queues-2.13.0.tgz", 543 | "integrity": "sha512-Gf/a6M1mJL03iOvNqh3JNahcBfvEMPHnO28n0gkCoyYWGvddIr9lwCdFIa0qwNJsC1fIDRxhPg8PZ5cQLBMwRA==", 544 | "dev": true, 545 | "dependencies": { 546 | "@miniflare/shared": "2.13.0" 547 | }, 548 | "engines": { 549 | "node": ">=16.7" 550 | } 551 | }, 552 | "node_modules/@miniflare/r2": { 553 | "version": "2.13.0", 554 | "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.13.0.tgz", 555 | "integrity": "sha512-/5k6GHOYMNV/oBtilV9HDXBkJUrx8oXVigG5vxbnzEGRXyVRmR+Glzu7mFT8JiE94XiEbXHk9Qvu1S5Dej3wBw==", 556 | "dev": true, 557 | "dependencies": { 558 | "@miniflare/shared": "2.13.0", 559 | "undici": "5.20.0" 560 | }, 561 | "engines": { 562 | "node": ">=16.13" 563 | } 564 | }, 565 | "node_modules/@miniflare/runner-vm": { 566 | "version": "2.13.0", 567 | "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.13.0.tgz", 568 | "integrity": "sha512-VmKtF2cA8HmTuLXor1THWY0v+DmaobPct63iLcgWIaUdP3MIvL+9X8HDXFAviCR7bCTe6MKxckHkaOj0IE0aJQ==", 569 | "dev": true, 570 | "dependencies": { 571 | "@miniflare/shared": "2.13.0" 572 | }, 573 | "engines": { 574 | "node": ">=16.13" 575 | } 576 | }, 577 | "node_modules/@miniflare/scheduler": { 578 | "version": "2.13.0", 579 | "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.13.0.tgz", 580 | "integrity": "sha512-AOaQanoR4NjVEzVGWHnrL15A7aMx+d9AKLJhSDF7KaP+4NrT2Wo2BQuXCpn5oStx3itOdlQpMfqQ139e/I8WhQ==", 581 | "dev": true, 582 | "dependencies": { 583 | "@miniflare/core": "2.13.0", 584 | "@miniflare/shared": "2.13.0", 585 | "cron-schedule": "^3.0.4" 586 | }, 587 | "engines": { 588 | "node": ">=16.13" 589 | } 590 | }, 591 | "node_modules/@miniflare/shared": { 592 | "version": "2.13.0", 593 | "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.13.0.tgz", 594 | "integrity": "sha512-m8YFQzKmbjberrV9hPzNcQjNCXxjTjXUpuNrIGjAJO7g+BDztUHaZbdd26H9maBDlkeiWxA3hf0mDyCT/6MCMA==", 595 | "dev": true, 596 | "dependencies": { 597 | "@types/better-sqlite3": "^7.6.0", 598 | "kleur": "^4.1.4", 599 | "npx-import": "^1.1.4", 600 | "picomatch": "^2.3.1" 601 | }, 602 | "engines": { 603 | "node": ">=16.13" 604 | } 605 | }, 606 | "node_modules/@miniflare/sites": { 607 | "version": "2.13.0", 608 | "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.13.0.tgz", 609 | "integrity": "sha512-/tuzIu00o6CF2tkSv01q02MgEShXBSKx85h9jwWvc+6u7prGacAOer0FA1YNRFbE+t9QIfutAkoPGMA9zYf8+Q==", 610 | "dev": true, 611 | "dependencies": { 612 | "@miniflare/kv": "2.13.0", 613 | "@miniflare/shared": "2.13.0", 614 | "@miniflare/storage-file": "2.13.0" 615 | }, 616 | "engines": { 617 | "node": ">=16.13" 618 | } 619 | }, 620 | "node_modules/@miniflare/storage-file": { 621 | "version": "2.13.0", 622 | "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.13.0.tgz", 623 | "integrity": "sha512-LuAeAAY5046rq5U1eFLVkz+ppiFEWytWacpkQw92DvVKFFquZcXSj6WPxZF4rSs23WDk+rdcwuLekbb52aDR7A==", 624 | "dev": true, 625 | "dependencies": { 626 | "@miniflare/shared": "2.13.0", 627 | "@miniflare/storage-memory": "2.13.0" 628 | }, 629 | "engines": { 630 | "node": ">=16.13" 631 | } 632 | }, 633 | "node_modules/@miniflare/storage-memory": { 634 | "version": "2.13.0", 635 | "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.13.0.tgz", 636 | "integrity": "sha512-FnkYcBNXa/ym1ksNilNZycg9WYYKo6cWKplVBeSthRon3e8QY6t3n7/XRseBUo7O6mhDybVTy4wNCP1R2nBiEw==", 637 | "dev": true, 638 | "dependencies": { 639 | "@miniflare/shared": "2.13.0" 640 | }, 641 | "engines": { 642 | "node": ">=16.13" 643 | } 644 | }, 645 | "node_modules/@miniflare/watcher": { 646 | "version": "2.13.0", 647 | "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.13.0.tgz", 648 | "integrity": "sha512-teAacWcpMStoBLbLae95IUaL5lPzjPlXa9lhK9CbRaio/KRMibTMRGWrYos3IVGQRZvklvLwcms/nTvgcdb6yw==", 649 | "dev": true, 650 | "dependencies": { 651 | "@miniflare/shared": "2.13.0" 652 | }, 653 | "engines": { 654 | "node": ">=16.13" 655 | } 656 | }, 657 | "node_modules/@miniflare/web-sockets": { 658 | "version": "2.13.0", 659 | "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.13.0.tgz", 660 | "integrity": "sha512-+U2/HCf+BetRIgjAnNQjkuN6UeAjQmXifhQC+7CCaX834XJhrKXoR6z2xr2xkg1qj0qQs4D2jWG0KzrO5OUpug==", 661 | "dev": true, 662 | "dependencies": { 663 | "@miniflare/core": "2.13.0", 664 | "@miniflare/shared": "2.13.0", 665 | "undici": "5.20.0", 666 | "ws": "^8.2.2" 667 | }, 668 | "engines": { 669 | "node": ">=16.13" 670 | } 671 | }, 672 | "node_modules/@types/better-sqlite3": { 673 | "version": "7.6.4", 674 | "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.4.tgz", 675 | "integrity": "sha512-dzrRZCYPXIXfSR1/surNbJ/grU3scTaygS0OMzjlGf71i9sc2fGyHPXXiXmEvNIoE0cGwsanEFMVJxPXmco9Eg==", 676 | "dev": true, 677 | "dependencies": { 678 | "@types/node": "*" 679 | } 680 | }, 681 | "node_modules/@types/node": { 682 | "version": "18.15.11", 683 | "dev": true, 684 | "license": "MIT" 685 | }, 686 | "node_modules/@types/stack-trace": { 687 | "version": "0.0.29", 688 | "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", 689 | "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", 690 | "dev": true 691 | }, 692 | "node_modules/anymatch": { 693 | "version": "3.1.3", 694 | "dev": true, 695 | "license": "ISC", 696 | "dependencies": { 697 | "normalize-path": "^3.0.0", 698 | "picomatch": "^2.0.4" 699 | }, 700 | "engines": { 701 | "node": ">= 8" 702 | } 703 | }, 704 | "node_modules/binary-extensions": { 705 | "version": "2.2.0", 706 | "dev": true, 707 | "license": "MIT", 708 | "engines": { 709 | "node": ">=8" 710 | } 711 | }, 712 | "node_modules/blake3-wasm": { 713 | "version": "2.1.5", 714 | "dev": true, 715 | "license": "MIT" 716 | }, 717 | "node_modules/braces": { 718 | "version": "3.0.2", 719 | "dev": true, 720 | "license": "MIT", 721 | "dependencies": { 722 | "fill-range": "^7.0.1" 723 | }, 724 | "engines": { 725 | "node": ">=8" 726 | } 727 | }, 728 | "node_modules/buffer-from": { 729 | "version": "1.1.2", 730 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 731 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 732 | "dev": true 733 | }, 734 | "node_modules/builtins": { 735 | "version": "5.0.1", 736 | "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", 737 | "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", 738 | "dev": true, 739 | "dependencies": { 740 | "semver": "^7.0.0" 741 | } 742 | }, 743 | "node_modules/busboy": { 744 | "version": "1.6.0", 745 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 746 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 747 | "dev": true, 748 | "dependencies": { 749 | "streamsearch": "^1.1.0" 750 | }, 751 | "engines": { 752 | "node": ">=10.16.0" 753 | } 754 | }, 755 | "node_modules/chokidar": { 756 | "version": "3.5.3", 757 | "dev": true, 758 | "funding": [ 759 | { 760 | "type": "individual", 761 | "url": "https://paulmillr.com/funding/" 762 | } 763 | ], 764 | "license": "MIT", 765 | "dependencies": { 766 | "anymatch": "~3.1.2", 767 | "braces": "~3.0.2", 768 | "glob-parent": "~5.1.2", 769 | "is-binary-path": "~2.1.0", 770 | "is-glob": "~4.0.1", 771 | "normalize-path": "~3.0.0", 772 | "readdirp": "~3.6.0" 773 | }, 774 | "engines": { 775 | "node": ">= 8.10.0" 776 | }, 777 | "optionalDependencies": { 778 | "fsevents": "~2.3.2" 779 | } 780 | }, 781 | "node_modules/cookie": { 782 | "version": "0.4.2", 783 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 784 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 785 | "dev": true, 786 | "engines": { 787 | "node": ">= 0.6" 788 | } 789 | }, 790 | "node_modules/cron-schedule": { 791 | "version": "3.0.6", 792 | "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", 793 | "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==", 794 | "dev": true 795 | }, 796 | "node_modules/cross-spawn": { 797 | "version": "7.0.3", 798 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 799 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 800 | "dev": true, 801 | "dependencies": { 802 | "path-key": "^3.1.0", 803 | "shebang-command": "^2.0.0", 804 | "which": "^2.0.1" 805 | }, 806 | "engines": { 807 | "node": ">= 8" 808 | } 809 | }, 810 | "node_modules/dotenv": { 811 | "version": "10.0.0", 812 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", 813 | "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", 814 | "dev": true, 815 | "engines": { 816 | "node": ">=10" 817 | } 818 | }, 819 | "node_modules/esbuild": { 820 | "version": "0.17.15", 821 | "dev": true, 822 | "hasInstallScript": true, 823 | "license": "MIT", 824 | "peer": true, 825 | "bin": { 826 | "esbuild": "bin/esbuild" 827 | }, 828 | "engines": { 829 | "node": ">=12" 830 | }, 831 | "optionalDependencies": { 832 | "@esbuild/android-arm": "0.17.15", 833 | "@esbuild/android-arm64": "0.17.15", 834 | "@esbuild/android-x64": "0.17.15", 835 | "@esbuild/darwin-arm64": "0.17.15", 836 | "@esbuild/darwin-x64": "0.17.15", 837 | "@esbuild/freebsd-arm64": "0.17.15", 838 | "@esbuild/freebsd-x64": "0.17.15", 839 | "@esbuild/linux-arm": "0.17.15", 840 | "@esbuild/linux-arm64": "0.17.15", 841 | "@esbuild/linux-ia32": "0.17.15", 842 | "@esbuild/linux-loong64": "0.17.15", 843 | "@esbuild/linux-mips64el": "0.17.15", 844 | "@esbuild/linux-ppc64": "0.17.15", 845 | "@esbuild/linux-riscv64": "0.17.15", 846 | "@esbuild/linux-s390x": "0.17.15", 847 | "@esbuild/linux-x64": "0.17.15", 848 | "@esbuild/netbsd-x64": "0.17.15", 849 | "@esbuild/openbsd-x64": "0.17.15", 850 | "@esbuild/sunos-x64": "0.17.15", 851 | "@esbuild/win32-arm64": "0.17.15", 852 | "@esbuild/win32-ia32": "0.17.15", 853 | "@esbuild/win32-x64": "0.17.15" 854 | } 855 | }, 856 | "node_modules/esbuild/node_modules/@esbuild/android-arm": { 857 | "version": "0.17.15", 858 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.15.tgz", 859 | "integrity": "sha512-sRSOVlLawAktpMvDyJIkdLI/c/kdRTOqo8t6ImVxg8yT7LQDUYV5Rp2FKeEosLr6ZCja9UjYAzyRSxGteSJPYg==", 860 | "cpu": [ 861 | "arm" 862 | ], 863 | "dev": true, 864 | "optional": true, 865 | "os": [ 866 | "android" 867 | ], 868 | "peer": true, 869 | "engines": { 870 | "node": ">=12" 871 | } 872 | }, 873 | "node_modules/esbuild/node_modules/@esbuild/android-arm64": { 874 | "version": "0.17.15", 875 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.15.tgz", 876 | "integrity": "sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==", 877 | "cpu": [ 878 | "arm64" 879 | ], 880 | "dev": true, 881 | "optional": true, 882 | "os": [ 883 | "android" 884 | ], 885 | "peer": true, 886 | "engines": { 887 | "node": ">=12" 888 | } 889 | }, 890 | "node_modules/esbuild/node_modules/@esbuild/android-x64": { 891 | "version": "0.17.15", 892 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.15.tgz", 893 | "integrity": "sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==", 894 | "cpu": [ 895 | "x64" 896 | ], 897 | "dev": true, 898 | "optional": true, 899 | "os": [ 900 | "android" 901 | ], 902 | "peer": true, 903 | "engines": { 904 | "node": ">=12" 905 | } 906 | }, 907 | "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { 908 | "version": "0.17.15", 909 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.15.tgz", 910 | "integrity": "sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==", 911 | "cpu": [ 912 | "x64" 913 | ], 914 | "dev": true, 915 | "optional": true, 916 | "os": [ 917 | "darwin" 918 | ], 919 | "peer": true, 920 | "engines": { 921 | "node": ">=12" 922 | } 923 | }, 924 | "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { 925 | "version": "0.17.15", 926 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.15.tgz", 927 | "integrity": "sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==", 928 | "cpu": [ 929 | "arm64" 930 | ], 931 | "dev": true, 932 | "optional": true, 933 | "os": [ 934 | "freebsd" 935 | ], 936 | "peer": true, 937 | "engines": { 938 | "node": ">=12" 939 | } 940 | }, 941 | "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { 942 | "version": "0.17.15", 943 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.15.tgz", 944 | "integrity": "sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==", 945 | "cpu": [ 946 | "x64" 947 | ], 948 | "dev": true, 949 | "optional": true, 950 | "os": [ 951 | "freebsd" 952 | ], 953 | "peer": true, 954 | "engines": { 955 | "node": ">=12" 956 | } 957 | }, 958 | "node_modules/esbuild/node_modules/@esbuild/linux-arm": { 959 | "version": "0.17.15", 960 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.15.tgz", 961 | "integrity": "sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==", 962 | "cpu": [ 963 | "arm" 964 | ], 965 | "dev": true, 966 | "optional": true, 967 | "os": [ 968 | "linux" 969 | ], 970 | "peer": true, 971 | "engines": { 972 | "node": ">=12" 973 | } 974 | }, 975 | "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { 976 | "version": "0.17.15", 977 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.15.tgz", 978 | "integrity": "sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==", 979 | "cpu": [ 980 | "arm64" 981 | ], 982 | "dev": true, 983 | "optional": true, 984 | "os": [ 985 | "linux" 986 | ], 987 | "peer": true, 988 | "engines": { 989 | "node": ">=12" 990 | } 991 | }, 992 | "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { 993 | "version": "0.17.15", 994 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.15.tgz", 995 | "integrity": "sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==", 996 | "cpu": [ 997 | "ia32" 998 | ], 999 | "dev": true, 1000 | "optional": true, 1001 | "os": [ 1002 | "linux" 1003 | ], 1004 | "peer": true, 1005 | "engines": { 1006 | "node": ">=12" 1007 | } 1008 | }, 1009 | "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { 1010 | "version": "0.17.15", 1011 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.15.tgz", 1012 | "integrity": "sha512-k7FsUJjGGSxwnBmMh8d7IbObWu+sF/qbwc+xKZkBe/lTAF16RqxRCnNHA7QTd3oS2AfGBAnHlXL67shV5bBThQ==", 1013 | "cpu": [ 1014 | "loong64" 1015 | ], 1016 | "dev": true, 1017 | "optional": true, 1018 | "os": [ 1019 | "linux" 1020 | ], 1021 | "peer": true, 1022 | "engines": { 1023 | "node": ">=12" 1024 | } 1025 | }, 1026 | "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { 1027 | "version": "0.17.15", 1028 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.15.tgz", 1029 | "integrity": "sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==", 1030 | "cpu": [ 1031 | "mips64el" 1032 | ], 1033 | "dev": true, 1034 | "optional": true, 1035 | "os": [ 1036 | "linux" 1037 | ], 1038 | "peer": true, 1039 | "engines": { 1040 | "node": ">=12" 1041 | } 1042 | }, 1043 | "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { 1044 | "version": "0.17.15", 1045 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.15.tgz", 1046 | "integrity": "sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==", 1047 | "cpu": [ 1048 | "ppc64" 1049 | ], 1050 | "dev": true, 1051 | "optional": true, 1052 | "os": [ 1053 | "linux" 1054 | ], 1055 | "peer": true, 1056 | "engines": { 1057 | "node": ">=12" 1058 | } 1059 | }, 1060 | "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { 1061 | "version": "0.17.15", 1062 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.15.tgz", 1063 | "integrity": "sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==", 1064 | "cpu": [ 1065 | "riscv64" 1066 | ], 1067 | "dev": true, 1068 | "optional": true, 1069 | "os": [ 1070 | "linux" 1071 | ], 1072 | "peer": true, 1073 | "engines": { 1074 | "node": ">=12" 1075 | } 1076 | }, 1077 | "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { 1078 | "version": "0.17.15", 1079 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.15.tgz", 1080 | "integrity": "sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==", 1081 | "cpu": [ 1082 | "s390x" 1083 | ], 1084 | "dev": true, 1085 | "optional": true, 1086 | "os": [ 1087 | "linux" 1088 | ], 1089 | "peer": true, 1090 | "engines": { 1091 | "node": ">=12" 1092 | } 1093 | }, 1094 | "node_modules/esbuild/node_modules/@esbuild/linux-x64": { 1095 | "version": "0.17.15", 1096 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.15.tgz", 1097 | "integrity": "sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==", 1098 | "cpu": [ 1099 | "x64" 1100 | ], 1101 | "dev": true, 1102 | "optional": true, 1103 | "os": [ 1104 | "linux" 1105 | ], 1106 | "peer": true, 1107 | "engines": { 1108 | "node": ">=12" 1109 | } 1110 | }, 1111 | "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { 1112 | "version": "0.17.15", 1113 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.15.tgz", 1114 | "integrity": "sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==", 1115 | "cpu": [ 1116 | "x64" 1117 | ], 1118 | "dev": true, 1119 | "optional": true, 1120 | "os": [ 1121 | "netbsd" 1122 | ], 1123 | "peer": true, 1124 | "engines": { 1125 | "node": ">=12" 1126 | } 1127 | }, 1128 | "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { 1129 | "version": "0.17.15", 1130 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.15.tgz", 1131 | "integrity": "sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==", 1132 | "cpu": [ 1133 | "x64" 1134 | ], 1135 | "dev": true, 1136 | "optional": true, 1137 | "os": [ 1138 | "openbsd" 1139 | ], 1140 | "peer": true, 1141 | "engines": { 1142 | "node": ">=12" 1143 | } 1144 | }, 1145 | "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { 1146 | "version": "0.17.15", 1147 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.15.tgz", 1148 | "integrity": "sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==", 1149 | "cpu": [ 1150 | "x64" 1151 | ], 1152 | "dev": true, 1153 | "optional": true, 1154 | "os": [ 1155 | "sunos" 1156 | ], 1157 | "peer": true, 1158 | "engines": { 1159 | "node": ">=12" 1160 | } 1161 | }, 1162 | "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { 1163 | "version": "0.17.15", 1164 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.15.tgz", 1165 | "integrity": "sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==", 1166 | "cpu": [ 1167 | "arm64" 1168 | ], 1169 | "dev": true, 1170 | "optional": true, 1171 | "os": [ 1172 | "win32" 1173 | ], 1174 | "peer": true, 1175 | "engines": { 1176 | "node": ">=12" 1177 | } 1178 | }, 1179 | "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { 1180 | "version": "0.17.15", 1181 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.15.tgz", 1182 | "integrity": "sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==", 1183 | "cpu": [ 1184 | "ia32" 1185 | ], 1186 | "dev": true, 1187 | "optional": true, 1188 | "os": [ 1189 | "win32" 1190 | ], 1191 | "peer": true, 1192 | "engines": { 1193 | "node": ">=12" 1194 | } 1195 | }, 1196 | "node_modules/esbuild/node_modules/@esbuild/win32-x64": { 1197 | "version": "0.17.15", 1198 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.15.tgz", 1199 | "integrity": "sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==", 1200 | "cpu": [ 1201 | "x64" 1202 | ], 1203 | "dev": true, 1204 | "optional": true, 1205 | "os": [ 1206 | "win32" 1207 | ], 1208 | "peer": true, 1209 | "engines": { 1210 | "node": ">=12" 1211 | } 1212 | }, 1213 | "node_modules/escape-string-regexp": { 1214 | "version": "4.0.0", 1215 | "dev": true, 1216 | "license": "MIT", 1217 | "engines": { 1218 | "node": ">=10" 1219 | }, 1220 | "funding": { 1221 | "url": "https://github.com/sponsors/sindresorhus" 1222 | } 1223 | }, 1224 | "node_modules/estree-walker": { 1225 | "version": "0.6.1", 1226 | "dev": true, 1227 | "license": "MIT" 1228 | }, 1229 | "node_modules/execa": { 1230 | "version": "6.1.0", 1231 | "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", 1232 | "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", 1233 | "dev": true, 1234 | "dependencies": { 1235 | "cross-spawn": "^7.0.3", 1236 | "get-stream": "^6.0.1", 1237 | "human-signals": "^3.0.1", 1238 | "is-stream": "^3.0.0", 1239 | "merge-stream": "^2.0.0", 1240 | "npm-run-path": "^5.1.0", 1241 | "onetime": "^6.0.0", 1242 | "signal-exit": "^3.0.7", 1243 | "strip-final-newline": "^3.0.0" 1244 | }, 1245 | "engines": { 1246 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1247 | }, 1248 | "funding": { 1249 | "url": "https://github.com/sindresorhus/execa?sponsor=1" 1250 | } 1251 | }, 1252 | "node_modules/fill-range": { 1253 | "version": "7.0.1", 1254 | "dev": true, 1255 | "license": "MIT", 1256 | "dependencies": { 1257 | "to-regex-range": "^5.0.1" 1258 | }, 1259 | "engines": { 1260 | "node": ">=8" 1261 | } 1262 | }, 1263 | "node_modules/fsevents": { 1264 | "version": "2.3.2", 1265 | "dev": true, 1266 | "license": "MIT", 1267 | "optional": true, 1268 | "os": [ 1269 | "darwin" 1270 | ], 1271 | "engines": { 1272 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1273 | } 1274 | }, 1275 | "node_modules/get-stream": { 1276 | "version": "6.0.1", 1277 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", 1278 | "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", 1279 | "dev": true, 1280 | "engines": { 1281 | "node": ">=10" 1282 | }, 1283 | "funding": { 1284 | "url": "https://github.com/sponsors/sindresorhus" 1285 | } 1286 | }, 1287 | "node_modules/glob-parent": { 1288 | "version": "5.1.2", 1289 | "dev": true, 1290 | "license": "ISC", 1291 | "dependencies": { 1292 | "is-glob": "^4.0.1" 1293 | }, 1294 | "engines": { 1295 | "node": ">= 6" 1296 | } 1297 | }, 1298 | "node_modules/html-rewriter-wasm": { 1299 | "version": "0.4.1", 1300 | "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", 1301 | "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==", 1302 | "dev": true 1303 | }, 1304 | "node_modules/http-cache-semantics": { 1305 | "version": "4.1.1", 1306 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", 1307 | "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", 1308 | "dev": true 1309 | }, 1310 | "node_modules/human-signals": { 1311 | "version": "3.0.1", 1312 | "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", 1313 | "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", 1314 | "dev": true, 1315 | "engines": { 1316 | "node": ">=12.20.0" 1317 | } 1318 | }, 1319 | "node_modules/is-binary-path": { 1320 | "version": "2.1.0", 1321 | "dev": true, 1322 | "license": "MIT", 1323 | "dependencies": { 1324 | "binary-extensions": "^2.0.0" 1325 | }, 1326 | "engines": { 1327 | "node": ">=8" 1328 | } 1329 | }, 1330 | "node_modules/is-extglob": { 1331 | "version": "2.1.1", 1332 | "dev": true, 1333 | "license": "MIT", 1334 | "engines": { 1335 | "node": ">=0.10.0" 1336 | } 1337 | }, 1338 | "node_modules/is-glob": { 1339 | "version": "4.0.3", 1340 | "dev": true, 1341 | "license": "MIT", 1342 | "dependencies": { 1343 | "is-extglob": "^2.1.1" 1344 | }, 1345 | "engines": { 1346 | "node": ">=0.10.0" 1347 | } 1348 | }, 1349 | "node_modules/is-number": { 1350 | "version": "7.0.0", 1351 | "dev": true, 1352 | "license": "MIT", 1353 | "engines": { 1354 | "node": ">=0.12.0" 1355 | } 1356 | }, 1357 | "node_modules/is-stream": { 1358 | "version": "3.0.0", 1359 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", 1360 | "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", 1361 | "dev": true, 1362 | "engines": { 1363 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1364 | }, 1365 | "funding": { 1366 | "url": "https://github.com/sponsors/sindresorhus" 1367 | } 1368 | }, 1369 | "node_modules/isexe": { 1370 | "version": "2.0.0", 1371 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1372 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1373 | "dev": true 1374 | }, 1375 | "node_modules/itty-router": { 1376 | "version": "3.0.12", 1377 | "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-3.0.12.tgz", 1378 | "integrity": "sha512-s98XTPhle6GGbaFf0kYrOD3Q8gyhnqvOqkwYijC3AmkceNKqWUp13YHg6dWmqmVv4pP7l7c94XI92I0EXVGO0w==" 1379 | }, 1380 | "node_modules/kleur": { 1381 | "version": "4.1.5", 1382 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 1383 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 1384 | "dev": true, 1385 | "engines": { 1386 | "node": ">=6" 1387 | } 1388 | }, 1389 | "node_modules/lru-cache": { 1390 | "version": "6.0.0", 1391 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1392 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1393 | "dev": true, 1394 | "dependencies": { 1395 | "yallist": "^4.0.0" 1396 | }, 1397 | "engines": { 1398 | "node": ">=10" 1399 | } 1400 | }, 1401 | "node_modules/magic-string": { 1402 | "version": "0.25.9", 1403 | "dev": true, 1404 | "license": "MIT", 1405 | "dependencies": { 1406 | "sourcemap-codec": "^1.4.8" 1407 | } 1408 | }, 1409 | "node_modules/merge-stream": { 1410 | "version": "2.0.0", 1411 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 1412 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 1413 | "dev": true 1414 | }, 1415 | "node_modules/mime": { 1416 | "version": "3.0.0", 1417 | "dev": true, 1418 | "license": "MIT", 1419 | "bin": { 1420 | "mime": "cli.js" 1421 | }, 1422 | "engines": { 1423 | "node": ">=10.0.0" 1424 | } 1425 | }, 1426 | "node_modules/mimic-fn": { 1427 | "version": "4.0.0", 1428 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", 1429 | "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", 1430 | "dev": true, 1431 | "engines": { 1432 | "node": ">=12" 1433 | }, 1434 | "funding": { 1435 | "url": "https://github.com/sponsors/sindresorhus" 1436 | } 1437 | }, 1438 | "node_modules/miniflare": { 1439 | "version": "2.13.0", 1440 | "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.13.0.tgz", 1441 | "integrity": "sha512-ayNhVa4a6bZiOuHtrPmOt4BCYcmW1fBQ/+qGL85smq1m2OBBm3aUs6f4ISf38xH8tk+qewgmAywetyVtn6KHPw==", 1442 | "dev": true, 1443 | "dependencies": { 1444 | "@miniflare/cache": "2.13.0", 1445 | "@miniflare/cli-parser": "2.13.0", 1446 | "@miniflare/core": "2.13.0", 1447 | "@miniflare/d1": "2.13.0", 1448 | "@miniflare/durable-objects": "2.13.0", 1449 | "@miniflare/html-rewriter": "2.13.0", 1450 | "@miniflare/http-server": "2.13.0", 1451 | "@miniflare/kv": "2.13.0", 1452 | "@miniflare/queues": "2.13.0", 1453 | "@miniflare/r2": "2.13.0", 1454 | "@miniflare/runner-vm": "2.13.0", 1455 | "@miniflare/scheduler": "2.13.0", 1456 | "@miniflare/shared": "2.13.0", 1457 | "@miniflare/sites": "2.13.0", 1458 | "@miniflare/storage-file": "2.13.0", 1459 | "@miniflare/storage-memory": "2.13.0", 1460 | "@miniflare/web-sockets": "2.13.0", 1461 | "kleur": "^4.1.4", 1462 | "semiver": "^1.1.0", 1463 | "source-map-support": "^0.5.20", 1464 | "undici": "5.20.0" 1465 | }, 1466 | "bin": { 1467 | "miniflare": "bootstrap.js" 1468 | }, 1469 | "engines": { 1470 | "node": ">=16.13" 1471 | }, 1472 | "peerDependencies": { 1473 | "@miniflare/storage-redis": "2.13.0", 1474 | "cron-schedule": "^3.0.4", 1475 | "ioredis": "^4.27.9" 1476 | }, 1477 | "peerDependenciesMeta": { 1478 | "@miniflare/storage-redis": { 1479 | "optional": true 1480 | }, 1481 | "cron-schedule": { 1482 | "optional": true 1483 | }, 1484 | "ioredis": { 1485 | "optional": true 1486 | } 1487 | } 1488 | }, 1489 | "node_modules/mustache": { 1490 | "version": "4.2.0", 1491 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1492 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1493 | "dev": true, 1494 | "bin": { 1495 | "mustache": "bin/mustache" 1496 | } 1497 | }, 1498 | "node_modules/nanoid": { 1499 | "version": "3.3.6", 1500 | "dev": true, 1501 | "funding": [ 1502 | { 1503 | "type": "github", 1504 | "url": "https://github.com/sponsors/ai" 1505 | } 1506 | ], 1507 | "license": "MIT", 1508 | "bin": { 1509 | "nanoid": "bin/nanoid.cjs" 1510 | }, 1511 | "engines": { 1512 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1513 | } 1514 | }, 1515 | "node_modules/node-forge": { 1516 | "version": "1.3.1", 1517 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", 1518 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", 1519 | "dev": true, 1520 | "engines": { 1521 | "node": ">= 6.13.0" 1522 | } 1523 | }, 1524 | "node_modules/normalize-path": { 1525 | "version": "3.0.0", 1526 | "dev": true, 1527 | "license": "MIT", 1528 | "engines": { 1529 | "node": ">=0.10.0" 1530 | } 1531 | }, 1532 | "node_modules/npm-run-path": { 1533 | "version": "5.1.0", 1534 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", 1535 | "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", 1536 | "dev": true, 1537 | "dependencies": { 1538 | "path-key": "^4.0.0" 1539 | }, 1540 | "engines": { 1541 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1542 | }, 1543 | "funding": { 1544 | "url": "https://github.com/sponsors/sindresorhus" 1545 | } 1546 | }, 1547 | "node_modules/npm-run-path/node_modules/path-key": { 1548 | "version": "4.0.0", 1549 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", 1550 | "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", 1551 | "dev": true, 1552 | "engines": { 1553 | "node": ">=12" 1554 | }, 1555 | "funding": { 1556 | "url": "https://github.com/sponsors/sindresorhus" 1557 | } 1558 | }, 1559 | "node_modules/npx-import": { 1560 | "version": "1.1.4", 1561 | "resolved": "https://registry.npmjs.org/npx-import/-/npx-import-1.1.4.tgz", 1562 | "integrity": "sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==", 1563 | "dev": true, 1564 | "dependencies": { 1565 | "execa": "^6.1.0", 1566 | "parse-package-name": "^1.0.0", 1567 | "semver": "^7.3.7", 1568 | "validate-npm-package-name": "^4.0.0" 1569 | } 1570 | }, 1571 | "node_modules/onetime": { 1572 | "version": "6.0.0", 1573 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", 1574 | "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", 1575 | "dev": true, 1576 | "dependencies": { 1577 | "mimic-fn": "^4.0.0" 1578 | }, 1579 | "engines": { 1580 | "node": ">=12" 1581 | }, 1582 | "funding": { 1583 | "url": "https://github.com/sponsors/sindresorhus" 1584 | } 1585 | }, 1586 | "node_modules/parse-package-name": { 1587 | "version": "1.0.0", 1588 | "resolved": "https://registry.npmjs.org/parse-package-name/-/parse-package-name-1.0.0.tgz", 1589 | "integrity": "sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==", 1590 | "dev": true 1591 | }, 1592 | "node_modules/path-key": { 1593 | "version": "3.1.1", 1594 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1595 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1596 | "dev": true, 1597 | "engines": { 1598 | "node": ">=8" 1599 | } 1600 | }, 1601 | "node_modules/path-to-regexp": { 1602 | "version": "6.2.1", 1603 | "dev": true, 1604 | "license": "MIT" 1605 | }, 1606 | "node_modules/picomatch": { 1607 | "version": "2.3.1", 1608 | "dev": true, 1609 | "license": "MIT", 1610 | "engines": { 1611 | "node": ">=8.6" 1612 | }, 1613 | "funding": { 1614 | "url": "https://github.com/sponsors/jonschlinkert" 1615 | } 1616 | }, 1617 | "node_modules/readdirp": { 1618 | "version": "3.6.0", 1619 | "dev": true, 1620 | "license": "MIT", 1621 | "dependencies": { 1622 | "picomatch": "^2.2.1" 1623 | }, 1624 | "engines": { 1625 | "node": ">=8.10.0" 1626 | } 1627 | }, 1628 | "node_modules/rollup-plugin-inject": { 1629 | "version": "3.0.2", 1630 | "dev": true, 1631 | "license": "MIT", 1632 | "dependencies": { 1633 | "estree-walker": "^0.6.1", 1634 | "magic-string": "^0.25.3", 1635 | "rollup-pluginutils": "^2.8.1" 1636 | } 1637 | }, 1638 | "node_modules/rollup-plugin-node-polyfills": { 1639 | "version": "0.2.1", 1640 | "dev": true, 1641 | "license": "MIT", 1642 | "dependencies": { 1643 | "rollup-plugin-inject": "^3.0.0" 1644 | } 1645 | }, 1646 | "node_modules/rollup-pluginutils": { 1647 | "version": "2.8.2", 1648 | "dev": true, 1649 | "license": "MIT", 1650 | "dependencies": { 1651 | "estree-walker": "^0.6.1" 1652 | } 1653 | }, 1654 | "node_modules/selfsigned": { 1655 | "version": "2.1.1", 1656 | "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", 1657 | "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", 1658 | "dev": true, 1659 | "dependencies": { 1660 | "node-forge": "^1" 1661 | }, 1662 | "engines": { 1663 | "node": ">=10" 1664 | } 1665 | }, 1666 | "node_modules/semiver": { 1667 | "version": "1.1.0", 1668 | "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", 1669 | "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", 1670 | "dev": true, 1671 | "engines": { 1672 | "node": ">=6" 1673 | } 1674 | }, 1675 | "node_modules/semver": { 1676 | "version": "7.5.0", 1677 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", 1678 | "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", 1679 | "dev": true, 1680 | "dependencies": { 1681 | "lru-cache": "^6.0.0" 1682 | }, 1683 | "bin": { 1684 | "semver": "bin/semver.js" 1685 | }, 1686 | "engines": { 1687 | "node": ">=10" 1688 | } 1689 | }, 1690 | "node_modules/set-cookie-parser": { 1691 | "version": "2.6.0", 1692 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", 1693 | "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", 1694 | "dev": true 1695 | }, 1696 | "node_modules/shebang-command": { 1697 | "version": "2.0.0", 1698 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1699 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1700 | "dev": true, 1701 | "dependencies": { 1702 | "shebang-regex": "^3.0.0" 1703 | }, 1704 | "engines": { 1705 | "node": ">=8" 1706 | } 1707 | }, 1708 | "node_modules/shebang-regex": { 1709 | "version": "3.0.0", 1710 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1711 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1712 | "dev": true, 1713 | "engines": { 1714 | "node": ">=8" 1715 | } 1716 | }, 1717 | "node_modules/signal-exit": { 1718 | "version": "3.0.7", 1719 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1720 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", 1721 | "dev": true 1722 | }, 1723 | "node_modules/source-map": { 1724 | "version": "0.6.1", 1725 | "dev": true, 1726 | "license": "BSD-3-Clause", 1727 | "engines": { 1728 | "node": ">=0.10.0" 1729 | } 1730 | }, 1731 | "node_modules/source-map-support": { 1732 | "version": "0.5.21", 1733 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1734 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1735 | "dev": true, 1736 | "dependencies": { 1737 | "buffer-from": "^1.0.0", 1738 | "source-map": "^0.6.0" 1739 | } 1740 | }, 1741 | "node_modules/sourcemap-codec": { 1742 | "version": "1.4.8", 1743 | "dev": true, 1744 | "license": "MIT" 1745 | }, 1746 | "node_modules/stack-trace": { 1747 | "version": "0.0.10", 1748 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1749 | "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", 1750 | "dev": true, 1751 | "engines": { 1752 | "node": "*" 1753 | } 1754 | }, 1755 | "node_modules/streamsearch": { 1756 | "version": "1.1.0", 1757 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1758 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 1759 | "dev": true, 1760 | "engines": { 1761 | "node": ">=10.0.0" 1762 | } 1763 | }, 1764 | "node_modules/strip-final-newline": { 1765 | "version": "3.0.0", 1766 | "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", 1767 | "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", 1768 | "dev": true, 1769 | "engines": { 1770 | "node": ">=12" 1771 | }, 1772 | "funding": { 1773 | "url": "https://github.com/sponsors/sindresorhus" 1774 | } 1775 | }, 1776 | "node_modules/to-regex-range": { 1777 | "version": "5.0.1", 1778 | "dev": true, 1779 | "license": "MIT", 1780 | "dependencies": { 1781 | "is-number": "^7.0.0" 1782 | }, 1783 | "engines": { 1784 | "node": ">=8.0" 1785 | } 1786 | }, 1787 | "node_modules/undici": { 1788 | "version": "5.20.0", 1789 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", 1790 | "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", 1791 | "dev": true, 1792 | "dependencies": { 1793 | "busboy": "^1.6.0" 1794 | }, 1795 | "engines": { 1796 | "node": ">=12.18" 1797 | } 1798 | }, 1799 | "node_modules/urlpattern-polyfill": { 1800 | "version": "4.0.3", 1801 | "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", 1802 | "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==", 1803 | "dev": true 1804 | }, 1805 | "node_modules/validate-npm-package-name": { 1806 | "version": "4.0.0", 1807 | "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", 1808 | "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", 1809 | "dev": true, 1810 | "dependencies": { 1811 | "builtins": "^5.0.0" 1812 | }, 1813 | "engines": { 1814 | "node": "^12.13.0 || ^14.15.0 || >=16.0.0" 1815 | } 1816 | }, 1817 | "node_modules/which": { 1818 | "version": "2.0.2", 1819 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1820 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1821 | "dev": true, 1822 | "dependencies": { 1823 | "isexe": "^2.0.0" 1824 | }, 1825 | "bin": { 1826 | "node-which": "bin/node-which" 1827 | }, 1828 | "engines": { 1829 | "node": ">= 8" 1830 | } 1831 | }, 1832 | "node_modules/wrangler": { 1833 | "version": "2.16.0", 1834 | "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.16.0.tgz", 1835 | "integrity": "sha512-jhkOmEAF7jH58VvnGx7Uqjs2u2T17e/5r9W3OsqESyBjc/8ALUYuwqQ2gr8JsXFny/cE0ysJas0fdY9wggWMCw==", 1836 | "dev": true, 1837 | "dependencies": { 1838 | "@cloudflare/kv-asset-handler": "^0.2.0", 1839 | "@esbuild-plugins/node-globals-polyfill": "^0.1.1", 1840 | "@esbuild-plugins/node-modules-polyfill": "^0.1.4", 1841 | "@miniflare/core": "2.13.0", 1842 | "@miniflare/d1": "2.13.0", 1843 | "@miniflare/durable-objects": "2.13.0", 1844 | "blake3-wasm": "^2.1.5", 1845 | "chokidar": "^3.5.3", 1846 | "esbuild": "0.16.3", 1847 | "miniflare": "2.13.0", 1848 | "nanoid": "^3.3.3", 1849 | "path-to-regexp": "^6.2.0", 1850 | "selfsigned": "^2.0.1", 1851 | "source-map": "^0.7.4", 1852 | "xxhash-wasm": "^1.0.1" 1853 | }, 1854 | "bin": { 1855 | "wrangler": "bin/wrangler.js", 1856 | "wrangler2": "bin/wrangler.js" 1857 | }, 1858 | "engines": { 1859 | "node": ">=16.13.0" 1860 | }, 1861 | "optionalDependencies": { 1862 | "fsevents": "~2.3.2" 1863 | } 1864 | }, 1865 | "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": { 1866 | "version": "0.16.3", 1867 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.3.tgz", 1868 | "integrity": "sha512-DO8WykMyB+N9mIDfI/Hug70Dk1KipavlGAecxS3jDUwAbTpDXj0Lcwzw9svkhxfpCagDmpaTMgxWK8/C/XcXvw==", 1869 | "cpu": [ 1870 | "arm64" 1871 | ], 1872 | "dev": true, 1873 | "optional": true, 1874 | "os": [ 1875 | "darwin" 1876 | ], 1877 | "engines": { 1878 | "node": ">=12" 1879 | } 1880 | }, 1881 | "node_modules/wrangler/node_modules/esbuild": { 1882 | "version": "0.16.3", 1883 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.3.tgz", 1884 | "integrity": "sha512-71f7EjPWTiSguen8X/kxEpkAS7BFHwtQKisCDDV3Y4GLGWBaoSCyD5uXkaUew6JDzA9FEN1W23mdnSwW9kqCeg==", 1885 | "dev": true, 1886 | "hasInstallScript": true, 1887 | "bin": { 1888 | "esbuild": "bin/esbuild" 1889 | }, 1890 | "engines": { 1891 | "node": ">=12" 1892 | }, 1893 | "optionalDependencies": { 1894 | "@esbuild/android-arm": "0.16.3", 1895 | "@esbuild/android-arm64": "0.16.3", 1896 | "@esbuild/android-x64": "0.16.3", 1897 | "@esbuild/darwin-arm64": "0.16.3", 1898 | "@esbuild/darwin-x64": "0.16.3", 1899 | "@esbuild/freebsd-arm64": "0.16.3", 1900 | "@esbuild/freebsd-x64": "0.16.3", 1901 | "@esbuild/linux-arm": "0.16.3", 1902 | "@esbuild/linux-arm64": "0.16.3", 1903 | "@esbuild/linux-ia32": "0.16.3", 1904 | "@esbuild/linux-loong64": "0.16.3", 1905 | "@esbuild/linux-mips64el": "0.16.3", 1906 | "@esbuild/linux-ppc64": "0.16.3", 1907 | "@esbuild/linux-riscv64": "0.16.3", 1908 | "@esbuild/linux-s390x": "0.16.3", 1909 | "@esbuild/linux-x64": "0.16.3", 1910 | "@esbuild/netbsd-x64": "0.16.3", 1911 | "@esbuild/openbsd-x64": "0.16.3", 1912 | "@esbuild/sunos-x64": "0.16.3", 1913 | "@esbuild/win32-arm64": "0.16.3", 1914 | "@esbuild/win32-ia32": "0.16.3", 1915 | "@esbuild/win32-x64": "0.16.3" 1916 | } 1917 | }, 1918 | "node_modules/wrangler/node_modules/source-map": { 1919 | "version": "0.7.4", 1920 | "dev": true, 1921 | "license": "BSD-3-Clause", 1922 | "engines": { 1923 | "node": ">= 8" 1924 | } 1925 | }, 1926 | "node_modules/ws": { 1927 | "version": "8.13.0", 1928 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", 1929 | "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", 1930 | "dev": true, 1931 | "engines": { 1932 | "node": ">=10.0.0" 1933 | }, 1934 | "peerDependencies": { 1935 | "bufferutil": "^4.0.1", 1936 | "utf-8-validate": ">=5.0.2" 1937 | }, 1938 | "peerDependenciesMeta": { 1939 | "bufferutil": { 1940 | "optional": true 1941 | }, 1942 | "utf-8-validate": { 1943 | "optional": true 1944 | } 1945 | } 1946 | }, 1947 | "node_modules/xxhash-wasm": { 1948 | "version": "1.0.2", 1949 | "dev": true, 1950 | "license": "MIT" 1951 | }, 1952 | "node_modules/yallist": { 1953 | "version": "4.0.0", 1954 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1955 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1956 | "dev": true 1957 | }, 1958 | "node_modules/youch": { 1959 | "version": "2.2.2", 1960 | "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", 1961 | "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", 1962 | "dev": true, 1963 | "dependencies": { 1964 | "@types/stack-trace": "0.0.29", 1965 | "cookie": "^0.4.1", 1966 | "mustache": "^4.2.0", 1967 | "stack-trace": "0.0.10" 1968 | } 1969 | } 1970 | } 1971 | } 1972 | -------------------------------------------------------------------------------- /example-weather-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-workers-chatgpt-plugin-example", 3 | "version": "0.0.1", 4 | "devDependencies": { 5 | "@cloudflare/workers-types": "^4.20230404.0", 6 | "wrangler": "^2.15.1" 7 | }, 8 | "private": true, 9 | "scripts": { 10 | "start": "wrangler dev", 11 | "deploy": "wrangler publish" 12 | }, 13 | "dependencies": { 14 | "@cloudflare/itty-router-openapi": "^0.1.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example-weather-plugin/src/forecast.ts: -------------------------------------------------------------------------------- 1 | import { ApiException, OpenAPIRoute, Query, ValidationError } from "@cloudflare/itty-router-openapi"; 2 | 3 | export class GetForecast extends OpenAPIRoute { 4 | static schema = { 5 | tags: ['Forecast'], 6 | summary: 'Get forecast by latitude and longitute query parameters', 7 | parameters: { 8 | latitude: Query(String, { 9 | description: 'The latitude to search for', 10 | default: '30.26' 11 | }), 12 | longitude: Query(String, { 13 | description: 'The longitude to search for', 14 | default: '-97.74' 15 | }), 16 | }, 17 | responses: { 18 | '200': { 19 | schema: { 20 | latitude: 45.42, 21 | longitude: -74.30000000000001, 22 | timezone: "America/Toronto", 23 | offset: -4.0, 24 | currently: { 25 | time: 1654056000.0, 26 | summary: "clear-night", 27 | icon: "clear-night", 28 | precipIntensity: 0.004297839575262448, 29 | precipType: "none", 30 | temperature: 0.0, 31 | apparentTemperature: 0.0, 32 | dewPoint: 100626.40625, 33 | pressure: 0.0, 34 | windSpeed: 15.155382707144021, 35 | windBearing: 71.83404347077446, 36 | cloudCover: 0.0 37 | }, 38 | hourly: { 39 | data: [ 40 | { 41 | time: 1654056000.0, 42 | icon: "clear-night", 43 | summary: "clear-night", 44 | precipAccumulation: 0.0, 45 | precipType: "none", 46 | temperature: 15.225000000000023, 47 | apparentTemperature: 15.472222470944814, 48 | dewPoint: 7.600000000000023, 49 | pressure: 1006.2640625, 50 | windSpeed: 15.155382707144021, 51 | windBearing: 71.83404347077446, 52 | cloudCover: 0.0 53 | }, 54 | ] 55 | }, 56 | daily: { 57 | data: [ 58 | { 59 | time: 1654056000.0, 60 | icon: "rain", 61 | summary: "rain", 62 | sunriseTime: 1654074748.0, 63 | sunsetTime: 1654130288.0, 64 | moonPhase: 0.06510658568536382, 65 | precipAccumulation: 0.726318359375, 66 | precipType: "rain", 67 | temperatureHigh: 16.350000000000023, 68 | temperatureHighTime: 1654102800.0, 69 | temperatureLow: 12.412500000000023, 70 | temperatureLowTime: 1654092000.0, 71 | apparentTemperatureHigh: 18.945154173378285, 72 | apparentTemperatureHighTime: 1654120800.0, 73 | apparentTemperatureLow: 12.736014638445567, 74 | apparentTemperatureLowTime: 13.018278210795586, 75 | dewPoint: 9.745833333333357, 76 | pressure: 1002.41076171875, 77 | windSpeed: 15.194513142232859, 78 | windBearing: 0.3026326497395833, 79 | cloudCover: 0.38462293016675536, 80 | temperatureMin: 12.412500000000023, 81 | temperatureMinTime: 1654092000.0, 82 | temperatureMax: 16.350000000000023, 83 | temperatureMaxTime: 1654102800.0, 84 | apparentTemperatureMin: 12.736014638445567, 85 | apparentTemperatureMinTime: 13.018278210795586, 86 | apparentTemperatureMax: 18.945154173378285, 87 | apparentTemperatureMaxTime: 1654120800.0 88 | } 89 | ] 90 | } 91 | }, 92 | }, 93 | }, 94 | } 95 | 96 | async handle(request: Request, env, ctx, data: Record) { 97 | const url = `https://api.pirateweather.net/forecast/${env.PIRATE_WEATHER_API_KEY}/${data.latitude},${data.longitude}` 98 | 99 | const resp = await fetch(url, { 100 | headers: { 101 | 'Accept': 'application/vnd.github.v3+json', 102 | 'User-Agent': 'Cloudflare Workers ChatGPT Weather Plugin Example' 103 | } 104 | }) 105 | 106 | if (!resp.ok) { 107 | return new Response(await resp.text(), { status: 400 }) 108 | } 109 | 110 | return resp 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /example-weather-plugin/src/index.ts: -------------------------------------------------------------------------------- 1 | import { OpenAPIRouter } from "@cloudflare/itty-router-openapi"; 2 | import { GetForecast } from "./forecast"; 3 | 4 | declare global { 5 | const PIRATE_WEATHER_API_KEY: string 6 | } 7 | 8 | export const router = OpenAPIRouter({ 9 | schema: { 10 | info: { 11 | title: 'Weather API', 12 | description: 'A plugin that allows the user to get weather information', 13 | version: 'v0.0.1', 14 | }, 15 | }, 16 | docs_url: '/', 17 | aiPlugin: { 18 | name_for_human: 'Pirate Weather', 19 | name_for_model: 'pirate_weather', 20 | description_for_human: "Pirate Weather plugin for ChatGPT.", 21 | description_for_model: "Pirate Weather plugin for ChatGPT. You can query weather information using this plugin", 22 | contact_email: 'support@example.com', 23 | legal_info_url: 'http://www.example.com/legal', 24 | logo_url: 'https://workers.cloudflare.com/resources/logo/logo.svg', 25 | }, 26 | }) 27 | 28 | router.get('/forecast', GetForecast) 29 | 30 | // 404 for everything else 31 | router.all('*', () => new Response('Not Found.', { status: 404 })) 32 | 33 | export default { 34 | fetch: router.handle 35 | } 36 | -------------------------------------------------------------------------------- /example-weather-plugin/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "cloudflare-workers-weather-plugin-example" 2 | main = "src/index.ts" 3 | compatibility_date = "2023-04-07" 4 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-ai-example-chatgpt-plugin", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | --------------------------------------------------------------------------------