diff --git a/.env.example b/.env.example
index 04f13da..c97daf5 100644
--- a/.env.example
+++ b/.env.example
@@ -39,4 +39,7 @@ S3_BUCKET_NAME=s3
# Github OAuth Provider
GITHUB_ID=EXAMPLE_GITHUB_ID
-GITHUB_SECRET=EXAMPLE_GITHUB_SECRET
\ No newline at end of file
+GITHUB_SECRET=EXAMPLE_GITHUB_SECRET
+
+# OpenAI
+OPENAI_API_KEY=EXAMPLE_OPENAI_API_KEY
\ No newline at end of file
diff --git a/package.json b/package.json
index 4c31170..292326b 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"next": "^13.4.13",
"next-auth": "^4.22.4",
"npm-run-all": "^4.1.5",
+ "openai": "^4.14.1",
"react": "18.2.0",
"react-codemirror-merge": "^4.21.15",
"react-dom": "18.2.0",
diff --git a/prisma/postgres/schema.prisma b/prisma/postgres/schema.prisma
index cbedef3..2318b80 100644
--- a/prisma/postgres/schema.prisma
+++ b/prisma/postgres/schema.prisma
@@ -102,21 +102,22 @@ model CodeSessionUserAuth {
}
model User {
- id String @id @default(cuid())
- name String?
- email String? @unique
- emailVerified DateTime?
- image String?
- password String?
- accounts Account[]
- sessions Session[]
- codeSpace CodeSpace[]
- attempts QuestionAttempt[]
- role Role @default(USER)
- codeSessionAuthRecv CodeSessionUserAuth[] @relation(name: "auth_receiver")
- matchRequest MatchRequest?
- joinRequest JoinRequest[]
- sessionMessage SessionMessage[]
+ id String @id @default(cuid())
+ name String?
+ email String? @unique
+ emailVerified DateTime?
+ image String?
+ password String?
+ accounts Account[]
+ sessions Session[]
+ codeSpace CodeSpace[]
+ attempts QuestionAttempt[]
+ role Role @default(USER)
+ codeSessionAuthRecv CodeSessionUserAuth[] @relation(name: "auth_receiver")
+ matchRequest MatchRequest?
+ joinRequest JoinRequest[]
+ sessionUserAndUserMessages SessionUserAndUserMessage[]
+ sessionUserAndAIMessages SessionUserAndAIMessage[]
}
enum Difficulty {
@@ -177,7 +178,7 @@ model QuestionAttempt {
}
-model SessionMessage {
+model SessionUserAndUserMessage {
id String @id @default(cuid())
sessionId String
userId String
@@ -188,3 +189,15 @@ model SessionMessage {
@@index([sessionId])
}
+
+model SessionUserAndAIMessage {
+ id String @id @default(cuid())
+ sessionId String
+ userId String
+ message String
+ role String
+ createdAt DateTime @default(now())
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@index([sessionId])
+}
diff --git a/src/components/AIBox.tsx b/src/components/AIBox.tsx
new file mode 100644
index 0000000..f6213b3
--- /dev/null
+++ b/src/components/AIBox.tsx
@@ -0,0 +1,67 @@
+import useAIComm from "~/hooks/useAIComm";
+
+const AIBox = ({
+ sessionId,
+ userId,
+ userName,
+ className,
+}: {
+ sessionId: string;
+ userId: string;
+ userName: string;
+ className?: string | undefined;
+}) => {
+ const [
+ allSessionMessages,
+ sendMessage,
+ onTyping,
+ currentMessage,
+ isAIResponding,
+ ] = useAIComm(sessionId, userId);
+
+ return (
+
+
+ {allSessionMessages?.map((message) => {
+ return (
+
+
+ {message.role === "user" ? userName : "GPT-3.5"}
+
+
{message.message}
+
+ );
+ })}
+
+
+
+ );
+};
+
+export default AIBox;
diff --git a/src/env.mjs b/src/env.mjs
index 3a2fc2b..464d1e7 100644
--- a/src/env.mjs
+++ b/src/env.mjs
@@ -29,6 +29,7 @@ export const env = createEnv({
// Add `.min(1) on ID and SECRET if you want to make sure they're not empty
GITHUB_ID: z.string().min(1),
GITHUB_SECRET: z.string().min(1),
+ OPENAI_API_KEY: z.string().min(1),
},
/**
@@ -59,6 +60,7 @@ export const env = createEnv({
S3_BUCKET_NAME: process.env.S3_BUCKET_NAME,
GITHUB_ID: process.env.GITHUB_ID,
GITHUB_SECRET: process.env.GITHUB_SECRET,
+ OPENAI_API_KEY: process.env.OPENAI_API_KEY,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
diff --git a/src/hooks/useAIComm.ts b/src/hooks/useAIComm.ts
new file mode 100644
index 0000000..e165fc5
--- /dev/null
+++ b/src/hooks/useAIComm.ts
@@ -0,0 +1,102 @@
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import { useState } from "react";
+import type { UserAndAIMessage } from "~/server/api/routers/userAndAIComm";
+import { api } from "~/utils/api";
+
+export type AICommResult = [
+ UserAndAIMessage[],
+ () => void,
+ (value: string) => void,
+ currentMessage: string,
+ isAIResponding: boolean,
+];
+
+export default function useAIComm(
+ sessionId: string,
+ userId: string,
+): AICommResult {
+ const [chatState, setChatState] = useState({
+ currentMessage: "",
+ isAIResponding: false,
+ });
+
+ const utils = api.useContext();
+
+ const allSessionMessages =
+ api.userAndAIMessages.getAllSessionUserAndAIMessages.useQuery({
+ sessionId,
+ userId,
+ }).data;
+
+ const addMessageMutation =
+ api.userAndAIMessages.addUserAndAIMessage.useMutation();
+
+ api.userAndAIMessages.subscribeToSessionUserAndAIMessages.useSubscription(
+ { sessionId, userId },
+ {
+ onData: (data: UserAndAIMessage) => {
+ if (allSessionMessages)
+ allSessionMessages.push({
+ ...data,
+ id: data.id!,
+ message: data.message,
+ createdAt: data.createdAt!,
+ });
+
+ setChatState((state) => ({
+ ...state,
+ isAIResponding: false,
+ }));
+ },
+ onError(err) {
+ console.log("Subscription error: ", err);
+ void Promise.resolve(utils.userAndUserMessages.invalidate());
+ },
+ },
+ );
+
+ const sendMessage = () => {
+ // Don't send empty messages, or messages with only whitespace. Could change this later
+ if (chatState.currentMessage.trim().length === 0) return;
+
+ allSessionMessages?.push({
+ id: "",
+ sessionId,
+ userId,
+ message: chatState.currentMessage,
+ role: "user",
+ createdAt: new Date(),
+ });
+
+ addMessageMutation.mutate({
+ sessionId,
+ userId,
+ message: chatState.currentMessage,
+ });
+
+ setChatState((state) => ({
+ ...state,
+ currentMessage: "",
+ isAIResponding: true,
+ }));
+ };
+
+ const onTyping = (value: string) => {
+ setChatState((prev) => {
+ return {
+ ...prev,
+ currentMessage: value,
+ };
+ });
+ };
+
+ return [
+ allSessionMessages as UserAndAIMessage[],
+ sendMessage,
+ onTyping,
+ chatState.currentMessage,
+ chatState.isAIResponding,
+ ];
+}
diff --git a/src/hooks/useSessionComm.ts b/src/hooks/useSessionComm.ts
index 4544f59..c82bf72 100644
--- a/src/hooks/useSessionComm.ts
+++ b/src/hooks/useSessionComm.ts
@@ -1,9 +1,12 @@
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { useState } from "react";
-import { type Message } from "~/server/api/routers/communication";
+import { type UserAndUserMessage } from "~/server/api/routers/userAndUserComm";
import { api } from "~/utils/api";
export type SessionCommResult = [
- Message[],
+ UserAndUserMessage[],
() => void,
(value: string) => void,
currentMessage: string,
@@ -23,16 +26,18 @@ export default function useSessionComm(
const utils = api.useContext();
- const allSessionMessages = api.messages.getAllSessionMessages.useQuery({
- sessionId,
- }).data;
+ const allSessionMessages =
+ api.userAndUserMessages.getAllSessionUserAndUserMessages.useQuery({
+ sessionId,
+ }).data;
- const addMessageMutation = api.messages.addMessage.useMutation();
+ const addMessageMutation =
+ api.userAndUserMessages.addUserAndUserMessage.useMutation();
- api.messages.subscribeToSessionMessages.useSubscription(
+ api.userAndUserMessages.subscribeToSessionUserAndUserMessages.useSubscription(
{ sessionId, userId },
{
- onData: (data: Message) => {
+ onData: (data: UserAndUserMessage) => {
if (data.message) {
if (data.userId !== userId)
setChatState((state) => ({
@@ -58,12 +63,13 @@ export default function useSessionComm(
},
onError(err) {
console.log("Subscription error: ", err);
- void Promise.resolve(utils.messages.invalidate());
+ void Promise.resolve(utils.userAndUserMessages.invalidate());
},
},
);
- const addWhoIsTypingMutation = api.messages.addWhoIsTyping.useMutation();
+ const addWhoIsTypingMutation =
+ api.userAndUserMessages.addWhoIsTyping.useMutation();
const sendMessage = () => {
// Don't send empty messages, or messages with only whitespace. Could change this later
@@ -101,7 +107,7 @@ export default function useSessionComm(
: "";
return [
- allSessionMessages as Message[],
+ allSessionMessages as UserAndUserMessage[],
sendMessage,
onTyping,
chatState.currentMessage,
diff --git a/src/pages/collab/rooms/[id].tsx b/src/pages/collab/rooms/[id].tsx
index 3f8825a..d9280f3 100644
--- a/src/pages/collab/rooms/[id].tsx
+++ b/src/pages/collab/rooms/[id].tsx
@@ -25,6 +25,7 @@ import { getLanguage } from "~/utils/utils";
import { Tabs, TabList, Tab, TabPanel } from "react-tabs";
import { useSession } from "next-auth/react";
import Chatbox from "~/components/ChatBox";
+import AIBox from "~/components/AIBox";
const SharedEditor = ({
onSave,
@@ -267,6 +268,7 @@ const Room = () => {
Output
Chat
+ GPT-3.5
+
+
+
diff --git a/src/server/api/root.ts b/src/server/api/root.ts
index 85da0c4..c6c5765 100644
--- a/src/server/api/root.ts
+++ b/src/server/api/root.ts
@@ -6,8 +6,9 @@ import { formRouter } from "~/server/api/routers/form";
import answerRouter from "./routers/answer";
import { codeSessionRouter } from "./routers/codeSession";
import { judgeRouter } from "~/server/api/routers/judge";
-import { messagesRouter } from "./routers/communication";
+import { userAndUserMessagesRouter } from "./routers/userAndUserComm";
import { sharedCodeSessionRouter } from "./routers/sharedCodeSession";
+import { userAndAIMessagesRouter } from "./routers/userAndAIComm";
/**
* This is the primary router for your server.
@@ -22,7 +23,8 @@ export const appRouter = createTRPCRouter({
answer: answerRouter,
codeSession: codeSessionRouter,
judge: judgeRouter,
- messages: messagesRouter,
+ userAndUserMessages: userAndUserMessagesRouter,
+ userAndAIMessages: userAndAIMessagesRouter,
sharedSession: sharedCodeSessionRouter,
});
diff --git a/src/server/api/routers/userAndAIComm.ts b/src/server/api/routers/userAndAIComm.ts
new file mode 100644
index 0000000..aeb1a96
--- /dev/null
+++ b/src/server/api/routers/userAndAIComm.ts
@@ -0,0 +1,129 @@
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-return */
+import { observable } from "@trpc/server/observable";
+import { EventEmitter } from "events";
+import { z } from "zod";
+import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
+import OpenAI from "openai";
+import type { ChatCompletionRole } from "openai/resources";
+
+const ee = new EventEmitter();
+const openai = new OpenAI({
+ apiKey: process.env.OPENAI_API_KEY,
+});
+
+export type UserAndAIMessage = {
+ id?: string;
+ sessionId: string;
+ userId: string;
+ message: string;
+ role: ChatCompletionRole;
+ createdAt?: Date;
+};
+
+export const userAndAIMessagesRouter = createTRPCRouter({
+ getAllSessionUserAndAIMessages: protectedProcedure
+ .input(
+ z.object({
+ sessionId: z.string(),
+ userId: z.string(),
+ }),
+ )
+ .query(async ({ ctx, input }) => {
+ const { sessionId, userId } = input;
+
+ const messages =
+ await ctx.prismaPostgres.sessionUserAndAIMessage.findMany({
+ where: {
+ sessionId,
+ userId,
+ },
+ orderBy: {
+ createdAt: "asc",
+ },
+ });
+
+ return messages;
+ }),
+
+ addUserAndAIMessage: protectedProcedure
+ .input(
+ z.object({
+ sessionId: z.string(),
+ userId: z.string(),
+ message: z.string(),
+ }),
+ )
+ .mutation(async ({ ctx, input }) => {
+ const { sessionId, userId, message } = input;
+
+ const messageObject =
+ await ctx.prismaPostgres.sessionUserAndAIMessage.create({
+ data: {
+ sessionId,
+ userId,
+ message,
+ role: "user",
+ },
+ });
+
+ const currentSessionMessages =
+ await ctx.prismaPostgres.sessionUserAndAIMessage.findMany({
+ where: {
+ sessionId,
+ userId,
+ },
+ orderBy: {
+ createdAt: "asc",
+ },
+ });
+
+ const response = await openai.chat.completions.create({
+ messages: currentSessionMessages.map((message) => {
+ return {
+ role: message.role as ChatCompletionRole,
+ content: message.message,
+ };
+ }),
+ model: "gpt-3.5-turbo",
+ });
+
+ const aiMessage = response.choices[0]?.message;
+
+ if (aiMessage) {
+ const aiMessageObject =
+ await ctx.prismaPostgres.sessionUserAndAIMessage.create({
+ data: {
+ sessionId,
+ userId,
+ message: aiMessage.content!,
+ role: aiMessage.role,
+ },
+ });
+
+ ee.emit("aiMessage", aiMessageObject);
+ }
+
+ return messageObject;
+ }),
+
+ subscribeToSessionUserAndAIMessages: protectedProcedure
+ .input(z.object({ sessionId: z.string(), userId: z.string() }))
+ .subscription(({ input }) => {
+ return observable((emit) => {
+ const onMessage = (data: UserAndAIMessage) => {
+ if (
+ data.sessionId === input.sessionId &&
+ data.userId === input.userId
+ )
+ emit.next(data);
+ };
+ ee.on("aiMessage", onMessage);
+ return () => {
+ ee.off("aiMessage", onMessage);
+ };
+ });
+ }),
+});
diff --git a/src/server/api/routers/communication.ts b/src/server/api/routers/userAndUserComm.ts
similarity index 80%
rename from src/server/api/routers/communication.ts
rename to src/server/api/routers/userAndUserComm.ts
index 1329844..44e8e4f 100644
--- a/src/server/api/routers/communication.ts
+++ b/src/server/api/routers/userAndUserComm.ts
@@ -9,7 +9,7 @@ import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
const ee = new EventEmitter();
-export type Message = {
+export type UserAndUserMessage = {
id?: string;
sessionId: string;
userId: string;
@@ -20,8 +20,8 @@ export type Message = {
};
// Methods include userId as well because users do not have unique names, so who is typing is decided by the userId
-export const messagesRouter = createTRPCRouter({
- getAllSessionMessages: protectedProcedure
+export const userAndUserMessagesRouter = createTRPCRouter({
+ getAllSessionUserAndUserMessages: protectedProcedure
.input(
z.object({
sessionId: z.string(),
@@ -30,7 +30,7 @@ export const messagesRouter = createTRPCRouter({
.query(async ({ ctx, input }) => {
const { sessionId } = input;
- const messages = await ctx.prismaPostgres.sessionMessage.findMany({
+ const messages = await ctx.prismaPostgres.sessionUserAndUserMessage.findMany({
where: {
sessionId,
},
@@ -42,7 +42,7 @@ export const messagesRouter = createTRPCRouter({
return messages;
}),
- addMessage: protectedProcedure
+ addUserAndUserMessage: protectedProcedure
.input(
z.object({
sessionId: z.string(),
@@ -54,7 +54,7 @@ export const messagesRouter = createTRPCRouter({
.mutation(async ({ ctx, input }) => {
const { sessionId, userId, userName, message } = input;
- const messageObject = await ctx.prismaPostgres.sessionMessage
+ const messageObject = await ctx.prismaPostgres.sessionUserAndUserMessage
.create({
data: {
sessionId,
@@ -63,9 +63,6 @@ export const messagesRouter = createTRPCRouter({
message,
},
})
- .then((req) => {
- return req;
- });
ee.emit("message", messageObject);
ee.emit("typing", {
@@ -78,14 +75,14 @@ export const messagesRouter = createTRPCRouter({
return messageObject;
}),
- subscribeToSessionMessages: protectedProcedure
+ subscribeToSessionUserAndUserMessages: protectedProcedure
.input(z.object({ sessionId: z.string(), userId: z.string() }))
.subscription(({ input }) => {
- return observable((emit) => {
- const onMessage = (data: Message) => {
+ return observable((emit) => {
+ const onMessage = (data: UserAndUserMessage) => {
if (data.sessionId === input.sessionId) emit.next(data);
};
- const onTyping = (data: Message) => {
+ const onTyping = (data: UserAndUserMessage) => {
if (
data.sessionId === input.sessionId &&
data.userId !== input.userId
diff --git a/yarn.lock b/yarn.lock
index 137de24..79aaf1f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2059,11 +2059,26 @@
resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz"
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
+"@types/node-fetch@^2.6.4":
+ version "2.6.7"
+ resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.7.tgz#a1abe2ce24228b58ad97f99480fdcf9bbc6ab16d"
+ integrity sha512-lX17GZVpJ/fuCjguZ5b3TjEbSENxmEk1B2z02yoXSK9WMEWRivhdSY73wWMn6bpcCDAOh6qAdktpKHIlkDk2lg==
+ dependencies:
+ "@types/node" "*"
+ form-data "^4.0.0"
+
"@types/node@*":
version "20.5.9"
resolved "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz"
integrity sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==
+"@types/node@^18.11.18":
+ version "18.18.7"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.7.tgz#bb3a7068dc4ba421b6968f2a259298b3a4e129e8"
+ integrity sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==
+ dependencies:
+ undici-types "~5.26.4"
+
"@types/node@^18.16.0":
version "18.17.12"
resolved "https://registry.npmjs.org/@types/node/-/node-18.17.12.tgz"
@@ -2427,6 +2442,13 @@ abbrev@1:
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+abort-controller@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
+ integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
+ dependencies:
+ event-target-shim "^5.0.0"
+
accepts@^1.3.5:
version "1.3.8"
resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz"
@@ -2457,6 +2479,13 @@ agent-base@6:
dependencies:
debug "4"
+agentkeepalive@^4.2.1:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
+ integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
+ dependencies:
+ humanize-ms "^1.2.1"
+
ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
@@ -2692,6 +2721,11 @@ balanced-match@^1.0.0:
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+base-64@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
+ integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==
+
bcrypt@^5.1.1:
version "5.1.1"
resolved "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz"
@@ -2847,6 +2881,11 @@ character-entities@^2.0.0:
resolved "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz"
integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
+charenc@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
+ integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
+
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
@@ -3104,6 +3143,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
+crypt@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
+ integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
+
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
@@ -3229,6 +3273,14 @@ diff@^5.0.0:
resolved "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz"
integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
+digest-fetch@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/digest-fetch/-/digest-fetch-1.3.0.tgz#898e69264d00012a23cf26e8a3e40320143fc661"
+ integrity sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==
+ dependencies:
+ base-64 "^0.1.0"
+ md5 "^2.3.0"
+
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz"
@@ -3701,6 +3753,11 @@ esutils@^2.0.2:
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+event-target-shim@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
+ integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
+
events@^3.2.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
@@ -3829,6 +3886,11 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"
+form-data-encoder@1.7.2:
+ version "1.7.2"
+ resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040"
+ integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==
+
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
@@ -3838,6 +3900,14 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
+formdata-node@^4.3.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2"
+ integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==
+ dependencies:
+ node-domexception "1.0.0"
+ web-streams-polyfill "4.0.0-beta.3"
+
fraction.js@^4.2.0:
version "4.3.2"
resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.2.tgz"
@@ -4240,6 +4310,13 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
+humanize-ms@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
+ integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
+ dependencies:
+ ms "^2.0.0"
+
humanize-number@0.0.2:
version "0.0.2"
resolved "https://registry.npmjs.org/humanize-number/-/humanize-number-0.0.2.tgz"
@@ -4356,6 +4433,11 @@ is-buffer@^2.0.0:
resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
+is-buffer@~1.1.6:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
version "1.2.7"
resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz"
@@ -4819,6 +4901,15 @@ markdown-table@^3.0.0:
resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz"
integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==
+md5@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
+ integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==
+ dependencies:
+ charenc "0.0.2"
+ crypt "0.0.2"
+ is-buffer "~1.1.6"
+
mdast-util-definitions@^5.0.0:
version "5.1.2"
resolved "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz"
@@ -5368,7 +5459,7 @@ ms@2.1.2:
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-ms@^2.1.1:
+ms@^2.0.0, ms@^2.1.1:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@@ -5461,6 +5552,11 @@ node-addon-api@^5.0.0:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz"
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
+node-domexception@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
+ integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
+
node-fetch@^2.6.7:
version "2.7.0"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
@@ -5641,6 +5737,21 @@ only@~0.0.2:
resolved "https://registry.npmjs.org/only/-/only-0.0.2.tgz"
integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==
+openai@^4.14.1:
+ version "4.14.1"
+ resolved "https://registry.yarnpkg.com/openai/-/openai-4.14.1.tgz#26ffa8e86a57da6595b46350355a754d934e61a1"
+ integrity sha512-aBb7DVdzSnEUBFHTbnVoitauefvjRuUHS5pa7lm1m5JmHifD+1Hff1RzxYC12ogugVcCmWT99NZNfzyD6n/0IQ==
+ dependencies:
+ "@types/node" "^18.11.18"
+ "@types/node-fetch" "^2.6.4"
+ abort-controller "^3.0.0"
+ agentkeepalive "^4.2.1"
+ digest-fetch "^1.3.0"
+ form-data-encoder "1.7.2"
+ formdata-node "^4.3.2"
+ node-fetch "^2.6.7"
+ web-streams-polyfill "^3.2.1"
+
openid-client@^5.4.0:
version "5.4.3"
resolved "https://registry.npmjs.org/openid-client/-/openid-client-5.4.3.tgz"
@@ -6896,6 +7007,11 @@ unbox-primitive@^1.0.2:
has-symbols "^1.0.3"
which-boxed-primitive "^1.0.2"
+undici-types@~5.26.4:
+ version "5.26.5"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
+ integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+
unified@^10.0.0:
version "10.1.2"
resolved "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz"
@@ -7138,6 +7254,16 @@ web-namespaces@^2.0.0:
resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz"
integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==
+web-streams-polyfill@4.0.0-beta.3:
+ version "4.0.0-beta.3"
+ resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38"
+ integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==
+
+web-streams-polyfill@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
+ integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
+
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"