diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..2b78eb34e --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +# ------------------------------------------------------------------------------ +# This is an example .env file. +# +# All of these environment vars must be defined either in your environment or in +# a local .env file in order to run the demo for this project. +# ------------------------------------------------------------------------------ + +# ----------------------------------------------------------------------------- +# ChatGPT +# ----------------------------------------------------------------------------- + +# see the readme for how to find this +SESSION_TOKEN= diff --git a/docs/classes/ChatGPTAPI.md b/docs/classes/ChatGPTAPI.md index 78c0d1ac6..e0822df44 100644 --- a/docs/classes/ChatGPTAPI.md +++ b/docs/classes/ChatGPTAPI.md @@ -10,53 +10,53 @@ ### Methods -- [close](ChatGPTAPI.md#close) -- [getIsSignedIn](ChatGPTAPI.md#getissignedin) -- [getLastMessage](ChatGPTAPI.md#getlastmessage) -- [getMessages](ChatGPTAPI.md#getmessages) -- [getPrompts](ChatGPTAPI.md#getprompts) -- [init](ChatGPTAPI.md#init) +- [ensureAuth](ChatGPTAPI.md#ensureauth) +- [getIsAuthenticated](ChatGPTAPI.md#getisauthenticated) +- [refreshAccessToken](ChatGPTAPI.md#refreshaccesstoken) - [sendMessage](ChatGPTAPI.md#sendmessage) ## Constructors ### constructor -• **new ChatGPTAPI**(`opts?`) +• **new ChatGPTAPI**(`opts`) + +Creates a new client wrapper around the unofficial ChatGPT REST API. #### Parameters | Name | Type | Description | | :------ | :------ | :------ | | `opts` | `Object` | - | -| `opts.chatUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/'` * | -| `opts.headless?` | `boolean` | **`Default Value`** `false` * | +| `opts.apiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/api'` * | +| `opts.backendApiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/backend-api'` * | | `opts.markdown?` | `boolean` | **`Default Value`** `true` * | -| `opts.userDataDir?` | `string` | **`Default Value`** `'/tmp/chatgpt'` * | +| `opts.sessionToken` | `string` | = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions) | +| `opts.userAgent?` | `string` | **`Default Value`** `'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'` * | #### Defined in -[chatgpt-api.ts:20](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L20) +[chatgpt-api.ts:29](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/chatgpt-api.ts#L29) ## Methods -### close +### ensureAuth -▸ **close**(): `Promise`<`void`\> +▸ **ensureAuth**(): `Promise`<`string`\> #### Returns -`Promise`<`void`\> +`Promise`<`string`\> #### Defined in -[chatgpt-api.ts:186](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L186) +[chatgpt-api.ts:72](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/chatgpt-api.ts#L72) ___ -### getIsSignedIn +### getIsAuthenticated -▸ **getIsSignedIn**(): `Promise`<`boolean`\> +▸ **getIsAuthenticated**(): `Promise`<`boolean`\> #### Returns @@ -64,13 +64,13 @@ ___ #### Defined in -[chatgpt-api.ts:94](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L94) +[chatgpt-api.ts:63](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/chatgpt-api.ts#L63) ___ -### getLastMessage +### refreshAccessToken -▸ **getLastMessage**(): `Promise`<`string`\> +▸ **refreshAccessToken**(): `Promise`<`string`\> #### Returns @@ -78,68 +78,25 @@ ___ #### Defined in -[chatgpt-api.ts:104](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L104) - -___ - -### getMessages - -▸ **getMessages**(): `Promise`<`string`[]\> - -#### Returns - -`Promise`<`string`[]\> - -#### Defined in - -[chatgpt-api.ts:124](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L124) - -___ - -### getPrompts - -▸ **getPrompts**(): `Promise`<`string`[]\> - -#### Returns - -`Promise`<`string`[]\> - -#### Defined in - -[chatgpt-api.ts:114](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L114) - -___ - -### init - -▸ **init**(`opts?`): `Promise`<`Page`\> - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `opts` | `Object` | -| `opts.auth?` | ``"blocking"`` \| ``"eager"`` | - -#### Returns - -`Promise`<`Page`\> - -#### Defined in - -[chatgpt-api.ts:48](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L48) +[chatgpt-api.ts:163](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/chatgpt-api.ts#L163) ___ ### sendMessage -▸ **sendMessage**(`message`): `Promise`<`string`\> +▸ **sendMessage**(`message`, `opts?`): `Promise`<`string`\> + +Sends a message to ChatGPT, waits for the response to resolve, and returns +the response. #### Parameters -| Name | Type | -| :------ | :------ | -| `message` | `string` | +| Name | Type | Description | +| :------ | :------ | :------ | +| `message` | `string` | The plaintext message to send. | +| `opts` | `Object` | - | +| `opts.converstationId?` | `string` | - | +| `opts.onProgress?` | (`partialResponse`: `string`) => `void` | - | #### Returns @@ -147,4 +104,4 @@ ___ #### Defined in -[chatgpt-api.ts:162](https://github.com/transitive-bullshit/chatgpt-api/blob/ddd9545/src/chatgpt-api.ts#L162) +[chatgpt-api.ts:84](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/chatgpt-api.ts#L84) diff --git a/docs/modules.md b/docs/modules.md index af94969ee..2e947622c 100644 --- a/docs/modules.md +++ b/docs/modules.md @@ -7,3 +7,386 @@ ### Classes - [ChatGPTAPI](classes/ChatGPTAPI.md) + +### Type Aliases + +- [AvailableModerationModels](modules.md#availablemoderationmodels) +- [ContentType](modules.md#contenttype) +- [ConversationJSONBody](modules.md#conversationjsonbody) +- [ConversationResponseEvent](modules.md#conversationresponseevent) +- [Message](modules.md#message) +- [MessageContent](modules.md#messagecontent) +- [MessageFeedbackJSONBody](modules.md#messagefeedbackjsonbody) +- [MessageFeedbackRating](modules.md#messagefeedbackrating) +- [MessageFeedbackResult](modules.md#messagefeedbackresult) +- [MessageFeedbackTags](modules.md#messagefeedbacktags) +- [MessageMetadata](modules.md#messagemetadata) +- [Model](modules.md#model) +- [ModelsResult](modules.md#modelsresult) +- [ModerationsJSONBody](modules.md#moderationsjsonbody) +- [ModerationsJSONResult](modules.md#moderationsjsonresult) +- [Prompt](modules.md#prompt) +- [PromptContent](modules.md#promptcontent) +- [Role](modules.md#role) +- [SessionResult](modules.md#sessionresult) +- [User](modules.md#user) + +### Functions + +- [markdownToText](modules.md#markdowntotext) + +## Type Aliases + +### AvailableModerationModels + +Ƭ **AvailableModerationModels**: ``"text-moderation-playground"`` + +#### Defined in + +[types.ts:104](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L104) + +___ + +### ContentType + +Ƭ **ContentType**: ``"text"`` + +#### Defined in + +[types.ts:1](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L1) + +___ + +### ConversationJSONBody + +Ƭ **ConversationJSONBody**: `Object` + +https://chat.openapi.com/backend-api/conversation + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `action` | `string` | The action to take | +| `conversation_id?` | `string` | The ID of the conversation | +| `messages` | [`Prompt`](modules.md#prompt)[] | Prompts to provide | +| `model` | `string` | The model to use | +| `parent_message_id` | `string` | The parent message ID | + +#### Defined in + +[types.ts:129](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L129) + +___ + +### ConversationResponseEvent + +Ƭ **ConversationResponseEvent**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `conversation_id?` | `string` | +| `error?` | `string` \| ``null`` | +| `message?` | [`Message`](modules.md#message) | + +#### Defined in + +[types.ts:246](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L246) + +___ + +### Message + +Ƭ **Message**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `content` | [`MessageContent`](modules.md#messagecontent) | +| `create_time` | `string` \| ``null`` | +| `end_turn` | ``null`` | +| `id` | `string` | +| `metadata` | [`MessageMetadata`](modules.md#messagemetadata) | +| `recipient` | `string` | +| `role` | `string` | +| `update_time` | `string` \| ``null`` | +| `user` | `string` \| ``null`` | +| `weight` | `number` | + +#### Defined in + +[types.ts:252](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L252) + +___ + +### MessageContent + +Ƭ **MessageContent**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `content_type` | `string` | +| `parts` | `string`[] | + +#### Defined in + +[types.ts:265](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L265) + +___ + +### MessageFeedbackJSONBody + +Ƭ **MessageFeedbackJSONBody**: `Object` + +https://chat.openapi.com/backend-api/conversation/message_feedback + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `conversation_id` | `string` | The ID of the conversation | +| `message_id` | `string` | The message ID | +| `rating` | [`MessageFeedbackRating`](modules.md#messagefeedbackrating) | The rating | +| `tags?` | [`MessageFeedbackTags`](modules.md#messagefeedbacktags)[] | Tags to give the rating | +| `text?` | `string` | The text to include | + +#### Defined in + +[types.ts:188](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L188) + +___ + +### MessageFeedbackRating + +Ƭ **MessageFeedbackRating**: ``"thumbsUp"`` \| ``"thumbsDown"`` + +#### Defined in + +[types.ts:244](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L244) + +___ + +### MessageFeedbackResult + +Ƭ **MessageFeedbackResult**: `Object` + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `conversation_id` | `string` | The ID of the conversation | +| `message_id` | `string` | The message ID | +| `rating` | [`MessageFeedbackRating`](modules.md#messagefeedbackrating) | The rating | +| `text?` | `string` | The text the server received, including tags | +| `user_id` | `string` | The ID of the user | + +#### Defined in + +[types.ts:217](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L217) + +___ + +### MessageFeedbackTags + +Ƭ **MessageFeedbackTags**: ``"harmful"`` \| ``"false"`` \| ``"not-helpful"`` + +#### Defined in + +[types.ts:215](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L215) + +___ + +### MessageMetadata + +Ƭ **MessageMetadata**: `any` + +#### Defined in + +[types.ts:270](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L270) + +___ + +### Model + +Ƭ **Model**: `Object` + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `is_special` | `boolean` | Whether or not the model is special | +| `max_tokens` | `number` | Max tokens of the model | +| `slug` | `string` | Name of the model | + +#### Defined in + +[types.ts:72](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L72) + +___ + +### ModelsResult + +Ƭ **ModelsResult**: `Object` + +https://chat.openapi.com/backend-api/models + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `models` | [`Model`](modules.md#model)[] | Array of models | + +#### Defined in + +[types.ts:65](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L65) + +___ + +### ModerationsJSONBody + +Ƭ **ModerationsJSONBody**: `Object` + +https://chat.openapi.com/backend-api/moderations + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `input` | `string` | Input for the moderation decision | +| `model` | [`AvailableModerationModels`](modules.md#availablemoderationmodels) | The model to use in the decision | + +#### Defined in + +[types.ts:92](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L92) + +___ + +### ModerationsJSONResult + +Ƭ **ModerationsJSONResult**: `Object` + +https://chat.openapi.com/backend-api/moderations + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `blocked` | `boolean` | Whether or not the input is blocked | +| `flagged` | `boolean` | Whether or not the input is flagged | +| `moderation_id` | `string` | The ID of the decision | + +#### Defined in + +[types.ts:109](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L109) + +___ + +### Prompt + +Ƭ **Prompt**: `Object` + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `content` | [`PromptContent`](modules.md#promptcontent) | The content of the prompt | +| `id` | `string` | The ID of the prompt | +| `role` | [`Role`](modules.md#role) | The role played in the prompt | + +#### Defined in + +[types.ts:156](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L156) + +___ + +### PromptContent + +Ƭ **PromptContent**: `Object` + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `content_type` | [`ContentType`](modules.md#contenttype) | The content type of the prompt | +| `parts` | `string`[] | The parts to the prompt | + +#### Defined in + +[types.ts:173](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L173) + +___ + +### Role + +Ƭ **Role**: ``"user"`` \| ``"assistant"`` + +#### Defined in + +[types.ts:3](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L3) + +___ + +### SessionResult + +Ƭ **SessionResult**: `Object` + +https://chat.openapi.com/api/auth/session + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `accessToken` | `string` | The access token | +| `expires` | `string` | ISO date of the expiration date of the access token | +| `user` | [`User`](modules.md#user) | Object of the current user | + +#### Defined in + +[types.ts:8](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L8) + +___ + +### User + +Ƭ **User**: `Object` + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `email` | `string` | Email of the user | +| `features` | `string`[] \| [] | Features the user is in | +| `groups` | `string`[] \| [] | Groups the user is in | +| `id` | `string` | ID of the user | +| `image` | `string` | Image of the user | +| `name` | `string` | Name of the user | +| `picture` | `string` | Picture of the user | + +#### Defined in + +[types.ts:25](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/types.ts#L25) + +## Functions + +### markdownToText + +▸ **markdownToText**(`markdown?`): `string` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `markdown?` | `string` | + +#### Returns + +`string` + +#### Defined in + +[utils.ts:4](https://github.com/transitive-bullshit/chatgpt-api/blob/549e9b4/src/utils.ts#L4) diff --git a/docs/readme.md b/docs/readme.md index 892400e66..17f7e9bbe 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -2,7 +2,7 @@ chatgpt / [Exports](modules.md) # ChatGPT API -> Node.js wrapper around [ChatGPT](https://openai.com/blog/chatgpt/). Uses headless Chrome until the official API is released. +> Node.js client for the unofficial [ChatGPT](https://openai.com/blog/chatgpt/) API. [![NPM](https://img.shields.io/npm/v/chatgpt.svg)](https://www.npmjs.com/package/chatgpt) [![Build Status](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml/badge.svg)](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml) [![MIT License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/transitive-bullshit/chatgpt-api/blob/main/license) [![Prettier Code Formatting](https://img.shields.io/badge/code_style-prettier-brightgreen.svg)](https://prettier.io) @@ -11,7 +11,8 @@ chatgpt / [Exports](modules.md) - [Install](#install) - [Usage](#usage) - [Docs](#docs) -- [Related](#related) +- [Examples](#examples) +- [Credit](#credit) - [License](#license) ## Intro @@ -22,12 +23,21 @@ You can use it to start building projects powered by ChatGPT like chatbots, webs ## How it works -We use headless Chromium via [Playwright](https://playwright.dev) to automate the webapp, so **you still need to have access to ChatGPT**. It just makes building API-like integrations much easier. +This package requires a valid session token from OpenAI's ChatGPT webapp to access it's unofficial REST API. -Chromium will be opened in non-headless mode by default, which is important because the first time you run `ChatGPTAPI.init()`, you'll need to log in manually. We launch Chromium with a persistent context, however, so you shouldn't need to keep re-logging in after the first time. When you log in the first time, _make sure that you also dismiss the welcome modal_. +1. Go to https://chat.openai.com/chat and log in or sign up +2. Open the dev tools console +3. Open `Application` > `Cookies` + ![ChatGPT cookies](./media/cookies.png) +4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment. + +If you want to run the built-in demo, + +> **Note** +> This package will switch to using the official API once it's released. > **Note** -> We'll replace headless chrome with the official API once it's released. +> Prior to v1.0.0, this package used headless Chromium via [Playwright](https://playwright.dev/) to automate the web UI. Here are the [docs for the initial browser version](https://github.com/transitive-bullshit/chatgpt-api/tree/v0.4.2). ## Install @@ -47,64 +57,49 @@ import { ChatGPTAPI } from 'chatgpt' async function example() { const api = new ChatGPTAPI() - // open chromium and wait until you've logged in - await api.init({ auth: 'blocking' }) + // ensure the API is properly authenticated (optional) + await api.ensureAuth() // send a message and wait for the response const response = await api.sendMessage( 'Write a python version of bubble sort. Do not include example usage.' ) + + // response is a markdown-formatted string console.log(response) } ``` -Which outputs a similar reponse to this (as a markdown string, including the _```python_ code block prefix): - -```python -def bubble_sort(lst): - # Set the initial flag to True to start the loop - swapped = True - - # Keep looping until there are no more swaps - while swapped: - # Set the flag to False initially - swapped = False - - # Loop through the list - for i in range(len(lst) - 1): - # If the current element is greater than the next element, - # swap them and set the flag to True - if lst[i] > lst[i + 1]: - lst[i], lst[i + 1] = lst[i + 1], lst[i] - swapped = True - - # Return the sorted list - return lst -``` - -By default, ChatGPT responses are parsed as markdown using [html-to-md](https://github.com/stonehank/html-to-md). I've found that this works really well during my testing, but if you'd rather output plaintext, you can use: - -```ts -const api = new ChatGPTAPI({ markdown: false }) -``` - A full [example](./src/example.ts) is included for testing purposes: ```bash -# clone repo -# install node deps -# then run +# 1. clone repo +# 2. install node deps +# 3. set `SESSION_TOKEN` in .env +# 4. run: npx tsx src/example.ts ``` ## Docs -See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on methods parameters. +See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on methods and parameters. + +## Examples + +All of these awesome projects use the `chatgpt` package. 🤯 + +- [Twitter Bot](https://github.com/transitive-bullshit/chatgpt-twitter-bot) powered by ChatGPT ✨ + - Mention [@ChatGPTBot](https://twitter.com/ChatGPTBot) on Twitter with your prompt to try it out +- [Chrome Extension](https://github.com/gragland/chatgpt-everywhere) ([demo](https://twitter.com/gabe_ragland/status/1599466486422470656)) +- [VSCode Extension](https://github.com/mpociot/chatgpt-vscode) ([demo](https://twitter.com/marcelpociot/status/1599180144551526400)) +- [Go Telegram Bot](https://github.com/m1guelpf/chatgpt-telegram) +- [Lovelines.xyz](https://lovelines.xyz) + +If you create a cool integration, feel free to open a PR and add it to the list. -## Related +## Credit - Inspired by this [Go module](https://github.com/danielgross/whatsapp-gpt) by [Daniel Gross](https://github.com/danielgross) -- [Python port](https://github.com/taranjeet/chatgpt-api) ## License diff --git a/media/cookies.png b/media/cookies.png new file mode 100644 index 000000000..d7f52e299 Binary files /dev/null and b/media/cookies.png differ diff --git a/package.json b/package.json index 6d5ed9c82..fe67b9a78 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "chatgpt", "version": "0.4.2", - "description": "Node.js wrapper around ChatGPT. Uses headless Chrome until the official API is released.", + "description": "Node.js client for the unofficial ChatGPT API.", "author": "Travis Fischer ", "repository": "transitive-bullshit/chatgpt-api", "license": "MIT", @@ -34,18 +34,22 @@ "test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check" }, "dependencies": { - "html-to-md": "npm:@fisch0920/html-to-md@^0.8.1", - "playwright": "^1.28.1" + "eventsource-parser": "^0.0.5", + "expiry-map": "^2.0.0", + "node-fetch": "^3.3.0", + "remark": "^14.0.2", + "strip-markdown": "^5.0.0", + "uuid": "^9.0.0" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.0.0", "@types/node": "^18.11.9", + "@types/uuid": "^9.0.0", "del-cli": "^5.0.0", - "delay": "^5.0.0", + "dotenv-safe": "^8.2.0", "husky": "^8.0.2", "lint-staged": "^13.0.3", "npm-run-all": "^4.1.5", - "ora": "^6.1.2", "prettier": "^2.8.0", "tsup": "^6.5.0", "tsx": "^3.12.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 523c80e33..bca9f84c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,34 +3,42 @@ lockfileVersion: 5.4 specifiers: '@trivago/prettier-plugin-sort-imports': ^4.0.0 '@types/node': ^18.11.9 + '@types/uuid': ^9.0.0 del-cli: ^5.0.0 - delay: ^5.0.0 - html-to-md: npm:@fisch0920/html-to-md@^0.8.1 + dotenv-safe: ^8.2.0 + eventsource-parser: ^0.0.5 + expiry-map: ^2.0.0 husky: ^8.0.2 lint-staged: ^13.0.3 + node-fetch: ^3.3.0 npm-run-all: ^4.1.5 - ora: ^6.1.2 - playwright: ^1.28.1 prettier: ^2.8.0 + remark: ^14.0.2 + strip-markdown: ^5.0.0 tsup: ^6.5.0 tsx: ^3.12.1 typedoc: ^0.23.21 typedoc-plugin-markdown: ^3.13.6 typescript: ^4.9.3 + uuid: ^9.0.0 dependencies: - html-to-md: /@fisch0920/html-to-md/0.8.1 - playwright: 1.28.1 + eventsource-parser: 0.0.5 + expiry-map: 2.0.0 + node-fetch: 3.3.0 + remark: 14.0.2 + strip-markdown: 5.0.0 + uuid: 9.0.0 devDependencies: '@trivago/prettier-plugin-sort-imports': 4.0.0_prettier@2.8.0 '@types/node': 18.11.10 + '@types/uuid': 9.0.0 del-cli: 5.0.0 - delay: 5.0.0 + dotenv-safe: 8.2.0 husky: 8.0.2 lint-staged: 13.0.4 npm-run-all: 4.1.5 - ora: 6.1.2 prettier: 2.8.0 tsup: 6.5.0_typescript@4.9.3 tsx: 3.12.1 @@ -323,10 +331,6 @@ packages: dev: true optional: true - /@fisch0920/html-to-md/0.8.1: - resolution: {integrity: sha512-CEOoGuhwjNKLl1OQN4dAxLLK3zAYyQG8JDDbCOTRUc+9dDllvv1REfXjN1D0wQf+ZYEQr+EmfIvbKjkifX7x9w==} - dev: false - /@jridgewell/gen-mapping/0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} @@ -404,10 +408,26 @@ packages: - supports-color dev: true + /@types/debug/4.1.7: + resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + dependencies: + '@types/ms': 0.7.31 + dev: false + + /@types/mdast/3.0.10: + resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} + dependencies: + '@types/unist': 2.0.6 + dev: false + /@types/minimist/1.2.2: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true + /@types/ms/0.7.31: + resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + dev: false + /@types/node/18.11.10: resolution: {integrity: sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==} dev: true @@ -416,6 +436,14 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/unist/2.0.6: + resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} + dev: false + + /@types/uuid/9.0.0: + resolution: {integrity: sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==} + dev: true + /aggregate-error/3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -495,27 +523,19 @@ packages: engines: {node: '>=8'} dev: true + /bail/2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + dev: false + /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /base64-js/1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true - /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} dev: true - /bl/5.1.0: - resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.0 - dev: true - /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -551,13 +571,6 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true - /buffer/6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: true - /bundle-require/3.1.2_esbuild@0.15.16: resolution: {integrity: sha512-Of6l6JBAxiyQ5axFxUM6dYeP/W7X2Sozeo/4EYB9sJhL+dqL7TKjg+shwxp6jlu/6ZSERfsYtIpSJ1/x3XkAEA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -608,10 +621,9 @@ packages: supports-color: 5.5.0 dev: true - /chalk/5.1.2: - resolution: {integrity: sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: true + /character-entities/2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + dev: false /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} @@ -647,18 +659,6 @@ packages: restore-cursor: 3.1.0 dev: true - /cli-cursor/4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - restore-cursor: 4.0.0 - dev: true - - /cli-spinners/2.7.0: - resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} - engines: {node: '>=6'} - dev: true - /cli-truncate/2.1.0: resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} engines: {node: '>=8'} @@ -675,11 +675,6 @@ packages: string-width: 5.1.2 dev: true - /clone/1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - dev: true - /color-convert/1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -743,6 +738,11 @@ packages: which: 2.0.2 dev: true + /data-uri-to-buffer/4.0.0: + resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==} + engines: {node: '>= 12'} + dev: false + /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -753,7 +753,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /decamelize-keys/1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -773,11 +772,11 @@ packages: engines: {node: '>=10'} dev: true - /defaults/1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + /decode-named-character-reference/1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} dependencies: - clone: 1.0.4 - dev: true + character-entities: 2.0.2 + dev: false /define-properties/1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} @@ -810,10 +809,15 @@ packages: slash: 4.0.0 dev: true - /delay/5.0.0: - resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} - engines: {node: '>=10'} - dev: true + /dequal/2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + + /diff/5.1.0: + resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + engines: {node: '>=0.3.1'} + dev: false /dir-glob/3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -822,6 +826,17 @@ packages: path-type: 4.0.0 dev: true + /dotenv-safe/8.2.0: + resolution: {integrity: sha512-uWwWWdUQkSs5a3mySDB22UtNwyEYi0JtEQu+vDzIqr9OjbDdC2Ip13PnSpi/fctqlYmzkxCeabiyCAOROuAIaA==} + dependencies: + dotenv: 8.6.0 + dev: true + + /dotenv/8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + dev: true + /eastasianwidth/0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true @@ -1108,6 +1123,11 @@ packages: engines: {node: '>=12'} dev: true + /eventsource-parser/0.0.5: + resolution: {integrity: sha512-BAq82bC3ZW9fPYYZlofXBOAfbpmDzXIOsj+GOehQwgTUYsQZ6HtHs6zuRtge7Ph8OhS6lNH1kJF8q9dj17RcmA==} + engines: {node: '>=12'} + dev: false + /execa/5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1138,6 +1158,17 @@ packages: strip-final-newline: 3.0.0 dev: true + /expiry-map/2.0.0: + resolution: {integrity: sha512-K1I5wJe2fiqjyUZf/xhxwTpaopw3F+19DsO7Oggl20+3SVTXDIevVRJav0aBMfposQdkl2E4+gnuOKd3j2X0sA==} + engines: {node: '>=8'} + dependencies: + map-age-cleaner: 0.2.0 + dev: false + + /extend/3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: false + /fast-glob/3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -1155,6 +1186,14 @@ packages: reusify: 1.0.4 dev: true + /fetch-blob/3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.2.1 + dev: false + /fill-range/7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -1170,6 +1209,13 @@ packages: path-exists: 4.0.0 dev: true + /formdata-polyfill/4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + dependencies: + fetch-blob: 3.2.0 + dev: false + /fs.realpath/1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -1370,10 +1416,6 @@ packages: hasBin: true dev: true - /ieee754/1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true - /ignore/5.2.1: resolution: {integrity: sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==} engines: {node: '>= 4'} @@ -1434,6 +1476,11 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-buffer/2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + dev: false + /is-callable/1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -1474,11 +1521,6 @@ packages: is-extglob: 2.1.1 dev: true - /is-interactive/2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - dev: true - /is-negative-zero/2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -1511,6 +1553,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-plain-obj/4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + dev: false + /is-regex/1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -1549,11 +1596,6 @@ packages: has-symbols: 1.0.3 dev: true - /is-unicode-supported/1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - dev: true - /is-weakref/1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: @@ -1606,6 +1648,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /kleur/4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: false + /lilconfig/2.0.6: resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} engines: {node: '>=10'} @@ -1687,14 +1734,6 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /log-symbols/5.1.0: - resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} - engines: {node: '>=12'} - dependencies: - chalk: 5.1.2 - is-unicode-supported: 1.3.0 - dev: true - /log-update/4.0.0: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} @@ -1705,6 +1744,10 @@ packages: wrap-ansi: 6.2.0 dev: true + /longest-streak/3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + dev: false + /lru-cache/6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1716,6 +1759,13 @@ packages: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} dev: true + /map-age-cleaner/0.2.0: + resolution: {integrity: sha512-AvxTC6id0fzSf6OyNBTp1syyCuKO7nOJvHgYlhT0Qkkjvk40zZo+av3ayVgXlxnF/DxEzEfY9mMdd7FHsd+wKQ==} + engines: {node: '>=7.6'} + dependencies: + p-defer: 1.0.0 + dev: false + /map-obj/1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -1732,6 +1782,41 @@ packages: hasBin: true dev: true + /mdast-util-from-markdown/1.2.0: + resolution: {integrity: sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==} + dependencies: + '@types/mdast': 3.0.10 + '@types/unist': 2.0.6 + decode-named-character-reference: 1.0.2 + mdast-util-to-string: 3.1.0 + micromark: 3.1.0 + micromark-util-decode-numeric-character-reference: 1.0.0 + micromark-util-decode-string: 1.0.2 + micromark-util-normalize-identifier: 1.0.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + unist-util-stringify-position: 3.0.2 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-to-markdown/1.3.0: + resolution: {integrity: sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==} + dependencies: + '@types/mdast': 3.0.10 + '@types/unist': 2.0.6 + longest-streak: 3.1.0 + mdast-util-to-string: 3.1.0 + micromark-util-decode-string: 1.0.2 + unist-util-visit: 4.1.1 + zwitch: 2.0.4 + dev: false + + /mdast-util-to-string/3.1.0: + resolution: {integrity: sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==} + dev: false + /memorystream/0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} @@ -1764,6 +1849,182 @@ packages: engines: {node: '>= 8'} dev: true + /micromark-core-commonmark/1.0.6: + resolution: {integrity: sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-factory-destination: 1.0.0 + micromark-factory-label: 1.0.2 + micromark-factory-space: 1.0.0 + micromark-factory-title: 1.0.2 + micromark-factory-whitespace: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-chunked: 1.0.0 + micromark-util-classify-character: 1.0.0 + micromark-util-html-tag-name: 1.1.0 + micromark-util-normalize-identifier: 1.0.0 + micromark-util-resolve-all: 1.0.0 + micromark-util-subtokenize: 1.0.2 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-factory-destination/1.0.0: + resolution: {integrity: sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-factory-label/1.0.2: + resolution: {integrity: sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-factory-space/1.0.0: + resolution: {integrity: sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-types: 1.0.2 + dev: false + + /micromark-factory-title/1.0.2: + resolution: {integrity: sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==} + dependencies: + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-factory-whitespace/1.0.0: + resolution: {integrity: sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==} + dependencies: + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-character/1.1.0: + resolution: {integrity: sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==} + dependencies: + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-chunked/1.0.0: + resolution: {integrity: sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==} + dependencies: + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-classify-character/1.0.0: + resolution: {integrity: sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-combine-extensions/1.0.0: + resolution: {integrity: sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==} + dependencies: + micromark-util-chunked: 1.0.0 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-decode-numeric-character-reference/1.0.0: + resolution: {integrity: sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==} + dependencies: + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-decode-string/1.0.2: + resolution: {integrity: sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 1.1.0 + micromark-util-decode-numeric-character-reference: 1.0.0 + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-encode/1.0.1: + resolution: {integrity: sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==} + dev: false + + /micromark-util-html-tag-name/1.1.0: + resolution: {integrity: sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==} + dev: false + + /micromark-util-normalize-identifier/1.0.0: + resolution: {integrity: sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==} + dependencies: + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-resolve-all/1.0.0: + resolution: {integrity: sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==} + dependencies: + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-sanitize-uri/1.1.0: + resolution: {integrity: sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-encode: 1.0.1 + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-subtokenize/1.0.2: + resolution: {integrity: sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==} + dependencies: + micromark-util-chunked: 1.0.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-util-symbol/1.0.1: + resolution: {integrity: sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==} + dev: false + + /micromark-util-types/1.0.2: + resolution: {integrity: sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==} + dev: false + + /micromark/3.1.0: + resolution: {integrity: sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==} + dependencies: + '@types/debug': 4.1.7 + debug: 4.3.4 + decode-named-character-reference: 1.0.2 + micromark-core-commonmark: 1.0.6 + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-chunked: 1.0.0 + micromark-util-combine-extensions: 1.0.0 + micromark-util-decode-numeric-character-reference: 1.0.0 + micromark-util-encode: 1.0.1 + micromark-util-normalize-identifier: 1.0.0 + micromark-util-resolve-all: 1.0.0 + micromark-util-sanitize-uri: 1.1.0 + micromark-util-subtokenize: 1.0.2 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + dev: false + /micromatch/4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -1813,9 +2074,13 @@ packages: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} dev: true + /mri/1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: false + /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /mz/2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -1833,6 +2098,20 @@ packages: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} dev: true + /node-domexception/1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + dev: false + + /node-fetch/3.3.0: + resolution: {integrity: sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + data-uri-to-buffer: 4.0.0 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + dev: false + /node-releases/2.0.6: resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} dev: true @@ -1935,20 +2214,10 @@ packages: mimic-fn: 4.0.0 dev: true - /ora/6.1.2: - resolution: {integrity: sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - bl: 5.1.0 - chalk: 5.1.2 - cli-cursor: 4.0.0 - cli-spinners: 2.7.0 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - strip-ansi: 7.0.1 - wcwidth: 1.0.1 - dev: true + /p-defer/1.0.0: + resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} + engines: {node: '>=4'} + dev: false /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} @@ -2068,21 +2337,6 @@ packages: engines: {node: '>= 6'} dev: true - /playwright-core/1.28.1: - resolution: {integrity: sha512-3PixLnGPno0E8rSBJjtwqTwJe3Yw72QwBBBxNoukIj3lEeBNXwbNiKrNuB1oyQgTBw5QHUhNO3SteEtHaMK6ag==} - engines: {node: '>=14'} - hasBin: true - dev: false - - /playwright/1.28.1: - resolution: {integrity: sha512-92Sz6XBlfHlb9tK5UCDzIFAuIkHHpemA9zwUaqvo+w7sFMSmVMGmvKcbptof/eJObq63PGnMhM75x7qxhTR78Q==} - engines: {node: '>=14'} - hasBin: true - requiresBuild: true - dependencies: - playwright-core: 1.28.1 - dev: false - /postcss-load-config/3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} @@ -2147,15 +2401,6 @@ packages: type-fest: 1.4.0 dev: true - /readable-stream/3.6.0: - resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - dev: true - /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2180,6 +2425,35 @@ packages: functions-have-names: 1.2.3 dev: true + /remark-parse/10.0.1: + resolution: {integrity: sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==} + dependencies: + '@types/mdast': 3.0.10 + mdast-util-from-markdown: 1.2.0 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-stringify/10.0.2: + resolution: {integrity: sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==} + dependencies: + '@types/mdast': 3.0.10 + mdast-util-to-markdown: 1.3.0 + unified: 10.1.2 + dev: false + + /remark/14.0.2: + resolution: {integrity: sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==} + dependencies: + '@types/mdast': 3.0.10 + remark-parse: 10.0.1 + remark-stringify: 10.0.2 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /resolve-from/5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -2202,14 +2476,6 @@ packages: signal-exit: 3.0.7 dev: true - /restore-cursor/4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - dev: true - /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -2246,9 +2512,12 @@ packages: tslib: 2.4.1 dev: true - /safe-buffer/5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true + /sade/1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: false /safe-regex-test/1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -2454,12 +2723,6 @@ packages: es-abstract: 1.20.4 dev: true - /string_decoder/1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - dev: true - /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2496,6 +2759,14 @@ packages: min-indent: 1.0.1 dev: true + /strip-markdown/5.0.0: + resolution: {integrity: sha512-PXSts6Ta9A/TwGxVVSRlQs1ukJTAwwtbip2OheJEjPyfykaQ4sJSTnQWjLTI2vYWNts/R/91/csagp15W8n9gA==} + dependencies: + '@types/mdast': 3.0.10 + '@types/unist': 2.0.6 + unified: 10.1.2 + dev: false + /sucrase/3.29.0: resolution: {integrity: sha512-bZPAuGA5SdFHuzqIhTAqt9fvNEo9rESqXIG3oiKdF8K4UmkQxC4KlNL3lVyAErXp+mPvUqZ5l13qx6TrDIGf3A==} engines: {node: '>=8'} @@ -2566,6 +2837,10 @@ packages: engines: {node: '>=12'} dev: true + /trough/2.1.0: + resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} + dev: false + /ts-interface-checker/0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true @@ -2677,6 +2952,43 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unified/10.1.2: + resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + dependencies: + '@types/unist': 2.0.6 + bail: 2.0.2 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 4.1.0 + trough: 2.1.0 + vfile: 5.3.6 + dev: false + + /unist-util-is/5.1.1: + resolution: {integrity: sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==} + dev: false + + /unist-util-stringify-position/3.0.2: + resolution: {integrity: sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==} + dependencies: + '@types/unist': 2.0.6 + dev: false + + /unist-util-visit-parents/5.1.1: + resolution: {integrity: sha512-gks4baapT/kNRaWxuGkl5BIhoanZo7sC/cUT/JToSRNL1dYoXRFl75d++NkjYk4TAu2uv2Px+l8guMajogeuiw==} + dependencies: + '@types/unist': 2.0.6 + unist-util-is: 5.1.1 + dev: false + + /unist-util-visit/4.1.1: + resolution: {integrity: sha512-n9KN3WV9k4h1DxYR1LoajgN93wpEi/7ZplVe02IoB4gH5ctI1AaF2670BLHQYbwj+pY83gFtyeySFiyMHJklrg==} + dependencies: + '@types/unist': 2.0.6 + unist-util-is: 5.1.1 + unist-util-visit-parents: 5.1.1 + dev: false + /update-browserslist-db/1.0.10_browserslist@4.21.4: resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true @@ -2688,9 +3000,21 @@ packages: picocolors: 1.0.0 dev: true - /util-deprecate/1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true + /uuid/9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + + /uvu/0.5.6: + resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + dequal: 2.0.3 + diff: 5.1.0 + kleur: 4.1.5 + sade: 1.8.1 + dev: false /validate-npm-package-license/3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -2699,6 +3023,22 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /vfile-message/3.1.3: + resolution: {integrity: sha512-0yaU+rj2gKAyEk12ffdSbBfjnnj+b1zqTBv3OQCTn8yEB02bsPizwdBPrLJjHnK+cU9EMMcUnNv938XcZIkmdA==} + dependencies: + '@types/unist': 2.0.6 + unist-util-stringify-position: 3.0.2 + dev: false + + /vfile/5.3.6: + resolution: {integrity: sha512-ADBsmerdGBs2WYckrLBEmuETSPyTD4TuLxTrw0DvjirxW1ra4ZwkbzG8ndsv3Q57smvHxo677MHaQrY9yxH8cA==} + dependencies: + '@types/unist': 2.0.6 + is-buffer: 2.0.5 + unist-util-stringify-position: 3.0.2 + vfile-message: 3.1.3 + dev: false + /vscode-oniguruma/1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} dev: true @@ -2707,11 +3047,10 @@ packages: resolution: {integrity: sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==} dev: true - /wcwidth/1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - dependencies: - defaults: 1.0.4 - dev: true + /web-streams-polyfill/3.2.1: + resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} + engines: {node: '>= 8'} + dev: false /webidl-conversions/4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -2799,3 +3138,7 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + + /zwitch/2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + dev: false diff --git a/readme.md b/readme.md index 4c998fd1b..37df62475 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # ChatGPT API -> Node.js wrapper around [ChatGPT](https://openai.com/blog/chatgpt/). Uses headless Chrome until the official API is released. +> Node.js client for the unofficial [ChatGPT](https://openai.com/blog/chatgpt/) API. [![NPM](https://img.shields.io/npm/v/chatgpt.svg)](https://www.npmjs.com/package/chatgpt) [![Build Status](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml/badge.svg)](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml) [![MIT License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/transitive-bullshit/chatgpt-api/blob/main/license) [![Prettier Code Formatting](https://img.shields.io/badge/code_style-prettier-brightgreen.svg)](https://prettier.io) @@ -10,7 +10,7 @@ - [Usage](#usage) - [Docs](#docs) - [Examples](#examples) -- [Related](#related) +- [Credit](#credit) - [License](#license) ## Intro @@ -21,17 +21,21 @@ You can use it to start building projects powered by ChatGPT like chatbots, webs ## How it works -It uses headless Chromium via [Playwright](https://playwright.dev) to automate the webapp, so **you still need to have access to ChatGPT**. It just makes building API-like integrations much easier. +This package requires a valid session token from OpenAI's ChatGPT webapp to access it's unofficial REST API. -Chromium will be opened in non-headless mode by default, which is important because the first time you run `ChatGPTAPI.init()`, you'll need to log in manually. We launch Chromium with a persistent session, however, so you shouldn't need to keep re-logging in after the first time. +1. Go to https://chat.openai.com/chat and log in or sign up +2. Open the dev tools console +3. Open `Application` > `Cookies` + ![ChatGPT cookies](./media/cookies.png) +4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment. -When you log in the first time, we recommend dismissing the welcome modal so you can watch the progress. This isn't strictly necessary, but it helps to understand what's going on. +If you want to run the built-in demo, -- [Demo video](https://www.loom.com/share/0c44525b07354d679f30c45d8eec6271) showing how the initial auth flow works (29 seconds) -- [Demo video](https://www.loom.com/share/98e712dbddf843289e2b6615095bbdd7) showing how it works if you're already authed (13 seconds) +> **Note** +> This package will switch to using the official API once it's released. > **Note** -> We'll replace headless chrome with the official API once it's released. +> Prior to v1.0.0, this package used headless Chromium via [Playwright](https://playwright.dev/) to automate the web UI. Here are the [docs for the initial browser version](https://github.com/transitive-bullshit/chatgpt-api/tree/v0.4.2). ## Install @@ -51,8 +55,8 @@ import { ChatGPTAPI } from 'chatgpt' async function example() { const api = new ChatGPTAPI() - // open chromium and wait until you've logged in - await api.init({ auth: 'blocking' }) + // ensure the API is properly authenticated (optional) + await api.ensureAuth() // send a message and wait for the response const response = await api.sendMessage( @@ -64,18 +68,13 @@ async function example() { } ``` -By default, ChatGPT responses are parsed as markdown using [html-to-md](https://github.com/stonehank/html-to-md). I've found that this works really well during my testing, but if you'd rather output plaintext, you can use: - -```ts -const api = new ChatGPTAPI({ markdown: false }) -``` - A full [example](./src/example.ts) is included for testing purposes: ```bash -# clone repo -# install node deps -# then run +# 1. clone repo +# 2. install node deps +# 3. set `SESSION_TOKEN` in .env +# 4. run: npx tsx src/example.ts ``` @@ -97,10 +96,9 @@ All of these awesome projects use the `chatgpt` package. 🤯 If you create a cool integration, feel free to open a PR and add it to the list. -## Related +## Credit - Inspired by this [Go module](https://github.com/danielgross/whatsapp-gpt) by [Daniel Gross](https://github.com/danielgross) -- [Python port](https://github.com/taranjeet/chatgpt-api) ## License diff --git a/src/chatgpt-api.ts b/src/chatgpt-api.ts index bbaea6c20..54f60e9ff 100644 --- a/src/chatgpt-api.ts +++ b/src/chatgpt-api.ts @@ -1,208 +1,210 @@ -import delay from 'delay' -import html2md from 'html-to-md' -import { type ChromiumBrowserContext, type Page, chromium } from 'playwright' +import { createParser } from 'eventsource-parser' +import ExpiryMap from 'expiry-map' +import fetch from 'node-fetch' +import { v4 as uuidv4 } from 'uuid' + +import * as types from './types' +import { markdownToText } from './utils' + +const KEY_ACCESS_TOKEN = 'accessToken' +const USER_AGENT = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' export class ChatGPTAPI { - protected _userDataDir: string - protected _headless: boolean + protected _sessionToken: string protected _markdown: boolean - protected _chatUrl: string - - protected _browser: ChromiumBrowserContext - protected _page: Page + protected _apiBaseUrl: string + protected _backendApiBaseUrl: string + protected _userAgent: string + protected _accessTokenCache = new ExpiryMap(10 * 1000) /** - * @param opts.userDataDir — Path to a directory for storing persistent chromium session data - * @param opts.chatUrl — OpenAI chat URL - * @param opts.headless - Whether or not to use headless mode - * @param opts.markdown — Whether or not to parse chat messages as markdown + * Creates a new client wrapper around the unofficial ChatGPT REST API. + * + * @param opts.sessionToken = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions) + * @param apiBaseUrl - Optional override; the base URL for ChatGPT webapp's API (`/api`) + * @param backendApiBaseUrl - Optional override; the base URL for the ChatGPT backend API (`/backend-api`) + * @param userAgent - Optional override; the `user-agent` header to use with ChatGPT requests */ - constructor( - opts: { - /** @defaultValue `'/tmp/chatgpt'` **/ - userDataDir?: string + constructor(opts: { + sessionToken: string - /** @defaultValue `'https://chat.openai.com/'` **/ - chatUrl?: string + /** @defaultValue `true` **/ + markdown?: boolean - /** @defaultValue `false` **/ - headless?: boolean + /** @defaultValue `'https://chat.openai.com/api'` **/ + apiBaseUrl?: string - /** @defaultValue `true` **/ - markdown?: boolean - } = {} - ) { + /** @defaultValue `'https://chat.openai.com/backend-api'` **/ + backendApiBaseUrl?: string + + /** @defaultValue `'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'` **/ + userAgent?: string + }) { const { - userDataDir = '/tmp/chatgpt', - chatUrl = 'https://chat.openai.com/', - headless = false, - markdown = true + sessionToken, + markdown = true, + apiBaseUrl = 'https://chat.openai.com/api', + backendApiBaseUrl = 'https://chat.openai.com/backend-api', + userAgent = USER_AGENT } = opts - this._userDataDir = userDataDir - this._headless = !!headless - this._chatUrl = chatUrl + this._sessionToken = sessionToken this._markdown = !!markdown - } - - async init(opts: { auth?: 'blocking' | 'eager' } = {}) { - const { auth = 'eager' } = opts - - if (this._browser) { - await this.close() - } - - this._browser = await chromium.launchPersistentContext(this._userDataDir, { - headless: this._headless - }) - - this._page = await this._browser.newPage() - await this._page.goto(this._chatUrl) - - // dismiss welcome modal - do { - const modalSelector = '[data-headlessui-state="open"]' - - if (!(await this._page.$(modalSelector))) { - break - } - - try { - await this._page.click(`${modalSelector} button:last-child`, { - timeout: 1000 - }) - } catch (err) { - // "next" button not found in welcome modal - break - } - } while (true) - - if (auth === 'blocking') { - do { - const isSignedIn = await this.getIsSignedIn() - if (isSignedIn) { - break - } + this._apiBaseUrl = apiBaseUrl + this._backendApiBaseUrl = backendApiBaseUrl + this._userAgent = userAgent - console.log( - 'Please sign in to ChatGPT using the Chromium browser window and dismiss the welcome modal...' - ) - - await delay(1000) - } while (true) + if (!this._sessionToken) { + throw new Error('ChatGPT invalid session token') } - - return this._page } - async getIsSignedIn() { + async getIsAuthenticated() { try { - const inputBox = await this._getInputBox() - return !!inputBox + void (await this.refreshAccessToken()) + return true } catch (err) { - // can happen when navigating during login return false } } - async getLastMessage(): Promise { - const messages = await this.getMessages() - - if (messages) { - return messages[messages.length - 1] - } else { - return null - } + async ensureAuth() { + return await this.refreshAccessToken() } - async getPrompts(): Promise { - // Get all prompts - const messages = await this._page.$$( - '[class*="ConversationItem__Message"]:has([class*="ConversationItem__ActionButtons"]):has([class*="ConversationItem__Role"] [class*="Avatar__Wrapper"])' - ) + /** + * Sends a message to ChatGPT, waits for the response to resolve, and returns + * the response. + * + * @param message - The plaintext message to send. + * @param opts.conversationId - Optional ID of the previous message in a conversation + * @param opts.onProgress - Optional listener which will be called every time the partial response is updated + */ + async sendMessage( + message: string, + opts: { + converstationId?: string + onProgress?: (partialResponse: string) => void + } = {} + ): Promise { + const { converstationId = uuidv4(), onProgress } = opts + + const accessToken = await this.refreshAccessToken() + + const body: types.ConversationJSONBody = { + action: 'next', + messages: [ + { + id: uuidv4(), + role: 'user', + content: { + content_type: 'text', + parts: [message] + } + } + ], + model: 'text-davinci-002-render', + parent_message_id: converstationId + } - // prompts are always plaintext - return Promise.all(messages.map((a) => a.innerText())) + const url = `${this._backendApiBaseUrl}/conversation` + + // TODO: What's the best way to differentiate btwn wanting just the response text + // versus wanting the full response message, so you can extract the ID and other + // metadata? + // let fullResponse: types.Message = null + let response = '' + + return new Promise((resolve, reject) => { + this._fetchSSE(url, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + 'user-agent': this._userAgent + }, + body: JSON.stringify(body), + onMessage: (data: string) => { + if (data === '[DONE]') { + return resolve(response) + } + + try { + const parsedData: types.ConversationResponseEvent = JSON.parse(data) + const message = parsedData.message + console.log('event', JSON.stringify(parsedData, null, 2)) + + if (message) { + let text = message?.content?.parts?.[0] + + if (text) { + if (!this._markdown) { + text = markdownToText(text) + } + + response = text + + if (onProgress) { + onProgress(text) + } + // fullResponse = message + } + } + } catch (err) { + console.warn('fetchSSE onMessage unexpected error', err) + reject(err) + } + } + }).catch(reject) + }) } - async getMessages(): Promise { - // Get all complete messages - // (in-progress messages that are being streamed back don't contain action buttons) - const messages = await this._page.$$( - '[class*="ConversationItem__Message"]:has([class*="ConversationItem__ActionButtons"]):not(:has([class*="ConversationItem__Role"] [class*="Avatar__Wrapper"]))' - ) - - if (this._markdown) { - const htmlMessages = await Promise.all(messages.map((a) => a.innerHTML())) - - const markdownMessages = htmlMessages.map((messageHtml) => { - // parse markdown from message HTML - messageHtml = messageHtml.replace('Copy code', '') - return html2md(messageHtml, { - ignoreTags: [ - 'button', - 'svg', - 'style', - 'form', - 'noscript', - 'script', - 'meta', - 'head' - ], - skipTags: ['button', 'svg'] - }) - }) - - return markdownMessages - } else { - // plaintext - const plaintextMessages = await Promise.all( - messages.map((a) => a.innerText()) - ) - return plaintextMessages + async refreshAccessToken(): Promise { + const cachedAccessToken = this._accessTokenCache.get(KEY_ACCESS_TOKEN) + if (cachedAccessToken) { + return cachedAccessToken } - } - - async sendMessage(message: string): Promise { - const inputBox = await this._getInputBox() - if (!inputBox) throw new Error('not signed in') - - const lastMessage = await this.getLastMessage() - await inputBox.click({ force: true }) - await inputBox.fill(message, { force: true }) - await inputBox.press('Enter') + try { + const res = await fetch('https://chat.openai.com/api/auth/session', { + headers: { + cookie: `__Secure-next-auth.session-token=${this._sessionToken}`, + 'user-agent': this._userAgent + } + }).then((r) => r.json() as any as types.SessionResult) - do { - await delay(1000) + const accessToken = res?.accessToken - // TODO: this logic needs some work because we can have repeat messages... - const newLastMessage = await this.getLastMessage() - if ( - newLastMessage && - lastMessage?.toLowerCase() !== newLastMessage?.toLowerCase() - ) { - return newLastMessage + if (!accessToken) { + console.warn('no auth token', res) + throw new Error('Unauthorized') } - } while (true) - } - async resetThread() { - const resetButton = await this._page.$('nav > a:nth-child(1)') - if (!resetButton) throw new Error('not signed in') - - await resetButton.click() + this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken) + return accessToken + } catch (err: any) { + throw new Error(`ChatGPT failed to refresh auth token: ${err.toString()}`) + } } - async close() { - await this._browser.close() - this._page = null - this._browser = null - } + protected async _fetchSSE( + url: string, + options: Parameters[1] & { onMessage: (data: string) => void } + ) { + const { onMessage, ...fetchOptions } = options + const resp = await fetch(url, fetchOptions) + const parser = createParser((event) => { + if (event.type === 'event') { + onMessage(event.data) + } + }) - protected async _getInputBox(): Promise { - return this._page.$( - 'div[class*="PromptTextarea__TextareaWrapper"] textarea' - ) + resp.body.on('readable', () => { + let chunk: string | Buffer + while (null !== (chunk = resp.body.read())) { + parser.feed(chunk.toString()) + } + }) } } diff --git a/src/example.ts b/src/example.ts index 49818741d..27f973cc3 100644 --- a/src/example.ts +++ b/src/example.ts @@ -1,57 +1,28 @@ -import delay from 'delay' -import { oraPromise } from 'ora' +import dotenv from 'dotenv-safe' import { ChatGPTAPI } from './chatgpt-api' +dotenv.config() + /** * Example CLI for testing functionality. */ async function main() { - const api = new ChatGPTAPI() - await api.init() - - const isSignedIn = await api.getIsSignedIn() - - if (!isSignedIn) { - // Wait until the user signs in via the chromium browser - await oraPromise( - new Promise(async (resolve, reject) => { - do { - try { - await delay(1000) - - const isSignedIn = await api.getIsSignedIn() - - if (isSignedIn) { - return resolve() - } - } catch (err) { - return reject(err) - } - } while (true) - }), - 'Please sign in to ChatGPT and dismiss the welcome modal' - ) - } + const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN }) + await api.ensureAuth() const response = await api.sendMessage( - // 'Write a TypeScript function for conway sort.' 'Write a python version of bubble sort. Do not include example usage.' ) - // const prompts = await api.getPrompts() - // const messages = await api.getMessages() - // console.log('prompts', prompts) - // console.log('messages', messages) - - // Wait forever; useful for debugging chromium sessions - // await new Promise(() => {}) - - await api.close() - return response } -main().then((res) => { - console.log(res) -}) +main() + .then((res) => { + console.log(res) + }) + .catch((err) => { + console.error(err) + process.exit(1) + }) diff --git a/src/index.ts b/src/index.ts index b043d0eff..451712eaa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export * from './chatgpt-api' -export * from './chatgpt-api-types' +export * from './types' +export * from './utils' diff --git a/src/chatgpt-api-types.ts b/src/types.ts similarity index 75% rename from src/chatgpt-api-types.ts rename to src/types.ts index ffac5fbd4..7a392efea 100644 --- a/src/chatgpt-api-types.ts +++ b/src/types.ts @@ -1,3 +1,7 @@ +export type ContentType = 'text' + +export type Role = 'user' | 'assistant' + /** * https://chat.openapi.com/api/auth/session */ @@ -5,7 +9,7 @@ export type SessionResult = { /** * Object of the current user */ - user: APIUser + user: User /** * ISO date of the expiration date of the access token @@ -18,7 +22,7 @@ export type SessionResult = { accessToken: string } -export type APIUser = { +export type User = { /** * ID of the user */ @@ -62,10 +66,10 @@ export type ModelsResult = { /** * Array of models */ - models: APIModel[] + models: Model[] } -export type APIModel = { +export type Model = { /** * Name of the model */ @@ -136,7 +140,7 @@ export type ConversationJSONBody = { /** * Prompts to provide */ - messages: APIPrompt[] + messages: Prompt[] /** * The model to use @@ -149,11 +153,11 @@ export type ConversationJSONBody = { parent_message_id: string } -export type APIPrompt = { +export type Prompt = { /** * The content of the prompt */ - content: APIPromptContent + content: PromptContent /** * The ID of the prompt @@ -163,14 +167,14 @@ export type APIPrompt = { /** * The role played in the prompt */ - role: APIPromptRole + role: Role } -export type APIPromptContent = { +export type PromptContent = { /** * The content type of the prompt */ - content_type: APIPromptContentType + content_type: ContentType /** * The parts to the prompt @@ -178,10 +182,6 @@ export type APIPromptContent = { parts: string[] } -export type APIPromptContentType = 'text' - -export type APIPromptRole = 'user' - /** * https://chat.openapi.com/backend-api/conversation/message_feedback */ @@ -199,12 +199,12 @@ export type MessageFeedbackJSONBody = { /** * The rating */ - rating: APIMessageFeedbackRating + rating: MessageFeedbackRating /** * Tags to give the rating */ - tags?: APIMessageFeedbackTags[] + tags?: MessageFeedbackTags[] /** * The text to include @@ -212,7 +212,7 @@ export type MessageFeedbackJSONBody = { text?: string } -export type APIMessageFeedbackTags = 'harmful' | 'false' | 'not-helpful' +export type MessageFeedbackTags = 'harmful' | 'false' | 'not-helpful' export type MessageFeedbackResult = { /** @@ -233,7 +233,7 @@ export type MessageFeedbackResult = { /** * The rating */ - rating: APIMessageFeedbackRating + rating: MessageFeedbackRating /** * The text the server received, including tags @@ -241,4 +241,30 @@ export type MessageFeedbackResult = { text?: string } -export type APIMessageFeedbackRating = 'thumbsUp' | 'thumbsDown' +export type MessageFeedbackRating = 'thumbsUp' | 'thumbsDown' + +export type ConversationResponseEvent = { + message?: Message + conversation_id?: string + error?: string | null +} + +export type Message = { + id: string + content: MessageContent + role: string + user: string | null + create_time: string | null + update_time: string | null + end_turn: null + weight: number + recipient: string + metadata: MessageMetadata +} + +export type MessageContent = { + content_type: string + parts: string[] +} + +export type MessageMetadata = any diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 000000000..724f3b2a5 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,9 @@ +import { remark } from 'remark' +import stripMarkdown from 'strip-markdown' + +export function markdownToText(markdown?: string): string { + return remark() + .use(stripMarkdown) + .processSync(markdown ?? '') + .toString() +}