├── .gitignore ├── .idea ├── .gitignore ├── modules.xml └── vcs.xml ├── .npmignore ├── CLI_DOCS.md ├── DOCS.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── babel.config.cjs ├── cli ├── html_template.html ├── index.js ├── package-lock.json ├── package.json ├── pnpm-lock.yaml └── templates │ ├── c.txt │ ├── code.txt │ ├── flashcards.txt │ ├── json.txt │ ├── md.txt │ ├── reflection.txt │ ├── search.txt │ ├── suggest.txt │ ├── writefiles.txt │ └── yt.txt ├── demo.mp4 ├── examples ├── coding.md └── json.md ├── index.d.ts ├── index.js ├── index.test.js ├── jest.config.mjs ├── package.json ├── pnpm-lock.yaml ├── resources └── doc.docx └── rest_api ├── index.js ├── package.json └── pnpm-lock.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | cli/node_modules 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Optional stylelint cache 59 | .stylelintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variable files 77 | .env 78 | .env.development.local 79 | .env.test.local 80 | .env.production.local 81 | .env.local 82 | 83 | # parcel-bundler cache (https://parceljs.org/) 84 | .cache 85 | .parcel-cache 86 | 87 | # Next.js build output 88 | .next 89 | out 90 | 91 | # Nuxt.js build / generate output 92 | .nuxt 93 | dist 94 | 95 | # Gatsby files 96 | .cache/ 97 | # Comment in the public line in if your project uses Gatsby and not Next.js 98 | # https://nextjs.org/blog/next-9-1#public-directory-support 99 | # public 100 | 101 | # vuepress build output 102 | .vuepress/dist 103 | 104 | # vuepress v2.x temp and cache directory 105 | .temp 106 | .cache 107 | 108 | # Docusaurus cache and generated files 109 | .docusaurus 110 | 111 | # Serverless directories 112 | .serverless/ 113 | 114 | # FuseBox cache 115 | .fusebox/ 116 | 117 | # DynamoDB Local files 118 | .dynamodb/ 119 | 120 | # TernJS port file 121 | .tern-port 122 | 123 | # Stores VSCode versions used for testing VSCode extensions 124 | .vscode-test 125 | 126 | # yarn v2 127 | .yarn/cache 128 | .yarn/unplugged 129 | .yarn/build-state.yml 130 | .yarn/install-state.gz 131 | .pnp.* 132 | 133 | conversation*.json 134 | 135 | claude-*.* 136 | out.txt 137 | testing_claude -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.mp4 2 | resources 3 | converage 4 | cli 5 | rest_api -------------------------------------------------------------------------------- /CLI_DOCS.md: -------------------------------------------------------------------------------- 1 | # CLI Docs 2 | 3 | ## Usage 4 | 5 | ``` 6 | $ claude [input] [options] 7 | ``` 8 | 9 | | Option | Default | Description | 10 | |--------|---------|-------------| 11 | | `--conversation-id` | | Conversation ID to continue | 12 | | `--json` | `false` | Print response as JSON | 13 | | `--files` | | Comma-separated list of files to attach | 14 | | `--help` | | Show help message | 15 | | `--model` | `claude-2` | Claude model to use | 16 | | `--markdown` | `true` | Whether to render markdown in the terminal | 17 | | `--key` | `~/.claude_key` | Path to a text file containing the sessionKey cookie value from https://claude.ai | 18 | 19 | ## Examples 20 | 21 | ``` 22 | $ claude --conversation-id fc6d1a1a-8722-476c-8db9-8a871c121ee9 23 | ``` 24 | 25 | Continue an existing conversation by ID 26 | 27 | ``` 28 | $ claude --json 29 | ``` 30 | 31 | Print responses in JSON format 32 | 33 | ``` 34 | $ claude --files file1.txt,file2.txt 35 | ``` 36 | 37 | Attach files file1.txt and file2.txt to the conversation 38 | 39 | ``` 40 | $ claude --model claude-1 41 | ``` 42 | 43 | Use the claude-1 model 44 | 45 | ``` 46 | $ echo "hello world" | claude 47 | ``` 48 | 49 | Pipe input to claude 50 | 51 | ## Installation 52 | 53 | ``` 54 | $ npm install claude-cli 55 | ``` 56 | 57 | ``` 58 | claude --version 59 | ``` -------------------------------------------------------------------------------- /DOCS.md: -------------------------------------------------------------------------------- 1 | # Docs 2 | 3 | ## Installation 4 | 5 | ```bash 6 | npm install claude-ai 7 | ``` 8 | 9 | ## Usage 10 | 11 | First, import the library: 12 | 13 | ```js 14 | const Claude = require("claude-ai"); 15 | ``` 16 | 17 | Initialize a new Claude instance with your session key: 18 | 19 | > **Note** 20 | > Get `sessionKey` from the `sessionKey` cookie via the Claude website. 21 | 22 | ```js 23 | const claude = new Claude({ 24 | sessionKey: "YOUR_SESSION_KEY", 25 | }); 26 | ``` 27 | 28 | ### `Claude` class 29 | 30 | The main class for interfacing with the Claude API. 31 | 32 | #### Constructor 33 | 34 | ```js 35 | const claude = new Claude({ 36 | sessionKey: string, 37 | proxy: string | function 38 | }) 39 | ``` 40 | 41 | - `sessionKey` \ - Your Claude `sessionKey` cookie 42 | - `proxy` \ 43 | - If `proxy` is a string, it will be prepended before the API endpoint, example: `https://claude.ai/` 44 | - If `proxy` is a function, it will be passed the API route to fetch as well as the fetch options which can then be manipulated before running through fetch. 45 | 46 | #### Methods 47 | 48 | - `startConversation(prompt, params)` \\> - Starts a new conversation with the given prompt message 49 | - `prompt` \ - The initial prompt for the conversation 50 | - `params` \ - The parameters to pass to the initial `sendMessage` call. 51 | - `getConversations()` \\> - Gets recent conversations 52 | - `clearConversations()` \\> - Clear all conversations 53 | - `uploadFile(file)` \\> - Uploads a file 54 | - `file` \ - File object to upload 55 | 56 | ### `Conversation` class 57 | 58 | Returned by `Claude.startConversation()`. 59 | 60 | #### Methods 61 | 62 | - `sendMessage(message, options)` \\> - Sends a followup message in the conversation 63 | 64 | - `message` \ - The message to send 65 | - `options` \ 66 | - `timezone` \ - The timezone for completion (default: `"America/New_York"`) 67 | - `attachments` \ - Array of file attachments 68 | - `model` \ - The Claude model to use (default: `"claude-2"`) 69 | - `done` \<(MessageStream) =\> void\> - Callback when completed 70 | - `progress` \<(MessageStream) =\> void\> - Progress callback from the progress, a block of text where each line starts with "data:" then has some JSON. 71 | - `rawResponse` \ Passed the raw response text 72 | 73 | - `getInfo()` \\> - Gets the conversation info (includes messages, name, created_at, updated_at, etc) 74 | 75 | - `delete()` \\> - Delete the conversation 76 | 77 | - `rename(title)` \\> - Rename a conversation 78 | 79 | - `retryMessage()` \\> - Retry the last message in the conversation (claude's API doesn't support retrying other messages than the most recent) 80 | 81 | - `getMessages` \\> - The same as calling `getInfo().then(a =\> a.chat_messages)` 82 | 83 | #### Callbacks 84 | 85 | - `done(response)` 86 | 87 | - `response` \ 88 | - `completion` \ - The text response from Claude 89 | 90 | - `progress(response)` 91 | 92 | - `response` \ 93 | - `completion` \ - The text response from Claude (if available) 94 | 95 | ### `Message` class 96 | 97 | Returned in `conversationInstance.getInfo()`'s response (`chat_messages` key) 98 | 99 | #### Methods 100 | 101 | - `sendFeedback(type, reason)` \\> - Send feedback about a message 102 | 103 | #### Properties 104 | 105 | - `createdAt` \ - Created at date 106 | - `editedAt` \ - Edited at date (Note, there's currently no way to edit messages via the API yet) 107 | - `updatedAt` \ - Updated at date 108 | - `isBot` \ - Whether the message was send by a human or claude 109 | - `json` \ - The JSON for the message (this is what's returned from the chat_messages key of a conversation's getInfo fetch request) 110 | 111 | ### Types 112 | 113 | ```ts 114 | type Attachment { 115 | modified_at: string 116 | created_at: string 117 | filetype: string 118 | size: int 119 | } 120 | ``` 121 | 122 | ```ts 123 | type MessageStream { 124 | completion: string 125 | stop_reason: string | null 126 | model: string 127 | log_id: string 128 | // "within_limit" or probably "exceeded_limit" 129 | messageLimit: {type: string} 130 | } 131 | ``` 132 | 133 | ```ts 134 | type Message { 135 | attachments: Attachment[] 136 | chat_feedback: ?? 137 | edited_at: string? 138 | index: int 139 | sender: 'human' | 'assistant' 140 | text: string 141 | created_at: string 142 | updated_at: string 143 | uuid: string 144 | } 145 | ``` 146 | 147 | ## CLI 148 | 149 | The `claude-cli` CLI tool is also available: 150 | 151 | ``` 152 | npm install -g claude-cli 153 | ``` 154 | 155 | ``` 156 | Usage: 157 | claude [options] 158 | 159 | Options: 160 | 161 | --conversation-id Conversation ID to continue 162 | --json Print response as JSON 163 | --files Comma-separated list of files to attach 164 | --help Show help message 165 | --model Claude model to use (default: claude-2) 166 | --markdown Whether to render markdown in the terminal (default: true) 167 | --key Path to a text file containing the sessionKey cookie value 168 | 169 | Examples: 170 | 171 | claude --conversation-id fc6d1a1a-8722-476c-8db9-8a871c121ee9 172 | claude --json 173 | claude --files file1.txt,file2.txt 174 | echo "hello world" | claude 175 | ``` 176 | 177 | ## Contributing 178 | 179 | Contributions welcome! This library was created by @Explosion-Scratch on GitHub. Please submit PRs to help improve it.# Docs 180 | 181 | ## Installation 182 | 183 | ```bash 184 | npm install claude-ai 185 | ``` 186 | 187 | ## Usage 188 | 189 | First, import the library: 190 | 191 | ```js 192 | const Claude = require("claude-ai"); 193 | ``` 194 | 195 | Initialize a new Claude instance with your session key: 196 | 197 | > **Note** 198 | > Get `sessionKey` from the `sessionKey` cookie via the Claude website. 199 | 200 | ```js 201 | const claude = new Claude({ 202 | sessionKey: "YOUR_SESSION_KEY", 203 | }); 204 | ``` 205 | 206 | ### `Claude` class 207 | 208 | The main class for interfacing with the Claude API. 209 | 210 | #### Constructor 211 | 212 | ```js 213 | const claude = new Claude({ 214 | sessionKey: string, 215 | proxy: string | function 216 | }) 217 | ``` 218 | 219 | - `sessionKey` \ - Your Claude `sessionKey` cookie 220 | - `proxy` \ 221 | - If `proxy` is a string, it will be prepended before the API endpoint, example: `https://claude.ai/` 222 | - If `proxy` is a function, it will be passed the API route to fetch as well as the fetch options which can then be manipulated before running through fetch. 223 | 224 | #### Methods 225 | 226 | - `startConversation(prompt, params)` \\> - Starts a new conversation with the given prompt message 227 | - `prompt` \ - The initial prompt for the conversation 228 | - `params` \ - The parameters to pass to the initial `sendMessage` call. 229 | - `getConversations()` \\> - Gets recent conversations 230 | - `clearConversations()` \\> - Clear all conversations 231 | - `uploadFile(file)` \\> - Uploads a file 232 | - `file` \ - File object to upload 233 | 234 | ### `Conversation` class 235 | 236 | Returned by `Claude.startConversation()`. 237 | 238 | #### Methods 239 | 240 | - `sendMessage(message, options)` \\> - Sends a followup message in the conversation 241 | 242 | - `message` \ - The message to send 243 | - `options` \ 244 | - `timezone` \ - The timezone for completion (default: `"America/New_York"`) 245 | - `attachments` \ - Array of file attachments 246 | - `model` \ - The Claude model to use (default: `"claude-2"`) 247 | - `done` \<(MessageStream) =\> void\> - Callback when completed 248 | - `progress` \<(MessageStream) =\> void\> - Progress callback from the progress, a block of text where each line starts with "data:" then has some JSON. 249 | - `rawResponse` \ Passed the raw response text 250 | 251 | - `getInfo()` \\> - Gets the conversation info (includes messages, name, created_at, updated_at, etc) 252 | 253 | - `delete()` \\> - Delete the conversation 254 | 255 | - `rename(title)` \\> - Rename a conversation 256 | 257 | - `retryMessage()` \\> - Retry the last message in the conversation (claude's API doesn't support retrying other messages than the most recent) 258 | 259 | - `getMessages` \\> - The same as calling `getInfo().then(a =\> a.chat_messages)` 260 | 261 | #### Callbacks 262 | 263 | - `done(response)` 264 | 265 | - `response` \ 266 | - `completion` \ - The text response from Claude 267 | 268 | - `progress(response)` 269 | 270 | - `response` \ 271 | - `completion` \ - The text response from Claude (if available) 272 | 273 | ### `Message` class 274 | 275 | Returned in `conversationInstance.getInfo()`'s response (`chat_messages` key) 276 | 277 | #### Methods 278 | 279 | - `sendFeedback(type, reason)` \\> - Send feedback about a message 280 | 281 | #### Properties 282 | 283 | - `createdAt` \ - Created at date 284 | - `editedAt` \ - Edited at date (Note, there's currently no way to edit messages via the API yet) 285 | - `updatedAt` \ - Updated at date 286 | - `isBot` \ - Whether the message was send by a human or claude 287 | - `json` \ - The JSON for the message (this is what's returned from the chat_messages key of a conversation's getInfo fetch request) 288 | 289 | ### Types 290 | 291 | ```ts 292 | type Attachment { 293 | modified_at: string 294 | created_at: string 295 | filetype: string 296 | size: int 297 | } 298 | ``` 299 | 300 | ```ts 301 | type MessageStream { 302 | completion: string 303 | stop_reason: string | null 304 | model: string 305 | log_id: string 306 | // "within_limit" or probably "exceeded_limit" 307 | messageLimit: {type: string} 308 | } 309 | ``` 310 | 311 | ```ts 312 | type Message { 313 | attachments: Attachment[] 314 | chat_feedback: ?? 315 | edited_at: string? 316 | index: int 317 | sender: 'human' | 'assistant' 318 | text: string 319 | created_at: string 320 | updated_at: string 321 | uuid: string 322 | } 323 | ``` 324 | 325 | ## CLI 326 | 327 | The `claude-cli` CLI tool is also available: 328 | 329 | ``` 330 | npm install -g claude-cli 331 | ``` 332 | 333 | ``` 334 | Usage: 335 | claude [options] 336 | 337 | Options: 338 | 339 | --conversation-id Conversation ID to continue 340 | --json Print response as JSON 341 | --files Comma-separated list of files to attach 342 | --help Show help message 343 | --model Claude model to use (default: claude-2) 344 | --markdown Whether to render markdown in the terminal (default: true) 345 | --key Path to a text file containing the sessionKey cookie value 346 | 347 | Examples: 348 | 349 | claude --conversation-id fc6d1a1a-8722-476c-8db9-8a871c121ee9 350 | claude --json 351 | claude --files file1.txt,file2.txt 352 | echo "hello world" | claude 353 | ``` 354 | 355 | ## Contributing 356 | 357 | Contributions welcome! This library was created by @Explosion-Scratch on GitHub. Please submit PRs to help improve it. 358 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > Claude seems to have blocked all non-browser traffic to their internal API – Effectively rendering this project useless. **If you have a fix/method to get around this please PR**. Sorry – [@Explosion-Scratch](https://github.com/Explosion-Scratch) 3 | 4 | 5 | 6 |

7 | 8 | https://github.com/Explosion-Scratch/claude-unofficial-api/assets/61319150/6c3f706d-bddf-42e6-9745-aa1f7561ca40 9 | 10 | This is a lightweight (isomorphic, 0 dependency) JavaScript library for interacting with the [Claude AI](https://www.claude.ai/) chatbot's unofficial internal API. [CLI installation](#cli-installation), [API installation + usage](#usage) 11 | 12 | > _Psst. It can also [code full projects](https://github.com/Explosion-Scratch/claude-unofficial-api/blob/main/examples/coding.md) and [output valid JSON](https://github.com/Explosion-Scratch/claude-unofficial-api/blob/main/examples/json.md)_ 13 | 14 | ## Features 15 | - 💬 Start new conversations 16 | - 📎 Upload files 17 | - 🧪 [Unit tests included with 85% coverage of code and 100% pass rates!](https://github.com/Explosion-Scratch/claude-unofficial-api/assets/61319150/b65d32f4-2b43-4bc3-8e2c-4cf977fe7e89) 18 | 19 | - 🌎 Isomorphic (supposing you setup a proxy, cors make me sad) 20 | - 🔄 Async/await ready with modern syntax 21 | - 💾 Get and respond to existing conversations 22 | - 🚀 Upcoming 23 | - CLI: ~~Retrying responses, [Reflexion](https://arxiv.org/abs/2303.11366) implementation, prompt templates~~, auto conversation saving 24 | - API: ~~Better error handling, automated unit tests~~, caching layer, searching, ~~`setActiveModel`, list available models, send message directly to existing conversation~~, hooks for events, used tokens count (percentage/raw), token estimator, ~~available tokens for model~~ 25 | - 💪 Supports all claude models (`claude-2`, `claude-1.3`, `claude-instant-100k` - See `--model` flag) 26 | 27 | 28 | ## Installation 29 | 30 | ``` 31 | npm install claude-ai 32 | ``` 33 | 34 | ## CLI installation 35 | ``` 36 | npm install -g claude-cli 37 | ``` 38 | > **Note** 39 | > Run `claude --help` or see [CLI_DOCS.md](CLI_DOCS.md) for more info about the CLI 40 | 41 | ## Usage 42 | 43 | First, import the library: 44 | 45 | ```js 46 | const Claude = require('claude-ai'); 47 | ``` 48 | 49 | Initialize a new Claude instance with your session key: 50 | 51 | > **Note** 52 | > Get `sessionKey` from the `sessionKey` cookie via the Claude website. 53 | 54 | ```js 55 | const claude = new Claude({ 56 | sessionKey: 'YOUR_SESSION_KEY' 57 | }); 58 | ``` 59 | 60 | Start a conversation by calling `startConversation()` with a prompt message (or get existing conversations via `.getConversations()`): 61 | 62 | ```js 63 | const conversation = await claude.startConversation('Hello Claude!'); 64 | ``` 65 | 66 | The `Conversation` instance exposes methods like `sendMessage()` to continue the chat: 67 | 68 | ```js 69 | await conversation.sendMessage('How are you today?'); 70 | ``` 71 | 72 | The full code would look like: 73 | 74 | ```js 75 | const Claude = require('claude-ai'); 76 | 77 | const claude = new Claude({ 78 | sessionKey: 'YOUR_SESSION_KEY' 79 | }); 80 | 81 | await claude.init(); 82 | 83 | const conversation = await claude.startConversation('Hello Claude!'); 84 | 85 | await conversation.sendMessage('How are you today?'); 86 | ``` 87 | 88 | See the [documentation](#documentation) below for the full API reference. 89 | 90 | ## Documentation 91 | 92 | ### `Claude` 93 | 94 | The main class for interfacing with the Claude API. 95 | 96 | **Constructor:** 97 | ```js 98 | const claude_instance = new Claude({ 99 | sessionKey: string, 100 | proxy: string | ({endpoint, options}) => ({endpoint, options}) 101 | }) 102 | ``` 103 | 104 | - If proxy is a function it will be passed the API route to fetch as well as the fetch options which can then be manipulated before running through fetch. If you're feeling adventurous you could also just modify the `claude.request` functionnn (see source for more info) 105 | - If `proxy` is a string, it will simply be prepended before the API endpoint, example: `https://claude.ai/` 106 | 107 | **Parameters:** 108 | 109 | - `sessionKey` - Your Claude `sessionKey` cookie 110 | 111 | **Methods (on an instance):** 112 | 113 | - `startConversation(prompt)` - Starts a new conversation with the given prompt message 114 | - `getConversations()` - Gets recent conversations 115 | - `clearConversations()` - Clear all conversations 116 | - `uploadFile(file)` - Uploads a file 117 | 118 | ### `Conversation` 119 | 120 | Returned by `Claude.startConversation()`. 121 | 122 | **Methods:** 123 | 124 | - `sendMessage(message, options)` - Sends a followup message in the conversation 125 | - `getInfo()` - Gets the conversation (includes messages, name, created_at, update_at, etc) 126 | - `delete()` - Delete the conversation (returns fetch response) 127 | 128 | **SendMessage Options:** 129 | 130 | - `timezone` - The timezone for completion 131 | - `attachments` - Array of file attachments 132 | - `model` - The Claude model to use (default: `claude-2`, other models that I know of include `claude-1.3`, and `claude-instant-100k`. Seems to also accept `claude-1` but transform it to `claude-1.3`) 133 | - `done` - Callback when completed 134 | - `progress` - Progress callback 135 | 136 | ## Contributing 137 | 138 | Contributions welcome! This library was created by @Explosion-Scratch on GitHub. Please submit PRs to help improve it. 139 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | - Implement a really nice prompt template parser which has basic programming template language syntax 3 | - Call functions via `{#func param}` or `{#func}param{/func}` (Example: `{#claude "...."}`) 4 | - Connect this prompt parser to the CLI to give claude access to tools like ChatGPT can use, these could be toggled on and off and would be stored in the form of templates 5 | - E.g. a web search helper could look like the following: 6 | 7 | ``` 8 | You now have access to a web search engine, to use this, before responding totally respond with only `search(searchterm)`. The user will then reply back with the relevant search results. After being given the relevant search results continue your conversation 9 | 10 | {#on user_message} 11 | {#js} 12 | return await doSearching(response.split('search(')[0].split(')')[0]).then(r => r.map(i => `${i.title} - ${i.url}\n\n${i.description}`).join('\n')) 13 | {/js} 14 | {/on} 15 | ``` 16 | 17 | - Another example could be allowing Claude to reflect on and improve its answers, or allowing it to write to files, do precise calculations, or even output valid JSON (wow) 18 | - It would also be cool to make the CLI more general, e.g. work with any other AI models as well, providing similar functionality + prompt templates. (maybe time for a separate repo at this point for the parser + cli) 19 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { presets: ['@babel/preset-env'] } -------------------------------------------------------------------------------- /cli/html_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {TITLE} 6 | 7 | 8 | 9 | 10 |

11 | 12 |
13 | 154 | 155 | 156 | 157 | 158 | 298 | -------------------------------------------------------------------------------- /cli/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "inquirer": "^9.2.8" 9 | } 10 | }, 11 | "node_modules/ansi-escapes": { 12 | "version": "4.3.2", 13 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", 14 | "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", 15 | "dependencies": { 16 | "type-fest": "^0.21.3" 17 | }, 18 | "engines": { 19 | "node": ">=8" 20 | }, 21 | "funding": { 22 | "url": "https://github.com/sponsors/sindresorhus" 23 | } 24 | }, 25 | "node_modules/ansi-regex": { 26 | "version": "5.0.1", 27 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 28 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 29 | "engines": { 30 | "node": ">=8" 31 | } 32 | }, 33 | "node_modules/ansi-styles": { 34 | "version": "4.3.0", 35 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 36 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 37 | "dependencies": { 38 | "color-convert": "^2.0.1" 39 | }, 40 | "engines": { 41 | "node": ">=8" 42 | }, 43 | "funding": { 44 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 45 | } 46 | }, 47 | "node_modules/base64-js": { 48 | "version": "1.5.1", 49 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 50 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 51 | "funding": [ 52 | { 53 | "type": "github", 54 | "url": "https://github.com/sponsors/feross" 55 | }, 56 | { 57 | "type": "patreon", 58 | "url": "https://www.patreon.com/feross" 59 | }, 60 | { 61 | "type": "consulting", 62 | "url": "https://feross.org/support" 63 | } 64 | ] 65 | }, 66 | "node_modules/bl": { 67 | "version": "4.1.0", 68 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 69 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 70 | "dependencies": { 71 | "buffer": "^5.5.0", 72 | "inherits": "^2.0.4", 73 | "readable-stream": "^3.4.0" 74 | } 75 | }, 76 | "node_modules/buffer": { 77 | "version": "5.7.1", 78 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 79 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 80 | "funding": [ 81 | { 82 | "type": "github", 83 | "url": "https://github.com/sponsors/feross" 84 | }, 85 | { 86 | "type": "patreon", 87 | "url": "https://www.patreon.com/feross" 88 | }, 89 | { 90 | "type": "consulting", 91 | "url": "https://feross.org/support" 92 | } 93 | ], 94 | "dependencies": { 95 | "base64-js": "^1.3.1", 96 | "ieee754": "^1.1.13" 97 | } 98 | }, 99 | "node_modules/chalk": { 100 | "version": "5.3.0", 101 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", 102 | "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", 103 | "engines": { 104 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 105 | }, 106 | "funding": { 107 | "url": "https://github.com/chalk/chalk?sponsor=1" 108 | } 109 | }, 110 | "node_modules/chardet": { 111 | "version": "0.7.0", 112 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", 113 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" 114 | }, 115 | "node_modules/cli-cursor": { 116 | "version": "3.1.0", 117 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", 118 | "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", 119 | "dependencies": { 120 | "restore-cursor": "^3.1.0" 121 | }, 122 | "engines": { 123 | "node": ">=8" 124 | } 125 | }, 126 | "node_modules/cli-spinners": { 127 | "version": "2.9.0", 128 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", 129 | "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", 130 | "engines": { 131 | "node": ">=6" 132 | }, 133 | "funding": { 134 | "url": "https://github.com/sponsors/sindresorhus" 135 | } 136 | }, 137 | "node_modules/cli-width": { 138 | "version": "4.0.0", 139 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz", 140 | "integrity": "sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==", 141 | "engines": { 142 | "node": ">= 12" 143 | } 144 | }, 145 | "node_modules/clone": { 146 | "version": "1.0.4", 147 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", 148 | "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", 149 | "engines": { 150 | "node": ">=0.8" 151 | } 152 | }, 153 | "node_modules/color-convert": { 154 | "version": "2.0.1", 155 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 156 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 157 | "dependencies": { 158 | "color-name": "~1.1.4" 159 | }, 160 | "engines": { 161 | "node": ">=7.0.0" 162 | } 163 | }, 164 | "node_modules/color-name": { 165 | "version": "1.1.4", 166 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 167 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 168 | }, 169 | "node_modules/defaults": { 170 | "version": "1.0.4", 171 | "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", 172 | "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", 173 | "dependencies": { 174 | "clone": "^1.0.2" 175 | }, 176 | "funding": { 177 | "url": "https://github.com/sponsors/sindresorhus" 178 | } 179 | }, 180 | "node_modules/emoji-regex": { 181 | "version": "8.0.0", 182 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 183 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 184 | }, 185 | "node_modules/escape-string-regexp": { 186 | "version": "5.0.0", 187 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", 188 | "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", 189 | "engines": { 190 | "node": ">=12" 191 | }, 192 | "funding": { 193 | "url": "https://github.com/sponsors/sindresorhus" 194 | } 195 | }, 196 | "node_modules/external-editor": { 197 | "version": "3.1.0", 198 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", 199 | "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", 200 | "dependencies": { 201 | "chardet": "^0.7.0", 202 | "iconv-lite": "^0.4.24", 203 | "tmp": "^0.0.33" 204 | }, 205 | "engines": { 206 | "node": ">=4" 207 | } 208 | }, 209 | "node_modules/figures": { 210 | "version": "5.0.0", 211 | "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", 212 | "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", 213 | "dependencies": { 214 | "escape-string-regexp": "^5.0.0", 215 | "is-unicode-supported": "^1.2.0" 216 | }, 217 | "engines": { 218 | "node": ">=14" 219 | }, 220 | "funding": { 221 | "url": "https://github.com/sponsors/sindresorhus" 222 | } 223 | }, 224 | "node_modules/has-flag": { 225 | "version": "4.0.0", 226 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 227 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 228 | "engines": { 229 | "node": ">=8" 230 | } 231 | }, 232 | "node_modules/iconv-lite": { 233 | "version": "0.4.24", 234 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 235 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 236 | "dependencies": { 237 | "safer-buffer": ">= 2.1.2 < 3" 238 | }, 239 | "engines": { 240 | "node": ">=0.10.0" 241 | } 242 | }, 243 | "node_modules/ieee754": { 244 | "version": "1.2.1", 245 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 246 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 247 | "funding": [ 248 | { 249 | "type": "github", 250 | "url": "https://github.com/sponsors/feross" 251 | }, 252 | { 253 | "type": "patreon", 254 | "url": "https://www.patreon.com/feross" 255 | }, 256 | { 257 | "type": "consulting", 258 | "url": "https://feross.org/support" 259 | } 260 | ] 261 | }, 262 | "node_modules/inherits": { 263 | "version": "2.0.4", 264 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 265 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 266 | }, 267 | "node_modules/inquirer": { 268 | "version": "9.2.8", 269 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.8.tgz", 270 | "integrity": "sha512-SJ0fVfgIzZL1AD6WvFhivlh5/3hN6WeAvpvPrpPXH/8MOcQHeXhinmSm5CDJNRC2Q+sLh9YJ5k8F8/5APMXSfw==", 271 | "dependencies": { 272 | "ansi-escapes": "^4.3.2", 273 | "chalk": "^5.3.0", 274 | "cli-cursor": "^3.1.0", 275 | "cli-width": "^4.0.0", 276 | "external-editor": "^3.0.3", 277 | "figures": "^5.0.0", 278 | "lodash": "^4.17.21", 279 | "mute-stream": "1.0.0", 280 | "ora": "^5.4.1", 281 | "run-async": "^3.0.0", 282 | "rxjs": "^7.8.1", 283 | "string-width": "^4.2.3", 284 | "strip-ansi": "^6.0.1", 285 | "through": "^2.3.6", 286 | "wrap-ansi": "^6.0.1" 287 | }, 288 | "engines": { 289 | "node": ">=14.18.0" 290 | } 291 | }, 292 | "node_modules/is-fullwidth-code-point": { 293 | "version": "3.0.0", 294 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 295 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 296 | "engines": { 297 | "node": ">=8" 298 | } 299 | }, 300 | "node_modules/is-interactive": { 301 | "version": "1.0.0", 302 | "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", 303 | "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", 304 | "engines": { 305 | "node": ">=8" 306 | } 307 | }, 308 | "node_modules/is-unicode-supported": { 309 | "version": "1.3.0", 310 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", 311 | "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", 312 | "engines": { 313 | "node": ">=12" 314 | }, 315 | "funding": { 316 | "url": "https://github.com/sponsors/sindresorhus" 317 | } 318 | }, 319 | "node_modules/lodash": { 320 | "version": "4.17.21", 321 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 322 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 323 | }, 324 | "node_modules/log-symbols": { 325 | "version": "4.1.0", 326 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 327 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 328 | "dependencies": { 329 | "chalk": "^4.1.0", 330 | "is-unicode-supported": "^0.1.0" 331 | }, 332 | "engines": { 333 | "node": ">=10" 334 | }, 335 | "funding": { 336 | "url": "https://github.com/sponsors/sindresorhus" 337 | } 338 | }, 339 | "node_modules/log-symbols/node_modules/chalk": { 340 | "version": "4.1.2", 341 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 342 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 343 | "dependencies": { 344 | "ansi-styles": "^4.1.0", 345 | "supports-color": "^7.1.0" 346 | }, 347 | "engines": { 348 | "node": ">=10" 349 | }, 350 | "funding": { 351 | "url": "https://github.com/chalk/chalk?sponsor=1" 352 | } 353 | }, 354 | "node_modules/log-symbols/node_modules/is-unicode-supported": { 355 | "version": "0.1.0", 356 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 357 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 358 | "engines": { 359 | "node": ">=10" 360 | }, 361 | "funding": { 362 | "url": "https://github.com/sponsors/sindresorhus" 363 | } 364 | }, 365 | "node_modules/mimic-fn": { 366 | "version": "2.1.0", 367 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 368 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 369 | "engines": { 370 | "node": ">=6" 371 | } 372 | }, 373 | "node_modules/mute-stream": { 374 | "version": "1.0.0", 375 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", 376 | "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", 377 | "engines": { 378 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0" 379 | } 380 | }, 381 | "node_modules/onetime": { 382 | "version": "5.1.2", 383 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", 384 | "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", 385 | "dependencies": { 386 | "mimic-fn": "^2.1.0" 387 | }, 388 | "engines": { 389 | "node": ">=6" 390 | }, 391 | "funding": { 392 | "url": "https://github.com/sponsors/sindresorhus" 393 | } 394 | }, 395 | "node_modules/ora": { 396 | "version": "5.4.1", 397 | "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", 398 | "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", 399 | "dependencies": { 400 | "bl": "^4.1.0", 401 | "chalk": "^4.1.0", 402 | "cli-cursor": "^3.1.0", 403 | "cli-spinners": "^2.5.0", 404 | "is-interactive": "^1.0.0", 405 | "is-unicode-supported": "^0.1.0", 406 | "log-symbols": "^4.1.0", 407 | "strip-ansi": "^6.0.0", 408 | "wcwidth": "^1.0.1" 409 | }, 410 | "engines": { 411 | "node": ">=10" 412 | }, 413 | "funding": { 414 | "url": "https://github.com/sponsors/sindresorhus" 415 | } 416 | }, 417 | "node_modules/ora/node_modules/chalk": { 418 | "version": "4.1.2", 419 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 420 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 421 | "dependencies": { 422 | "ansi-styles": "^4.1.0", 423 | "supports-color": "^7.1.0" 424 | }, 425 | "engines": { 426 | "node": ">=10" 427 | }, 428 | "funding": { 429 | "url": "https://github.com/chalk/chalk?sponsor=1" 430 | } 431 | }, 432 | "node_modules/ora/node_modules/is-unicode-supported": { 433 | "version": "0.1.0", 434 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 435 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 436 | "engines": { 437 | "node": ">=10" 438 | }, 439 | "funding": { 440 | "url": "https://github.com/sponsors/sindresorhus" 441 | } 442 | }, 443 | "node_modules/os-tmpdir": { 444 | "version": "1.0.2", 445 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 446 | "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", 447 | "engines": { 448 | "node": ">=0.10.0" 449 | } 450 | }, 451 | "node_modules/readable-stream": { 452 | "version": "3.6.2", 453 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 454 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 455 | "dependencies": { 456 | "inherits": "^2.0.3", 457 | "string_decoder": "^1.1.1", 458 | "util-deprecate": "^1.0.1" 459 | }, 460 | "engines": { 461 | "node": ">= 6" 462 | } 463 | }, 464 | "node_modules/restore-cursor": { 465 | "version": "3.1.0", 466 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", 467 | "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", 468 | "dependencies": { 469 | "onetime": "^5.1.0", 470 | "signal-exit": "^3.0.2" 471 | }, 472 | "engines": { 473 | "node": ">=8" 474 | } 475 | }, 476 | "node_modules/run-async": { 477 | "version": "3.0.0", 478 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", 479 | "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", 480 | "engines": { 481 | "node": ">=0.12.0" 482 | } 483 | }, 484 | "node_modules/rxjs": { 485 | "version": "7.8.1", 486 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", 487 | "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", 488 | "dependencies": { 489 | "tslib": "^2.1.0" 490 | } 491 | }, 492 | "node_modules/safe-buffer": { 493 | "version": "5.2.1", 494 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 495 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 496 | "funding": [ 497 | { 498 | "type": "github", 499 | "url": "https://github.com/sponsors/feross" 500 | }, 501 | { 502 | "type": "patreon", 503 | "url": "https://www.patreon.com/feross" 504 | }, 505 | { 506 | "type": "consulting", 507 | "url": "https://feross.org/support" 508 | } 509 | ] 510 | }, 511 | "node_modules/safer-buffer": { 512 | "version": "2.1.2", 513 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 514 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 515 | }, 516 | "node_modules/signal-exit": { 517 | "version": "3.0.7", 518 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 519 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 520 | }, 521 | "node_modules/string_decoder": { 522 | "version": "1.3.0", 523 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 524 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 525 | "dependencies": { 526 | "safe-buffer": "~5.2.0" 527 | } 528 | }, 529 | "node_modules/string-width": { 530 | "version": "4.2.3", 531 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 532 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 533 | "dependencies": { 534 | "emoji-regex": "^8.0.0", 535 | "is-fullwidth-code-point": "^3.0.0", 536 | "strip-ansi": "^6.0.1" 537 | }, 538 | "engines": { 539 | "node": ">=8" 540 | } 541 | }, 542 | "node_modules/strip-ansi": { 543 | "version": "6.0.1", 544 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 545 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 546 | "dependencies": { 547 | "ansi-regex": "^5.0.1" 548 | }, 549 | "engines": { 550 | "node": ">=8" 551 | } 552 | }, 553 | "node_modules/supports-color": { 554 | "version": "7.2.0", 555 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 556 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 557 | "dependencies": { 558 | "has-flag": "^4.0.0" 559 | }, 560 | "engines": { 561 | "node": ">=8" 562 | } 563 | }, 564 | "node_modules/through": { 565 | "version": "2.3.8", 566 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 567 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" 568 | }, 569 | "node_modules/tmp": { 570 | "version": "0.0.33", 571 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 572 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 573 | "dependencies": { 574 | "os-tmpdir": "~1.0.2" 575 | }, 576 | "engines": { 577 | "node": ">=0.6.0" 578 | } 579 | }, 580 | "node_modules/tslib": { 581 | "version": "2.6.0", 582 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", 583 | "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" 584 | }, 585 | "node_modules/type-fest": { 586 | "version": "0.21.3", 587 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", 588 | "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", 589 | "engines": { 590 | "node": ">=10" 591 | }, 592 | "funding": { 593 | "url": "https://github.com/sponsors/sindresorhus" 594 | } 595 | }, 596 | "node_modules/util-deprecate": { 597 | "version": "1.0.2", 598 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 599 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 600 | }, 601 | "node_modules/wcwidth": { 602 | "version": "1.0.1", 603 | "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", 604 | "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", 605 | "dependencies": { 606 | "defaults": "^1.0.3" 607 | } 608 | }, 609 | "node_modules/wrap-ansi": { 610 | "version": "6.2.0", 611 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", 612 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", 613 | "dependencies": { 614 | "ansi-styles": "^4.0.0", 615 | "string-width": "^4.1.0", 616 | "strip-ansi": "^6.0.0" 617 | }, 618 | "engines": { 619 | "node": ">=8" 620 | } 621 | } 622 | } 623 | } 624 | -------------------------------------------------------------------------------- /cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claude-cli", 3 | "version": "1.0.4", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "claude": "index.js" 8 | }, 9 | "keywords": [], 10 | "author": "@Explosion-Scratch", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/Explosion-Scratch/claude-unofficial-api.git" 14 | }, 15 | "license": "Unlicense", 16 | "dependencies": { 17 | "@web-std/file": "^3.0.2", 18 | "chalk": "^5.3.0", 19 | "claude-ai": "^1.2.2", 20 | "dotenv": "^16.3.1", 21 | "inquirer": "^9.2.7", 22 | "isomorphic-fetch": "^3.0.0", 23 | "marked": "^5.1.0", 24 | "marked-terminal": "^5.2.0", 25 | "meow": "^12.0.1", 26 | "mime-types": "^2.1.35", 27 | "open": "^9.1.0", 28 | "ora": "^6.3.1" 29 | }, 30 | "type": "module" 31 | } -------------------------------------------------------------------------------- /cli/templates/c.txt: -------------------------------------------------------------------------------- 1 | {__silent=true} 2 | 3 | {prompt} 4 | 5 | First write out your initial code draft, then think through 10 thoughts and criticisms of the code you wrote. Make sure to use the latest syntax, try not to rely on libraries if possible. Don't include debug or comments or empty lines. Include testing, make the code modular and implement checks to make sure it works properly. Make sure your code is readable. After your draft, write out some final thoughts and then write your final version. Make sure your final doesn't have empty lines, non JSDoc comments, placeholders, etc. Start your final version with FINAL: 6 | 7 | {#every} 8 | {#js} 9 | if (variables.claude_response.completion.includes("FINAL:")){ 10 | console.log(md(variables.claude_response.completion.split("FINAL:")[1])) 11 | } 12 | {/js} 13 | {/every} -------------------------------------------------------------------------------- /cli/templates/code.txt: -------------------------------------------------------------------------------- 1 | You are an expert software engineer at Google. Be concise! Your job is to implement the code designed and detailed in the uploaded file requirements.txt. This project is {prompt} 2 | 3 | Make sure to use the latest syntax (e.g. ESM/ES6 imports and syntax in JavaScript, HTML5, CSS3), don't include debug or comments or empty lines. Include error handling to make sure it works properly. Make sure your code is readable and does not include any placeholders or psuedocode. 4 | 5 | DONT INCLUDE ANY TODO COMMENTS. DONT INCLUDE PSUEDOCODE. DONT INCLUDE PLACEHOLDERS. Adhere to the user's request as closely as possible. Your code's output, variables and function should match the example output given in the design document exactly. 6 | 7 | First write you initial implementation of the requirements, then write out 4 criticisms of it. Finally write your final code. Begin the final code with "FINAL:" 8 | 9 | The requirements and design details of this project are located in the uploaded file requirements.txt 10 | 11 | Make sure that the final code matches the example output from the requirements.txt file. Your final code should also not contain any bugs or errors, and must hold up to the Google JS style guide. Make sure all functions you use are defined in the environment. 12 | 13 | {#file requirements.txt} 14 | {#claude} 15 | Write out a design document of the following javascript program. Include details about the functions needed, the code structure, variables and outputs from each function. Also include example outputs of the program (in code blocks). Don't implement or output any code. 16 | 17 | {prompt} 18 | {/claude} 19 | {/file} 20 | 21 | {#followup} 22 | Now write the files you just wrote in the following format. (Only code files that you authored such as 'index.js' or 'package.json') 23 | {#import "writefiles.txt"} 24 | {/followup} -------------------------------------------------------------------------------- /cli/templates/flashcards.txt: -------------------------------------------------------------------------------- 1 | Generate flashcards and questions about {prompt} in the following format: 2 | 3 | ``` 4 | # Topic header 5 | 6 | front : back 7 | note attached indented by 2 spaces and will be shown after the flip side of the flashcard is revealed while practicing 8 | ``` 9 | 10 | Example with 2 flashcards and a topic header: 11 | ``` 12 | # Cognitive biases 13 | From https://www.titlemax.com/discovery-center/lifestyle/50-cognitive-biases-to-be-aware-of-so-you-can-be-the-very-best-version-of-you/ 14 | 15 | Fundamental Attribution Error: We judge others on their personality or fundamental character, but we judge ourselves on the situation. 16 | Example: “Sally is late to class; she's lazy. You're late to class; it was a bad morning.” 17 | Self-Serving Bias: Our failures are situational but our successes are our responsibility. 18 | Example: “You won that award due to hard work rather than help or luck. Meanwhile, you failed a test because you hadn't gotten enough sleep.” 19 | ``` 20 | 21 | Your flashcards 22 | Now generate 15 flashcards about {prompt}. Refer to transcript.txt for lesson transcript. Put your result in a code block. 23 | 24 | {#file transcript.txt} 25 | WEBVTT FILE 26 | 27 | 1 28 | 00:00:11.339 --> 00:00:15.630 29 | Howdy and welcome back. This time we're 30 | going to talk about a little bit more 31 | 32 | 2 33 | 00:00:15.630 --> 00:00:19.579 34 | naming than we did the last session. 35 | We're going to talk this time about Covalin 36 | 37 | 3 38 | 00:00:19.579 --> 00:00:24.039 39 | compounds in about acids. So last 40 | time we did ionic compounds. This time 41 | 42 | 4 43 | 00:00:24.039 --> 00:00:28.070 44 | we're going to do compounds that have 45 | Covalin Bonds. What we're going to look 46 | 47 | 5 48 | 00:00:28.070 --> 00:00:32.060 49 | at this point is just looking at 50 | the names and the formulas. A quick review. 51 | 52 | 6 53 | 00:00:32.070 --> 00:00:35.409 54 | I might throw some terms out there 55 | that you might not remember, and that's 56 | 57 | 7 58 | 00:00:35.409 --> 00:00:39.189 59 | okay, because they're not really important 60 | for this unit. But the more you 61 | 62 | 8 63 | 00:00:39.189 --> 00:00:43.460 64 | can hear things, the better off you will 65 | be later on. So let's take a look 66 | 67 | 9 68 | 00:00:43.469 --> 00:00:47.700 69 | at first of all, just how we want 70 | to go ahead and name things when you're 71 | 72 | 10 73 | 00:00:47.700 --> 00:00:52.070 74 | naming Covalin compounds. What we have to 75 | look at here is we're not dealing 76 | 77 | 11 78 | 00:00:52.070 --> 00:00:56.990 79 | with charges anymore. We're dealing with 80 | using prefixes to tell us how many 81 | 82 | 12 83 | 00:00:56.990 --> 00:01:04.590 84 | of each atom we're going to be looking 85 | at. So we have prefixes and the prefixes 86 | 87 | 13 88 | 00:01:04.590 --> 00:01:14.110 89 | are pretty simple. Mono die, try 90 | And here's where they get a little 91 | 92 | 14 93 | 00:01:14.110 --> 00:01:21.930 94 | different for is tetra five is 95 | Penta like pent oxide Whatever you want to 96 | 97 | 15 98 | 00:01:21.930 --> 00:01:30.769 99 | call there Six hex a like a hexagon seven helped. 100 | 101 | 16 102 | 00:01:31.940 --> 00:01:41.629 103 | Eight is Oct like Octagon Like a stop 104 | sign. Nine is non or Nana and 10 105 | 106 | 17 107 | 00:01:41.709 --> 00:01:47.410 108 | is deck like a deck again 10 sided 109 | figure. Okay, so some of these that kind 110 | 111 | 18 112 | 00:01:47.410 --> 00:01:50.310 113 | of end with a or you can 114 | leave it off, depending upon what you're looking 115 | 116 | 19 117 | 00:01:50.310 --> 00:01:54.800 118 | for. So once you have these prefixes, 119 | what you're looking at is each prefix 120 | 121 | 20 122 | 00:01:54.800 --> 00:01:59.230 123 | just tells you how many of each 124 | Adam you're going to have within your compound 125 | 126 | 21 127 | 00:01:59.239 --> 00:02:04.270 128 | and again, prefix means it's happening 129 | at the beginning of the word. So 130 | 131 | 22 132 | 00:02:04.270 --> 00:02:09.750 133 | when you're naming these compounds, you 134 | have a prefix usually and then the 135 | 136 | 23 137 | 00:02:09.750 --> 00:02:15.250 138 | name of the element and then another 139 | prefix, and then you change the ending 140 | 141 | 24 142 | 00:02:15.250 --> 00:02:19.199 143 | toe. I'd just like we did with 144 | the Ionic Compounds. Let's take a look at 145 | 146 | 25 147 | 00:02:19.199 --> 00:02:25.580 148 | a few examples. So the examples I'm 149 | going to do are actually the examples of 150 | 151 | 26 152 | 00:02:25.580 --> 00:02:30.400 153 | compounds that I've created up here. The 154 | first one that we have is this 155 | 156 | 27 157 | 00:02:30.400 --> 00:02:35.910 158 | one right here. The yellow represents 159 | a sulfur atom, and then the green 160 | 161 | 28 162 | 00:02:35.910 --> 00:02:44.860 163 | ones represent fluoride atoms. So this is 164 | one sulfur, and then we have 123456 165 | 166 | 29 167 | 00:02:45.039 --> 00:02:55.039 168 | fluoride atoms. So we have s f 169 | six. This one we have one sulfur, six 170 | 171 | 30 172 | 00:02:55.039 --> 00:02:59.650 173 | florins. So the one time that we don't 174 | use a prefix was going to be our very 175 | 176 | 31 177 | 00:02:59.650 --> 00:03:04.110 178 | first example here. We don't use 179 | the prefix mono when you're talking about 180 | 181 | 32 182 | 00:03:04.110 --> 00:03:13.960 183 | the very first element in the system. 184 | So this is just sulfur hex, a fluoride. 185 | 186 | 33 187 | 00:03:16.740 --> 00:03:21.610 188 | Okay, so one sulfur don't need the 189 | prefix here for the first element. And 190 | 191 | 34 192 | 00:03:21.610 --> 00:03:26.930 193 | then six, the prefix hex a and 194 | then fluoride notice we change the florin 195 | 196 | 35 197 | 00:03:26.930 --> 00:03:31.080 198 | to fluoride to be the end of the 199 | word there. All right, let's take a look 200 | 201 | 36 202 | 00:03:31.080 --> 00:03:40.910 203 | at the next one. This particular compound is B F 204 | 205 | 37 206 | 00:03:40.920 --> 00:03:47.480 207 | or B H three. Sorry, It is 208 | a plainer molecule. It's flat, which means it's 209 | 210 | 38 211 | 00:03:47.480 --> 00:03:51.650 212 | it's all in one plane here on 213 | it's a triangle, So it's tribunal plainer. 214 | 215 | 39 216 | 00:03:51.660 --> 00:03:57.250 217 | It's kind of fun to think about it that 218 | way, but the blue one is a B, and 219 | 220 | 40 221 | 00:03:57.250 --> 00:04:03.640 222 | the white ones are h is. So we 223 | have one boron and three hydrogen. So this 224 | 225 | 41 226 | 00:04:03.640 --> 00:04:11.170 227 | is boron. Three is the prefix try. 228 | And then when hydrogen is that second 229 | 230 | 42 231 | 00:04:11.170 --> 00:04:17.260 232 | element there we change it from 233 | hydrogen toe hydride. So boron try hydride. 234 | 235 | 43 236 | 00:04:18.940 --> 00:04:26.990 237 | Let's hit one more example here. 238 | How about water? We want to have something 239 | 240 | 44 241 | 00:04:26.990 --> 00:04:30.839 242 | that doesn't just have a single element in 243 | the front of it. So we have water. 244 | 245 | 45 246 | 00:04:30.850 --> 00:04:36.950 247 | Everybody knows the formula for water. H 248 | 20 But the name for water is a 249 | 250 | 46 251 | 00:04:36.950 --> 00:04:41.490 252 | little bit different. So we know water. 253 | That's its common name. But if we 254 | 255 | 47 256 | 00:04:41.490 --> 00:04:45.250 257 | set this thing up and we look at 258 | it, we see that there are two hydrogen 259 | 260 | 48 261 | 00:04:45.250 --> 00:04:49.149 262 | and one oxygen. If we're going to 263 | use the same naming convention that we've 264 | 265 | 49 266 | 00:04:49.149 --> 00:04:54.329 267 | been using, this has a really 268 | different official name than the one we've 269 | 270 | 50 271 | 00:04:54.339 --> 00:05:03.490 272 | been giving it. So this is 273 | to hydrogen. So that's di hydrogen mon 274 | 275 | 51 276 | 00:05:03.940 --> 00:05:11.520 277 | oxide. So there we go. Here's the 278 | word. The mon comes into play. So again 279 | 280 | 52 281 | 00:05:11.520 --> 00:05:16.860 282 | we have a prefix and the word and 283 | then a prefix. And then the word with 284 | 285 | 53 286 | 00:05:16.860 --> 00:05:21.850 287 | that I d. E ending at the end of 288 | it. Let's do one or two going the other 289 | 290 | 54 291 | 00:05:21.850 --> 00:05:31.680 292 | direction. Here's one of my favorites Tetra 293 | 294 | 55 295 | 00:05:31.680 --> 00:05:41.410 296 | phosphorus deck oxide. All right, so 297 | let's take this word apart and see 298 | 299 | 56 300 | 00:05:41.410 --> 00:05:45.990 301 | if we can create the formula from it. 302 | So we have two things going on here. 303 | 304 | 57 305 | 00:05:46.000 --> 00:05:55.570 306 | We have Tetra which means four and 307 | then we know the phosphorus is P. Then 308 | 309 | 58 310 | 00:05:55.570 --> 00:06:04.930 311 | we have deck or Deco. That means 10 and 312 | the oxide is for Oh, so if we take 313 | 314 | 59 315 | 00:06:04.930 --> 00:06:12.770 316 | a look at this whole thing, we can 317 | break everything apart. We get P 40 10. 318 | 319 | 60 320 | 00:06:13.240 --> 00:06:16.150 321 | What's kind of neat about this is when 322 | you take this and you put it into 323 | 324 | 61 325 | 00:06:16.150 --> 00:06:23.440 326 | water. If you take this compound into 327 | water, it turns into H three po four, 328 | 329 | 62 330 | 00:06:23.450 --> 00:06:31.360 331 | which we're going to use as a segue 332 | into Let's talk about some acids. Assets 333 | 334 | 63 335 | 00:06:31.360 --> 00:06:38.320 336 | are pretty neat. So acids are 337 | covalin compounds that in some ways behave 338 | 339 | 64 340 | 00:06:38.320 --> 00:06:44.080 341 | like ionic compounds. This is an example 342 | of an acid. This is nitric acid. 343 | 344 | 65 345 | 00:06:44.090 --> 00:06:52.350 346 | It's h and then n in the middle. And 347 | then 03 So H n 03 this is nitric acid. 348 | 349 | 66 350 | 00:06:52.540 --> 00:06:57.200 351 | What's cool about acids is these 352 | air Allco violently bonded together. But 353 | 354 | 67 355 | 00:06:57.200 --> 00:07:02.140 356 | when I take this thing and I stick 357 | it in the water, it loses this hydrogen 358 | 359 | 68 360 | 00:07:02.330 --> 00:07:08.970 361 | and this hydrogen goes off and attach 362 | is to a water in a different place 363 | 364 | 69 365 | 00:07:09.740 --> 00:07:15.220 366 | to give me a nitrate ion and a 367 | hydro Nia, my on. So you have this weird 368 | 369 | 70 370 | 00:07:15.220 --> 00:07:21.430 371 | chemical reaction where a covalin compound 372 | ends up having a charge. This 373 | 374 | 71 375 | 00:07:21.430 --> 00:07:25.670 376 | becomes the nitrate ion and a three 377 | minus. And this becomes the hydro Nia, 378 | 379 | 72 380 | 00:07:25.670 --> 00:07:30.840 381 | my on h 30 plus, What's kind 382 | of need about these things is because they 383 | 384 | 73 385 | 00:07:30.840 --> 00:07:34.260 386 | have different behaviors. So even though 387 | they're co violent, they have different 388 | 389 | 74 390 | 00:07:34.260 --> 00:07:39.420 391 | behaviors. So they have different naming 392 | schemes to assets are a little 393 | 394 | 75 395 | 00:07:39.420 --> 00:07:44.920 396 | bit more challenging to work with. 397 | All right, our first example with our 398 | 399 | 76 400 | 00:07:44.920 --> 00:07:50.140 401 | acids, we're going to look at our binary 402 | acids. So just like binary ionic compounds 403 | 404 | 77 405 | 00:07:50.160 --> 00:07:54.120 406 | or binary coagulant compounds, we're only 407 | dealing with two kinds of atoms 408 | 409 | 78 410 | 00:07:54.120 --> 00:08:01.360 411 | here. So for our binary acids, what 412 | we're looking at is to write the formula 413 | 414 | 79 415 | 00:08:03.740 --> 00:08:09.250 416 | you're going to use some number of h 417 | is that are going to kind of act as 418 | 419 | 80 420 | 00:08:09.250 --> 00:08:13.700 421 | a positive charge even though there 422 | it's a covalin bond. And then you're 423 | 424 | 81 425 | 00:08:13.700 --> 00:08:19.660 426 | going to pair that with an ion, which, 427 | if you don't remember that is a negative 428 | 429 | 82 430 | 00:08:19.660 --> 00:08:28.340 431 | ion from the periodic table. So these 432 | were going to be things that are non 433 | 434 | 83 435 | 00:08:28.340 --> 00:08:34.620 436 | metals sulfide oxide, fluoride chloride. Any 437 | of those air going to be paired 438 | 439 | 84 440 | 00:08:34.620 --> 00:08:38.510 441 | up here? So that's how we're going to 442 | write the formula you want to balance 443 | 444 | 85 445 | 00:08:38.510 --> 00:08:44.090 446 | out The quote charges when we write 447 | the name for a binary ionic camp are 448 | 449 | 86 450 | 00:08:44.090 --> 00:08:49.460 451 | a binary acid. What we're dealing with 452 | here is you're going to use the prefix 453 | 454 | 87 455 | 00:08:49.470 --> 00:08:56.549 456 | hydro than the root word, and then 457 | you're going to have a Suffolk's IQ and 458 | 459 | 88 460 | 00:08:56.549 --> 00:09:01.650 461 | then the word acid. So hydro something 462 | ic acid. If I just said that your 463 | 464 | 89 465 | 00:09:01.650 --> 00:09:05.390 466 | brain probably went hydrochloric acid because 467 | that's the only one you know, 468 | 469 | 90 470 | 00:09:05.400 --> 00:09:08.880 471 | right, that's the only one that we 472 | always think of. So you might have put 473 | 474 | 91 475 | 00:09:08.880 --> 00:09:12.540 476 | some of that in your pool, or you 477 | might have heard about it from a different 478 | 479 | 92 480 | 00:09:12.540 --> 00:09:16.590 481 | chemistry class. But that's probably the 482 | most common binary acid that we 483 | 484 | 93 485 | 00:09:16.590 --> 00:09:22.080 486 | all hear about. And so let's use 487 | that as our first example. Chloride 488 | 489 | 94 490 | 00:09:22.080 --> 00:09:26.880 491 | ion, if you go back to your periodic 492 | table, is in Group seven, so it has 493 | 494 | 95 495 | 00:09:26.880 --> 00:09:33.120 496 | a negative one charge. Hydrogen can have 497 | either a positive one or a negative 498 | 499 | 96 500 | 00:09:33.120 --> 00:09:37.070 501 | one charge. So since the chloride is 502 | a negative one, that clearly has to 503 | 504 | 97 505 | 00:09:37.070 --> 00:09:43.840 506 | be the positive. And so we have our 507 | compound of HCL, and we use the chloral 508 | 509 | 98 510 | 00:09:43.850 --> 00:09:50.960 511 | components to help fill in the gap 512 | for the name and we have hydro chloral 513 | 514 | 99 515 | 00:09:50.960 --> 00:09:58.870 516 | IQ acid. And there's our first example. Let's do one more. 517 | 518 | 100 519 | 00:10:03.140 --> 00:10:09.490 520 | Let's do hte to s. And I'm going to 521 | make a point about writing a quiz here 522 | 523 | 101 524 | 00:10:09.490 --> 00:10:14.510 525 | after this particular molecule. And the 526 | reason for that is without water 527 | 528 | 102 529 | 00:10:14.520 --> 00:10:22.910 530 | and acid is just a covalin molecule. So 531 | if I had hte us gas, I would call 532 | 533 | 103 534 | 00:10:22.910 --> 00:10:32.700 535 | this di hydrogen sulfide or die 536 | hydrogen mono sulfide is probably more 537 | 538 | 104 539 | 00:10:32.700 --> 00:10:37.320 540 | appropriate. Okay, But as soon as I take 541 | that and I put it into water, it 542 | 543 | 105 544 | 00:10:37.320 --> 00:10:41.880 545 | goes through a chemical reaction and 546 | it dissociates. And so it's behavior 547 | 548 | 106 549 | 00:10:41.880 --> 00:10:45.830 550 | becomes that oven acid. It's different. And 551 | so now when I take a look at 552 | 553 | 107 554 | 00:10:45.830 --> 00:10:54.750 555 | this thing now it's an acid, 556 | and I'm going to say hydro sulfuric 557 | 558 | 108 559 | 00:10:55.540 --> 00:11:04.000 560 | acid. There's the route. There's the 561 | prefix. There's the Suffolk six. So 562 | 563 | 109 564 | 00:11:04.000 --> 00:11:11.060 565 | there's the whole thing. Other assets that 566 | you might get that are, um, binary 567 | 568 | 110 569 | 00:11:11.060 --> 00:11:16.600 570 | acids would be like Hydrofluoric hydro. 571 | I otik hydro nitric. There are very 572 | 573 | 111 574 | 00:11:16.600 --> 00:11:24.990 575 | few, um, acids that are binary that 576 | we use other than the halogen, so that 577 | 578 | 112 579 | 00:11:24.990 --> 00:11:31.420 580 | makes it a little bit easier for us. 581 | Okay, so up next we have turn Eri acids 582 | 583 | 113 584 | 00:11:31.420 --> 00:11:34.790 585 | and there's going to be two different 586 | types of coronary acids. All the turn 587 | 588 | 114 589 | 00:11:34.790 --> 00:11:40.630 590 | Eri acids are based on which Polly 591 | Atomic Ion are we working with And what 592 | 593 | 115 594 | 00:11:40.630 --> 00:11:44.590 595 | does it end with? So we have we're 596 | going to kind of split this up so that we 597 | 598 | 116 599 | 00:11:44.590 --> 00:11:49.660 600 | can see it. So if the an eye on ends with ICT, 601 | 602 | 117 603 | 00:11:55.140 --> 00:11:59.030 604 | What we're going to do with that is 605 | that the name is going to become we're 606 | 607 | 118 608 | 00:11:59.030 --> 00:12:07.220 609 | going to have the route and then it's 610 | going to become owe us acid. Whereas if 611 | 612 | 119 613 | 00:12:07.220 --> 00:12:17.160 614 | the an ion ends with eight, then 615 | we're gonna have the route, and it's beginning 616 | 617 | 120 618 | 00:12:17.160 --> 00:12:25.540 619 | to become an I C. Acid or an 620 | IQ acid. The same charge rules apply. You're 621 | 622 | 121 623 | 00:12:25.540 --> 00:12:29.640 624 | still gonna balance out whatever the 625 | negative charges on the anti on with 626 | 627 | 122 628 | 00:12:29.650 --> 00:12:34.400 629 | the H is being positive. So let's do 630 | an example for each one of these. So 631 | 632 | 123 633 | 00:12:34.400 --> 00:12:39.180 634 | let's use we use sulfur as an 635 | example for our hydro sulfuric acid. So let's 636 | 637 | 124 638 | 00:12:39.180 --> 00:12:46.460 639 | use the next two possible ions. Here 640 | we have the sole fight Ion. So that's 641 | 642 | 125 643 | 00:12:46.470 --> 00:12:53.890 644 | s 03 minus two. And then over here 645 | we have the sulfate ion notice the eight 646 | 647 | 126 648 | 00:12:53.890 --> 00:12:57.800 649 | has one more oxygen than the 650 | height. That becomes an important factor a 651 | 652 | 127 653 | 00:12:57.800 --> 00:13:02.380 654 | little bit later on. To get our formula, 655 | we just have to look at the charges 656 | 657 | 128 658 | 00:13:02.380 --> 00:13:06.790 659 | and balance them out with h is Each 660 | of these has a negative to charge on 661 | 662 | 129 663 | 00:13:06.790 --> 00:13:11.350 664 | the Poly Atomic Ion. And so what 665 | we need there is just to balance those 666 | 667 | 130 668 | 00:13:11.350 --> 00:13:18.620 669 | out with h is. So we're gonna need 670 | to h is to balance out the negative to 671 | 672 | 131 673 | 00:13:18.620 --> 00:13:24.290 674 | over here and same thing on this side 675 | over here. So on this side we have 676 | 677 | 132 678 | 00:13:24.290 --> 00:13:29.370 679 | H two s 03 on this side. We have 680 | H two s 04 Same number of H is because 681 | 682 | 133 683 | 00:13:29.370 --> 00:13:33.310 684 | they both have the same charge now 685 | they were They get really different is 686 | 687 | 134 688 | 00:13:33.310 --> 00:13:41.080 689 | how we name them. So we change the 690 | ICT ending to oh us, and it becomes so 691 | 692 | 135 693 | 00:13:41.080 --> 00:13:50.270 694 | furious acid. And then on this side, 695 | we change the eight ending toe. I 696 | 697 | 136 698 | 00:13:50.270 --> 00:13:57.920 699 | see, and it becomes sulfuric acid. 700 | There's a good chance you've heard of 701 | 702 | 137 703 | 00:13:57.920 --> 00:14:02.760 704 | sulfuric acid, but maybe not sulfurous 705 | acid. This one's used a little bit 706 | 707 | 138 708 | 00:14:02.760 --> 00:14:07.380 709 | more commonly and like cleaning 710 | products and in industrial processes. So 711 | 712 | 139 713 | 00:14:07.380 --> 00:14:12.150 714 | you're going to be more likely to come 715 | across sulfuric acid than you are. It's 716 | 717 | 140 718 | 00:14:12.150 --> 00:14:16.880 719 | one less oxygen counterpart. So we 720 | have these two things sitting here, and 721 | 722 | 141 723 | 00:14:16.880 --> 00:14:19.960 724 | you're like, Oh, my gosh, How doe I 725 | remember all of this. Well, I have this 726 | 727 | 142 728 | 00:14:19.960 --> 00:14:25.710 729 | really silly little thing that I tell 730 | my kids, Um, and to know I'm telling 731 | 732 | 143 733 | 00:14:25.710 --> 00:14:35.670 734 | you because you're my kids and it's 735 | a little sentence. So I ate IQ and 736 | 737 | 144 738 | 00:14:35.670 --> 00:14:45.670 739 | got the aight us. So, you know, it's a little bit 740 | 741 | 145 742 | 00:14:45.670 --> 00:14:50.270 743 | silly, but it works because you keep 744 | the eight and the it together. So you 745 | 746 | 146 747 | 00:14:50.270 --> 00:14:55.190 748 | have I ate it like, Oh, it's something 749 | bad and they got the itis. So when 750 | 751 | 147 752 | 00:14:55.190 --> 00:14:58.510 753 | you get sick, what do you get? 754 | If you have a pancreatic infection, you get 755 | 756 | 148 757 | 00:14:58.510 --> 00:15:04.390 758 | pancreatitis. If you have a lung 759 | infection, you get bronchitis. So you know, 760 | 761 | 149 762 | 00:15:04.390 --> 00:15:09.430 763 | you have this something. You ate something 764 | bad, and it caused this to happen. 765 | 766 | 150 767 | 00:15:09.430 --> 00:15:15.780 768 | So I ate it and I got the 769 | itis and it keeps together. The Poly Atomic ion 770 | 771 | 151 772 | 00:15:15.780 --> 00:15:20.010 773 | comes first, and then the Suffolk's for 774 | your acid comes after it. So if 775 | 776 | 152 777 | 00:15:20.010 --> 00:15:25.210 778 | the Poly Atomic ion ends in eight, 779 | your Suffolk's is it. If the Poly atomic 780 | 781 | 153 782 | 00:15:25.210 --> 00:15:30.260 783 | ion ends in ICT, your Suffolk's is us. 784 | So let's go backwards so we can see 785 | 786 | 154 787 | 00:15:30.260 --> 00:15:37.070 788 | what that's going to look like. A Well, 789 | here's our next two examples. We have 790 | 791 | 155 792 | 00:15:37.080 --> 00:15:46.530 793 | H three p 04 and H three p 794 | 03 So again, notice there's a single oxygen difference 795 | 796 | 156 797 | 00:15:46.530 --> 00:15:49.290 798 | between these two molecules, and 799 | that's going to be something that's important 800 | 801 | 157 802 | 00:15:49.300 --> 00:15:52.850 803 | in a little while. We need to go 804 | back to our reference sheet, and we need 805 | 806 | 158 807 | 00:15:52.850 --> 00:15:58.850 808 | to figure out what the name of each 809 | of these poly atomic ions is. So you 810 | 811 | 159 812 | 00:15:58.850 --> 00:16:02.220 813 | go back to your handy dandy little 814 | Polly Atomic I in reference sheet. Do 815 | 816 | 160 817 | 00:16:02.220 --> 00:16:11.360 818 | that now and you'll see that this is 819 | the Foss fate I on while this is the 820 | 821 | 161 822 | 00:16:11.650 --> 00:16:18.830 823 | Foss fight I on. So this one ends 824 | in eight. This one ends tonight. And as 825 | 826 | 162 827 | 00:16:18.830 --> 00:16:24.070 828 | you take a look at this, you're like, 829 | Okay, I remember this. I ate IQ, so 830 | 831 | 163 832 | 00:16:24.070 --> 00:16:30.010 833 | I got the ICT us. So we're 834 | going to take this phosphorus part right here, and 835 | 836 | 164 837 | 00:16:30.010 --> 00:16:39.050 838 | we're going to change that into an 839 | I C. So we have phosphoric acid, 840 | 841 | 165 842 | 00:16:39.840 --> 00:16:49.190 843 | and over here the ICT becomes O U s. So we have Foss for us 844 | 845 | 166 846 | 00:16:50.140 --> 00:16:54.220 847 | acid, and I'm just going to say I 848 | can't believe I made the words fit where 849 | 850 | 167 851 | 00:16:54.220 --> 00:16:58.010 852 | they were supposed to go. That was 853 | kind of a miracle. All right, so you 854 | 855 | 168 856 | 00:16:58.010 --> 00:17:05.150 857 | got those down. Let's throw some together. 858 | All right, so here we go. Here's 859 | 860 | 169 861 | 00:17:05.150 --> 00:17:13.490 862 | a couple of them H N 02 We need 863 | to take a look. We need to know what this 864 | 865 | 170 866 | 00:17:13.490 --> 00:17:19.109 867 | Polly Atomic ion is. So we go back to 868 | our sheet, I take a look at it. I 869 | 870 | 171 871 | 00:17:19.109 --> 00:17:27.800 872 | go. Oh, that's n o to minus, which 873 | is the nitrite ion. So I know that ICT 874 | 875 | 172 876 | 00:17:27.810 --> 00:17:36.890 877 | becomes oh, us. And I know that 878 | this becomes nitrous acid. All well and 879 | 880 | 173 881 | 00:17:36.890 --> 00:17:40.890 882 | good. I bet you got it. But there's 883 | one more I want to show you because 884 | 885 | 174 886 | 00:17:40.890 --> 00:17:44.760 887 | it's kind of a weird one. It 888 | doesn't follow the rules just like a whole 889 | 890 | 175 891 | 00:17:44.760 --> 00:17:54.450 892 | lot of things in chemistry. And here it is. 893 | H c n. This is a very odd ball 894 | 895 | 176 896 | 00:17:54.450 --> 00:18:03.850 897 | little thing because it contains a poly 898 | atomic ion. But that ion ends in 899 | 900 | 177 901 | 00:18:04.440 --> 00:18:10.860 902 | I d. E. So this is like the 903 | one random example where we actually have to 904 | 905 | 178 906 | 00:18:10.860 --> 00:18:16.760 907 | go back to treating this almost a 908 | ziff. It was a binary acid. So because 909 | 910 | 179 911 | 00:18:16.760 --> 00:18:21.130 912 | it ends an I d. It doesn't end 913 | a night. It doesn't end in eight. This actually 914 | 915 | 180 916 | 00:18:21.130 --> 00:18:30.950 917 | becomes hydro scion IQ acid. So 918 | that's the sort of one seriously random 919 | 920 | 181 921 | 00:18:30.950 --> 00:18:35.160 922 | exception. And then the last thing I 923 | want to show you with our acids are 924 | 925 | 182 926 | 00:18:35.160 --> 00:18:43.400 927 | the I guess I would call them 928 | the stepwise acids. And they are probably 929 | 930 | 183 931 | 00:18:43.400 --> 00:18:47.520 932 | atomic ions that have increasing numbers 933 | of oxygens. And so we're going to 934 | 935 | 184 936 | 00:18:47.520 --> 00:18:54.610 937 | start download. We're going to start with 938 | Cielo minus and then Cielo to minus 939 | 940 | 185 941 | 00:18:55.440 --> 00:19:02.680 942 | cielo three minus and Cielo four 943 | minus. These, if you notice, have increasing 944 | 945 | 186 946 | 00:19:02.680 --> 00:19:07.060 947 | numbers of oxygen's as you go up. So 948 | what happens is each one of these has 949 | 950 | 187 951 | 00:19:07.060 --> 00:19:12.280 952 | to have a slightly different name. 953 | On DSO. We had prefixes and suffixes. 954 | 955 | 188 956 | 00:19:12.280 --> 00:19:16.590 957 | In addition to what we already had. 958 | We already saw twice that eight had 959 | 960 | 189 961 | 00:19:16.590 --> 00:19:21.830 962 | one more oxygen than I and that's 963 | the case here. So what happens is this 964 | 965 | 190 966 | 00:19:21.830 --> 00:19:28.800 967 | one with the Cielo three. That's the 968 | chlorate ion. And so one less is the 969 | 970 | 191 971 | 00:19:28.800 --> 00:19:32.820 972 | chloride ion. Well, now you're like, 973 | Well, there's one less and there's 974 | 975 | 192 976 | 00:19:32.820 --> 00:19:37.940 977 | one more. So what do I do with 978 | those? So have you ever heard of diabetes 979 | 980 | 193 981 | 00:19:37.950 --> 00:19:43.180 982 | about the answer is yes. Have you 983 | ever heard of the word hypoglycemia? Do 984 | 985 | 194 986 | 00:19:43.180 --> 00:19:47.840 987 | you know what it means? Yep. It 988 | means low blood sugar. So when the blood 989 | 990 | 195 991 | 00:19:47.840 --> 00:19:54.400 992 | sugar gets low, you have a hypo glassy 993 | mia. So we use that same prefix here 994 | 995 | 196 996 | 00:19:54.400 --> 00:20:02.730 997 | to represent a low number of oxygen's. 998 | So this becomes the hypo Clo, right? 999 | 1000 | 197 1001 | 00:20:02.970 --> 00:20:08.360 1002 | Ion. Unfortunately, my diabetes example does 1003 | not work in the other direction. 1004 | 1005 | 198 1006 | 00:20:08.370 --> 00:20:12.200 1007 | But there's something even more fun to 1008 | help you with this one. We keep Arklow 1009 | 1010 | 199 1011 | 00:20:12.200 --> 00:20:20.210 1012 | rate for this Cielo four. But now we 1013 | add the prefix Per so it's per chlorate. 1014 | 1015 | 200 1016 | 00:20:20.220 --> 00:20:24.100 1017 | Ion and I totally admit that one of 1018 | my kids came up with this, but you can 1019 | 1020 | 201 1021 | 00:20:24.100 --> 00:20:29.980 1022 | all enjoy the effects of it. You 1023 | always have a very happy kitty cat when 1024 | 1025 | 202 1026 | 00:20:29.980 --> 00:20:37.540 1027 | it has four legs. So it pers, I 1028 | know it's really bad, but the per does help 1029 | 1030 | 203 1031 | 00:20:37.540 --> 00:20:41.360 1032 | you remember that is supposed to be 1033 | with you. Have something with 04 on 1034 | 1035 | 204 1036 | 00:20:41.360 --> 00:20:48.110 1037 | it. It's going to be the per eight 1038 | ion. Um, this pattern applies for all of 1039 | 1040 | 205 1041 | 00:20:48.110 --> 00:20:56.580 1042 | the halogen, so you can have x 1043 | cielo four minus ex Cielo three minus ex 1044 | 1045 | 206 1046 | 00:20:56.590 --> 00:21:03.660 1047 | cielo to minus and ex Cielo minus, and 1048 | you can replace that X with any halogen. 1049 | 1050 | 207 1051 | 00:21:07.640 --> 00:21:14.160 1052 | However, the cool ride ones are the 1053 | most commonly seen. I think that's probably 1054 | 1055 | 208 1056 | 00:21:14.160 --> 00:21:18.350 1057 | enough for the moment. You've gotten 1058 | a lot of good experience with your 1059 | 1060 | 209 1061 | 00:21:18.360 --> 00:21:23.720 1062 | with your covalin compounds in your acids 1063 | here, and I hope to see you next 1064 | 1065 | 210 1066 | 00:21:23.720 --> 00:21:24.960 1067 | time. Take care. 1068 | {/file} -------------------------------------------------------------------------------- /cli/templates/json.txt: -------------------------------------------------------------------------------- 1 | {__silent=true} 2 | {prompt} 3 | 4 | {#followup} 5 | Output only JSON in a code block from your previous response. Fill in any fields as necessary. Every non-optional field must be filled. Match the schema below exactly 6 | 7 | TypeScript schema: 8 | 9 | ``` 10 | {schema} 11 | ``` 12 | {/followup} 13 | {#every} 14 | {#js} 15 | const res = variables.claude_response.completion; 16 | try { 17 | let codeblock = res.split("```").slice(1).slice(0, -1).join("```").replace(/^json/, ""); 18 | console.log(JSON.stringify(JSON.parse(codeblock), null, 2)); 19 | }catch(e){} 20 | {/js} 21 | {/every} -------------------------------------------------------------------------------- /cli/templates/md.txt: -------------------------------------------------------------------------------- 1 | {__silent="true"} 2 | Write short markdown notes in bullet points using sentence fragments about {prompt} for a high school class. Include important terms, examples and explanations. Put your result in a code block surrounded by "```" 3 | 4 | Use double brackets notation inline to link to other notes, e.g. "Pedir is a [[Stem Changing Verb]]". At the top of the document include YAML frontmatter in the following format: 5 | ``` 6 | --- 7 | title: "Accuracy and Precision" 8 | tags: ["science"] 9 | banner_icon: 🎯 10 | --- 11 | ``` 12 | 13 | Use a clever emoji in the `banner_icon` that matches the title, not the class. Use latex for math equations ("$ inline math $" or "$$ block math $$"). Be very detailed with your notes but use short sentences and sentence fragments. Use four spaces for indents. Make sure to include all important terms (with informal definitions), relevant details and examples. Use sub bullet points and headings when you need them. When you use key terms make sure to include the definitions. Focus on being very readable. 14 | 15 | {#followup} 16 | {#js} 17 | try { 18 | const out = variables.claude_response.completion.split("```").slice(1).slice(0, -1).join("```").replace(/^(md|markdown|yaml|yml)\n/, "").trim(); 19 | console.log(out); 20 | const filename = variables.prompt.split(":")[0].replace(/[^a-z0-9 ]+/gi, " ").trim() + ".md" 21 | writeFileSync(filename, out); 22 | console.log(chalk.green.bold(`Wrote ${out.split("\n").length} lines to ` + filename)); 23 | } catch(e){ console.log(chalk.red.bold("Error", md(variables.claude_response.completion))) } 24 | return; 25 | {/js} 26 | {/followup} 27 | -------------------------------------------------------------------------------- /cli/templates/reflection.txt: -------------------------------------------------------------------------------- 1 | {__clear=true} 2 | 3 | Request: {prompt} 4 | 5 | Before completing the request write, first think through the information you need for the request, what assumptions you need to make, and the logic of the problem. Then write out your initial response, 10 criticisms of your thoughts, logical process, gaps/wrong sections in the response and if it answers the user's question (with ways to improve your draft and make the answer correctly answer the prompt— be harsh), then 5 final thoughts. Then write your final version. Make sure your final version is easy to read quickly, isn't too long, is concise and relevant. Use bullet points in your final if it seems appropriate (i.e. it's not an essay) and limit your final to 2 paragraphs or so. Don't be afraid to have your own opinions and be informal. Start with "FINAL:" for the final version 6 | {#every} 7 | {#js} 8 | if (variables.claude_response.completion.includes("FINAL:")){ 9 | console.log(md(variables.claude_response.completion.split("FINAL:")[1])) 10 | } 11 | {/js} 12 | {/every} -------------------------------------------------------------------------------- /cli/templates/search.txt: -------------------------------------------------------------------------------- 1 | From now on you have access to a search engine where you can search for current web information and get the result sent back. To search respond with only: "SEARCH[term]". Only search when you need to or if inquired about recent events and don't search if the prompt requests you not to search. Wait for the user (me) to respond before you continue with your response. After you get the search results respond to the original prompt (given below) 2 | 3 | Prompt: "{prompt}" 4 | 5 | Example: 6 | 7 | User: What's the weather today 8 | 9 | Claude (you): SEARCH[weather] 10 | 11 | User: [search results] 12 | 13 | Claude (you): The weather is 95 degrees and sunny today! ☀️ 14 | 15 | Your first prompt is: "{prompt}" 16 | 17 | {#every} 18 | {#jsd} 19 | const claude_res = variables.claude_response.completion.trim(); 20 | const RE = /^SEARCH\[(.+)\]$/; 21 | if (RE.test(claude_res)) { 22 | const cmd = `google-it --query=${JSON.stringify(claude_res.match(RE)[1])} --limit 5`; 23 | return await shell(cmd); 24 | } else { 25 | return ''; 26 | } 27 | {/jsd} 28 | {/every} -------------------------------------------------------------------------------- /cli/templates/suggest.txt: -------------------------------------------------------------------------------- 1 | {prompt} 2 | 3 | Output your suggested edits in the following format: 4 | - `>` - Indicates a line being inserted 5 | - `<` - Indicates a line being removed 6 | 7 | Focus your suggestions on the following request: "{prompt}" 8 | 9 | Here is an example: 10 | ```js 11 | > import express from "express"; 12 | > import {join} from "path"; 13 | < const express = require("express"); 14 | < const path = require("path"); 15 | 16 | // Start the server 17 | app.get("/", () => { 18 | < const path = path.join(__dirname, "index.html"); 19 | > const path = join(import.meta.url.replace("file:/", ""), "index.html"); 20 | res.sendFile(path); 21 | }) 22 | 23 | < const PORT = 3000; 24 | > const PORT = process.env.PORT || 3000; 25 | 26 | app.listen(PORT, () => { 27 | < console.log("Listening on port " + PORT); 28 | > console.log(`Listening on port ${PORT}`); 29 | }) 30 | ``` -------------------------------------------------------------------------------- /cli/templates/writefiles.txt: -------------------------------------------------------------------------------- 1 | From now on you can write to files. See instructions.txt for details on how to write files. 2 | 3 | {#file instructions.txt} 4 | Write files using the following syntax: 5 | 6 | filename.ext 7 | ```language 8 | file content 9 | ``` 10 | 11 | You must match this format EXACTLY. Example, to write to hello_world.txt: 12 | 13 | hello_world.txt 14 | ``` 15 | This is the content of hello_world.txt 16 | ``` 17 | {/file} 18 | 19 | Follow the following request, write files as needed: 20 | {prompt} 21 | 22 | {#every} 23 | {#js} 24 | const regex = /(?([\w_-]+\.)+\w+)\n?```(\w+\n)?\n?(?[\s\S]+?)\n?```/g 25 | let match; 26 | while (match = regex.exec(variables.claude_response.completion)) { 27 | writeFileSync(match.groups.file, match.groups.body); 28 | console.log(chalk.bold.green(`Wrote ${match.groups.body.split('\n').length} lines to ${chalk.bold.red(match.groups.file)}`)) 29 | } 30 | {/js} 31 | {/every} -------------------------------------------------------------------------------- /cli/templates/yt.txt: -------------------------------------------------------------------------------- 1 | {#shell} 2 | rm ${TMPDIR%?}/sub.* 3 | {/shell} 4 | {#shell} 5 | yt-dlp --skip-download --write-sub --write-auto-sub --sub-lang "en.*" --output ${TMPDIR%?}/sub '{prompt}' 6 | {/shell} 7 | 8 | Summarize the video transcript in the uploaded file sub.vtt bullet points. Cover everything in the transcript in readable and concise bullet points. 9 | 10 | {#file sub.vtt} 11 | {#shd} 12 | cat ${TMPDIR%?}/sub.* 13 | {/shd} 14 | {/file} -------------------------------------------------------------------------------- /demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Explosion-Scratch/claude-unofficial-api/3d55419f9025152e8bea708365a9c25a02909529/demo.mp4 -------------------------------------------------------------------------------- /examples/coding.md: -------------------------------------------------------------------------------- 1 | # Coding Demo (Rec is 6x speed) 2 | 3 | https://github.com/Explosion-Scratch/claude-unofficial-api/assets/61319150/68b11264-d56e-4ade-a2bf-d4625b1397bb 4 | 5 | See the [Regular speed](https://github.com/Explosion-Scratch/claude-unofficial-api/assets/61319150/110a85f4-5ce2-45c3-94e6-f2a2a175183a) recording as well. 6 | 7 | 8 | # How to run this: 9 | ``` 10 | echo "cli math game with simple multiplication, division and subtraction node.js" | claude --template code 11 | ``` 12 | 13 | # What's it doing 14 |
15 | 1. Creates a design document and uploads it as requirements.txt to the main conversation 16 | 17 | ``` 18 | {#file requirements.txt} 19 | {#claude} 20 | Write out a design document of the following javascript program. Include details about the functions needed, the code structure, variables and outputs from each function. Also include example outputs of the program (in code blocks). Don't implement or output any code. 21 | 22 | {prompt} 23 | {/claude} 24 | {/file} 25 | ``` 26 | 27 |
28 | 29 |
30 | 2. Reads design document and writes an initial draft of the code (3), then criticisms (4), then a final version (5) 31 | 32 | ``` 33 | You are an expert software engineer at Google. Be concise! Your job is to implement the code designed and detailed in the uploaded file requirements.txt. This project is {prompt} 34 | 35 | Make sure to use the latest syntax (e.g. ESM/ES6 imports and syntax in JavaScript, HTML5, CSS3), don't include debug or comments or empty lines. Include error handling to make sure it works properly. Make sure your code is readable and does not include any placeholders or psuedocode. 36 | 37 | DONT INCLUDE ANY TODO COMMENTS. DONT INCLUDE PSUEDOCODE. DONT INCLUDE PLACEHOLDERS. Adhere to the user's request as closely as possible. Your code's output, variables and function should match the example output given in the design document exactly. 38 | 39 | First write you initial implementation of the requirements, then write out 4 criticisms of it. Finally write your final code. Begin the final code with "FINAL:" 40 | 41 | The requirements and design details of this project are located in the uploaded file requirements.txt 42 | 43 | Make sure that the final code matches the example output from the requirements.txt file. Your final code should also not contain any bugs or errors, and must hold up to the Google JS style guide. Make sure all functions you use are defined in the environment. 44 | 45 | ``` 46 | 47 |
48 | 49 |
50 | 6. Writes the files to your computer using the writefiles prompt template 51 | 52 |
 53 | From now on you can write to files using the following syntax:
 54 | 
 55 | filename.ext
 56 | ```language
 57 | file content
 58 | ```
 59 | 
 60 | You must match this format EXACTLY. Example, to write to hello_world.txt:
 61 | 
 62 | hello_world.txt
 63 | ```
 64 | This is the content of hello_world.txt
 65 | ```
 66 | 
 67 | {prompt}
 68 | 
 69 | {#every}
 70 |     {#js}
 71 |         // Parse claude's response and write files if applicable
 72 |         const regex = /(?([\w_-]+\.)+\w+)\n?```(\w+\n)?\n?(?[\s\S]+?)\n?```/g
 73 |         let match;
 74 |         while (match = regex.exec(variables.claude_response.completion)) {
 75 |             writeFileSync(match.groups.file, match.groups.body);
 76 |             console.log(chalk.bold.green(`Wrote ${match.groups.body.split('\n').length} lines to ${chalk.bold.red(match.groups.file)}`))
 77 |         }
 78 |     {/js}
 79 | {/every}
 80 | 
81 | 82 |
83 | 84 | # Output (code was _very_ lightly edited ~2 lines changed): 85 | Pause the recording to view total code 86 | 87 |
88 | node index.js output: 89 | 90 | ``` 91 | What is 10 + 9? 19 92 | Correct! 93 | What is 8 - 18? -10 94 | Correct! 95 | What is 10 + 18? 27 96 | Incorrect. The answer is 28 97 | What is 6 - 9? -3 98 | Correct! 99 | What is 19 * 20? 380 100 | Correct! 101 | What is 12 + 18? 111111 102 | Incorrect. The answer is 30 103 | What is 17 * 8? 111 104 | Incorrect. The answer is 136 105 | What is 12 * 17? 1 106 | Incorrect. The answer is 204 107 | What is 4 - 11? 1 108 | Incorrect. The answer is -7 109 | What is 15 + 1? 1 110 | Incorrect. The answer is 16 111 | Final Score: 4/10 112 | ``` 113 | 114 |
115 | 116 |
117 | index.js 118 | 119 | 120 | ```js 121 | // Requires 122 | import readline from 'readline'; 123 | 124 | // Game configuration 125 | const maxQuestions = 10; 126 | const maxNum = 20; 127 | 128 | // Game operations 129 | const ops = ['+', '-', '*']; 130 | 131 | // Game class 132 | class Game { 133 | constructor() { 134 | this.score = 0; 135 | this.numQuestions = 0; 136 | } 137 | 138 | start() { 139 | // Initialize game 140 | this.score = 0; 141 | this.numQuestions = 0; 142 | 143 | // Start game loop 144 | this.askQuestion(); 145 | } 146 | 147 | askQuestion() { 148 | // Generate random numbers 149 | const num1 = Math.floor(Math.random() * maxNum) + 1; 150 | const num2 = Math.floor(Math.random() * maxNum) + 1; 151 | 152 | // Generate random op 153 | const opIndex = Math.floor(Math.random() * ops.length); 154 | const op = ops[opIndex]; 155 | 156 | // Format question 157 | const question = `What is ${num1} ${op} ${num2}? `; 158 | 159 | // Get user's answer 160 | const rl = readline.createInterface({ 161 | input: process.stdin, 162 | output: process.stdout 163 | }); 164 | 165 | rl.question(question, answer => { 166 | // Validate input 167 | const numAnswer = parseInt(answer); 168 | if (Number.isNaN(numAnswer)) { 169 | console.log('Invalid input. Please enter a number.'); 170 | return this.askQuestion(); 171 | } 172 | 173 | // Check answer 174 | const expected = eval(`${num1} ${op} ${num2}`); 175 | if (numAnswer === expected) { 176 | console.log('Correct!'); 177 | this.score++; 178 | } else { 179 | console.log(`Incorrect. The answer is ${expected}`); 180 | } 181 | 182 | // Update counters and check end condition 183 | this.numQuestions++; 184 | if (this.numQuestions < maxQuestions) { 185 | rl.close(); 186 | this.askQuestion(); 187 | } else { 188 | this.end(); 189 | } 190 | 191 | }); 192 | } 193 | 194 | end() { 195 | // Print final score 196 | console.log(`Final Score: ${this.score}/${this.numQuestions}`); 197 | process.exit(0); 198 | } 199 | } 200 | 201 | // Create game instance and start 202 | const game = new Game(); 203 | game.start(); 204 | ``` 205 | 206 |
207 | 208 |
209 | package.json 210 | 211 | ```json 212 | { 213 | "name": "math-game", 214 | "type": "module", 215 | "main": "index.js" 216 | } 217 | ``` 218 | 219 | 220 |
221 | -------------------------------------------------------------------------------- /examples/json.md: -------------------------------------------------------------------------------- 1 | # Valid JSON Output Demo (Rec 6x speed) 2 | 3 | https://github.com/Explosion-Scratch/claude-unofficial-api/assets/61319150/f6c24b43-231c-47fb-941f-8533bc1b28cf 4 | 5 | See the [original recording](https://github.com/Explosion-Scratch/claude-unofficial-api/assets/61319150/979ba3d4-78bc-4560-b5e0-cc4043320102) for 1x speed 6 | 7 | # How to run this 8 | ``` 9 | echo "Explain in great detail the plot, themes and chapters of harry potter book 1" | claude --prompt.schema ./book_schema.d.ts --template json 10 | ``` 11 | 12 | # What it's doing 13 |
14 | 1. Answers the prompt 15 | It just answers the prompt in plain text. I set `{__silent=true}` in the template so no output is shown 16 |
17 | 18 |
19 | 2. Asks Claude to redo it as JSON 20 | Asks Claude to format its last response as JSON matching the typescript schema given in `{schema}`. 21 |
22 | 23 |
24 | 3. JSON is parsed and formatted using JavaScript 25 | If the JSON is invalid nothing is logged, if it's valid it's formatted and logged to the console 26 |
27 | 28 | > **Note:** 29 | > You could also modify this so that it writes the JSON directly to a file very easily 30 | 31 | # Output 32 | 33 |
34 | Title 35 | 36 | 37 | ```json 38 | { 39 | "title": "Harry Potter and the Philosopher's Stone", 40 | "author": { 41 | "name": "J.K. Rowling", 42 | "fame": 100 43 | }, 44 | "themes": [ 45 | "Good vs evil", 46 | "Friendship", 47 | "Coming of age", 48 | "Prejudice" 49 | ], 50 | "chapterCount": 19, 51 | "chapters": [ 52 | { 53 | "title": "The Boy Who Lived", 54 | "summary": "Baby Harry survives Voldemort's attack and is left with his aunt and uncle.", 55 | "importance": 80, 56 | "characterIds": [ 57 | "Harry Potter", 58 | "Voldemort", 59 | "Vernon Dursley", 60 | "Petunia Dursley" 61 | ] 62 | }, 63 | { 64 | "title": "The Vanishing Glass", 65 | "summary": "Strange events around Harry hint at his magical abilities.", 66 | "importance": 50, 67 | "characterIds": [ 68 | "Harry Potter", 69 | "Dudley Dursley", 70 | "Petunia Dursley", 71 | "Vernon Dursley" 72 | ] 73 | }, 74 | { 75 | "title": "The Letters from No One", 76 | "summary": "Harry's Hogwarts acceptance letters keep arriving despite the Dursleys' efforts to block them.", 77 | "importance": 70, 78 | "characterIds": [ 79 | "Harry Potter", 80 | "Vernon Dursley", 81 | "Petunia Dursley", 82 | "Dudley Dursley" 83 | ] 84 | }, 85 | { 86 | "title": "The Keeper of the Keys", 87 | "summary": "Hagrid arrives and informs Harry he is a wizard and takes him to Diagon Alley.", 88 | "importance": 90, 89 | "characterIds": [ 90 | "Harry Potter", 91 | "Rubeus Hagrid", 92 | "Vernon Dursley", 93 | "Dudley Dursley" 94 | ] 95 | }, 96 | { 97 | "title": "Diagon Alley", 98 | "summary": "Harry shops for his school supplies and equipment.", 99 | "importance": 70, 100 | "characterIds": [ 101 | "Harry Potter", 102 | "Rubeus Hagrid" 103 | ] 104 | }, 105 | { 106 | "title": "The Journey from Platform Nine and Three-Quarters", 107 | "summary": "Harry travels to Hogwarts via the Hogwarts Express train and meets Ron.", 108 | "importance": 80, 109 | "characterIds": [ 110 | "Harry Potter", 111 | "Ron Weasley" 112 | ] 113 | }, 114 | { 115 | "title": "The Sorting Hat", 116 | "summary": "Harry and classmates are sorted into school Houses by the Sorting Hat.", 117 | "importance": 90, 118 | "characterIds": [ 119 | "Harry Potter", 120 | "Ron Weasley", 121 | "Hermione Granger" 122 | ] 123 | }, 124 | { 125 | "title": "The Potions Master", 126 | "summary": "Harry attends his first Potions class with the hostile Professor Snape.", 127 | "importance": 60, 128 | "characterIds": [ 129 | "Harry Potter", 130 | "Severus Snape" 131 | ] 132 | }, 133 | { 134 | "title": "The Midnight Duel", 135 | "summary": "Harry and Ron arrange to duel Draco Malfoy at midnight.", 136 | "importance": 50, 137 | "characterIds": [ 138 | "Harry Potter", 139 | "Ron Weasley", 140 | "Draco Malfoy" 141 | ] 142 | }, 143 | { 144 | "title": "Halloween", 145 | "summary": "A troll gets into Hogwarts on Halloween night.", 146 | "importance": 80, 147 | "characterIds": [ 148 | "Harry Potter", 149 | "Ron Weasley", 150 | "Hermione Granger" 151 | ] 152 | }, 153 | { 154 | "title": "Quidditch", 155 | "summary": "Events surrounding Harry's first Quidditch match.", 156 | "importance": 70, 157 | "characterIds": [ 158 | "Harry Potter", 159 | "Severus Snape" 160 | ] 161 | }, 162 | { 163 | "title": "The Mirror of Erised", 164 | "summary": "Harry finds a mirror that shows a person's deepest desires.", 165 | "importance": 80, 166 | "characterIds": [ 167 | "Harry Potter", 168 | "Albus Dumbledore" 169 | ] 170 | }, 171 | { 172 | "title": "Nicolas Flamel", 173 | "summary": "Nicolas Flamel is revealed as creator of the Philosopher's Stone.", 174 | "importance": 80, 175 | "characterIds": [ 176 | "Harry Potter", 177 | "Ron Weasley", 178 | "Hermione Granger" 179 | ] 180 | }, 181 | { 182 | "title": "Norbert the Norwegian Ridgeback", 183 | "summary": "Hagrid hatches a dragon egg and Harry helps smuggle it away.", 184 | "importance": 60, 185 | "characterIds": [ 186 | "Harry Potter", 187 | "Ron Weasley", 188 | "Rubeus Hagrid" 189 | ] 190 | }, 191 | { 192 | "title": "The Forbidden Forest", 193 | "summary": "Detention in the Forbidden Forest for Harry, Hermione, Neville and Draco.", 194 | "importance": 70, 195 | "characterIds": [ 196 | "Harry Potter", 197 | "Draco Malfoy", 198 | "Neville Longbottom" 199 | ] 200 | }, 201 | { 202 | "title": "Through the Trapdoor", 203 | "summary": "Harry goes through the trapdoor to find the Stone before Snape.", 204 | "importance": 100, 205 | "characterIds": [ 206 | "Harry Potter", 207 | "Ron Weasley", 208 | "Hermione Granger" 209 | ] 210 | }, 211 | { 212 | "title": "The Man with Two Faces", 213 | "summary": "Harry confronts Quirrell and Voldemort.", 214 | "importance": 100, 215 | "characterIds": [ 216 | "Harry Potter", 217 | "Voldemort", 218 | "Professor Quirrell" 219 | ] 220 | }, 221 | { 222 | "title": "The Parting of the Ways", 223 | "summary": "Harry wakes in hospital and gets the end-of-term House Cup.", 224 | "importance": 80, 225 | "characterIds": [ 226 | "Harry Potter", 227 | "Ron Weasley", 228 | "Hermione Granger", 229 | "Albus Dumbledore" 230 | ] 231 | } 232 | ] 233 | } 234 | ``` 235 | 236 |
237 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The main Claude API client class. 3 | * @typedef Claude 4 | * @class 5 | * @classdesc Creates an instance of the Claude API client. 6 | */ 7 | export class Claude { 8 | /** 9 | * A UUID string 10 | * @typedef UUID 11 | * @example "222aa20a-bc79-48d2-8f6d-c819a1b5eaed" 12 | */ 13 | /** 14 | * Create a new Claude API client instance. 15 | * @param {Object} options - Options 16 | * @param {string} options.sessionKey - Claude session key 17 | * @param {string|function} [options.proxy] - Proxy URL or proxy function 18 | * @param {function} [options.fetch] - Fetch function 19 | * @example 20 | * const claude = new Claude({ 21 | * sessionKey: 'sk-ant-sid01-*****', 22 | * fetch: globalThis.fetch 23 | * }) 24 | * 25 | * await claude.init(); 26 | * claude.sendMessage('Hello world').then(console.log) 27 | */ 28 | constructor({ sessionKey, proxy, fetch }: { 29 | sessionKey: string; 30 | proxy?: string | Function; 31 | fetch?: Function; 32 | }); 33 | /** 34 | * If the Claude client has initialized yet (call `init()` if you haven't and this is false) 35 | * @property {boolean} 36 | */ 37 | ready: boolean; 38 | /** 39 | * A proxy function/string to connect via 40 | * @property {({endpoint: string, options: Object}) => {endpoint: string, options: Object} | string} 41 | */ 42 | proxy: Function; 43 | /** 44 | * A fetch function, defaults to globalThis.fetch 45 | * @property {Function} 46 | */ 47 | fetch: Function; 48 | /** 49 | * The session key string (from the cookie) 50 | * @property {string} 51 | */ 52 | sessionKey: string; 53 | /** 54 | * Get available Claude models. 55 | * @returns {string[]} Array of model names 56 | */ 57 | models(): string[]; 58 | /** 59 | * Get total token count for a Claude model. 60 | * @param {string} [model] - Claude model name 61 | * @returns {number} Total token count 62 | */ 63 | totalTokens(model?: string): number; 64 | /** 65 | * Get the default Claude model. 66 | * @returns {string} Default model name 67 | */ 68 | defaultModel(): string; 69 | /** 70 | * A partial or total completion for a message. 71 | * @typedef MessageStreamChunk 72 | * @property {String} completion The markdown text completion for this response 73 | * @property {String | null} stop_reason The reason for the response stop (if any) 74 | * @property {String} model The model used 75 | * @property {String} stop The string at which Claude stopped responding at, e.g. "\n\nHuman:" 76 | * @property {String} log_id A logging ID 77 | * @property {Object} messageLimit If you're within the message limit 78 | * @param {String} messageLimit.type The type of message limit ("within_limit") 79 | */ 80 | /** 81 | * Send a message to a new or existing conversation. 82 | * @param {string} message - Initial message 83 | * @param {SendMessageParams} [params] - Additional parameters 84 | * @param {string} [params.conversation] - Existing conversation ID 85 | * @param {boolean} [params.temporary=true] - Delete after getting response 86 | * @returns {Promise} Result message 87 | */ 88 | sendMessage(message: string, { conversation, temporary, ...params }?: SendMessageParams): Promise<{ 89 | /** 90 | * The markdown text completion for this response 91 | */ 92 | completion: string; 93 | /** 94 | * The reason for the response stop (if any) 95 | */ 96 | stop_reason: string | null; 97 | /** 98 | * The model used 99 | */ 100 | model: string; 101 | /** 102 | * The string at which Claude stopped responding at, e.g. "\n\nHuman:" 103 | */ 104 | stop: string; 105 | /** 106 | * A logging ID 107 | */ 108 | log_id: string; 109 | /** 110 | * If you're within the message limit 111 | */ 112 | messageLimit: any; 113 | }>; 114 | /** 115 | * Make an API request. 116 | * @param {string} endpoint - API endpoint 117 | * @param {Object} options - Request options 118 | * @returns {Promise} Fetch response 119 | * @example 120 | * await claude.request('/api/organizations').then(r => r.json()) 121 | */ 122 | request(endpoint: string, options: any): Promise; 123 | /** 124 | * Initialize the client. 125 | * @async 126 | * @returns {Promise} Void 127 | */ 128 | init(): Promise; 129 | organizationId: string; 130 | recent_conversations: Conversation[]; 131 | /** 132 | * An organization 133 | * @typedef Organization 134 | * @property {String} join_token A token 135 | * @property {String} name The organization name 136 | * @property {String} uuid The organization UUID 137 | * @property {String} created_at The organization creation date 138 | * @property {String} updated_at The organization update date 139 | * @property {String[]} capabilities What the organization can do 140 | * @property {Object} settings The organization's settings 141 | * @property {Array} active_flags Organization's flags (none that I've found) 142 | */ 143 | /** 144 | * Get the organizations list. 145 | * @async 146 | * @returns {Promise} A list of organizations 147 | * @example 148 | * await claude.getOrganizations().then(organizations => { 149 | * console.log('Users organization name is:', organizations[0].name) 150 | * }) 151 | */ 152 | getOrganizations(): Promise<{ 153 | /** 154 | * A token 155 | */ 156 | join_token: string; 157 | /** 158 | * The organization name 159 | */ 160 | name: string; 161 | /** 162 | * The organization UUID 163 | */ 164 | uuid: string; 165 | /** 166 | * The organization creation date 167 | */ 168 | created_at: string; 169 | /** 170 | * The organization update date 171 | */ 172 | updated_at: string; 173 | /** 174 | * What the organization can do 175 | */ 176 | capabilities: string[]; 177 | /** 178 | * The organization's settings 179 | */ 180 | settings: any; 181 | /** 182 | * Organization's flags (none that I've found) 183 | */ 184 | active_flags: any[]; 185 | }[]>; 186 | /** 187 | * Delete all conversations 188 | * @async 189 | * @returns {Promise} An array of responses for the DELETE requests 190 | * @example 191 | * await claude.clearConversations(); 192 | * console.assert(await claude.getConversations().length === 0); 193 | */ 194 | clearConversations(): Promise; 195 | /** 196 | * @callback doneCallback 197 | * @param {MessageStreamChunk} a The completed response 198 | */ 199 | /** 200 | * @callback progressCallback 201 | * @param {MessageStreamChunk} a The response in progress 202 | */ 203 | /** 204 | * Start a new conversation 205 | * @param {String} message The message to send to start the conversation 206 | * @param {SendMessageParams} [params={}] Message params passed to Conversation.sendMessage 207 | * @returns {Promise} 208 | * @async 209 | * @example 210 | * const conversation = await claude.startConversation("Hello! How are you?") 211 | * console.log(await conversation.getInfo()); 212 | */ 213 | startConversation(message: string, params?: SendMessageParams): Promise; 214 | /** 215 | * Get a conversation by its ID 216 | * @param {UUID} id The uuid of the conversation (Conversation.uuid or Conversation.conversationId) 217 | * @async 218 | * @returns {Conversation | null} The conversation 219 | * @example 220 | * const conversation = await claude.getConversation("222aa20a-bc79-48d2-8f6d-c819a1b5eaed"); 221 | */ 222 | getConversation(id: any): Conversation | null; 223 | /** 224 | * Get all conversations 225 | * @async 226 | * @returns {Promise} A list of conversations 227 | * @example 228 | * console.log(`You have ${await claude.getConversations().length} conversations:`); 229 | */ 230 | getConversations(): Promise; 231 | /** 232 | * The response from uploading a file (an attachment) 233 | * @typedef Attachment 234 | * @property {String} file_name The file name 235 | * @property {String} file_type The file's mime type 236 | * @property {Number} file_size The file size in bytes 237 | * @property {String} extracted_content The contents of the file that were extracted 238 | * @property {Number | null} [totalPages] The total pages of the document 239 | */ 240 | /** 241 | * Extract the contents of a file 242 | * @param {File} file A JS File (like) object to upload. 243 | * @async 244 | * @returns {Promise} 245 | * @example 246 | * const file = await claude.uploadFile( 247 | * new File(["test"], "test.txt", { type: "text/plain" } 248 | * ); 249 | * console.log(await claude.sendMessage("What's the contents of test.txt?", { 250 | * attachments: [file] 251 | * })) 252 | */ 253 | uploadFile(file: File): Promise<{ 254 | /** 255 | * The file name 256 | */ 257 | file_name: string; 258 | /** 259 | * The file's mime type 260 | */ 261 | file_type: string; 262 | /** 263 | * The file size in bytes 264 | */ 265 | file_size: number; 266 | /** 267 | * The contents of the file that were extracted 268 | */ 269 | extracted_content: string; 270 | /** 271 | * The total pages of the document 272 | */ 273 | totalPages?: number | null; 274 | }>; 275 | } 276 | /** 277 | * @typedef SendMessageParams 278 | * @property {Boolean} [retry=false] Whether to retry the most recent message in the conversation instead of sending a new one 279 | * @property {String} [timezone="America/New_York"] The timezone 280 | * @property {Attachment[]} [attachments=[]] Attachments 281 | * @property {doneCallback} [done] Callback when done receiving the message response 282 | * @property {progressCallback} [progress] Callback on message response progress 283 | * @property {string} [model=claude.defaultModel()] The model to use 284 | */ 285 | /** 286 | * A Claude conversation instance. 287 | * @class 288 | * @typedef Conversation 289 | * @classdesc Represents an active Claude conversation. 290 | */ 291 | export class Conversation { 292 | /** 293 | * Create a Conversation instance. 294 | * @param {Claude} claude - Claude client instance 295 | * @param {Object} options - Options 296 | * @param {String} options.conversationId - Conversation ID 297 | * @param {String} [options.name] - Conversation name 298 | * @param {String} [options.summary] - Conversation summary 299 | * @param {String} [options.created_at] - Conversation created at 300 | * @param {String} [options.updated_at] - Conversation updated at 301 | * @param {String} [options.model] - Claude model 302 | */ 303 | constructor(claude: Claude, { model, conversationId, name, summary, created_at, updated_at }: { 304 | conversationId: string; 305 | name?: string; 306 | summary?: string; 307 | created_at?: string; 308 | updated_at?: string; 309 | model?: string; 310 | }); 311 | /** 312 | * The conversation ID 313 | * @property {string} 314 | */ 315 | conversationId: string; 316 | /** 317 | * The conversation name 318 | * @property {string} 319 | */ 320 | name: any; 321 | /** 322 | * The conversation summary (usually empty) 323 | * @property {string} 324 | */ 325 | summary: any; 326 | /** 327 | * The conversation created at 328 | * @property {string} 329 | */ 330 | created_at: any; 331 | /** 332 | * The conversation updated at 333 | * @property {string} 334 | */ 335 | updated_at: any; 336 | /** 337 | * The Claude client 338 | * @property {Claude} 339 | */ 340 | claude: any; 341 | /** 342 | * The request function (from parent claude instance) 343 | * @property {(url: string, options: object) => Response} 344 | */ 345 | request: (endpoint: string, options: any) => Promise; 346 | /** 347 | * The current model 348 | * @property {string} 349 | */ 350 | model: any; 351 | /** 352 | * If the Claude client has initialized yet (call `init()` if you haven't and this is false) 353 | * @property {boolean} 354 | */ 355 | ready: any; 356 | /** 357 | * A proxy function/string to connect via 358 | * @property {({endpoint: string, options: Object}) => {endpoint: string, options: Object} | string} 359 | */ 360 | proxy: any; 361 | /** 362 | * A fetch function, defaults to globalThis.fetch 363 | * @property {Function} 364 | */ 365 | fetch: any; 366 | /** 367 | * Convert the conversation to a JSON object 368 | * @returns {Conversation} The serializable object 369 | */ 370 | toJSON(): Conversation; 371 | /** 372 | * Retry the last message in the conversation 373 | * @param {SendMessageParams} [params={}] 374 | * @returns {Promise} 375 | */ 376 | retry(params?: SendMessageParams): Promise<{ 377 | /** 378 | * The markdown text completion for this response 379 | */ 380 | completion: string; 381 | /** 382 | * The reason for the response stop (if any) 383 | */ 384 | stop_reason: string; 385 | /** 386 | * The model used 387 | */ 388 | model: string; 389 | /** 390 | * The string at which Claude stopped responding at, e.g. "\n\nHuman:" 391 | */ 392 | stop: string; 393 | /** 394 | * A logging ID 395 | */ 396 | log_id: string; 397 | /** 398 | * If you're within the message limit 399 | */ 400 | messageLimit: any; 401 | }>; 402 | /** 403 | * Send a message to this conversation 404 | * @param {String} message 405 | * @async 406 | * @param {SendMessageParams} params The parameters to send along with the message 407 | * @returns {Promise} 408 | */ 409 | sendMessage(message: string, { retry, timezone, attachments, model, done, progress, rawResponse }?: SendMessageParams): Promise<{ 410 | /** 411 | * The markdown text completion for this response 412 | */ 413 | completion: string; 414 | /** 415 | * The reason for the response stop (if any) 416 | */ 417 | stop_reason: string; 418 | /** 419 | * The model used 420 | */ 421 | model: string; 422 | /** 423 | * The string at which Claude stopped responding at, e.g. "\n\nHuman:" 424 | */ 425 | stop: string; 426 | /** 427 | * A logging ID 428 | */ 429 | log_id: string; 430 | /** 431 | * If you're within the message limit 432 | */ 433 | messageLimit: any; 434 | }>; 435 | /** 436 | * Rename the current conversation 437 | * @async 438 | * @param {String} title The new title 439 | * @returns {Promise} A Response object 440 | */ 441 | rename(title: string): Promise; 442 | /** 443 | * Delete the conversation 444 | * @async 445 | * @returns Promise 446 | */ 447 | delete(): Promise; 448 | /** 449 | * @typedef Message 450 | * @property {UUID} uuid The message UUID 451 | * @property {String} text The message text 452 | * @property {String} created_at The message created at 453 | * @property {String} updated_at The message updated at 454 | * @property {String | null} edited_at When the message was last edited (no editing support via api/web client) 455 | * @property {Any | null} chat_feedback Feedback 456 | * @property {Attachment[]} attachments The attachments 457 | */ 458 | /** 459 | * @typedef ConversationInfo 460 | * @extends Conversation 461 | * @property {Message[]} chat_messages The messages in this conversation 462 | */ 463 | /** 464 | * Get information about this conversation 465 | * @returns {Promise} 466 | */ 467 | getInfo(): Promise; 468 | /** 469 | * Get all the files from this conversation 470 | * @async 471 | * @returns {Promise} 472 | */ 473 | getFiles(): Promise<{ 474 | /** 475 | * The file name 476 | */ 477 | file_name: string; 478 | /** 479 | * The file's mime type 480 | */ 481 | file_type: string; 482 | /** 483 | * The file size in bytes 484 | */ 485 | file_size: number; 486 | /** 487 | * The contents of the file that were extracted 488 | */ 489 | extracted_content: string; 490 | /** 491 | * The total pages of the document 492 | */ 493 | totalPages?: number; 494 | }[]>; 495 | /** 496 | * Get all messages in the conversation 497 | * @async 498 | * @returns {Promise} 499 | */ 500 | getMessages(): Promise<{ 501 | /** 502 | * The message UUID 503 | */ 504 | uuid: any; 505 | /** 506 | * The message text 507 | */ 508 | text: string; 509 | /** 510 | * The message created at 511 | */ 512 | created_at: string; 513 | /** 514 | * The message updated at 515 | */ 516 | updated_at: string; 517 | /** 518 | * When the message was last edited (no editing support via api/web client) 519 | */ 520 | edited_at: string | null; 521 | /** 522 | * Feedback 523 | */ 524 | chat_feedback: Any | null; 525 | /** 526 | * The attachments 527 | */ 528 | attachments: { 529 | /** 530 | * The file name 531 | */ 532 | file_name: string; 533 | /** 534 | * The file's mime type 535 | */ 536 | file_type: string; 537 | /** 538 | * The file size in bytes 539 | */ 540 | file_size: number; 541 | /** 542 | * The contents of the file that were extracted 543 | */ 544 | extracted_content: string; 545 | /** 546 | * The total pages of the document 547 | */ 548 | totalPages?: number; 549 | }[]; 550 | }[]>; 551 | #private; 552 | } 553 | /** 554 | * @typedef JSONResponse 555 | * @property {'human' | 'assistant'} sender The sender 556 | * @property {string} text The text 557 | * @property {UUID} uuid msg uuid 558 | * @property {string} created_at The message created at 559 | * @property {string} updated_at The message updated at 560 | * @property {string} edited_at When the message was last edited (no editing support via api/web client) 561 | * @property {Attachment[]} attachments The attachments 562 | * @property {string} chat_feedback Feedback 563 | */ 564 | /** 565 | * Message class 566 | * @class 567 | * @classdesc A class representing a message in a Conversation 568 | * @property {Function} request The request function (inherited from claude instance) 569 | * @property {JSONResponse} json The JSON representation 570 | * @property {Claude} claude The claude instance 571 | * @property {Conversation} conversation The conversation this message belongs to 572 | * @property {UUID} uuid The message uuid 573 | */ 574 | export class Message { 575 | /** 576 | * Create a Message instance. 577 | * @param {Object} params - Params 578 | * @param {Conversation} params.conversation - Conversation instance 579 | * @param {Claude} params.claude - Claude instance 580 | * @param {Message} message - Message data 581 | */ 582 | constructor({ conversation, claude }: { 583 | conversation: Conversation; 584 | claude: Claude; 585 | }, { uuid, text, sender, index, updated_at, edited_at, chat_feedback, attachments }: { 586 | /** 587 | * The message UUID 588 | */ 589 | uuid: any; 590 | /** 591 | * The message text 592 | */ 593 | text: string; 594 | /** 595 | * The message created at 596 | */ 597 | created_at: string; 598 | /** 599 | * The message updated at 600 | */ 601 | updated_at: string; 602 | /** 603 | * When the message was last edited (no editing support via api/web client) 604 | */ 605 | edited_at: string; 606 | /** 607 | * Feedback 608 | */ 609 | chat_feedback: any; 610 | /** 611 | * The attachments 612 | */ 613 | attachments: { 614 | /** 615 | * The file name 616 | */ 617 | file_name: string; 618 | /** 619 | * The file's mime type 620 | */ 621 | file_type: string; 622 | /** 623 | * The file size in bytes 624 | */ 625 | file_size: number; 626 | /** 627 | * The contents of the file that were extracted 628 | */ 629 | extracted_content: string; 630 | /** 631 | * The total pages of the document 632 | */ 633 | totalPages?: number; 634 | }[]; 635 | }); 636 | request: (endpoint: string, options: any) => Promise; 637 | json: { 638 | uuid: any; 639 | text: string; 640 | sender: any; 641 | index: any; 642 | updated_at: string; 643 | edited_at: string; 644 | chat_feedback: any; 645 | attachments: { 646 | /** 647 | * The file name 648 | */ 649 | file_name: string; 650 | /** 651 | * The file's mime type 652 | */ 653 | file_type: string; 654 | /** 655 | * The file size in bytes 656 | */ 657 | file_size: number; 658 | /** 659 | * The contents of the file that were extracted 660 | */ 661 | extracted_content: string; 662 | /** 663 | * The total pages of the document 664 | */ 665 | totalPages?: number; 666 | }[]; 667 | }; 668 | /** 669 | * Convert this message to a JSON representation 670 | * Necessary to prevent circular JSON errors 671 | * @returns {Message} 672 | */ 673 | toJSON(): { 674 | /** 675 | * The message UUID 676 | */ 677 | uuid: any; 678 | /** 679 | * The message text 680 | */ 681 | text: string; 682 | /** 683 | * The message created at 684 | */ 685 | created_at: string; 686 | /** 687 | * The message updated at 688 | */ 689 | updated_at: string; 690 | /** 691 | * When the message was last edited (no editing support via api/web client) 692 | */ 693 | edited_at: string; 694 | /** 695 | * Feedback 696 | */ 697 | chat_feedback: any; 698 | /** 699 | * The attachments 700 | */ 701 | attachments: { 702 | /** 703 | * The file name 704 | */ 705 | file_name: string; 706 | /** 707 | * The file's mime type 708 | */ 709 | file_type: string; 710 | /** 711 | * The file size in bytes 712 | */ 713 | file_size: number; 714 | /** 715 | * The contents of the file that were extracted 716 | */ 717 | extracted_content: string; 718 | /** 719 | * The total pages of the document 720 | */ 721 | totalPages?: number; 722 | }[]; 723 | }; 724 | /** 725 | * Returns the value of the "created_at" property as a Date object. 726 | * 727 | * @return {Date} The value of the "created_at" property as a Date object. 728 | */ 729 | get createdAt(): Date; 730 | /** 731 | * Returns the value of the "updated_at" property as a Date object. 732 | * 733 | * @return {Date} The value of the "updated_at" property as a Date object. 734 | */ 735 | get updatedAt(): Date; 736 | /** 737 | * Returns the value of the "edited_at" property as a Date object. 738 | * 739 | * @return {Date} The value of the "edited_at" property as a Date object. 740 | */ 741 | get editedAt(): Date; 742 | /** 743 | * Get if message is from the assistant. 744 | * @type {boolean} 745 | */ 746 | get isBot(): boolean; 747 | /** 748 | * @typedef MessageFeedback 749 | * @property {UUID} uuid - Message UUID 750 | * @property {"flag/bug" | "flag/harmful" | "flag/other"} type - Feedback type 751 | * @property {String | null} reason - Feedback reason (details box) 752 | * @property {String} created_at - Feedback creation date 753 | * @property {String} updated_at - Feedback update date 754 | */ 755 | /** 756 | * Send feedback on the message. 757 | * @param {string} type - Feedback type 758 | * @param {string} [reason] - Feedback reason 759 | * @returns {Promise} Response 760 | */ 761 | sendFeedback(type: string, reason?: string): Promise<{ 762 | /** 763 | * - Message UUID 764 | */ 765 | uuid: any; 766 | /** 767 | * - Feedback type 768 | */ 769 | type: "flag/bug" | "flag/harmful" | "flag/other"; 770 | /** 771 | * - Feedback reason (details box) 772 | */ 773 | reason: string | null; 774 | /** 775 | * - Feedback creation date 776 | */ 777 | created_at: string; 778 | /** 779 | * - Feedback update date 780 | */ 781 | updated_at: string; 782 | }>; 783 | } 784 | export default Claude; 785 | export type SendMessageParams = { 786 | /** 787 | * Whether to retry the most recent message in the conversation instead of sending a new one 788 | */ 789 | retry?: boolean; 790 | /** 791 | * The timezone 792 | */ 793 | timezone?: string; 794 | /** 795 | * Attachments 796 | */ 797 | attachments?: { 798 | /** 799 | * The file name 800 | */ 801 | file_name: string; 802 | /** 803 | * The file's mime type 804 | */ 805 | file_type: string; 806 | /** 807 | * The file size in bytes 808 | */ 809 | file_size: number; 810 | /** 811 | * The contents of the file that were extracted 812 | */ 813 | extracted_content: string; 814 | /** 815 | * The total pages of the document 816 | */ 817 | totalPages?: number; 818 | }[]; 819 | /** 820 | * Callback when done receiving the message response 821 | */ 822 | done?: (a: { 823 | /** 824 | * The markdown text completion for this response 825 | */ 826 | completion: string; 827 | /** 828 | * The reason for the response stop (if any) 829 | */ 830 | stop_reason: string; 831 | /** 832 | * The model used 833 | */ 834 | model: string; 835 | /** 836 | * The string at which Claude stopped responding at, e.g. "\n\nHuman:" 837 | */ 838 | stop: string; 839 | /** 840 | * A logging ID 841 | */ 842 | log_id: string; 843 | /** 844 | * If you're within the message limit 845 | */ 846 | messageLimit: any; 847 | }) => any; 848 | /** 849 | * Callback on message response progress 850 | */ 851 | progress?: (a: { 852 | /** 853 | * The markdown text completion for this response 854 | */ 855 | completion: string; 856 | /** 857 | * The reason for the response stop (if any) 858 | */ 859 | stop_reason: string; 860 | /** 861 | * The model used 862 | */ 863 | model: string; 864 | /** 865 | * The string at which Claude stopped responding at, e.g. "\n\nHuman:" 866 | */ 867 | stop: string; 868 | /** 869 | * A logging ID 870 | */ 871 | log_id: string; 872 | /** 873 | * If you're within the message limit 874 | */ 875 | messageLimit: any; 876 | }) => any; 877 | /** 878 | * The model to use 879 | */ 880 | model?: string; 881 | }; 882 | export type JSONResponse = { 883 | /** 884 | * The sender 885 | */ 886 | sender: 'human' | 'assistant'; 887 | /** 888 | * The text 889 | */ 890 | text: string; 891 | /** 892 | * msg uuid 893 | */ 894 | uuid: any; 895 | /** 896 | * The message created at 897 | */ 898 | created_at: string; 899 | /** 900 | * The message updated at 901 | */ 902 | updated_at: string; 903 | /** 904 | * When the message was last edited (no editing support via api/web client) 905 | */ 906 | edited_at: string; 907 | /** 908 | * The attachments 909 | */ 910 | attachments: { 911 | /** 912 | * The file name 913 | */ 914 | file_name: string; 915 | /** 916 | * The file's mime type 917 | */ 918 | file_type: string; 919 | /** 920 | * The file size in bytes 921 | */ 922 | file_size: number; 923 | /** 924 | * The contents of the file that were extracted 925 | */ 926 | extracted_content: string; 927 | /** 928 | * The total pages of the document 929 | */ 930 | totalPages?: number; 931 | }[]; 932 | /** 933 | * Feedback 934 | */ 935 | chat_feedback: string; 936 | }; 937 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | import Claude, { Conversation, Message } from './index.js'; 2 | import "dotenv/config"; 3 | import { readFileSync } from 'fs'; 4 | 5 | const UUIDS = { 6 | org: uuid(), 7 | conversation: uuid(), 8 | conversation2: uuid(), 9 | message: uuid(), 10 | } 11 | const demoFile = { 12 | "file_name": "test.txt", 13 | "file_type": "text/plain", 14 | "file_size": 11, 15 | "extracted_content": "Hello world", 16 | } 17 | const fetchCalls = []; 18 | 19 | const mockResponse = () => ({ 20 | body: { 21 | getReader: jest.fn().mockReturnValue({ 22 | read: jest.fn().mockResolvedValueOnce({ 23 | done: false, 24 | value: enc(`data: {"completion":" Hello","stop_reason":null,"model":"claude-2.0","stop":null,"log_id":"xxxxxx","messageLimit":{"type":"within_limit"}}`) 25 | }).mockResolvedValueOnce({ 26 | done: false, 27 | value: enc(`data: {"completion":" world","stop_reason":"stop_sequence","model":"claude-2.0","stop":"\\n\\nHuman:","log_id":"xxxxxx","messageLimit":{"type":"within_limit"}}`) 28 | }).mockResolvedValueOnce({ 29 | done: true, 30 | value: undefined 31 | }) 32 | }) 33 | } 34 | }) 35 | 36 | function enc(str) { 37 | return new TextEncoder().encode(str); 38 | } 39 | 40 | global.fetch = jest.fn(async function fetchMock(url, params) { 41 | let response = {}; 42 | if (url.endsWith('/api/organizations')) { 43 | response = [ 44 | { 45 | "uuid": UUIDS.org, 46 | "name": "example@gmail.com's Organization", 47 | "join_token": "TOKEN", 48 | "created_at": "2023-07-11T15:29:40.405434+00:00", 49 | "updated_at": "2023-07-11T15:30:11.107886+00:00", 50 | "capabilities": [ 51 | "chat", 52 | "legacy_non_strict_params" 53 | ], 54 | "settings": { 55 | "claude_console_privacy": "default_private" 56 | }, 57 | "active_flags": [] 58 | } 59 | ] 60 | } 61 | if (url.endsWith('/api/append_message')) { 62 | return Promise.resolve(mockResponse()); 63 | } 64 | if (url.endsWith('/api/rename_chat')) { 65 | response = {}; 66 | } 67 | if (url.endsWith('/api/generate_chat_title')) { 68 | response = { title: 'Hello world' } 69 | } 70 | console.log(url); 71 | if (url.endsWith(`/api/organizations/${UUIDS.org}/chat_conversations/${UUIDS.conversation}`)) { 72 | response = { 73 | "uuid": "f8ed74dc-e586-4407-9a31-b694d53b6ce9", 74 | "name": "Hello World", 75 | "summary": "", 76 | "created_at": "2023-07-17T12:50:44.486672+00:00", 77 | "updated_at": "2023-07-17T12:51:06.068542+00:00", 78 | "chat_messages": [ 79 | { 80 | "uuid": "ee248d02-976b-4317-8da1-52a9db8a07ef", 81 | "text": "hello", 82 | "sender": "human", 83 | "index": 0, 84 | "created_at": "2023-07-17T12:51:05.921141+00:00", 85 | "updated_at": "2023-07-17T12:51:05.921141+00:00", 86 | "edited_at": null, 87 | "chat_feedback": null, 88 | "attachments": [demoFile] 89 | }, 90 | { 91 | "uuid": "1c7130af-9be9-4e4f-93d2-0011931f6bf8", 92 | "text": "Hi there!", 93 | "sender": "assistant", 94 | "index": 1, 95 | "created_at": "2023-07-17T12:51:06.068542+00:00", 96 | "updated_at": "2023-07-17T12:51:06.068542+00:00", 97 | "edited_at": null, 98 | "chat_feedback": null, 99 | "attachments": [] 100 | } 101 | ] 102 | } 103 | } 104 | if (url.endsWith(`/api/organizations/${UUIDS.org}/chat_conversations`)) { 105 | response = [ 106 | { 107 | "uuid": UUIDS.conversation, 108 | "name": "Hello world", 109 | "summary": "", 110 | "created_at": "2023-07-14T21:13:06.311109+00:00", 111 | "updated_at": "2023-07-14T21:13:07.818718+00:00" 112 | }, 113 | { 114 | "uuid": UUIDS.conversation2, 115 | "name": "", 116 | "summary": "", 117 | "created_at": "2023-07-14T21:20:06.421771+00:00", 118 | "updated_at": "2023-07-14T21:23:09.117339+00:00" 119 | }, 120 | ] 121 | if (params.method === 'POST') { 122 | response = { 123 | uuid: UUIDS.conversation, 124 | name: 'Test conversation', 125 | summary: '', 126 | "created_at": "2023-07-14T21:20:06.421771+00:00", 127 | "updated_at": "2023-07-14T21:23:09.117339+00:00" 128 | } 129 | } 130 | } 131 | if (url.endsWith('/test')) { 132 | response = {}; 133 | } 134 | if (url.endsWith('/api/convert_document')) { 135 | response = { 136 | "file_name": "doc.docx", 137 | "file_size": 12242, 138 | "file_type": "docx", 139 | "extracted_content": "This is a demo file\n", 140 | "totalPages": null 141 | } 142 | } 143 | fetchCalls.push([url, response]); 144 | return { 145 | json: () => Promise.resolve(response || {}), 146 | status: 200, 147 | } 148 | }) 149 | 150 | describe('Claude', () => { 151 | let claude; 152 | beforeEach(async () => { 153 | claude = new Claude({ sessionKey: 'sk-ant-sid01-*****' }); 154 | if (!claude.ready) { await claude.init(); } 155 | }) 156 | describe('constructor', () => { 157 | it('throws if no session key', () => { 158 | expect(() => new Claude({})).toThrow(); 159 | }); 160 | it('throws if conversation created when no claude', () => { 161 | expect(() => { 162 | new Conversation(null, { conversationId: UUIDS.conversation }) 163 | }).toThrow() 164 | }) 165 | it('works with a proxy', () => { 166 | const c = new Claude({ 167 | sessionKey: 'sk-ant-sid01-*****', 168 | proxy: 'https://example.com' 169 | }) 170 | expect(c.proxy).toBeInstanceOf(Function); 171 | }) 172 | it('works with proxy functions', () => { 173 | const c = new Claude({ 174 | sessionKey: 'sk-ant-sid01-*****', 175 | proxy: ({ endpoint, options }) => ({ endpoint: 'https://example.com' + endpoint, options }) 176 | }); 177 | expect(c.proxy).toBeInstanceOf(Function); 178 | }) 179 | it('throws for invalid proxy', () => { 180 | expect(() => new Claude({ 181 | sessionKey: 'sk-ant-sid01-*****', 182 | proxy: {} 183 | })).toThrow(); 184 | }) 185 | it('throws if invalid session key', () => { 186 | expect(() => new Claude({ 187 | sessionKey: 'invalid', 188 | })).toThrow(); 189 | }); 190 | }); 191 | 192 | describe('request', () => { 193 | it('makes request to default endpoint', async () => { 194 | let r = await claude.request('/test'); 195 | expect(fetchCalls.pop()).toEqual(['https://claude.ai/test', {}]) 196 | }); 197 | it('works with proxies', async () => { 198 | claude.proxy = 'https://example.com' 199 | let r = await claude.request('/test'); 200 | expect(fetchCalls.pop()).toEqual(['https://example.com/test', {}]) 201 | }) 202 | }); 203 | describe('methods', () => { 204 | it('gets models', () => { 205 | expect(claude.models()).toBeInstanceOf(Array) 206 | }) 207 | it('total tokens', () => { 208 | expect(claude.totalTokens()).toBe(100_000) 209 | }) 210 | it('ready', async () => { 211 | expect(claude.ready).toBe(true); 212 | }); 213 | it('clears conversations', async () => { 214 | const response = await claude.clearConversations() 215 | expect(response).toBeInstanceOf(Array); 216 | expect(response[0].json).toBeDefined() 217 | }) 218 | it('gets conversation by ID', async () => { 219 | const convo = await claude.getConversation(UUIDS.conversation); 220 | expect(convo).toBeDefined(); 221 | expect(convo.conversationId).toBe(UUIDS.conversation); 222 | }) 223 | it('uploads files', async () => { 224 | const file = new File(["Hello world"], "test.txt", { type: 'text/plain' }); 225 | const response = await claude.uploadFile(file) 226 | expect(response).toStrictEqual(demoFile) 227 | }) 228 | it('gets files', async () => { 229 | const convo = await claude.getConversation(UUIDS.conversation); 230 | expect(await convo.getFiles()).toStrictEqual([demoFile]) 231 | }) 232 | it('uploads non-text files', async () => { 233 | const text = 'This is a demo file\n'; 234 | const attachment = await claude.uploadFile( 235 | new File([readFileSync('resources/doc.docx')], 'doc.docx', { 236 | type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' 237 | }) 238 | ); 239 | expect(attachment).toStrictEqual({ 240 | "file_name": "doc.docx", 241 | "file_size": 12242, 242 | "file_type": "docx", 243 | "extracted_content": text, 244 | "totalPages": null 245 | }) 246 | }) 247 | }) 248 | describe('startConversation', () => { 249 | it('starts new conversation', async () => { 250 | expect(claude.ready).toBe(true); 251 | const conversation = await claude.startConversation('Hi'); 252 | 253 | expect(conversation).toBeInstanceOf(Conversation); 254 | expect(conversation.conversationId).toBeDefined(); 255 | }); 256 | }); 257 | }) 258 | 259 | describe('Message', () => { 260 | let message; 261 | let conversation; 262 | let claude; 263 | 264 | beforeEach(() => { 265 | claude = new Claude({ 266 | sessionKey: 'sk-ant-sid01-*****', 267 | }); 268 | conversation = new Conversation(claude, { 269 | conversationId: UUIDS.conversation, 270 | }); 271 | message = new Message({ claude, conversation }, { 272 | uuid: UUIDS.message, 273 | text: 'hello', 274 | edited_at: '2023-07-11T15:30:11.107886+00:00', 275 | index: 0, 276 | attachments: [], 277 | chat_feedback: null, 278 | updated_at: '2023-07-11T15:30:11.107886+00:00' 279 | }); 280 | }); 281 | 282 | describe('constructor', () => { 283 | it('should throw when no claude/conversation', () => { 284 | expect(() => new Message({})).toThrow(); 285 | expect(() => new Message({ claude: null, conversation })).toThrow(); 286 | expect(() => new Message({ claude, conversation: null })).toThrow(); 287 | }) 288 | it('should work', async () => { 289 | const msg = new Message({ claude, conversation }, { 290 | uuid: UUIDS.message, text: 'hello', 291 | edited_at: '2023-07-11T15:30:11.107886+00:00', 292 | created_at: '2023-07-11T15:30:11.107886+00:00', 293 | index: 0, 294 | attachments: [], 295 | chat_feedback: null, 296 | sender: 'human', 297 | updated_at: '2023-07-11T15:30:11.107886+00:00' 298 | }); 299 | expect(msg).toBeInstanceOf(Message); 300 | expect(msg.claude).toBe(claude); 301 | expect(msg.conversation).toBe(conversation); 302 | }) 303 | }) 304 | test('initializes with correct properties', () => { 305 | expect(message.uuid).toBe(UUIDS.message); 306 | expect(message.text).toBe('hello'); 307 | expect(message.index).toBe(0); 308 | }); 309 | 310 | test('isBot returns false for user message', () => { 311 | expect(message.isBot).toBe(false); 312 | }); 313 | 314 | test('isBot returns true for assistant message', () => { 315 | message.sender = 'assistant'; 316 | expect(message.isBot).toBe(true); 317 | }); 318 | 319 | test('createdAt returns date object', () => { 320 | expect(message.createdAt).toBeInstanceOf(Date); 321 | }); 322 | 323 | test('updatedAt returns date object', () => { 324 | expect(message.updatedAt).toBeInstanceOf(Date); 325 | }); 326 | 327 | test('editedAt returns date object', () => { 328 | expect(message.editedAt).toBeInstanceOf(Date); 329 | }); 330 | 331 | test('sendFeedback makes API request', async () => { 332 | const mockRequest = jest.fn(() => Promise.resolve()); 333 | message.request = mockRequest; 334 | 335 | await message.sendFeedback('flag/bug', 'Typo'); 336 | 337 | expect(mockRequest).toHaveBeenCalledWith( 338 | expect.stringContaining('chat_feedback'), 339 | expect.objectContaining({ 340 | method: 'POST', 341 | body: JSON.stringify({ 342 | type: 'flag/bug', 343 | reason: 'Typo' 344 | }) 345 | }) 346 | ); 347 | }); 348 | }); 349 | 350 | describe('Conversation', () => { 351 | let conversation; 352 | let claude; 353 | 354 | beforeEach(async () => { 355 | claude = new Claude({ 356 | sessionKey: 'sk-ant-sid01-*****', 357 | }); 358 | await claude.init(); 359 | conversation = new Conversation(claude, { 360 | conversationId: UUIDS.conversation, 361 | }); 362 | }); 363 | 364 | describe('sendMessage', () => { 365 | it('sends message', async () => { 366 | const fn = jest.fn(); 367 | await conversation.sendMessage('Hi claude', { progress: fn }) 368 | expect(fn).toHaveBeenCalledWith(expect.objectContaining({ completion: expect.any(String) })) 369 | }); 370 | }); 371 | 372 | describe('rename', () => { 373 | it('renames conversation', async () => { 374 | expect(await conversation.rename('New title')).toBeDefined(); 375 | }); 376 | }); 377 | 378 | describe('getInfo', () => { 379 | it('gets conversation info', async () => { 380 | const res = await conversation.getInfo(); 381 | expect(res).toBeDefined(); 382 | res.chat_messages.forEach(a => expect(a).toBeInstanceOf(Message)) 383 | }); 384 | }) 385 | }); 386 | 387 | function uuid() { 388 | var h = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; 389 | var k = ['x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', '-', 'x', 'x', 'x', 'x', '-', '4', 'x', 'x', 'x', '-', 'y', 'x', 'x', 'x', '-', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x']; 390 | var u = '', i = 0, rb = Math.random() * 0xffffffff | 0; 391 | while (i++ < 36) { 392 | var c = k[i - 1], r = rb & 0xf, v = c == 'x' ? r : (r & 0x3 | 0x8); 393 | u += (c == '-' || c == '4') ? c : h[v]; rb = i % 8 == 0 ? Math.random() * 0xffffffff | 0 : rb >> 4 394 | } 395 | return u 396 | } -------------------------------------------------------------------------------- /jest.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | /** @type {import('jest').Config} */ 7 | const config = { 8 | // All imported modules in your tests should be mocked automatically 9 | // automock: false, 10 | 11 | // Stop running tests after `n` failures 12 | // bail: 0, 13 | 14 | // The directory where Jest should store its cached dependency information 15 | // cacheDirectory: "/private/var/folders/vc/1gxx69d95tv57ntx_qlycbym0000gp/T/jest_dy", 16 | 17 | // Automatically clear mock calls, instances, contexts and results before every test 18 | clearMocks: true, 19 | transform: { "^.+\\.(js|jsx)$": "babel-jest"}, 20 | // Indicates whether the coverage information should be collected while executing the test 21 | collectCoverage: true, 22 | 23 | // An array of glob patterns indicating a set of files for which coverage information should be collected 24 | // collectCoverageFrom: undefined, 25 | 26 | // The directory where Jest should output its coverage files 27 | coverageDirectory: "coverage", 28 | 29 | // An array of regexp pattern strings used to skip coverage collection 30 | // coveragePathIgnorePatterns: [ 31 | // "/node_modules/" 32 | // ], 33 | 34 | // Indicates which provider should be used to instrument code for coverage 35 | coverageProvider: "babel", 36 | 37 | // A list of reporter names that Jest uses when writing coverage reports 38 | // coverageReporters: [ 39 | // "json", 40 | // "text", 41 | // "lcov", 42 | // "clover" 43 | // ], 44 | 45 | // An object that configures minimum threshold enforcement for coverage results 46 | // coverageThreshold: undefined, 47 | 48 | // A path to a custom dependency extractor 49 | // dependencyExtractor: undefined, 50 | 51 | // Make calling deprecated APIs throw helpful error messages 52 | // errorOnDeprecated: false, 53 | 54 | // The default configuration for fake timers 55 | // fakeTimers: { 56 | // "enableGlobally": false 57 | // }, 58 | 59 | // Force coverage collection from ignored files using an array of glob patterns 60 | // forceCoverageMatch: [], 61 | 62 | // A path to a module which exports an async function that is triggered once before all test suites 63 | // globalSetup: undefined, 64 | 65 | // A path to a module which exports an async function that is triggered once after all test suites 66 | // globalTeardown: undefined, 67 | 68 | // A set of global variables that need to be available in all test environments 69 | // globals: {}, 70 | 71 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 72 | // maxWorkers: "50%", 73 | 74 | // An array of directory names to be searched recursively up from the requiring module's location 75 | // moduleDirectories: [ 76 | // "node_modules" 77 | // ], 78 | 79 | // An array of file extensions your modules use 80 | // moduleFileExtensions: [ 81 | // "js", 82 | // "mjs", 83 | // "cjs", 84 | // "jsx", 85 | // "ts", 86 | // "tsx", 87 | // "json", 88 | // "node" 89 | // ], 90 | 91 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 92 | // moduleNameMapper: {}, 93 | 94 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 95 | // modulePathIgnorePatterns: [], 96 | 97 | // Activates notifications for test results 98 | // notify: false, 99 | 100 | // An enum that specifies notification mode. Requires { notify: true } 101 | // notifyMode: "failure-change", 102 | 103 | // A preset that is used as a base for Jest's configuration 104 | // preset: undefined, 105 | 106 | // Run tests from one or more projects 107 | // projects: undefined, 108 | 109 | // Use this configuration option to add custom reporters to Jest 110 | // reporters: undefined, 111 | 112 | // Automatically reset mock state before every test 113 | // resetMocks: false, 114 | 115 | // Reset the module registry before running each individual test 116 | // resetModules: false, 117 | 118 | // A path to a custom resolver 119 | // resolver: undefined, 120 | 121 | // Automatically restore mock state and implementation before every test 122 | // restoreMocks: false, 123 | 124 | // The root directory that Jest should scan for tests and modules within 125 | // rootDir: undefined, 126 | 127 | // A list of paths to directories that Jest should use to search for files in 128 | // roots: [ 129 | // "" 130 | // ], 131 | 132 | // Allows you to use a custom runner instead of Jest's default test runner 133 | // runner: "jest-runner", 134 | 135 | // The paths to modules that run some code to configure or set up the testing environment before each test 136 | // setupFiles: [], 137 | 138 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 139 | // setupFilesAfterEnv: [], 140 | 141 | // The number of seconds after which a test is considered as slow and reported as such in the results. 142 | // slowTestThreshold: 5, 143 | 144 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 145 | // snapshotSerializers: [], 146 | 147 | // The test environment that will be used for testing 148 | // testEnvironment: "jest-environment-node", 149 | 150 | // Options that will be passed to the testEnvironment 151 | // testEnvironmentOptions: {}, 152 | 153 | // Adds a location field to test results 154 | // testLocationInResults: false, 155 | 156 | // The glob patterns Jest uses to detect test files 157 | // testMatch: [ 158 | // "**/__tests__/**/*.[jt]s?(x)", 159 | // "**/?(*.)+(spec|test).[tj]s?(x)" 160 | // ], 161 | 162 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 163 | // testPathIgnorePatterns: [ 164 | // "/node_modules/" 165 | // ], 166 | 167 | // The regexp pattern or array of patterns that Jest uses to detect test files 168 | // testRegex: [], 169 | 170 | // This option allows the use of a custom results processor 171 | // testResultsProcessor: undefined, 172 | 173 | // This option allows use of a custom test runner 174 | // testRunner: "jest-circus/runner", 175 | 176 | // A map from regular expressions to paths to transformers 177 | // transform: undefined, 178 | 179 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 180 | // transformIgnorePatterns: [ 181 | // "/node_modules/", 182 | // "\\.pnp\\.[^\\/]+$" 183 | // ], 184 | 185 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 186 | // unmockedModulePathPatterns: undefined, 187 | 188 | // Indicates whether each individual test should be reported during the run 189 | // verbose: undefined, 190 | 191 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 192 | // watchPathIgnorePatterns: [], 193 | 194 | // Whether to use watchman for file crawling 195 | // watchman: true, 196 | }; 197 | 198 | export default config; 199 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claude-ai", 3 | "type": "module", 4 | "version": "1.2.2", 5 | "description": "Unofficial library for interacting with Claude AI", 6 | "main": "index.js", 7 | "types": "index.d.ts", 8 | "scripts": { 9 | "test": "jest", 10 | "prepublishOnly": "tsc --declaration --allowJs --emitDeclarationOnly index.js", 11 | "publish": "git add . && git commit -m '📦 Release' && npm publish && cd cli && npm publish && git push" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/Explosion-Scratch/claude-unofficial-api.git" 16 | }, 17 | "homepage": "https://github.com/Explosion-Scratch/claude-unofficial-api#readme", 18 | "keywords": [ 19 | "claude", 20 | "claude-ai", 21 | "ai", 22 | "chatbot", 23 | "api", 24 | "unofficial" 25 | ], 26 | "author": "@Explosion-Scratch", 27 | "license": "Unlicense", 28 | "dependencies": { 29 | "isomorphic-fetch": "^3.0.0" 30 | }, 31 | "devDependencies": { 32 | "@babel/preset-env": "^7.22.9", 33 | "body-parser": "^1.20.2", 34 | "dotenv": "^16.3.1", 35 | "typescript": "^5.1.6" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/doc.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Explosion-Scratch/claude-unofficial-api/3d55419f9025152e8bea708365a9c25a02909529/resources/doc.docx -------------------------------------------------------------------------------- /rest_api/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { Claude } from '../index.js'; 3 | import { readFileSync } from 'fs'; 4 | import bodyParser from 'body-parser'; 5 | 6 | const app = express(); 7 | app.set('json spaces', 2) 8 | 9 | 10 | app.use(bodyParser.json()); 11 | app.use(bodyParser.urlencoded({ extended: true })); 12 | 13 | 14 | const version = JSON.parse(readFileSync('package.json', 'utf-8')).version + ` (Claude v${JSON.parse(readFileSync('../package.json', 'utf-8')).version})`; 15 | 16 | // Initialize Claude 17 | const claude = new Claude({ 18 | sessionKey: process.env.CLAUDE_KEY 19 | }); 20 | 21 | app.use(async (req, res, next) => { 22 | if (!claude.ready) { 23 | await claude.init(); 24 | } 25 | next(); 26 | }) 27 | 28 | app.get('/', (req, res) => { 29 | res.type('text/plain'); 30 | res.send(` 31 | Claude REST API v${version} 32 | 33 | Routes: 34 | - GET /conversations - Get conversations 35 | - GET /conversations/:id - Get conversation detail 36 | - POST /conversations/:id - Send message 37 | - POST /conversations/:id/files - Upload file 38 | - GET /conversations/:id/files - Get files 39 | - DELETE /conversations/:id - Delete conversation 40 | - PATCH /conversations/:id - Rename conversation 41 | - POST /ask - Sync ask 42 | `.split('\n').map(i => i.trim()).map(i => i.startsWith('-') ? ' ' + i : i).join('\n')); 43 | }); 44 | app.get('/version', (req, res) => { 45 | res.type('text/plain') 46 | res.send(version); 47 | }); 48 | app.get('/organizations', async (req, res) => { 49 | try { 50 | const organizations = await claude.getOrganizations(); 51 | res.json(organizations); 52 | } catch (err) { 53 | res.status(500).send({ error: 'Failed to get organizations' }); 54 | } 55 | }) 56 | // Get conversations 57 | app.get('/conversations', async (req, res) => { 58 | try { 59 | const conversations = await claude.getConversations(); 60 | res.json(conversations); 61 | } catch (err) { 62 | console.log(err) 63 | res.status(500).send({ error: 'Failed to get conversations' }); 64 | } 65 | }); 66 | 67 | // Get conversation details 68 | app.get('/conversations/:id', async (req, res) => { 69 | try { 70 | const conversation = await claude.getConversation(req.params.id); 71 | if (!conversation) { 72 | return res.status(404).send({ error: 'Conversation not found' }); 73 | } 74 | res.json(await conversation.getInfo()); 75 | } catch (err) { 76 | res.status(500).send({ error: 'Failed to get conversation' }); 77 | } 78 | }); 79 | 80 | // Delete conversation 81 | app.delete('/conversations/:id', async (req, res) => { 82 | try { 83 | const conversation = await claude.getConversation(req.params.id); 84 | if (!conversation) { 85 | return res.status(404).send({ error: 'Conversation not found' }); 86 | } 87 | 88 | await conversation.delete(); 89 | res.sendStatus(204); 90 | } catch (err) { 91 | res.status(500).send({ error: 'Failed to delete conversation' }); 92 | } 93 | }); 94 | 95 | app.delete('/conversations', async (req, res) => { 96 | try { 97 | await claude.clearConversations(); 98 | res.sendStatus(204); 99 | } catch (err) { 100 | res.status(500).send({ error: 'Failed to delete conversations' }); 101 | } 102 | }) 103 | 104 | // Rename conversation 105 | app.patch('/conversations/:id', async (req, res) => { 106 | try { 107 | const conversation = await claude.getConversation(req.params.id); 108 | if (!conversation) { 109 | return res.status(404).send({ error: 'Conversation not found' }); 110 | } 111 | 112 | await conversation.rename(req.body.name); 113 | res.sendStatus(204); 114 | 115 | } catch (err) { 116 | res.status(500).send({ error: 'Failed to rename conversation' }); 117 | } 118 | }); 119 | 120 | // Send message 121 | app.post('/conversations/:id', async (req, res) => { 122 | try { 123 | const conversation = await claude.getConversation(req.params.id); 124 | if (!conversation) { 125 | return res.status(404).send({ error: 'Conversation not found' }); 126 | } 127 | 128 | // Stream progress 129 | res.setHeader('Content-Type', 'text/event-stream'); 130 | res.setHeader('Connection', 'keep-alive'); 131 | res.flushHeaders(); 132 | 133 | const stream = conversation.sendMessage(req.body.message, { 134 | progress: (data) => res.write(`data: ${JSON.stringify(data)}\n\n`) 135 | }); 136 | 137 | stream.then(data => { 138 | res.write(`data: ${JSON.stringify(data)}\n\n`); 139 | res.end(); 140 | }); 141 | 142 | } catch (err) { 143 | res.status(500).send({ error: 'Failed to send message' }); 144 | } 145 | }); 146 | 147 | // Get files for conversation 148 | app.get('/conversations/:id/files', async (req, res) => { 149 | try { 150 | const conversation = await claude.getConversation(req.params.id); 151 | if (!conversation) { 152 | return res.status(404).send({ error: 'Conversation not found' }); 153 | } 154 | 155 | const files = await conversation.getFiles(); 156 | res.json(files); 157 | 158 | } catch (err) { 159 | res.status(500).send({ error: 'Failed to get files' }); 160 | } 161 | }); 162 | 163 | // Upload file 164 | app.post('/conversations/:id/files', async (req, res) => { 165 | try { 166 | const conversation = await claude.getConversation(req.params.id); 167 | if (!conversation) { 168 | return res.status(404).send({ error: 'Conversation not found' }); 169 | } 170 | 171 | const file = req.files.file; 172 | const result = await conversation.uploadFile(file); 173 | res.json(result); 174 | 175 | } catch (err) { 176 | res.status(500).send({ error: 'Failed to upload file' }); 177 | } 178 | }); 179 | 180 | // Message feedback 181 | app.post('/conversations/:conversationId/messages/:messageId/feedback', async (req, res) => { 182 | try { 183 | const conversation = await claude.getConversation(req.params.conversationId); 184 | if (!conversation) { 185 | return res.status(404).send({ error: 'Conversation not found' }); 186 | } 187 | 188 | const message = conversation.getMessage(req.params.messageId); 189 | if (!message) { 190 | return res.status(404).send({ error: 'Message not found' }); 191 | } 192 | 193 | await message.sendFeedback(req.body.type, req.body.reason); 194 | res.sendStatus(204); 195 | 196 | } catch (err) { 197 | res.status(500).send({ error: 'Failed to send feedback' }); 198 | } 199 | }); 200 | 201 | // Get messages 202 | app.get('/conversations/:conversationId/messages', async (req, res) => { 203 | try { 204 | const conversation = await claude.getConversation(req.params.conversationId); 205 | if (!conversation) { 206 | return res.status(404).send({ error: 'Conversation not found' }); 207 | } 208 | 209 | const messages = await conversation.getMessages(); 210 | res.json(messages); 211 | 212 | } catch (err) { 213 | res.status(500).send({ error: 'Failed to get messages' }); 214 | } 215 | }); 216 | 217 | // Retry conversation 218 | app.post('/conversations/:conversationId/retry', async (req, res) => { 219 | try { 220 | const conversation = await claude.getConversation(req.params.conversationId); 221 | if (!conversation) { 222 | return res.status(404).send({ error: 'Conversation not found' }); 223 | } 224 | 225 | await conversation.retry(); 226 | res.sendStatus(204); 227 | 228 | } catch (err) { 229 | res.status(500).send({ error: 'Failed to retry conversation' }); 230 | } 231 | }); 232 | 233 | // Sync ask route 234 | app.post('/ask', async (req, res) => { 235 | try { 236 | const conversation = await claude.startConversation(req.body.message); 237 | const response = await conversation.sendMessage(req.body.message); 238 | res.json(response); 239 | } catch (err) { 240 | res.status(500).send({ error: 'Failed to get response' }); 241 | } 242 | }); 243 | 244 | const PORT = process.env.PORT || 3000; 245 | 246 | app.listen(PORT, () => { 247 | console.log(`Claude REST API v${version} running on port ${PORT}`); 248 | }); 249 | -------------------------------------------------------------------------------- /rest_api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "claude-rest", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "type": "module", 13 | "dependencies": { 14 | "express": "^4.18.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rest_api/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | express: 9 | specifier: ^4.18.2 10 | version: 4.18.2 11 | 12 | packages: 13 | 14 | /accepts@1.3.8: 15 | resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} 16 | engines: {node: '>= 0.6'} 17 | dependencies: 18 | mime-types: 2.1.35 19 | negotiator: 0.6.3 20 | dev: false 21 | 22 | /array-flatten@1.1.1: 23 | resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} 24 | dev: false 25 | 26 | /body-parser@1.20.1: 27 | resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} 28 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 29 | dependencies: 30 | bytes: 3.1.2 31 | content-type: 1.0.5 32 | debug: 2.6.9 33 | depd: 2.0.0 34 | destroy: 1.2.0 35 | http-errors: 2.0.0 36 | iconv-lite: 0.4.24 37 | on-finished: 2.4.1 38 | qs: 6.11.0 39 | raw-body: 2.5.1 40 | type-is: 1.6.18 41 | unpipe: 1.0.0 42 | transitivePeerDependencies: 43 | - supports-color 44 | dev: false 45 | 46 | /bytes@3.1.2: 47 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 48 | engines: {node: '>= 0.8'} 49 | dev: false 50 | 51 | /call-bind@1.0.2: 52 | resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} 53 | dependencies: 54 | function-bind: 1.1.1 55 | get-intrinsic: 1.2.1 56 | dev: false 57 | 58 | /content-disposition@0.5.4: 59 | resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} 60 | engines: {node: '>= 0.6'} 61 | dependencies: 62 | safe-buffer: 5.2.1 63 | dev: false 64 | 65 | /content-type@1.0.5: 66 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 67 | engines: {node: '>= 0.6'} 68 | dev: false 69 | 70 | /cookie-signature@1.0.6: 71 | resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} 72 | dev: false 73 | 74 | /cookie@0.5.0: 75 | resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} 76 | engines: {node: '>= 0.6'} 77 | dev: false 78 | 79 | /debug@2.6.9: 80 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 81 | peerDependencies: 82 | supports-color: '*' 83 | peerDependenciesMeta: 84 | supports-color: 85 | optional: true 86 | dependencies: 87 | ms: 2.0.0 88 | dev: false 89 | 90 | /depd@2.0.0: 91 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 92 | engines: {node: '>= 0.8'} 93 | dev: false 94 | 95 | /destroy@1.2.0: 96 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 97 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 98 | dev: false 99 | 100 | /ee-first@1.1.1: 101 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 102 | dev: false 103 | 104 | /encodeurl@1.0.2: 105 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 106 | engines: {node: '>= 0.8'} 107 | dev: false 108 | 109 | /escape-html@1.0.3: 110 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 111 | dev: false 112 | 113 | /etag@1.8.1: 114 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 115 | engines: {node: '>= 0.6'} 116 | dev: false 117 | 118 | /express@4.18.2: 119 | resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} 120 | engines: {node: '>= 0.10.0'} 121 | dependencies: 122 | accepts: 1.3.8 123 | array-flatten: 1.1.1 124 | body-parser: 1.20.1 125 | content-disposition: 0.5.4 126 | content-type: 1.0.5 127 | cookie: 0.5.0 128 | cookie-signature: 1.0.6 129 | debug: 2.6.9 130 | depd: 2.0.0 131 | encodeurl: 1.0.2 132 | escape-html: 1.0.3 133 | etag: 1.8.1 134 | finalhandler: 1.2.0 135 | fresh: 0.5.2 136 | http-errors: 2.0.0 137 | merge-descriptors: 1.0.1 138 | methods: 1.1.2 139 | on-finished: 2.4.1 140 | parseurl: 1.3.3 141 | path-to-regexp: 0.1.7 142 | proxy-addr: 2.0.7 143 | qs: 6.11.0 144 | range-parser: 1.2.1 145 | safe-buffer: 5.2.1 146 | send: 0.18.0 147 | serve-static: 1.15.0 148 | setprototypeof: 1.2.0 149 | statuses: 2.0.1 150 | type-is: 1.6.18 151 | utils-merge: 1.0.1 152 | vary: 1.1.2 153 | transitivePeerDependencies: 154 | - supports-color 155 | dev: false 156 | 157 | /finalhandler@1.2.0: 158 | resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} 159 | engines: {node: '>= 0.8'} 160 | dependencies: 161 | debug: 2.6.9 162 | encodeurl: 1.0.2 163 | escape-html: 1.0.3 164 | on-finished: 2.4.1 165 | parseurl: 1.3.3 166 | statuses: 2.0.1 167 | unpipe: 1.0.0 168 | transitivePeerDependencies: 169 | - supports-color 170 | dev: false 171 | 172 | /forwarded@0.2.0: 173 | resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 174 | engines: {node: '>= 0.6'} 175 | dev: false 176 | 177 | /fresh@0.5.2: 178 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 179 | engines: {node: '>= 0.6'} 180 | dev: false 181 | 182 | /function-bind@1.1.1: 183 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 184 | dev: false 185 | 186 | /get-intrinsic@1.2.1: 187 | resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} 188 | dependencies: 189 | function-bind: 1.1.1 190 | has: 1.0.3 191 | has-proto: 1.0.1 192 | has-symbols: 1.0.3 193 | dev: false 194 | 195 | /has-proto@1.0.1: 196 | resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} 197 | engines: {node: '>= 0.4'} 198 | dev: false 199 | 200 | /has-symbols@1.0.3: 201 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 202 | engines: {node: '>= 0.4'} 203 | dev: false 204 | 205 | /has@1.0.3: 206 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 207 | engines: {node: '>= 0.4.0'} 208 | dependencies: 209 | function-bind: 1.1.1 210 | dev: false 211 | 212 | /http-errors@2.0.0: 213 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 214 | engines: {node: '>= 0.8'} 215 | dependencies: 216 | depd: 2.0.0 217 | inherits: 2.0.4 218 | setprototypeof: 1.2.0 219 | statuses: 2.0.1 220 | toidentifier: 1.0.1 221 | dev: false 222 | 223 | /iconv-lite@0.4.24: 224 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 225 | engines: {node: '>=0.10.0'} 226 | dependencies: 227 | safer-buffer: 2.1.2 228 | dev: false 229 | 230 | /inherits@2.0.4: 231 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 232 | dev: false 233 | 234 | /ipaddr.js@1.9.1: 235 | resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 236 | engines: {node: '>= 0.10'} 237 | dev: false 238 | 239 | /media-typer@0.3.0: 240 | resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} 241 | engines: {node: '>= 0.6'} 242 | dev: false 243 | 244 | /merge-descriptors@1.0.1: 245 | resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} 246 | dev: false 247 | 248 | /methods@1.1.2: 249 | resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 250 | engines: {node: '>= 0.6'} 251 | dev: false 252 | 253 | /mime-db@1.52.0: 254 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 255 | engines: {node: '>= 0.6'} 256 | dev: false 257 | 258 | /mime-types@2.1.35: 259 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 260 | engines: {node: '>= 0.6'} 261 | dependencies: 262 | mime-db: 1.52.0 263 | dev: false 264 | 265 | /mime@1.6.0: 266 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 267 | engines: {node: '>=4'} 268 | hasBin: true 269 | dev: false 270 | 271 | /ms@2.0.0: 272 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 273 | dev: false 274 | 275 | /ms@2.1.3: 276 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 277 | dev: false 278 | 279 | /negotiator@0.6.3: 280 | resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} 281 | engines: {node: '>= 0.6'} 282 | dev: false 283 | 284 | /object-inspect@1.12.3: 285 | resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} 286 | dev: false 287 | 288 | /on-finished@2.4.1: 289 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 290 | engines: {node: '>= 0.8'} 291 | dependencies: 292 | ee-first: 1.1.1 293 | dev: false 294 | 295 | /parseurl@1.3.3: 296 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 297 | engines: {node: '>= 0.8'} 298 | dev: false 299 | 300 | /path-to-regexp@0.1.7: 301 | resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} 302 | dev: false 303 | 304 | /proxy-addr@2.0.7: 305 | resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 306 | engines: {node: '>= 0.10'} 307 | dependencies: 308 | forwarded: 0.2.0 309 | ipaddr.js: 1.9.1 310 | dev: false 311 | 312 | /qs@6.11.0: 313 | resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} 314 | engines: {node: '>=0.6'} 315 | dependencies: 316 | side-channel: 1.0.4 317 | dev: false 318 | 319 | /range-parser@1.2.1: 320 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 321 | engines: {node: '>= 0.6'} 322 | dev: false 323 | 324 | /raw-body@2.5.1: 325 | resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} 326 | engines: {node: '>= 0.8'} 327 | dependencies: 328 | bytes: 3.1.2 329 | http-errors: 2.0.0 330 | iconv-lite: 0.4.24 331 | unpipe: 1.0.0 332 | dev: false 333 | 334 | /safe-buffer@5.2.1: 335 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 336 | dev: false 337 | 338 | /safer-buffer@2.1.2: 339 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 340 | dev: false 341 | 342 | /send@0.18.0: 343 | resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} 344 | engines: {node: '>= 0.8.0'} 345 | dependencies: 346 | debug: 2.6.9 347 | depd: 2.0.0 348 | destroy: 1.2.0 349 | encodeurl: 1.0.2 350 | escape-html: 1.0.3 351 | etag: 1.8.1 352 | fresh: 0.5.2 353 | http-errors: 2.0.0 354 | mime: 1.6.0 355 | ms: 2.1.3 356 | on-finished: 2.4.1 357 | range-parser: 1.2.1 358 | statuses: 2.0.1 359 | transitivePeerDependencies: 360 | - supports-color 361 | dev: false 362 | 363 | /serve-static@1.15.0: 364 | resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} 365 | engines: {node: '>= 0.8.0'} 366 | dependencies: 367 | encodeurl: 1.0.2 368 | escape-html: 1.0.3 369 | parseurl: 1.3.3 370 | send: 0.18.0 371 | transitivePeerDependencies: 372 | - supports-color 373 | dev: false 374 | 375 | /setprototypeof@1.2.0: 376 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 377 | dev: false 378 | 379 | /side-channel@1.0.4: 380 | resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} 381 | dependencies: 382 | call-bind: 1.0.2 383 | get-intrinsic: 1.2.1 384 | object-inspect: 1.12.3 385 | dev: false 386 | 387 | /statuses@2.0.1: 388 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 389 | engines: {node: '>= 0.8'} 390 | dev: false 391 | 392 | /toidentifier@1.0.1: 393 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 394 | engines: {node: '>=0.6'} 395 | dev: false 396 | 397 | /type-is@1.6.18: 398 | resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} 399 | engines: {node: '>= 0.6'} 400 | dependencies: 401 | media-typer: 0.3.0 402 | mime-types: 2.1.35 403 | dev: false 404 | 405 | /unpipe@1.0.0: 406 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 407 | engines: {node: '>= 0.8'} 408 | dev: false 409 | 410 | /utils-merge@1.0.1: 411 | resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 412 | engines: {node: '>= 0.4.0'} 413 | dev: false 414 | 415 | /vary@1.1.2: 416 | resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 417 | engines: {node: '>= 0.8'} 418 | dev: false 419 | --------------------------------------------------------------------------------