diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b60661b1..27a6511d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,7 +39,7 @@ repos: # and which break the standard YAML check. The alternative would be to # skip any unsafe errors (and thus break YAML compatibility) or use # some other checker that may not work in general. - exclude: ^copilot/web/addons/.*$ + exclude: ^copilot/.*/addons/.*$ - id: check-json - id: check-case-conflict - id: detect-private-key diff --git a/CODEOWNERS b/CODEOWNERS index b6ee695c..c5cc1467 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,4 @@ * @yk @andreaskoepf /website/ @fozziethebeat @k-nearest-neighbor @AbdBarho +/model/ @theblackcat102 @sanagno +/copilot/ @fozziethebeat @andreaskoepf @yk diff --git a/backend/main.py b/backend/main.py index e6b3bdce..cb682a9f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -26,8 +26,13 @@ app = fastapi.FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V @app.exception_handler(OasstError) async def oasst_exception_handler(request: fastapi.Request, ex: OasstError): logger.error(f"{request.method} {request.url} failed: {repr(ex)}") + return fastapi.responses.JSONResponse( - status_code=int(ex.http_status_code), content={"message": ex.message, "error_code": ex.error_code} + status_code=int(ex.http_status_code), + content=protocol_schema.OasstErrorResponse( + message=ex.message, + error_code=OasstErrorCode(ex.error_code), + ).dict(), ) diff --git a/copilot/api/manifest.yml b/copilot/api/manifest.yml new file mode 100644 index 00000000..b9262b51 --- /dev/null +++ b/copilot/api/manifest.yml @@ -0,0 +1,38 @@ +# The manifest for the "api" service. +# Read the full specification for the "Load Balanced Web Service" type at: +# https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/ + +name: api +type: Load Balanced Web Service + +http: + path: "/" + healthcheck: + path: "/docs" + +image: + build: + dockerfile: docker/Dockerfile.backend + context: ./ + port: 8080 + +cpu: 256 +memory: 512 +platform: linux/x86_64 +count: 1 +exec: true +network: + connect: true + +environments: + staging: + variables: + # Note: this has to be a valid JSON list for Pydantic to parse it. + BACKEND_CORS_ORIGINS: '["https://web.staging.open-assistant.surfacedata.org"]' + DEBUG_ALLOW_ANY_API_KEY: True + DEBUG_SKIP_API_KEY_CHECK: True + MAX_WORKERS: 1 + +secrets: + # Note: URI, not URL. + DATABASE_URI: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/API_DATABASE_URL diff --git a/copilot/web/addons/web-cluster.yml b/copilot/web/addons/web-cluster.yml deleted file mode 100644 index c7a337bf..00000000 --- a/copilot/web/addons/web-cluster.yml +++ /dev/null @@ -1,161 +0,0 @@ -Parameters: - App: - Type: String - Description: Your application's name. - Env: - Type: String - Description: - The environment name your service, job, or workflow is being deployed to. - Name: - Type: String - Description: The name of the service, job, or workflow being deployed. - # Customize your Aurora Serverless cluster by setting the default value of the following parameters. - webclusterDBName: - Type: String - Description: - The name of the initial database to be created in the Aurora Serverless v2 - cluster. - Default: oassist_web - # Cannot have special characters - # Naming constraints: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Limits.html#RDS_Limits.Constraints -Mappings: - webclusterEnvScalingConfigurationMap: - staging: - "DBMinCapacity": 0.5 # AllowedValues: from 0.5 through 128 - "DBMaxCapacity": 8 # AllowedValues: from 0.5 through 128 - - All: - "DBMinCapacity": 0.5 # AllowedValues: from 0.5 through 128 - "DBMaxCapacity": 8 # AllowedValues: from 0.5 through 128 - -Resources: - webclusterDBSubnetGroup: - Type: "AWS::RDS::DBSubnetGroup" - Properties: - DBSubnetGroupDescription: - Group of Copilot private subnets for Aurora Serverless v2 cluster. - SubnetIds: - !Split [",", { "Fn::ImportValue": !Sub "${App}-${Env}-PrivateSubnets" }] - webclusterSecurityGroup: - Metadata: - "aws:copilot:description": - "A security group for your workload to access the Aurora Serverless v2 - cluster webcluster" - Type: "AWS::EC2::SecurityGroup" - Properties: - GroupDescription: - !Sub "The Security Group for ${Name} to access Aurora Serverless v2 - cluster webcluster." - VpcId: - Fn::ImportValue: !Sub "${App}-${Env}-VpcId" - Tags: - - Key: Name - Value: !Sub "copilot-${App}-${Env}-${Name}-Aurora" - webclusterDBClusterSecurityGroup: - Metadata: - "aws:copilot:description": - "A security group for your Aurora Serverless v2 cluster webcluster" - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: The Security Group for the Aurora Serverless v2 cluster. - SecurityGroupIngress: - - ToPort: 5432 - FromPort: 5432 - IpProtocol: tcp - Description: - !Sub "From the Aurora Security Group of the workload ${Name}." - SourceSecurityGroupId: !Ref webclusterSecurityGroup - VpcId: - Fn::ImportValue: !Sub "${App}-${Env}-VpcId" - webclusterAuroraSecret: - Metadata: - "aws:copilot:description": - "A Secrets Manager secret to store your DB credentials" - Type: AWS::SecretsManager::Secret - Properties: - Description: !Sub Aurora main user secret for ${AWS::StackName} - GenerateSecretString: - SecretStringTemplate: '{"username": "postgres"}' - GenerateStringKey: "password" - ExcludePunctuation: true - IncludeSpace: false - PasswordLength: 16 - webclusterDBClusterParameterGroup: - Metadata: - "aws:copilot:description": - "A DB parameter group for engine configuration values" - Type: "AWS::RDS::DBClusterParameterGroup" - Properties: - Description: !Ref "AWS::StackName" - Family: "aurora-postgresql14" - Parameters: - client_encoding: "UTF8" - webclusterDBCluster: - Metadata: - "aws:copilot:description": - "The webcluster Aurora Serverless v2 database cluster" - Type: "AWS::RDS::DBCluster" - Properties: - MasterUsername: - !Join [ - "", - [ - "{{resolve:secretsmanager:", - !Ref webclusterAuroraSecret, - ":SecretString:username}}", - ], - ] - MasterUserPassword: - !Join [ - "", - [ - "{{resolve:secretsmanager:", - !Ref webclusterAuroraSecret, - ":SecretString:password}}", - ], - ] - DatabaseName: !Ref webclusterDBName - Engine: "aurora-postgresql" - EngineVersion: "14.4" - DBClusterParameterGroupName: !Ref webclusterDBClusterParameterGroup - DBSubnetGroupName: !Ref webclusterDBSubnetGroup - Port: 5432 - VpcSecurityGroupIds: - - !Ref webclusterDBClusterSecurityGroup - ServerlessV2ScalingConfiguration: - # Replace "All" below with "!Ref Env" to set different autoscaling limits per environment. - MinCapacity: - !FindInMap [webclusterEnvScalingConfigurationMap, All, DBMinCapacity] - MaxCapacity: - !FindInMap [webclusterEnvScalingConfigurationMap, All, DBMaxCapacity] - webclusterDBWriterInstance: - Metadata: - "aws:copilot:description": - "The webcluster Aurora Serverless v2 writer instance" - Type: "AWS::RDS::DBInstance" - Properties: - DBClusterIdentifier: !Ref webclusterDBCluster - DBInstanceClass: db.serverless - Engine: "aurora-postgresql" - PromotionTier: 1 - AvailabilityZone: !Select - - 0 - - !GetAZs - Ref: AWS::Region - - webclusterSecretAuroraClusterAttachment: - Type: AWS::SecretsManager::SecretTargetAttachment - Properties: - SecretId: !Ref webclusterAuroraSecret - TargetId: !Ref webclusterDBCluster - TargetType: AWS::RDS::DBCluster -Outputs: - webclusterSecret: # injected as WEBCLUSTER_SECRET environment variable by Copilot. - Description: - "The JSON secret that holds the database username and password. Fields are - 'host', 'port', 'dbname', 'username', 'password', 'dbClusterIdentifier' - and 'engine'" - Value: !Ref webclusterAuroraSecret - webclusterSecurityGroup: - Description: "The security group to attach to the workload." - Value: !Ref webclusterSecurityGroup diff --git a/copilot/web/manifest.yml b/copilot/web/manifest.yml index 18df80c1..aadc3297 100644 --- a/copilot/web/manifest.yml +++ b/copilot/web/manifest.yml @@ -26,6 +26,7 @@ environments: staging: variables: NEXTAUTH_URL: https://web.staging.open-assistant.surfacedata.org + FASTAPI_URL: https://api.staging.open-assistant.surfacedata.org secrets: DATABASE_URL: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/DATABASE_URL @@ -37,5 +38,4 @@ secrets: EMAIL_SERVER_USER: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/EMAIL_SERVER_USER EMAIL_FROM: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/EMAIL_FROM FASTAPI_KEY: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/FASTAPI_KEY - FASTAPI_URL: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/FASTAPI_URL NEXTAUTH_SECRET: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/NEXTAUTH_SECRET diff --git a/discord-bot/.env.example b/discord-bot/.env.example index 5cd18fac..8474ee90 100644 --- a/discord-bot/.env.example +++ b/discord-bot/.env.example @@ -1,7 +1,7 @@ BOT_TOKEN= DECLARE_GLOBAL_COMMANDS= OWNER_IDS=[, ] -PREFIX="./" +PREFIX="/" # DO NOT LEAVE EMPTY, slash command prefix in DMs OASST_API_URL="http://localhost:8080" # No trailing '/' OASST_API_KEY="" diff --git a/discord-bot/README.md b/discord-bot/README.md index 1ad99a71..000155ae 100644 --- a/discord-bot/README.md +++ b/discord-bot/README.md @@ -48,7 +48,7 @@ Remember to save your changes. below to invite your bot. ``` -https://discord.com/oauth2/authorize?client_id=YOUR_ID_HERE&permissions=8&scope=bot%20applications.commands +https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID_HERE&permissions=8&scope=bot%20applications.commands ``` ### Environment Setup @@ -66,6 +66,9 @@ pip install -e . cp .env.example .env # edit .env and add your bot token and other values +# BOT_TOKEN is given by the discord developer portal when you create a bot +# DECLARE_GLOBAL_COMMANDS is the id of the server where you added the bot (right click on the server icon and copy id) +# OWNER_ID can be leave as an empty list python -V # 3.10 diff --git a/discord-bot/bot/bot.py b/discord-bot/bot/bot.py index df3c5f2f..8c604e1a 100644 --- a/discord-bot/bot/bot.py +++ b/discord-bot/bot/bot.py @@ -6,7 +6,7 @@ import hikari import lightbulb import miru from bot.settings import Settings -from bot.utils import EMPTY, mention +from bot.utils import mention from oasst_shared.api_client import OasstApiClient settings = Settings() @@ -34,8 +34,11 @@ async def on_starting(event: hikari.StartingEvent): bot.d.oasst_api = OasstApiClient(settings.oasst_api_url, settings.oasst_api_key) - # A set of user id's that are currently doing work. - bot.d.currently_working = set() + # A `dict[hikari.Message | None, UUID | None]]` that maps user IDs to (task msg ID, task UUIDs). + # Either both are `None` or both are not `None`. + # If both are `None`, the user is not currently selecting a task. + # TODO: Grow this on startup so we don't have to re-allocate memory every time it needs to grow + bot.d.currently_working = {} @bot.listen() @@ -50,13 +53,13 @@ async def _send_error_embed( ) -> None: ctx.command embed = hikari.Embed( - title=f"`{exception.__class__.__name__}` Error{f' in `{ctx.command.name}`' if ctx.command else '' }", + title=f"`{exception.__class__.__name__}` Error{f' in `/{ctx.command.name}`' if ctx.command else '' }", description=content, color=0xFF0000, timestamp=datetime.now().astimezone(), ).set_author(name=ctx.author.username, url=str(ctx.author.avatar_url)) - await ctx.respond(EMPTY, embed=embed) + await ctx.respond(embed=embed) @bot.listen(lightbulb.CommandErrorEvent) @@ -65,6 +68,8 @@ async def on_error(event: lightbulb.CommandErrorEvent) -> None: # Unwrap the exception to get the original cause exc = event.exception.__cause__ or event.exception ctx = event.context + if not ctx.bot.rest.is_alive: + return if isinstance(event.exception, lightbulb.CommandInvocationError): if not event.context.command: @@ -114,6 +119,8 @@ async def on_error(event: lightbulb.CommandErrorEvent) -> None: ctx, ) elif isinstance(exc, lightbulb.errors.MissingRequiredAttachment): - await _send_error_embed("Not enough attachemnts were supplied to this command.", exc, ctx) + await _send_error_embed("Not enough attachments were supplied to this command.", exc, ctx) + elif isinstance(exc, lightbulb.errors.CommandNotFound): + await ctx.respond(f"`/{exc.invoked_with}` is not a valid command. Use `/help` to see a list of commands.") else: raise exc diff --git a/discord-bot/bot/extensions/guild_settings.py b/discord-bot/bot/extensions/guild_settings.py index 62f21305..5940f33a 100644 --- a/discord-bot/bot/extensions/guild_settings.py +++ b/discord-bot/bot/extensions/guild_settings.py @@ -78,7 +78,6 @@ async def log_channel(ctx: lightbulb.SlashContext) -> None: # if the bot's permissions for this channel don't contain SEND_MESSAGE # This will also filter out categories and voice channels - print(permissions_in(ch, own_member) & hikari.Permissions.SEND_MESSAGES) if not permissions_in(ch, own_member) & hikari.Permissions.SEND_MESSAGES: await ctx.respond(f"I don't have permission to send messages in {ch.mention}.") return diff --git a/discord-bot/bot/extensions/text_labels.py b/discord-bot/bot/extensions/text_labels.py index 388a93f0..a2607aec 100644 --- a/discord-bot/bot/extensions/text_labels.py +++ b/discord-bot/bot/extensions/text_labels.py @@ -7,7 +7,6 @@ import lightbulb import miru from aiosqlite import Connection from bot.db.schemas import GuildSettings -from bot.utils import EMPTY from loguru import logger plugin = lightbulb.Plugin( @@ -74,7 +73,7 @@ class LabelModal(miru.Modal): ) channel = await context.bot.rest.fetch_channel(guild_settings.log_channel_id) assert isinstance(channel, hikari.TextableChannel) - await channel.send(EMPTY, embed=embed) + await channel.send(embed=embed) class LabelSelect(miru.View): @@ -164,7 +163,7 @@ async def label_message_text(ctx: lightbulb.MessageContext): msg.content, timeout=60, ) - resp = await ctx.respond(EMPTY, embed=embed, components=label_select_view, flags=hikari.MessageFlag.EPHEMERAL) + resp = await ctx.respond(embed=embed, components=label_select_view, flags=hikari.MessageFlag.EPHEMERAL) await label_select_view.start(await resp.message()) await label_select_view.wait() diff --git a/discord-bot/bot/extensions/work.py b/discord-bot/bot/extensions/work.py index 19802c64..6b7f8ea4 100644 --- a/discord-bot/bot/extensions/work.py +++ b/discord-bot/bot/extensions/work.py @@ -1,15 +1,27 @@ """Work plugin for collecting user data.""" import asyncio import typing as t -from datetime import datetime +from uuid import UUID import hikari import lightbulb import lightbulb.decorators import miru from aiosqlite import Connection -from bot.db.schemas import GuildSettings -from bot.utils import EMPTY +from bot.messages import ( + assistant_reply_message, + confirm_ranking_response_message, + confirm_text_response_message, + initial_prompt_message, + invalid_user_input_embed, + plain_embed, + prompter_reply_message, + rank_assistant_reply_message, + rank_initial_prompts_message, + rank_prompter_reply_message, + task_complete_embed, +) +from bot.settings import Settings from loguru import logger from oasst_shared.api_client import OasstApiClient, TaskType from oasst_shared.schemas import protocol as protocol_schema @@ -20,6 +32,8 @@ plugin = lightbulb.Plugin("WorkPlugin") MAX_TASK_TIME = 60 * 60 # 1 hour MAX_TASK_ACCEPT_TIME = 60 # 1 minute +settings = Settings() + @plugin.command @lightbulb.option( @@ -31,31 +45,56 @@ MAX_TASK_ACCEPT_TIME = 60 # 1 minute type=str, ) @lightbulb.command("work", "Complete a task.") -@lightbulb.implements(lightbulb.SlashCommand) -async def work(ctx: lightbulb.SlashContext): +@lightbulb.implements(lightbulb.SlashCommand, lightbulb.PrefixCommand) +async def work(ctx: lightbulb.Context): """Create and handle a task.""" - # make sure the user isn't currently doing a task - currently_working: set[hikari.Snowflakeish] = ctx.bot.d.currently_working + # Only send this message if started from a server + if ctx.guild_id is not None: + await ctx.respond(embed=plain_embed("Sending you a task, check your DMs"), flags=hikari.MessageFlag.EPHEMERAL) + + # make sure the user isn't currently doing a task, and if they are, ask if they want to cancel it + currently_working: dict[ + hikari.Snowflakeish, tuple[hikari.Message | None, UUID | None] + ] = ctx.bot.d.currently_working + + oasst_api: OasstApiClient = ctx.bot.d.oasst_api if ctx.author.id in currently_working: - await ctx.respond( - "You are already performing a task. Please complete that one first.", flags=hikari.MessageFlag.EPHEMERAL + yn_view = YesNoView(timeout=MAX_TASK_ACCEPT_TIME) + msg = await ctx.author.send( + embed=plain_embed("You are already working. Would you like to cancel your old task start a new one?"), + flags=hikari.MessageFlag.EPHEMERAL, + components=yn_view, ) - return + await yn_view.start(msg) + await yn_view.wait() - currently_working.add(ctx.author.id) + match yn_view.choice: + case False | None: + return + case True: + old_msg, task_id = currently_working[ctx.author.id] + if old_msg is not None: + logger.info(f"User {ctx.author.id} cancelled task {task_id}, deleting message {old_msg.id}") + map(lambda c: c, old_msg.components) + await old_msg.delete() + if task_id is not None: + await oasst_api.nack_task(task_id, reason="user cancelled") + await msg.delete() + + currently_working[ctx.author.id] = (None, None) + + # Create a TaskRequestType from the stringified enum value task_type: TaskRequestType = TaskRequestType(ctx.options.type.split(".")[-1]) - await ctx.respond("Sending you a task, check your DMs", flags=hikari.MessageFlag.EPHEMERAL) logger.debug(f"Starting task_type: {task_type!r}") - try: await _handle_task(ctx, task_type) finally: - currently_working.remove(ctx.author.id) + del currently_working[ctx.author.id] -async def _handle_task(ctx: lightbulb.SlashContext, task_type: TaskRequestType) -> None: +async def _handle_task(ctx: lightbulb.Context, task_type: TaskRequestType) -> None: """Handle creating and collecting user input for a task. Continually present tasks to the user until they select one, cancel, or time out. @@ -72,38 +111,79 @@ async def _handle_task(ctx: lightbulb.SlashContext, task_type: TaskRequestType) task, msg_id = await _select_task(ctx, task_type) if task is None: + # User cancelled return # Task action loop completed = False while not completed: - await ctx.author.send("Please type your response here:") + await ctx.author.send(embed=plain_embed("Please type your response here")) try: event = await ctx.bot.wait_for( - hikari.DMMessageCreateEvent, timeout=MAX_TASK_TIME, predicate=lambda e: e.author.id == ctx.author.id + hikari.DMMessageCreateEvent, + timeout=MAX_TASK_TIME, + predicate=lambda e: e.author.id == ctx.author.id + and not (e.message.content or "").startswith(settings.prefix), ) except asyncio.TimeoutError: - await ctx.author.send("Task timed out. Exiting") + await ctx.author.send(embed=plain_embed("Task timed out. Exiting")) await oasst_api.nack_task(task.id, reason="timed out") logger.info(f"Task {task.id} timed out") return # Invalid response - if event.content is None or not _validate_user_input(event.content, task): - await ctx.author.send("Invalid response") + valid, err_msg = _validate_user_input(event.content, task) + if not valid or event.content is None: + + await ctx.author.send(embed=invalid_user_input_embed(err_msg)) continue logger.debug(f"Successful user input received: {event.content}") + # Confirm user input + if isinstance(task, protocol_schema.RankConversationRepliesTask): + content = confirm_ranking_response_message(event.content, task.replies) + elif isinstance(task, protocol_schema.RankInitialPromptsTask): + content = confirm_ranking_response_message(event.content, task.prompts) + elif isinstance(task, protocol_schema.ReplyToConversationTask | protocol_schema.InitialPromptTask): + content = confirm_text_response_message(event.content) + else: + logger.critical(f"Unknown task type: {task.type}") + raise ValueError(f"Unknown task type: {task.type}") + + confirm_resp_view = YesNoView(timeout=MAX_TASK_TIME) + msg = await ctx.author.send(content, components=confirm_resp_view) + await confirm_resp_view.start(msg) + await confirm_resp_view.wait() + + match confirm_resp_view.choice: + case False | None: + continue + case True: + await msg.delete() # buttons are already gone + # Send the response to the backend - reply = protocol_schema.TextReplyToMessage( - message_id=str(msg_id), - user_message_id=str(event.message_id), - user=protocol_schema.User( - auth_method="discord", id=str(ctx.author.id), display_name=ctx.author.username - ), - text=event.content, - ) + if isinstance(task, protocol_schema.RankConversationRepliesTask | protocol_schema.RankInitialPromptsTask): + reply = protocol_schema.MessageRanking( + message_id=str(msg_id), + ranking=[int(r) - 1 for r in event.content.replace(" ", "").split(",")], + user=protocol_schema.User( + auth_method="discord", id=str(ctx.author.id), display_name=ctx.author.username + ), + ) + elif isinstance(task, protocol_schema.ReplyToConversationTask | protocol_schema.InitialPromptTask): + reply = protocol_schema.TextReplyToMessage( + message_id=str(msg_id), + user_message_id=str(event.message_id), + user=protocol_schema.User( + auth_method="discord", id=str(ctx.author.id), display_name=ctx.author.username + ), + text=event.content, + ) + else: + logger.critical(f"Unexpected task type received: {task.type}") + raise ValueError(f"Unexpected task type received: {task.type}") + logger.debug(f"Sending reply to backend: {reply!r}") # Get next task @@ -111,63 +191,55 @@ async def _handle_task(ctx: lightbulb.SlashContext, task_type: TaskRequestType) logger.info(f"New task {new_task}") if new_task.type == TaskType.done: - await ctx.author.send("Task completed") + await ctx.author.send(embed=plain_embed("Task completed")) completed = True continue else: logger.critical(f"Unexpected task type received: {new_task.type}") - # Send a message in the log channel that the task is complete - # TODO: Maybe do something with the msg ID so users can rate the "answer" - assert ctx.guild_id is not None + # Send a message in all the log channels that the task is complete conn: Connection = ctx.bot.d.db - guild_settings = await GuildSettings.from_db(conn, ctx.guild_id) + async with conn.cursor() as cursor: + await cursor.execute("SELECT log_channel_id FROM guild_settings") + log_channel_ids = await cursor.fetchall() - if guild_settings is not None and guild_settings.log_channel_id is not None: + channels = [ + ctx.bot.cache.get_guild_channel(id[0]) or await ctx.bot.rest.fetch_channel(id[0]) + for id in log_channel_ids + ] - channel = await ctx.bot.rest.fetch_channel(guild_settings.log_channel_id) - assert isinstance(channel, hikari.TextableChannel) # option converter - - done_embed = ( - hikari.Embed( - title="Task Completion", - description=f"`{task.type}` completed by {ctx.author.mention}", - color=hikari.Color(0x00FF00), - timestamp=datetime.now().astimezone(), - ) - .add_field("Total Tasks", "0", inline=True) - .add_field("Server Ranking", "0/0", inline=True) - .add_field("Global Ranking", "0/0", inline=True) - .set_footer(f"Task ID: {task.id}") - ) - await channel.send(EMPTY, embed=done_embed) + done_embed = task_complete_embed(task, ctx.author.mention) + # This will definitely get the bot rate limited, but that's a future problem + asyncio.gather(*(ch.send(embed=done_embed) for ch in channels if isinstance(ch, hikari.TextableChannel))) # ask the user if they want to do another task - choice_view = ChoiceView(timeout=MAX_TASK_ACCEPT_TIME) - msg = await ctx.author.send("Would you like another task?", components=choice_view) - await choice_view.start(msg) - await choice_view.wait() + another_task_view = YesNoView(timeout=MAX_TASK_ACCEPT_TIME) + msg = await ctx.author.send(embed=plain_embed("Would you like another task?"), components=another_task_view) + await another_task_view.start(msg) + await another_task_view.wait() - match choice_view.choice: + match another_task_view.choice: case False | None: done = True - await ctx.author.send("Exiting, goodbye!") + await msg.edit(embed=plain_embed("Exiting, goodbye!")) case True: pass async def _select_task( - ctx: lightbulb.SlashContext, task_type: TaskRequestType, user: protocol_schema.User | None = None + ctx: lightbulb.Context, task_type: TaskRequestType, user: protocol_schema.User | None = None ) -> tuple[protocol_schema.Task | None, str]: """Present tasks to the user until they accept one, cancel, or time out.""" oasst_api: OasstApiClient = ctx.bot.d.oasst_api logger.debug(f"Starting task selection for {task_type}") # Loop until the user accepts a task, cancels, or times out + msg: hikari.UndefinedOr[hikari.Message] = hikari.UNDEFINED while True: logger.debug(f"Requesting task of type {task_type}") task = await oasst_api.fetch_task(task_type, user) - resp, msg_id = await _send_task(ctx, task) + resp, msg = await _send_task(ctx, task, msg) + msg_id = str(msg.id) logger.debug(f"User choice: {resp}") match resp: @@ -179,25 +251,24 @@ async def _select_task( case "next": logger.info(f"Task {task.id} rejected, sending NACK") await oasst_api.nack_task(task.id, "rejected") - await ctx.author.send("Sending next task...") continue case "cancel": logger.info(f"Task {task.id} canceled, sending NACK") await oasst_api.nack_task(task.id, "canceled") - await ctx.author.send("Task canceled. Exiting") + await ctx.author.send(embed=plain_embed("Task canceled. Exiting")) return None, msg_id case None: logger.info(f"Task {task.id} timed out, sending NACK") await oasst_api.nack_task(task.id, "timed out") - await ctx.author.send("Task timed out. Exiting") + await ctx.author.send(embed=plain_embed("Task timed out. Exiting")) return None, msg_id async def _send_task( - ctx: lightbulb.SlashContext, task: protocol_schema.Task -) -> tuple[t.Literal["accept", "next", "cancel"] | None, str]: + ctx: lightbulb.Context, task: protocol_schema.Task, msg: hikari.UndefinedOr[hikari.Message] +) -> tuple[t.Literal["accept", "next", "cancel"] | None, hikari.Message]: """Send a task to the user. Returns the user's choice and the message ID of the task message. @@ -206,37 +277,38 @@ async def _send_task( # but the tasks aren't discord specific so that doesn't really make sense. embed: hikari.UndefinedOr[hikari.Embed] = hikari.UNDEFINED + content: hikari.UndefinedOr[str] = hikari.UNDEFINED # Create an embed based on the task's type if task.type == TaskRequestType.initial_prompt: assert isinstance(task, protocol_schema.InitialPromptTask) logger.debug("sending initial prompt task") - embed = _initial_prompt_embed(task) + content = initial_prompt_message(task) elif task.type == TaskRequestType.rank_initial_prompts: assert isinstance(task, protocol_schema.RankInitialPromptsTask) logger.debug("sending rank initial prompt task") - embed = _rank_initial_prompt_embed(task) + content = rank_initial_prompts_message(task) elif task.type == TaskRequestType.rank_prompter_replies: assert isinstance(task, protocol_schema.RankPrompterRepliesTask) logger.debug("sending rank user reply task") - embed = _rank_prompter_reply_embed(task) + content = rank_prompter_reply_message(task) elif task.type == TaskRequestType.rank_assistant_replies: assert isinstance(task, protocol_schema.RankAssistantRepliesTask) logger.debug("sending rank assistant reply task") - embed = _rank_assistant_reply_embed(task) + content = rank_assistant_reply_message(task) elif task.type == TaskRequestType.prompter_reply: assert isinstance(task, protocol_schema.PrompterReplyTask) logger.debug("sending user reply task") - embed = _prompter_reply_embed(task) + content = prompter_reply_message(task) elif task.type == TaskRequestType.assistant_reply: assert isinstance(task, protocol_schema.AssistantReplyTask) logger.debug("sending assistant reply task") - embed = _assistant_reply_embed(task) + content = assistant_reply_message(task) elif task.type == TaskRequestType.summarize_story: raise NotImplementedError @@ -248,24 +320,34 @@ async def _send_task( raise ValueError(f"unknown task type {task.type}") view = TaskAcceptView(timeout=MAX_TASK_ACCEPT_TIME) - msg = await ctx.author.send( - EMPTY, - embed=embed, - components=view, - ) + if not msg: + msg = await ctx.author.send( + content, + embed=embed, + components=view, + ) + else: + await msg.edit( + content, + embed=embed, + components=view, + ) assert msg is not None + # Set the choice id as the current msg id + ctx.bot.d.currently_working[ctx.author.id] = (msg, task.id) + await view.start(msg) await view.wait() - return view.choice, str(msg.id) + return view.choice, msg -def _validate_user_input(content: str | None, task: protocol_schema.Task) -> bool: - """Returns whether the user's input is valid for the task type.""" +def _validate_user_input(content: str | None, task: protocol_schema.Task) -> tuple[bool, str]: + """Returns whether the user's input is valid for the task type and an error message.""" if content is None: - return False + return False, "No input provided" # User message input if ( @@ -277,22 +359,28 @@ def _validate_user_input(content: str | None, task: protocol_schema.Task) -> boo task, protocol_schema.InitialPromptTask | protocol_schema.PrompterReplyTask | protocol_schema.AssistantReplyTask, ) - return len(content) > 0 + return len(content) > 0, "Message must be at least one character long." # Ranking tasks elif task.type == TaskRequestType.rank_prompter_replies or task.type == TaskRequestType.rank_assistant_replies: assert isinstance(task, protocol_schema.RankPrompterRepliesTask | protocol_schema.RankAssistantRepliesTask) num_replies = len(task.replies) - rankings = content.split(",") - return set(rankings) == {str(i) for i in range(1, num_replies + 1)} and len(rankings) == num_replies + rankings = content.replace(" ", "").split(",") + return ( + set(rankings) == {str(i) for i in range(1, num_replies + 1)} and len(rankings) == num_replies, + "Message must contain numbers for all replies.", + ) elif task.type == TaskRequestType.rank_initial_prompts: assert isinstance(task, protocol_schema.RankInitialPromptsTask) num_prompts = len(task.prompts) - rankings = content.split(",") - return set(rankings) == {str(i) for i in range(1, num_prompts + 1)} and len(rankings) == num_prompts + rankings = content.replace(" ", "").split(",") + return ( + set(rankings) == {str(i) for i in range(1, num_prompts + 1)} and len(rankings) == num_prompts, + "Message must contain numbers for all prompts.", + ) elif task.type == TaskRequestType.summarize_story: raise NotImplementedError @@ -316,22 +404,29 @@ class TaskAcceptView(miru.View): async def accept_button(self, button: miru.Button, ctx: miru.ViewContext) -> None: logger.info("Accept button pressed") self.choice = "accept" + await ctx.message.edit(component=None) self.stop() @miru.button(label="Next Task", custom_id="next_task", row=0, style=hikari.ButtonStyle.SECONDARY) async def next_button(self, button: miru.Button, ctx: miru.ViewContext) -> None: logger.info("Next button pressed") self.choice = "next" + await ctx.message.edit(component=None) self.stop() @miru.button(label="Cancel", custom_id="cancel", row=0, style=hikari.ButtonStyle.DANGER) async def cancel_button(self, button: miru.Button, ctx: miru.ViewContext) -> None: logger.info("Cancel button pressed") self.choice = "cancel" + await ctx.message.edit(component=None) self.stop() + async def on_timeout(self) -> None: + if self.message is not None: + await self.message.edit(component=None) -class ChoiceView(miru.View): + +class YesNoView(miru.View): """View with two buttons: yes and no. The view stops once one of the buttons is pressed and the choice is stored in the `choice` attribute. @@ -342,115 +437,18 @@ class ChoiceView(miru.View): @miru.button(label="Yes", custom_id="yes", style=hikari.ButtonStyle.SUCCESS) async def yes_button(self, button: miru.Button, ctx: miru.ViewContext) -> None: self.choice = True + await ctx.message.edit(component=None) self.stop() @miru.button(label="No", custom_id="no", style=hikari.ButtonStyle.DANGER) async def no_button(self, button: miru.Button, ctx: miru.ViewContext) -> None: self.choice = False + await ctx.message.edit(component=None) self.stop() - -################################################################ -# Template Embeds # -################################################################ - -# TODO: Maybe implement a better way of creating embeds, like `from_json` or something - - -def _initial_prompt_embed(task: protocol_schema.InitialPromptTask) -> hikari.Embed: - return ( - hikari.Embed(title="Initial Prompt", description=f"Hint: {task.hint}", timestamp=datetime.now().astimezone()) - .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") - .set_footer(text=f"OASST Assistant | {task.id}") - ) - - -def _rank_initial_prompt_embed(task: protocol_schema.RankInitialPromptsTask) -> hikari.Embed: - embed = ( - hikari.Embed( - title="Rank Initial Prompt", - description="Rank the following tasks from best to worst (1,2,3,4,5)", - timestamp=datetime.now().astimezone(), - ) - .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") - .set_footer(text=f"OASST Assistant | {task.id}") - ) - - for i, prompt in enumerate(task.prompts): - embed.add_field(name=f"Prompt {i + 1}", value=prompt, inline=False) - - return embed - - -def _rank_prompter_reply_embed(task: protocol_schema.RankPrompterRepliesTask) -> hikari.Embed: - embed = ( - hikari.Embed( - title="Rank User Reply", - description="Rank the following user replies from best to worst. e.g. 1,2,5,3,4", - timestamp=datetime.now().astimezone(), - ) - .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: update image - .set_footer(text=f"OASST Assistant | {task.id}") - ) - - for i, reply in enumerate(task.replies): - embed.add_field(name=f"Reply {i + 1}", value=reply, inline=False) - - return embed - - -def _rank_assistant_reply_embed(task: protocol_schema.RankAssistantRepliesTask) -> hikari.Embed: - embed = ( - hikari.Embed( - title="Rank Assistant Reply", - description="Rank the following assistant replies from best to worst. e.g. 1,2,5,3,4", - timestamp=datetime.now().astimezone(), - ) - .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: update image - .set_footer(text=f"OASST Assistant | {task.id}") - ) - - for i, reply in enumerate(task.replies): - embed.add_field(name=f"Reply {i + 1}", value=reply, inline=False) - - return embed - - -def _prompter_reply_embed(task: protocol_schema.PrompterReplyTask) -> hikari.Embed: - embed = ( - hikari.Embed( - title="User Reply", - description=f"""\ - Send the next message in the conversation as if you were the user. - {'Hint: ' if task.hint else ''} - """, - timestamp=datetime.now().astimezone(), - ) - # .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: change image - .set_footer(text=f"OASST Assistant | {task.id}") - ) - - for message in task.conversation.messages: - embed.add_field(name="Assistant" if message.is_assistant else "User", value=message.text, inline=False) - - return embed - - -def _assistant_reply_embed(task: protocol_schema.AssistantReplyTask) -> hikari.Embed: - embed = ( - hikari.Embed( - title="User Reply", - description="Send the next message in the conversation as if you were the user.", - timestamp=datetime.now().astimezone(), - ) - # .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: change image - .set_footer(text=f"OASST Assistant | {task.id}") - ) - - for message in task.conversation.messages: - embed.add_field(name="Assistant" if message.is_assistant else "User", value=message.text, inline=False) - - return embed + async def on_timeout(self) -> None: + if self.message is not None: + await self.message.edit(component=None) def load(bot: lightbulb.BotApp): diff --git a/discord-bot/bot/messages.py b/discord-bot/bot/messages.py new file mode 100644 index 00000000..0f29511a --- /dev/null +++ b/discord-bot/bot/messages.py @@ -0,0 +1,207 @@ +"""All user-facing messages and embeds.""" + +from datetime import datetime + +import hikari +from oasst_shared.schemas import protocol as protocol_schema + +NUMBER_EMOJIS = [":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":ten:"] +NL = "\n" + +### +# Reusable 'components' +### + + +def _h1(text: str) -> str: + return f"\n:small_blue_diamond: __**{text}**__ :small_blue_diamond:" + + +def _h2(text: str) -> str: + return f"__**{text}**__" + + +def _h3(text: str) -> str: + return f"__{text}__" + + +def _writing_prompt(text: str) -> str: + return f":pencil: _{text}_" + + +def _ranking_prompt(text: str) -> str: + return f":trophy: _{text}_" + + +def _response_prompt(text: str) -> str: + return f":speech_balloon: _{text}_" + + +def _summarize_prompt(text: str) -> str: + return f":notepad_spiral: _{text}_" + + +def _user(text: str | None) -> str: + return f"""\ +:person_red_hair: {_h3("User")}:{f"{NL}> **{text}**" if text is not None else ""} +""" + + +def _assistant(text: str | None) -> str: + return f"""\ +:robot: {_h3("Assistant")}:{f"{NL}> {text}" if text is not None else ""} +""" + + +def _make_ordered_list(items: list[str]) -> list[str]: + return [f"{num} {item}" for num, item in zip(NUMBER_EMOJIS, items)] + + +def _ordered_list(items: list[str]) -> str: + return "\n\n".join(_make_ordered_list(items)) + + +def _conversation(conv: protocol_schema.Conversation) -> str: + return "\n".join([_assistant(msg.text) if msg.is_assistant else _user(msg.text) for msg in conv.messages]) + + +def _hint(hint: str | None) -> str: + return f"{NL}Hint: {hint}" if hint else "" + + +### +# Messages +### + + +def initial_prompt_message(task: protocol_schema.InitialPromptTask) -> str: + """Creates the message that gets sent to users when they request an `initial_prompt` task.""" + return f"""\ + +{_h1("INITIAL PROMPT")} + +{_writing_prompt("Please provide an initial prompt to the assistant.")} +{_hint(task.hint)} +""" + + +def rank_initial_prompts_message(task: protocol_schema.RankInitialPromptsTask) -> str: + """Creates the message that gets sent to users when they request a `rank_initial_prompts` task.""" + return f"""\ + +{_h1("RANK INITIAL PROMPTS")} + +{_ranking_prompt("Reply with the numbers of best to worst prompts separated by commas (example: '4,1,3,2')")} + + +{_ordered_list(task.prompts)} +""" + + +def rank_prompter_reply_message(task: protocol_schema.RankPrompterRepliesTask) -> str: + """Creates the message that gets sent to users when they request a `rank_prompter_replies` task.""" + return f"""\ + +{_h1("RANK PROMPTER REPLIES")} + +{_ranking_prompt("Reply with the numbers of best to worst replies separated by commas (example: '4,1,3,2')")} + + +{_conversation(task.conversation)} +{_user(None)} +{_ordered_list(task.replies)} +""" + + +def rank_assistant_reply_message(task: protocol_schema.RankAssistantRepliesTask) -> str: + """Creates the message that gets sent to users when they request a `rank_assistant_replies` task.""" + return f"""\ + +{_h1("RANK ASSISTANT REPLIES")} + +{_ranking_prompt("Reply with the numbers of best to worst replies separated by commas (example: '4,1,3,2')")} + + +{_conversation(task.conversation)} +{_assistant(None)} +{_ordered_list(task.replies)} +""" + + +def prompter_reply_message(task: protocol_schema.PrompterReplyTask) -> str: + """Creates the message that gets sent to users when they request a `prompter_reply` task.""" + return f"""\ + +{_h1("PROMPTER REPLY")} + +{_response_prompt("Please provide a reply to the assistant.")} + + +{_conversation(task.conversation)} +{_hint(task.hint)} +""" + + +def assistant_reply_message(task: protocol_schema.AssistantReplyTask) -> str: + """Creates the message that gets sent to users when they request a `assistant_reply` task.""" + return f"""\ +{_h1("ASSISTANT REPLY")} + +{_response_prompt("Please provide a reply to the assistant.")} + + +{_conversation(task.conversation)} +""" + + +def confirm_text_response_message(content: str) -> str: + return f"""\ +{_h2("CONFIRM RESPONSE")} + +> {content} +""" + + +def confirm_ranking_response_message(content: str, items: list[str]) -> str: + user_rankings = [int(r) for r in content.replace(" ", "").split(",")] + original_list = _make_ordered_list(items) + user_ranked_list = "\n\n".join([original_list[r - 1] for r in user_rankings]) + + return f"""\ +{_h2("CONFIRM RESPONSE")} + +{user_ranked_list} +""" + + +### +# Embeds +### + + +def task_complete_embed(task: protocol_schema.Task, mention: str) -> hikari.Embed: + return ( + hikari.Embed( + title="Task Completion", + description=f"`{task.type}` completed by {mention}", + color=hikari.Color(0x00FF00), + timestamp=datetime.now().astimezone(), + ) + .add_field("Total Tasks", "0", inline=True) + .add_field("Server Ranking", "0/0", inline=True) + .add_field("Global Ranking", "0/0", inline=True) + .set_footer(f"Task ID: {task.id}") + ) + + +def invalid_user_input_embed(error_message: str) -> hikari.Embed: + return hikari.Embed( + title="Invalid User Input", + description=error_message, + color=hikari.Color(0xFF0000), + timestamp=datetime.now().astimezone(), + ) + + +def plain_embed(text: str) -> hikari.Embed: + return hikari.Embed(color=0x36393F, description=text) diff --git a/discord-bot/bot/settings.py b/discord-bot/bot/settings.py index 24c837a3..a2e2c2ba 100644 --- a/discord-bot/bot/settings.py +++ b/discord-bot/bot/settings.py @@ -8,7 +8,7 @@ class Settings(BaseSettings): bot_token: str = Field(env="BOT_TOKEN", default="") declare_global_commands: int = Field(env="DECLARE_GLOBAL_COMMANDS", default=0) owner_ids: list[int] = Field(env="OWNER_IDS", default_factory=list) - prefix: str = Field(env="PREFIX", default="./") + prefix: str = Field(env="PREFIX", default="/") oasst_api_url: str = Field(env="OASST_API_URL", default="http://localhost:8080") oasst_api_key: str = Field(env="OASST_API_KEY", default="") diff --git a/discord-bot/bot/utils.py b/discord-bot/bot/utils.py index 2d968c93..530f402a 100644 --- a/discord-bot/bot/utils.py +++ b/discord-bot/bot/utils.py @@ -24,13 +24,6 @@ def format_time(dt: datetime, fmt: t.Literal["t", "T", "D", "f", "F", "R"]) -> s raise ValueError(f"`fmt` must be 't', 'T', 'D', 'f', 'F' or 'R', not {fmt}") -EMPTY = "\u200d" -"""Zero-width joiner. - -This appears as an empty message in Discord. -""" - - def mention( id: hikari.Snowflakeish, type: t.Literal["channel", "role", "user"], diff --git a/discord-bot/requirements.txt b/discord-bot/requirements.txt index b65b5e15..f6943cb0 100644 --- a/discord-bot/requirements.txt +++ b/discord-bot/requirements.txt @@ -1,5 +1,3 @@ -aiohttp # http client -aiohttp[speedups] # speedups for aiohttp aiosqlite # database hikari # discord framework hikari-lightbulb # command handler diff --git a/docker-compose.yaml b/docker-compose.yaml index dc147c73..6bc42c51 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,7 +9,7 @@ services: # Use `docker compose up frontend-dev --build --attach-dependencies` to start all services needed to work on the frontend. frontend-dev: image: sverrirab/sleep - depends_on: [db, webdb, adminer, maildev, backend] + depends_on: [db, webdb, adminer, maildev, backend, redis] # This DB is for the FastAPI Backend. db: diff --git a/docker/Dockerfile.backend b/docker/Dockerfile.backend index d9458ae0..1f3bdfcd 100644 --- a/docker/Dockerfile.backend +++ b/docker/Dockerfile.backend @@ -5,6 +5,7 @@ COPY ./backend/requirements.txt /app/requirements.txt RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt ENV PORT 8080 +EXPOSE 8080 COPY ./oasst-shared /oasst-shared RUN pip install -e /oasst-shared diff --git a/docs/data_schemas.md b/docs/data_schemas.md index 0bb9a96c..351e6bd4 100644 --- a/docs/data_schemas.md +++ b/docs/data_schemas.md @@ -99,6 +99,9 @@ The main tasks are a) generation of response text and b) ranking of responses. The following sections describe the data schemas for each of these tasks. Both should be implementable in parquet files. +Note: These files are meant to be consumed by ML algorithms and should ideally +be produced from the above files. + ## Common Data Structures ```python diff --git a/model/supervised_finetuning/README.md b/model/supervised_finetuning/README.md new file mode 100644 index 00000000..e223e1cd --- /dev/null +++ b/model/supervised_finetuning/README.md @@ -0,0 +1,38 @@ +# Train using supervised examples + +Requirements + +``` +wandb +evaluate +datasets +transformers +torch +``` + +Start training reward model + +```bash +python trainer.py --configs defaults galactica-125 +``` + +## Dataset + +For now we only support webgpt and summary dataset from OpenAI. Once +open-asisstant dataset are available it will be added here. + +## Model + +TBD + +## Results + +Experimental results in wandb +[here](https://wandb.ai/sanagnos/supervised-finetuning?workspace=user-sanagnos). + +## TODOS + +- decide on a model +- add special token to declare prompt and reply. Do nto freeze the weights for + these +- Merge utils etc with reward model diff --git a/model/supervised_finetuning/configs/config.yaml b/model/supervised_finetuning/configs/config.yaml new file mode 100644 index 00000000..f7164002 --- /dev/null +++ b/model/supervised_finetuning/configs/config.yaml @@ -0,0 +1,37 @@ +defaults: + learning_rate: 1e-5 + gradient_checkpointing: false + gradient_accumulation_steps: 32 + per_device_train_batch_size: 2 + per_device_eval_batch_size: 2 + weight_decay: 0.00 + warmup_steps: 600 + eval_steps: 200 + save_steps: 500 + max_length: 512 + num_train_epochs: 3 + logging_steps: 10 + max_grad_norm: 2.0 + save_total_limit: 4 + eval_accumulation_steps: + freeze_layer: + datasets: + - webgpt + cache_dir: ~/.cache + loss_fn: CrossEntropyLoss + eval_size: + log_dir: "base" + +galactica-125: + learning_rate: 5e-5 + model_name: facebook/galactica-125m + weight_decay: 0.01 + warmup_steps: 600 + gradient_checkpointing: false + gradient_accumulation_steps: 2 + per_device_train_batch_size: 4 + per_device_eval_batch_size: 4 + +debug: + eval_steps: 20 + eval_size: 100 diff --git a/model/supervised_finetuning/custom_datasets/__init__.py b/model/supervised_finetuning/custom_datasets/__init__.py new file mode 100644 index 00000000..fcab8a56 --- /dev/null +++ b/model/supervised_finetuning/custom_datasets/__init__.py @@ -0,0 +1,67 @@ +from datasets import load_dataset +from sklearn.model_selection import train_test_split +from torch.utils.data import Dataset, Subset + + +class SquadV2Dataset(Dataset): + def __init__(self, cache_dir, split): + self.dataset = load_dataset("squad_v2", cache_dir=cache_dir, split=split) + + def __len__(self): + return len(self.dataset) + + def __getitem__(self, idx): + data = self.dataset[idx] + # dummy return first answer + return "".join([data["title"], ". ", data["context"], " " + data["question"]]), data["answers"]["text"][0] + + +class WebGPT(Dataset): + def __init__(self) -> None: + super().__init__() + + dataset = load_dataset("openai/webgpt_comparisons") + questions = {} + # using prompt as our index will allows us + # to add additional generated prompt later + self.index2question = {} + for row in dataset["train"]: + question = row["question"]["full_text"] + if question not in self.index2question: + self.index2question[len(self.index2question)] = question + + # only keep the best answer + questions[question] = row["answer_0" if row["score_0"] > row["score_1"] else "answer_1"] + + self.questions = questions + + def __len__(self): + return len(self.index2question) + + def __getitem__(self, index): + question = self.index2question[index] + answer = self.questions[question] + return [question, answer] + + +def train_val_dataset(dataset, val_split=0.2): + train_idx, val_idx = train_test_split( + list(range(len(dataset))), test_size=val_split, random_state=666, shuffle=True + ) + return Subset(dataset, train_idx), Subset(dataset, val_idx) + + +def get_one_dataset(conf, dataset_name): + dataset_name = dataset_name.lower() + + if dataset_name == "squadv2": + raise ValueError("SquadV2 is not diverse enough for generation .. ") + train = SquadV2Dataset(conf.cache_dir, "train") + eval = SquadV2Dataset(conf.cache_dir, "validation") + elif dataset_name == "webgpt": + dataset = WebGPT() + train, eval = train_val_dataset(dataset, val_split=0.2) + else: + raise ValueError(f"Unknown dataset {dataset_name}") + + return train, eval diff --git a/model/supervised_finetuning/custom_datasets/dialogue_collator.py b/model/supervised_finetuning/custom_datasets/dialogue_collator.py new file mode 100644 index 00000000..17fe1082 --- /dev/null +++ b/model/supervised_finetuning/custom_datasets/dialogue_collator.py @@ -0,0 +1,85 @@ +from dataclasses import dataclass +from typing import Optional, Union + +import numpy as np +import torch +from torch.nn import functional as F +from transformers.tokenization_utils_base import PaddingStrategy, PreTrainedTokenizerBase + + +@dataclass +class DialogueDataCollator: + """ + Expects a list of texts corresponding to a sequence of [question, answer, question, answer, ...] pairs. + """ + + tokenizer: PreTrainedTokenizerBase + padding: Union[bool, str, PaddingStrategy] = True + max_length: Optional[int] = None + pad_to_multiple_of: Optional[int] = None + + def __call__(self, features): + # TODO add special tokens for question and answer here + # additional_special_tokens = ['', ''] + prompt_tokens = ["Question: ", "Answer: "] + + flatten_messages = [] + label_masks = [] + + for messages in features: + assert len(messages) % 2 == 0, "Number of messages must be even" + messages = [ + (prompt_tokens[0] if i % 2 == 0 else "") + x + ((" " + prompt_tokens[1]) if i % 2 == 0 else "") + for i, x in enumerate(messages) + ] + + # Add a way for the model to terminate generation, reinitialize prompter + messages.append(prompt_tokens[0]) + + flatten_messages.append( + self.tokenizer( + "".join(messages), + truncation=True, + max_length=self.max_length, + return_offsets_mapping=True, + ) + ) + + message_change_indices = np.cumsum([len(x) for x in messages[:-1]]) + # for each token an integer indicating the index of the message it belongs to. Just to create the label mask. + # TEXT: Question: Hello, how are you? Answer: I am fine. Question: What is your name? Answer: My name is John. + # MESSAGE_INDICES: 0 0 0 0 0 0 1 1 1 2 2 2 2 2 2 3 3 3 3 + + # If no result in next, we are predicting the last termination token(s) + message_indices = list( + map( + lambda x: next((i for i, val in enumerate(message_change_indices) if val >= x), -2), + list(map(lambda x: x[1], flatten_messages[-1]["offset_mapping"])), + ) + ) + label_mask = np.roll(list(map(lambda x: x % 2 == 1, message_indices)), -1, -1) + try: + label_mask[[i for i in range(len(message_indices)) if message_indices[i] == -2][0] - 1] = True + except IndexError: + # an aftermath of padding + pass + + label_masks.append(label_mask) + flatten_messages[-1].pop("offset_mapping") + + batch = self.tokenizer.pad( + flatten_messages, + padding=self.padding, + max_length=self.max_length, + pad_to_multiple_of=self.pad_to_multiple_of, + return_tensors="pt", + ) + dim = batch["input_ids"].shape[-1] + + batch["label_masks"] = torch.stack([F.pad(torch.tensor(x), (0, dim - len(x))) for x in label_masks]) + + for k in list(batch.keys()): + if k not in ["input_ids", "attention_mask", "label_masks"]: + batch.pop(k) + + return batch diff --git a/model/supervised_finetuning/losses.py b/model/supervised_finetuning/losses.py new file mode 100644 index 00000000..795396b9 --- /dev/null +++ b/model/supervised_finetuning/losses.py @@ -0,0 +1,15 @@ +from torch import nn + + +class CrossEntropyLoss(nn.CrossEntropyLoss): + def __init__(self, weight=None, size_average=None, ignore_index=-100, reduce=None, reduction="mean"): + super(CrossEntropyLoss, self).__init__(weight, size_average, ignore_index, reduce, reduction) + + def forward(self, input, target, mask=None): + if mask is not None: + mask = mask.view(-1) + input = input.view(-1, input.size(-1)) + target = target.view(-1) + input = input[mask] + target = target[mask] + return super(CrossEntropyLoss, self).forward(input, target) diff --git a/model/supervised_finetuning/trainer.py b/model/supervised_finetuning/trainer.py new file mode 100644 index 00000000..b44890df --- /dev/null +++ b/model/supervised_finetuning/trainer.py @@ -0,0 +1,200 @@ +import argparse +import os +from dataclasses import dataclass +from distutils.util import strtobool +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +import torch +from torch import nn +from torch.utils.data import Dataset +from transformers import ( + DataCollator, + EvalPrediction, + PreTrainedModel, + PreTrainedTokenizerBase, + Trainer, + TrainerCallback, + TrainingArguments, + get_cosine_schedule_with_warmup, +) +from utils import get_dataset, get_loss, get_model, get_tokenizer, read_yamls + +os.environ["WANDB_PROJECT"] = "supervised-finetuning" + + +@dataclass +class CustomTrainingArguments(TrainingArguments): + loss_function: str = "CrossEntropyLoss" + + +def compute_metrics(eval_pred): + pred_ids = eval_pred.predictions + labels = eval_pred.label_ids + + return {"accuracy": (pred_ids[labels > 0] == labels[labels > 0]).mean()} + + +def preprocess_logits_for_metrics(logits, labels): + pred_ids = torch.argmax(logits, dim=-1) + return pred_ids + + +class SFTTrainer(Trainer): + def __init__( + self, + model: Union[PreTrainedModel, nn.Module] = None, + args: TrainingArguments = None, + data_collator: Optional[DataCollator] = None, + train_dataset: Optional[Dataset] = None, + eval_dataset: Optional[Dataset] = None, + tokenizer: Optional[PreTrainedTokenizerBase] = None, + model_init: Callable[[], PreTrainedModel] = None, + compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None, + callbacks: Optional[List[TrainerCallback]] = None, + optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None), + preprocess_logits_for_metrics: Callable[[torch.Tensor, torch.Tensor], torch.Tensor] = None, + ): + super().__init__( + model, + args, + data_collator, + train_dataset, + eval_dataset, + tokenizer, + model_init, + compute_metrics, + callbacks, + optimizers, + preprocess_logits_for_metrics, + ) + self.loss_fct = get_loss(args.loss_function) + + def fetch_scheduler(self): + return get_cosine_schedule_with_warmup( + self.optimizer, + num_warmup_steps=self.args.warmup_steps, + num_training_steps=self.num_train_steps, + num_cycles=1, + last_epoch=-1, + ) + + def compute_loss(self, model, inputs, return_outputs=False): + labels_mask = inputs.pop("label_masks") + + outputs = model(**inputs) + + loss = self.loss_fct(outputs.get("logits"), torch.roll(inputs["input_ids"], -1, -1), mask=labels_mask) + + return (loss, outputs) if return_outputs else loss + + def _compute_loss(self, model, inputs): + + labels_mask = inputs.pop("label_masks") + + inputs = self._prepare_inputs(inputs) + + outputs = model(**inputs) + + logits = outputs.get("logits") + + targets = torch.roll(inputs["input_ids"], -1, -1) + loss = self.loss_fct(outputs.get("logits"), targets, mask=labels_mask) + + return loss, logits, targets, labels_mask + + def prediction_step( + self, + model: nn.Module, + inputs: Dict[str, Union[torch.Tensor, Any]], + prediction_loss_only: bool, + ignore_keys: Optional[List[str]] = None, + ) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor], Optional[torch.Tensor]]: + + with torch.no_grad(): + loss, logits, labels, labels_mask = self._compute_loss(model, inputs) + labels[~labels_mask] = -1 + + loss = loss.mean().detach() + + if self.args.prediction_loss_only: + return (loss, None, None) + + return (loss, logits, labels) + + +def _strtobool(x): + return bool(strtobool(x)) + + +def argument_parsing(notebook=False, notebook_args=None): + parser = argparse.ArgumentParser() + parser.add_argument("--configs", nargs="+", required=True) + + if notebook: + args, remaining = parser.parse_known_args(notebook_args) + else: + args, remaining = parser.parse_known_args() + + # Config from YAML + conf = {} + configs = read_yamls("./configs") + for name in args.configs: + if "," in name: + for n in name.split(","): + conf.update(configs[n]) + else: + conf.update(configs[name]) + + # Override config from command-line + parser = argparse.ArgumentParser() + for key, value in conf.items(): + type_ = type(value) if value is not None else str + if type_ == bool: + type_ = _strtobool + parser.add_argument(f"--{key}", type=type_, default=value) + + return parser.parse_args(remaining) + + +if __name__ == "__main__": + training_conf = argument_parsing() + + model = get_model(training_conf) + tokenizer = get_tokenizer(training_conf) + + train, evals, collate_fn = get_dataset(training_conf, tokenizer) + + args = CustomTrainingArguments( + output_dir=f"{training_conf.model_name}-{training_conf.log_dir}-finetuned", + num_train_epochs=training_conf.num_train_epochs, + warmup_steps=training_conf.warmup_steps, + loss_function=training_conf.loss_fn, + learning_rate=float(training_conf.learning_rate), + fp16=True, + gradient_checkpointing=training_conf.gradient_checkpointing, + gradient_accumulation_steps=training_conf.gradient_accumulation_steps, + per_device_train_batch_size=training_conf.per_device_train_batch_size, + per_device_eval_batch_size=training_conf.per_device_eval_batch_size, + weight_decay=training_conf.weight_decay, + max_grad_norm=training_conf.max_grad_norm, + logging_steps=training_conf.logging_steps, + save_total_limit=training_conf.save_total_limit, + evaluation_strategy="steps", + eval_steps=training_conf.eval_steps, + save_steps=training_conf.save_steps, + eval_accumulation_steps=training_conf.eval_accumulation_steps, + report_to="wandb", + ) + + assert len(evals) > 0 + trainer = SFTTrainer( + model, + args, + train_dataset=train, + eval_dataset=evals, + data_collator=collate_fn, + tokenizer=tokenizer, + compute_metrics=compute_metrics, + preprocess_logits_for_metrics=preprocess_logits_for_metrics, + ) + trainer.train() diff --git a/model/supervised_finetuning/utils.py b/model/supervised_finetuning/utils.py new file mode 100644 index 00000000..4a451bed --- /dev/null +++ b/model/supervised_finetuning/utils.py @@ -0,0 +1,111 @@ +from pathlib import Path + +import yaml +from custom_datasets import get_one_dataset +from custom_datasets.dialogue_collator import DialogueDataCollator +from losses import CrossEntropyLoss +from sklearn.model_selection import train_test_split +from torch.utils.data import ConcatDataset, Subset +from transformers import AutoModelForCausalLM, AutoTokenizer + +SUPPORTED_MODELS = ["galactica"] + + +def get_tokenizer(conf): + tokenizer = AutoTokenizer.from_pretrained(conf.model_name, cache_dir=conf.cache_dir) + + if "galactica" in conf.model_name: + tokenizer.add_special_tokens({"pad_token": "", "eos_token": ""}) + + return tokenizer + + +def get_model(conf): + if not any([x in conf.model_name for x in SUPPORTED_MODELS]): + raise ValueError( + f"Model {conf.model_name} not supported. Supported models: {SUPPORTED_MODELS}. " + "To include more make sure the masking is dne correctly... (decoder only supported for now)" + ) + + model = AutoModelForCausalLM.from_pretrained(conf.model_name, cache_dir=conf.cache_dir) + + if conf.freeze_layer: + model = freeze_top_n_layers(model, conf.freeze_layer) + + model_parameters = filter(lambda p: p.requires_grad, model.parameters()) + params = sum([p.numel() for p in model_parameters]) + print("Number of trainable parameters: {}M".format(int(params / 1e6))) + + return model + + +def get_dataset(conf, tokenizer): + train_datasets, evals = [], {} + + for dataset_name in conf.datasets: + train, val = get_one_dataset(conf, dataset_name) + train_datasets.append(train) + evals[dataset_name] = Subset(val, list(range(min(len(val), conf.eval_size)))) if conf.eval_size else val + + train = ConcatDataset(train_datasets) + + collate_fn = DialogueDataCollator(tokenizer, max_length=conf.max_length) + + return train, evals, collate_fn + + +def get_loss(loss): + if loss == "CrossEntropyLoss": + return CrossEntropyLoss() + else: + raise ValueError(f"Loss {loss} not supported") + + +def read_yamls(dir): + conf = {} + no_conf = True + + for config_file in Path(dir).glob("**/*.yaml"): + no_conf = False + with config_file.open("r") as f: + conf.update(yaml.safe_load(f)) + + if no_conf: + print(f"WARNING: No yaml files found in {dir}") + + return conf + + +def train_val_dataset(dataset, val_split=0.2): + train_idx, val_idx = train_test_split( + list(range(len(dataset))), test_size=val_split, random_state=666, shuffle=True + ) + return Subset(dataset, train_idx), Subset(dataset, val_idx) + + +def freeze_top_n_layers(model, target_layers): + # its possible we can simply detect which module is a ModuleList + # and simply freeze the module without doing string parsing + for name, param in model.named_parameters(): + if "embed" in name: + param.requires_grad = False + elif ".layer" in name or ".h." in name: + tokens = name.split(".") + layer_ = None + for token in tokens: + if token.isdigit(): + layer_ = int(token) + break + + if layer_ is not None and layer_ < target_layers: + # print('freeze ', layer_, name) + param.requires_grad = False + return model + + +if __name__ == "__main__": + from transformers import AutoModelForSequenceClassification + + model = AutoModelForSequenceClassification.from_pretrained("bigscience/bloomz-560m") + freeze_top_n_layers(model, 10) + print(model.state_dict().keys()) diff --git a/notebooks/data-argumentation/EssayRevision.ipynb b/notebooks/data-argumentation/EssayRevision.ipynb index 1b972579..bcd13d45 100644 --- a/notebooks/data-argumentation/EssayRevision.ipynb +++ b/notebooks/data-argumentation/EssayRevision.ipynb @@ -1,300 +1 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [], - "authorship_tag": "ABX9TyO8HHo9/NuZY8QnCvjrXaYb" - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "#Essay Revision\n", - "The goal of this notebook is to use data argumentation to have data on improving essays. The way this is done is by taking a template \"good\" essay and making step by step changes that make it worse and add intructions on how to fix it." - ], - "metadata": { - "id": "o0lAqmWhsiUe" - } - }, - { - "cell_type": "code", - "source": [ - "import nltk\n", - "\n", - "nltk.download(\"wordnet\")\n", - "nltk.download(\"omw-1.4\")" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "AFUIjc7xw25A", - "executionInfo": { - "status": "ok", - "timestamp": 1672489678465, - "user_tz": -60, - "elapsed": 240, - "user": { - "displayName": "Graverman", - "userId": "06659155231973912985" - } - }, - "outputId": "01c13cd7-7252-4948-fd9a-f36919f2214b" - }, - "execution_count": 35, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - "[nltk_data] Downloading package wordnet to /root/nltk_data...\n", - "[nltk_data] Package wordnet is already up-to-date!\n", - "[nltk_data] Downloading package omw-1.4 to /root/nltk_data...\n" - ] - }, - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "True" - ] - }, - "metadata": {}, - "execution_count": 35 - } - ] - }, - { - "cell_type": "markdown", - "source": [ - "Put your essay here, [source of the essay used ](https://www.thewisdompost.com/essay/technology-essay/3387#essay-on-technology-for-college-and-university-students-essay-2-750-words)" - ], - "metadata": { - "id": "EcDYv9cnv18v" - } - }, - { - "cell_type": "code", - "source": [ - "essay = \"\"\"\n", - "We live in a world driven by technology — hardly anyone would argue with you if you said this. Technology, literally meaning the “science of craft”, refers to the collection of techniques, skills, methods, and processes used to produce goods or services or for accomplishing objectives such as scientific investigation. Technology can be embedded in machines enabling them to be used by people even without a detailed knowledge of their inner workings.\n", - "Technological growth is closely linked to the expansion of scientific research and knowledge. In the last 50 years, thanks to the exponential increases in computing power and microchip design and manufacture, there has been unprecedented innovation and technological growth in nearly every field of human endeavour from health and transport to industrial production and education.\n", - "\n", - "It is automotive technology that drives today’s electric and hybrid cars, and which will drive tomorrow’s driverless cars, hover-taxis and space cabs.\n", - "It is technology that drives the ubiquitous mobile phones that you will now find in the hands of even the poorest of the world’s poor. It is technology that creates hybrid seeds that resist inhospitable climatic conditions and difficult terrain, giving high yields in shorter times.\n", - "It is advancing medical technology that makes remote surgery, minimally invasive surgery and life-saving cures using stem cell transplants. Technology puts spacecrafts on asteroids and distant planets and lets us see new worlds. Technology splits atoms, revealing their secrets, and gives us ways to exploit them to create energy, quantum storage for data, and virtual reality games.\n", - "\n", - "There are people who strongly oppose technology and claim that it spells the death of ‘humanity’, and that we are approaching the day when machines will rule everything. They refer to fans of technology as ‘techies’ or sometimes ‘geeks’. On the other hand, proponents of technology call these people Luddites, a derogatory name for someone who is opposed to industrialisation, automation, computerisation and new technologies in general.\n", - "Is this true? Is technology really a curse disguised as a blessing? Many believe that the convergence of biotechnology and AI might be the most consequential development of all.\n", - "\n", - "In the last five decades, two areas in particular have grown faster than the rest, powered by research and advances in computing power. One is artificial intelligence, or AI; the other is biotechnology. Huge benefits have emerged from each of them for human beings in general, such as self-driving cars — which will dramatically reduce the death rate from road accidents — and robotic surgery, which enables precise, highly efficient and targeted surgical interventions.\n", - "Yet, visionaries like Yuval Noah Harari, author of the best-selling Homo sapiens and Deus, are now warning that the convergence of biotechnology and AI will irreversibly and unpredictably change both the quality of human life and its challenges in the next few decades. A good example of this is the facial recognition technology that is now present in all photo management programs. The AI in the software is capable of not only spotting the faces in every photograph but also recognising the person by name.\n", - "This technology has now expanded so that photo apps can recognise cats, dogs, beaches, mountains and cars too. Computers with AI are already correctly identifying human emotions through observing facial expressions and body movements. Some robots are able to mimic human emotions. This is called affective computing, sometimes called artificial emotional intelligence, and refers to the study and development of systems and devices that can recognize, interpret, process, and simulate human affects.\n", - "\n", - "The ability to read human emotions is just a step away from predicting human emotions. For example, if a computer attached to a video camera could identify which products a consumer is showing greater interest in or which ones he is really keen to buy, various tactics could be used to influence her to buy it.\n", - "Activists worry that computers that can understand and anticipate human wishes and desires by scanning their irises and analysing their micro-expressions could also be programmed to exploit and manipulate them.\n", - "Another very real fear is that humanoid computers with human-like skin, speech, and expressions could jeopardise and dehumanise relationship and create emotional vacuums.\n", - "\n", - "An enduring fear of Luddites has always been that computers will rob humans of their livelihood by taking their jobs and doing them more efficiently at lower cost. However, in reality the exact opposite has happened. As computerised machines began taking over mechanical and repetitive human activities, new jobs for people opened up that needs thinking and analytical skills and judgement, or human interpersonal skills. A good example is the worldwide proliferation of call centres.\n", - "When drones were invented many feared that pilots would soon be redundant. However, few people know that it takes almost 30 people to fly one military drone, and an additional 50 people to analyze and make sense of the data being streamed back by the drone.\n", - "The US army suffers from a serious shortage of trained, high quality drone pilots; anyone who masters this skill will have a job. But a social scientist warns that in 10 years, it is certain that computers will be flying that drone and humans will be redundant. Equally sure is that some brand new skill requirement will have opened up with advancing technology, calling for new talents.\n", - "\n", - "In the 20th century, a young man was supposed to choose a skill, vocation or profession, master it through education and practice, and then earn a living from it till he or she retired. However, the fast-changing nature of technology is making skills obsolete at a higher rate than ever before. To survive, tomorrow young man must keep re-inventing himself and updating his skills continuously. Life could be difficult if every new skill has a shelf life of only a decade or so.\n", - "Or perhaps one could look at it the other way — and say that changing technology will keep human beings on their toes throughout their life.\n", - "\n", - "Technology is the result of human inventiveness. It reflects our evolutionary heritage. We are neither strong like gorillas or tigers, nor fast like cheetahs and hawks, but our brains and thinking powers have given us the greatest edge of any species on the planet. Technology is a result.\n", - "Technology is either inherently good or bad; it is how we use it that makes it so. The splitting of a hydrogen atom is technology at work. As history has shown us, technology can equally be used to make a nuclear bomb that kills millions — or generate electricity that lights up a million homes.\n", - "\"\"\"" - ], - "metadata": { - "id": "wvJHUeTJsiC7", - "executionInfo": { - "status": "ok", - "timestamp": 1672490871113, - "user_tz": -60, - "elapsed": 250, - "user": { - "displayName": "Graverman", - "userId": "06659155231973912985" - } - } - }, - "execution_count": 58, - "outputs": [] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "id": "_ttU0Ma8p1_U", - "executionInfo": { - "status": "ok", - "timestamp": 1672487908938, - "user_tz": -60, - "elapsed": 5, - "user": { - "displayName": "Graverman", - "userId": "06659155231973912985" - } - } - }, - "outputs": [], - "source": [ - "instructions = []" - ] - }, - { - "cell_type": "code", - "source": [ - "# Make stucture error (shuffle one paragraph with another)\n", - "essay_paragraphs = essay.split(\"\\n\\n\")\n", - "\n", - "rand1 = random.randint(0, len(essay_paragraphs) - 1)\n", - "rand2 = random.randint(0, len(essay_paragraphs) - 1)\n", - "\n", - "temp = essay_paragraphs[rand1]\n", - "essay_paragraphs[rand1] = essay_paragraphs[rand2]\n", - "essay_paragraphs[rand2] = temp\n", - "\n", - "essay = \"\"\n", - "for i in essay_paragraphs:\n", - " essay += i\n", - " essay += \"\\n\\n\"\n", - "\n", - "instructions.append(\"Fix structure errors in this essay\")" - ], - "metadata": { - "id": "Evaej8oH8VLH", - "executionInfo": { - "status": "ok", - "timestamp": 1672490937384, - "user_tz": -60, - "elapsed": 232, - "user": { - "displayName": "Graverman", - "userId": "06659155231973912985" - } - } - }, - "execution_count": 64, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "# Make grammar erros (more like: change random words into words of similar meaning)\n", - "import nltk\n", - "from nltk.corpus import wordnet\n", - "import random\n", - "\n", - "essay_words = essay.split()\n", - "\n", - "for i in range(len(essay_words)):\n", - " if random.randint(0, 100) < 30:\n", - " suggestion = []\n", - " for syn in wordnet.synsets(essay_words[i]):\n", - " for l in syn.lemmas():\n", - " suggestion.append(l.name())\n", - " if suggestion != []:\n", - " essay_words[i] = suggestion[random.randint(0, len(suggestion) - 1)]\n", - "\n", - "essay = \"\"\n", - "for i in essay_words:\n", - " essay += i\n", - " essay += \" \"\n", - "\n", - "\n", - "instructions.append(\"Fix grammar errors in this essay\")" - ], - "metadata": { - "id": "HhJXyfy-2OmT", - "executionInfo": { - "status": "ok", - "timestamp": 1672490091374, - "user_tz": -60, - "elapsed": 257, - "user": { - "displayName": "Graverman", - "userId": "06659155231973912985" - } - } - }, - "execution_count": 43, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "# Make typos\n", - "import string\n", - "import random\n", - "\n", - "# you can change the number 60 to change how much corrupted this essay will be\n", - "for i in range(len(essay) // 60):\n", - " rand = random.randint(0, len(essay))\n", - " essay = essay[:rand] + random.choice(string.ascii_letters) + essay[rand + 1 :]\n", - "\n", - "instructions.append(\"Fix typing errors in this essay\")" - ], - "metadata": { - "id": "delvA6xEzNwV", - "executionInfo": { - "status": "ok", - "timestamp": 1672490096010, - "user_tz": -60, - "elapsed": 231, - "user": { - "displayName": "Graverman", - "userId": "06659155231973912985" - } - } - }, - "execution_count": 44, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "# Prints intrcutions (final step)\n", - "for i in instructions:\n", - " print(i)" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "4XLAXom_zGsR", - "executionInfo": { - "status": "ok", - "timestamp": 1672484222869, - "user_tz": -60, - "elapsed": 364, - "user": { - "displayName": "Graverman", - "userId": "06659155231973912985" - } - }, - "outputId": "b741c776-41af-4ad5-8ab7-1825b19018ab" - }, - "execution_count": 8, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Fix typing errors in this essay\n" - ] - } - ] - } - ] -} +{"cells":[{"cell_type":"markdown","metadata":{"id":"o0lAqmWhsiUe"},"source":["#Essay Revision\n","The goal of this notebook is to use data argumentation to have data on improving essays. The way this is done is by taking a template \"good\" essay and making step by step changes that make it worse and add intructions on how to fix it."]},{"cell_type":"code","execution_count":5,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":240,"status":"ok","timestamp":1672489678465,"user":{"displayName":"Graverman","userId":"06659155231973912985"},"user_tz":-60},"id":"AFUIjc7xw25A","outputId":"01c13cd7-7252-4948-fd9a-f36919f2214b"},"outputs":[{"name":"stderr","output_type":"stream","text":["[nltk_data] Downloading package wordnet to\n","[nltk_data] C:\\Users\\Chandru\\AppData\\Roaming\\nltk_data...\n","[nltk_data] Package wordnet is already up-to-date!\n","[nltk_data] Downloading package omw-1.4 to\n","[nltk_data] C:\\Users\\Chandru\\AppData\\Roaming\\nltk_data...\n","[nltk_data] Package omw-1.4 is already up-to-date!\n"]}],"source":["import nltk\n","nltk.download('wordnet')\n","nltk.download('omw-1.4')\n","import random"]},{"cell_type":"markdown","metadata":{"id":"EcDYv9cnv18v"},"source":["Put your essay here, [source of the essay used ](https://www.thewisdompost.com/essay/technology-essay/3387#essay-on-technology-for-college-and-university-students-essay-2-750-words)"]},{"cell_type":"code","execution_count":6,"metadata":{"executionInfo":{"elapsed":250,"status":"ok","timestamp":1672490871113,"user":{"displayName":"Graverman","userId":"06659155231973912985"},"user_tz":-60},"id":"wvJHUeTJsiC7"},"outputs":[],"source":["essay = \"\"\"\n","We live in a world driven by technology — hardly anyone would argue with you if you said this. Technology, literally meaning the “science of craft”, refers to the collection of techniques, skills, methods, and processes used to produce goods or services or for accomplishing objectives such as scientific investigation. Technology can be embedded in machines enabling them to be used by people even without a detailed knowledge of their inner workings.\n","Technological growth is closely linked to the expansion of scientific research and knowledge. In the last 50 years, thanks to the exponential increases in computing power and microchip design and manufacture, there has been unprecedented innovation and technological growth in nearly every field of human endeavour from health and transport to industrial production and education.\n","\n","It is automotive technology that drives today’s electric and hybrid cars, and which will drive tomorrow’s driverless cars, hover-taxis and space cabs.\n","It is technology that drives the ubiquitous mobile phones that you will now find in the hands of even the poorest of the world’s poor. It is technology that creates hybrid seeds that resist inhospitable climatic conditions and difficult terrain, giving high yields in shorter times.\n","It is advancing medical technology that makes remote surgery, minimally invasive surgery and life-saving cures using stem cell transplants. Technology puts spacecrafts on asteroids and distant planets and lets us see new worlds. Technology splits atoms, revealing their secrets, and gives us ways to exploit them to create energy, quantum storage for data, and virtual reality games.\n","\n","There are people who strongly oppose technology and claim that it spells the death of ‘humanity’, and that we are approaching the day when machines will rule everything. They refer to fans of technology as ‘techies’ or sometimes ‘geeks’. On the other hand, proponents of technology call these people Luddites, a derogatory name for someone who is opposed to industrialisation, automation, computerisation and new technologies in general.\n","Is this true? Is technology really a curse disguised as a blessing? Many believe that the convergence of biotechnology and AI might be the most consequential development of all.\n","\n","In the last five decades, two areas in particular have grown faster than the rest, powered by research and advances in computing power. One is artificial intelligence, or AI; the other is biotechnology. Huge benefits have emerged from each of them for human beings in general, such as self-driving cars — which will dramatically reduce the death rate from road accidents — and robotic surgery, which enables precise, highly efficient and targeted surgical interventions.\n","Yet, visionaries like Yuval Noah Harari, author of the best-selling Homo sapiens and Deus, are now warning that the convergence of biotechnology and AI will irreversibly and unpredictably change both the quality of human life and its challenges in the next few decades. A good example of this is the facial recognition technology that is now present in all photo management programs. The AI in the software is capable of not only spotting the faces in every photograph but also recognising the person by name.\n","This technology has now expanded so that photo apps can recognise cats, dogs, beaches, mountains and cars too. Computers with AI are already correctly identifying human emotions through observing facial expressions and body movements. Some robots are able to mimic human emotions. This is called affective computing, sometimes called artificial emotional intelligence, and refers to the study and development of systems and devices that can recognize, interpret, process, and simulate human affects.\n","\n","The ability to read human emotions is just a step away from predicting human emotions. For example, if a computer attached to a video camera could identify which products a consumer is showing greater interest in or which ones he is really keen to buy, various tactics could be used to influence her to buy it.\n","Activists worry that computers that can understand and anticipate human wishes and desires by scanning their irises and analysing their micro-expressions could also be programmed to exploit and manipulate them.\n","Another very real fear is that humanoid computers with human-like skin, speech, and expressions could jeopardise and dehumanise relationship and create emotional vacuums.\n","\n","An enduring fear of Luddites has always been that computers will rob humans of their livelihood by taking their jobs and doing them more efficiently at lower cost. However, in reality the exact opposite has happened. As computerised machines began taking over mechanical and repetitive human activities, new jobs for people opened up that needs thinking and analytical skills and judgement, or human interpersonal skills. A good example is the worldwide proliferation of call centres.\n","When drones were invented many feared that pilots would soon be redundant. However, few people know that it takes almost 30 people to fly one military drone, and an additional 50 people to analyze and make sense of the data being streamed back by the drone.\n","The US army suffers from a serious shortage of trained, high quality drone pilots; anyone who masters this skill will have a job. But a social scientist warns that in 10 years, it is certain that computers will be flying that drone and humans will be redundant. Equally sure is that some brand new skill requirement will have opened up with advancing technology, calling for new talents.\n","\n","In the 20th century, a young man was supposed to choose a skill, vocation or profession, master it through education and practice, and then earn a living from it till he or she retired. However, the fast-changing nature of technology is making skills obsolete at a higher rate than ever before. To survive, tomorrow young man must keep re-inventing himself and updating his skills continuously. Life could be difficult if every new skill has a shelf life of only a decade or so.\n","Or perhaps one could look at it the other way — and say that changing technology will keep human beings on their toes throughout their life.\n","\n","Technology is the result of human inventiveness. It reflects our evolutionary heritage. We are neither strong like gorillas or tigers, nor fast like cheetahs and hawks, but our brains and thinking powers have given us the greatest edge of any species on the planet. Technology is a result.\n","Technology is either inherently good or bad; it is how we use it that makes it so. The splitting of a hydrogen atom is technology at work. As history has shown us, technology can equally be used to make a nuclear bomb that kills millions — or generate electricity that lights up a million homes.\n","\"\"\""]},{"cell_type":"code","execution_count":7,"metadata":{"executionInfo":{"elapsed":5,"status":"ok","timestamp":1672487908938,"user":{"displayName":"Graverman","userId":"06659155231973912985"},"user_tz":-60},"id":"_ttU0Ma8p1_U"},"outputs":[],"source":["instructions = []"]},{"cell_type":"code","execution_count":8,"metadata":{"executionInfo":{"elapsed":232,"status":"ok","timestamp":1672490937384,"user":{"displayName":"Graverman","userId":"06659155231973912985"},"user_tz":-60},"id":"Evaej8oH8VLH"},"outputs":[],"source":["# Make stucture error (shuffle one paragraph with another)\n","essay_paragraphs = essay.split('\\n\\n') #Splitting a String by newline character (\\n)\n","\n","rand1 = random.randint(0, len(essay_paragraphs) - 1)\n","rand2 = random.randint(0, len(essay_paragraphs) - 1)\n","\n","temp = essay_paragraphs[rand1]\n","essay_paragraphs[rand1] = essay_paragraphs[rand2]\n","essay_paragraphs[rand2] = temp\n","\n","essay = \"\"\n","for i in essay_paragraphs:\n"," essay += i\n"," essay += \"\\n\\n\"\n","\n","instructions.append(\"Fix structure errors in this essay\")"]},{"cell_type":"code","execution_count":9,"metadata":{"executionInfo":{"elapsed":257,"status":"ok","timestamp":1672490091374,"user":{"displayName":"Graverman","userId":"06659155231973912985"},"user_tz":-60},"id":"HhJXyfy-2OmT"},"outputs":[],"source":["# Make grammar erros (more like: change random words into words of similar meaning)\n","import nltk\n","from nltk.corpus import wordnet\n","import random\n","\n","essay_words = essay.split()\n","\n","for i in range(len(essay_words)):\n"," if random.randint(0, 100) < 30:\n"," suggestion = []\n"," for syn in wordnet.synsets(essay_words[i]):\n"," for l in syn.lemmas():\n"," suggestion.append(l.name())\n"," if suggestion != []:\n"," essay_words[i] = suggestion[random.randint(0, len(suggestion) - 1)]\n","\n","essay = \"\"\n","for i in essay_words:\n"," essay += i\n"," essay += \" \"\n","\n","\n","instructions.append(\"Fix grammar errors in this essay\")"]},{"cell_type":"code","execution_count":14,"metadata":{"executionInfo":{"elapsed":231,"status":"ok","timestamp":1672490096010,"user":{"displayName":"Graverman","userId":"06659155231973912985"},"user_tz":-60},"id":"delvA6xEzNwV"},"outputs":[],"source":["# Make typos\n","import string\n","import random\n","\n","# you can change the number 60 to change how much corrupted this essay will be\n","for i in range(len(essay) // 60):\n"," rand = random.randint(0, len(essay))\n"," essay = essay[:rand] + random.choice(string.ascii_letters) + essay[rand+1:]\n","\n","instructions.append(\"Fix typing errors in this essay\")"]},{"cell_type":"code","execution_count":15,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":364,"status":"ok","timestamp":1672484222869,"user":{"displayName":"Graverman","userId":"06659155231973912985"},"user_tz":-60},"id":"4XLAXom_zGsR","outputId":"b741c776-41af-4ad5-8ab7-1825b19018ab"},"outputs":[{"name":"stdout","output_type":"stream","text":["Fix typing errors in this essay\n"]}],"source":["# Prints intrcutions (final step)\n","for i in instructions:\n"," print(i)\n","instructions.clear()"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":[]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":[]}],"metadata":{"colab":{"authorship_tag":"ABX9TyO8HHo9/NuZY8QnCvjrXaYb","provenance":[]},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.4"},"vscode":{"interpreter":{"hash":"492d89208e1af30f4727fd53e254ea56e6b1a843b376782bfa5f6ce13d676265"}}},"nbformat":4,"nbformat_minor":0} diff --git a/notebooks/detoxify-evaluation/README.md b/notebooks/detoxify-evaluation/README.md index 163f2f79..84931726 100644 --- a/notebooks/detoxify-evaluation/README.md +++ b/notebooks/detoxify-evaluation/README.md @@ -24,10 +24,13 @@ only described in the notebook Charts showing detailed memory usages and times for different sentence lengths and batch sizes are inside the notebook Quick overview batch size 16, sentence -length 4k for training, batch size 128 sentence length 4k for inference | Model -name | Training memory| Training speed | Inference Memory| Inference Speed| | -:---: | :---: | :---: |:---: | :---: | |original| 11.8GB | 2.40s| 4.8GB|16.48s| -|unbiased| 12GB| 1.09s| 4.8GB | 5.59s| |multilingual|14GB| 1.00s| 5.5GB| 4.89s| +length 4k for training, batch size 128 sentence length 4k for Inference + +| Model name | Training memory | Training speed | Inference Memory | Inference Speed | +| :----------: | :-------------: | :------------: | :--------------: | :-------------: | +| original | 11.8GB | 2.40s | 4.8GB | 16.48s | +| unbiased | 12GB | 1.09s | 4.8GB | 5.59s | +| multilingual | 14GB | 1.00s | 5.5GB | 4.89s | # Filtering quality diff --git a/oasst-shared/oasst_shared/api_client.py b/oasst-shared/oasst_shared/api_client.py index 3a575e47..404521db 100644 --- a/oasst-shared/oasst_shared/api_client.py +++ b/oasst-shared/oasst_shared/api_client.py @@ -1,12 +1,15 @@ """API Client for interacting with the OASST backend.""" import enum import typing as t +from http import HTTPStatus from typing import Optional, Type from uuid import UUID import aiohttp from loguru import logger +from oasst_shared.exceptions.oasst_api_error import OasstError, OasstErrorCode from oasst_shared.schemas import protocol as protocol_schema +from pydantic import ValidationError # TODO: Move to `protocol`? @@ -27,7 +30,7 @@ class TaskType(str, enum.Enum): class OasstApiClient: """API Client for interacting with the OASST backend.""" - def __init__(self, backend_url: str, api_key: str): + def __init__(self, backend_url: str, api_key: str, session: Optional[aiohttp.ClientSession] = None): """Create a new OasstApiClient. Args: @@ -35,8 +38,12 @@ class OasstApiClient: backend_url (str): The base backend URL. api_key (str): The API key to use for authentication. """ - logger.debug("Opening OasstApiClient session") - self.session = aiohttp.ClientSession() + + if session is None: + logger.debug("Opening OasstApiClient session") + session = aiohttp.ClientSession() + + self.session = session self.backend_url = backend_url self.api_key = api_key @@ -56,7 +63,33 @@ class OasstApiClient: """Make a POST request to the backend.""" logger.debug(f"POST {self.backend_url}{path} DATA: {data}") response = await self.session.post(f"{self.backend_url}{path}", json=data, headers={"X-API-Key": self.api_key}) - response.raise_for_status() + + # If the response is not a 2XX, check to see + # if the json has the fields to create an + # OasstError. + if response.status >= 300: + data = await response.json() + try: + oasst_error = protocol_schema.OasstErrorResponse(**(data or {})) + raise OasstError( + error_code=oasst_error.error_code, + message=oasst_error.message, + ) + except ValidationError as e: + logger.debug(f"Got error from API but could not parse: {e}") + + raw_response = await response.text() + logger.debug(f"Raw response: {raw_response}") + + raise OasstError( + raw_response, + OasstErrorCode.GENERIC_ERROR, + HTTPStatus(response.status), + ) + + if response.status == 204: + # No content + return None return await response.json() def _parse_task(self, data: Optional[dict[str, t.Any]]) -> protocol_schema.Task: diff --git a/oasst-shared/oasst_shared/schemas/protocol.py b/oasst-shared/oasst_shared/schemas/protocol.py index 652a2c78..83375d8f 100644 --- a/oasst-shared/oasst_shared/schemas/protocol.py +++ b/oasst-shared/oasst_shared/schemas/protocol.py @@ -4,6 +4,7 @@ from typing import List, Literal, Optional, Union from uuid import UUID, uuid4 import pydantic +from oasst_shared.exceptions import OasstErrorCode from pydantic import BaseModel, Field @@ -293,3 +294,10 @@ class UserScore(BaseModel): class LeaderboardStats(BaseModel): leaderboard: List[UserScore] + + +class OasstErrorResponse(BaseModel): + """The format of an error response from the OASST API.""" + + error_code: OasstErrorCode + message: str diff --git a/oasst-shared/setup.py b/oasst-shared/setup.py index 22fbcc60..a04b34e8 100644 --- a/oasst-shared/setup.py +++ b/oasst-shared/setup.py @@ -11,5 +11,7 @@ setup( author="OASST Team", install_requires=[ "pydantic==1.9.1", + "aiohttp==3.8.3", + "aiohttp[speedups]", ], ) diff --git a/oasst-shared/tests/test_oasst_api_client.py b/oasst-shared/tests/test_oasst_api_client.py index be757e8f..fdb743ce 100644 --- a/oasst-shared/tests/test_oasst_api_client.py +++ b/oasst-shared/tests/test_oasst_api_client.py @@ -1,12 +1,21 @@ +from typing import Any +from unittest import mock from uuid import uuid4 +import aiohttp import pytest from oasst_shared.api_client import OasstApiClient +from oasst_shared.exceptions import OasstError, OasstErrorCode from oasst_shared.schemas import protocol as protocol_schema @pytest.fixture def oasst_api_client_mocked(): + """ + A an oasst_api_client pointed at the mocked backend. + Relies on ./scripts/backend-development/start-mock-server.sh + being run. + """ client = OasstApiClient(backend_url="http://localhost:8080", api_key="123") yield client # TODO The fixture should close this connection, but there seems to be a bug @@ -15,6 +24,30 @@ def oasst_api_client_mocked(): # await client.close() +class MockClientSession(aiohttp.ClientSession): + response: Any + + def set_response(self, response: Any): + self.response = response + + async def post(self, *args, **kwargs): + return self.response + + +@pytest.fixture +def mock_http_session(): + yield MockClientSession() + + +@pytest.fixture +def oasst_api_client_fake_http(mock_http_session): + """ + An oasst_api_client that uses a mocked http session. No real requests are made. + """ + client = OasstApiClient(backend_url="http://localhost:8080", api_key="123", session=mock_http_session) + yield client + + @pytest.mark.asyncio @pytest.mark.parametrize("task_type", protocol_schema.TaskRequestType) async def test_can_fetch_task(task_type: protocol_schema.TaskRequestType, oasst_api_client_mocked: OasstApiClient): @@ -49,3 +82,47 @@ async def test_can_post_interaction(oasst_api_client_mocked: OasstApiClient): ) is not None ) + + +@pytest.mark.asyncio +async def test_can_handle_oasst_error_from_api( + oasst_api_client_fake_http: OasstApiClient, + mock_http_session: MockClientSession, +): + # Return a 400 response with an OasstErrorResponse body + response_body = protocol_schema.OasstErrorResponse( + error_code=OasstErrorCode.GENERIC_ERROR, + message="Some error", + ) + status_code = 400 + + mock_http_session.set_response( + mock.AsyncMock( + status=status_code, + text=mock.AsyncMock(return_value=response_body.json()), + json=mock.AsyncMock(return_value=response_body.dict()), + ) + ) + + with pytest.raises(OasstError): + await oasst_api_client_fake_http.post("/some-path", data={}) + + +@pytest.mark.asyncio +async def test_can_handle_unknown_error_from_api( + oasst_api_client_fake_http: OasstApiClient, + mock_http_session: MockClientSession, +): + response_body = "Internal Server Error" + status_code = 500 + + mock_http_session.set_response( + mock.AsyncMock( + status=status_code, + text=mock.AsyncMock(return_value=response_body), + json=mock.AsyncMock(return_value=None), + ) + ) + + with pytest.raises(OasstError): + await oasst_api_client_fake_http.post("/some-path", data={}) diff --git a/scripts/frontend-development/run-bot-local.sh b/scripts/frontend-development/run-bot-local.sh index 56833b0a..5228e751 100755 --- a/scripts/frontend-development/run-bot-local.sh +++ b/scripts/frontend-development/run-bot-local.sh @@ -4,6 +4,6 @@ parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) # switch to bot directory pushd "$parent_path/../../discord-bot" -python3 __main__.py +python3 -m bot popd diff --git a/scripts/oasst-shared-development/test.sh b/scripts/oasst-shared-development/test.sh index e9324196..fcf94beb 100755 --- a/scripts/oasst-shared-development/test.sh +++ b/scripts/oasst-shared-development/test.sh @@ -4,6 +4,8 @@ parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) # switch to backend directory pushd "$parent_path/../../oasst-shared" +set -xe + pytest . popd diff --git a/website/next.config.js b/website/next.config.js index 1cbdb0e0..2c37ebe6 100644 --- a/website/next.config.js +++ b/website/next.config.js @@ -3,7 +3,10 @@ const nextConfig = { output: "standalone", reactStrictMode: true, experimental: { - scrollRestoration: true, + /* Disabling this for now only because it causes a warning in the console that cannot be silenced for eslint + If this can be resolved, we should re-enable this. + */ + // scrollRestoration: true, }, }; diff --git a/website/package-lock.json b/website/package-lock.json index 38f7446e..1803cbca 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -12,6 +12,7 @@ "@dnd-kit/core": "^6.0.6", "@dnd-kit/modifiers": "^6.0.1", "@dnd-kit/sortable": "^7.0.1", + "@dnd-kit/utilities": "^3.2.1", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@headlessui/react": "^1.7.7", @@ -29,9 +30,11 @@ "eslint-plugin-simple-import-sort": "^8.0.0", "focus-visible": "^5.2.0", "framer-motion": "^6.5.1", + "install": "^0.13.0", "next": "13.0.6", "next-auth": "^4.18.6", "nodemailer": "^6.8.0", + "npm": "^9.2.0", "postcss-focus-visible": "^7.1.0", "react": "18.2.0", "react-dom": "18.2.0", @@ -15579,9 +15582,9 @@ } }, "node_modules/dom-accessibility-api": { - "version": "0.5.14", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", - "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.15.tgz", + "integrity": "sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==", "dev": true }, "node_modules/dom-converter": { @@ -17155,9 +17158,9 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dependencies": { "reusify": "^1.0.4" } @@ -18141,9 +18144,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz", - "integrity": "sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.3.0.tgz", + "integrity": "sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ==", "funding": { "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } @@ -19034,6 +19037,14 @@ "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", "dev": true }, + "node_modules/install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/internal-slot": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", @@ -20120,9 +20131,9 @@ } }, "node_modules/jose": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", - "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==", + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz", + "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -20214,9 +20225,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -21861,6 +21872,158 @@ "node": ">=0.10.0" } }, + "node_modules/npm": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-9.2.0.tgz", + "integrity": "sha512-oypVdaWGHDuV79RXLvp+B9gh6gDyAmoHKrQ0/JBYTWWx5D8/+AAxFdZC84fSIiyDdyW4qfrSyYGKhekxDOaMXQ==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/run-script", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "mkdirp", + "ms", + "node-gyp", + "nopt", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "read-package-json", + "read-package-json-fast", + "rimraf", + "semver", + "ssri", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^6.1.5", + "@npmcli/config": "^6.1.0", + "@npmcli/map-workspaces": "^3.0.0", + "@npmcli/package-json": "^3.0.0", + "@npmcli/run-script": "^6.0.0", + "abbrev": "^2.0.0", + "archy": "~1.0.0", + "cacache": "^17.0.3", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.3", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "graceful-fs": "^4.2.10", + "hosted-git-info": "^6.1.1", + "ini": "^3.0.1", + "init-package-json": "^4.0.1", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^3.0.0", + "libnpmaccess": "^7.0.1", + "libnpmdiff": "^5.0.6", + "libnpmexec": "^5.0.6", + "libnpmfund": "^4.0.6", + "libnpmhook": "^9.0.1", + "libnpmorg": "^5.0.1", + "libnpmpack": "^5.0.6", + "libnpmpublish": "^7.0.6", + "libnpmsearch": "^6.0.1", + "libnpmteam": "^5.0.1", + "libnpmversion": "^4.0.1", + "make-fetch-happen": "^11.0.2", + "minimatch": "^5.1.1", + "minipass": "^4.0.0", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "ms": "^2.1.2", + "node-gyp": "^9.3.0", + "nopt": "^7.0.0", + "npm-audit-report": "^4.0.0", + "npm-install-checks": "^6.0.0", + "npm-package-arg": "^10.1.0", + "npm-pick-manifest": "^8.0.1", + "npm-profile": "^7.0.1", + "npm-registry-fetch": "^14.0.3", + "npm-user-validate": "^1.0.1", + "npmlog": "^7.0.1", + "p-map": "^4.0.0", + "pacote": "^15.0.7", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.8", + "ssri": "^10.0.1", + "tar": "^6.1.13", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.0", + "which": "^3.0.0", + "write-file-atomic": "^5.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -21873,6 +22036,2503 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/npm/node_modules/@gar/promisify": { + "version": "1.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "6.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^3.1.0", + "@npmcli/installed-package-contents": "^2.0.0", + "@npmcli/map-workspaces": "^3.0.0", + "@npmcli/metavuln-calculator": "^5.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^3.0.0", + "@npmcli/query": "^3.0.0", + "@npmcli/run-script": "^6.0.0", + "bin-links": "^4.0.1", + "cacache": "^17.0.3", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^6.1.1", + "json-parse-even-better-errors": "^3.0.0", + "json-stringify-nice": "^1.1.4", + "minimatch": "^5.1.1", + "nopt": "^7.0.0", + "npm-install-checks": "^6.0.0", + "npm-package-arg": "^10.1.0", + "npm-pick-manifest": "^8.0.1", + "npm-registry-fetch": "^14.0.3", + "npmlog": "^7.0.1", + "pacote": "^15.0.7", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^3.0.1", + "semver": "^7.3.7", + "ssri": "^10.0.1", + "treeverse": "^3.0.0", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "6.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^3.0.0", + "ini": "^3.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "read-package-json-fast": "^3.0.0", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "3.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "4.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^17.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^15.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tootallnate/once": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abort-controller": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/npm/node_modules/agentkeepalive": { + "version": "4.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^4.1.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/are-we-there-yet/node_modules/buffer": { + "version": "6.0.3", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/npm/node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/builtins": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "17.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "4.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "3.7.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "3.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^4.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/npm/node_modules/delegates": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/depd": { + "version": "1.1.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.1.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/event-target-shim": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/events": { + "version": "3.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/fs.realpath": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/gauge": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "8.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.10", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/has": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/npm/node_modules/has-flag": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "6.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.0", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/humanize-ms": { + "version": "1.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/infer-owner": { + "version": "1.0.4", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/inflight": { + "version": "1.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/inherits": { + "version": "2.0.4", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/ini": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^10.0.0", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/ip": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "4.0.2", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^3.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.10.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "5.1.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.4.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "5.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^6.1.5", + "@npmcli/disparity-colors": "^3.0.0", + "@npmcli/installed-package-contents": "^2.0.0", + "binary-extensions": "^2.2.0", + "diff": "^5.1.0", + "minimatch": "^5.1.1", + "npm-package-arg": "^10.1.0", + "pacote": "^15.0.7", + "tar": "^6.1.13" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "5.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^6.1.5", + "@npmcli/run-script": "^6.0.0", + "chalk": "^4.1.0", + "ci-info": "^3.7.0", + "npm-package-arg": "^10.1.0", + "npmlog": "^7.0.1", + "pacote": "^15.0.7", + "proc-log": "^3.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^3.0.1", + "semver": "^7.3.7", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "4.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^6.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "9.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^14.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^14.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "5.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^6.1.5", + "@npmcli/run-script": "^6.0.0", + "npm-package-arg": "^10.1.0", + "pacote": "^15.0.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "7.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^5.0.0", + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3", + "semver": "^7.3.7", + "ssri": "^10.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^14.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^14.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^4.0.1", + "@npmcli/run-script": "^6.0.0", + "json-parse-even-better-errors": "^3.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "7.13.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "11.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "5.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "1.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "0.0.8", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "9.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "2.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/move-file": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/abbrev": { + "version": "1.1.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { + "version": "16.1.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/cacache/node_modules/glob": { + "version": "8.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/cacache/node_modules/minimatch": { + "version": "5.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "10.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/ssri": { + "version": "9.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/unique-filename": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/unique-slug": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "5.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "6.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "10.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "7.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "8.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "14.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "1.0.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/npmlog": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^4.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/once": { + "version": "1.4.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "15.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^2.1.0", + "minipass": "^4.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/path-is-absolute": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/process": { + "version": "0.11.10", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "0.3.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "1" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "1.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/readable-stream": { + "version": "3.6.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.3.8", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "3.0.7", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.7.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.3.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.11", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "10.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/string_decoder": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "7.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.1.13", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/npm/node_modules/wrappy": { + "version": "1.0.2", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", @@ -40324,9 +42984,9 @@ } }, "dom-accessibility-api": { - "version": "0.5.14", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", - "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.15.tgz", + "integrity": "sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==", "dev": true }, "dom-converter": { @@ -41561,9 +44221,9 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "requires": { "reusify": "^1.0.4" } @@ -42338,9 +44998,9 @@ } }, "get-tsconfig": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.2.0.tgz", - "integrity": "sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.3.0.tgz", + "integrity": "sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ==" }, "get-value": { "version": "2.0.6", @@ -43006,6 +45666,11 @@ "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", "dev": true }, + "install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==" + }, "internal-slot": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", @@ -43775,9 +46440,9 @@ } }, "jose": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", - "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==" + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz", + "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==" }, "js-sdsl": { "version": "4.2.0", @@ -43850,9 +46515,9 @@ "dev": true }, "json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { "version": "6.1.0", @@ -45136,6 +47801,1755 @@ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" }, + "npm": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-9.2.0.tgz", + "integrity": "sha512-oypVdaWGHDuV79RXLvp+B9gh6gDyAmoHKrQ0/JBYTWWx5D8/+AAxFdZC84fSIiyDdyW4qfrSyYGKhekxDOaMXQ==", + "requires": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^6.1.5", + "@npmcli/config": "^6.1.0", + "@npmcli/map-workspaces": "^3.0.0", + "@npmcli/package-json": "^3.0.0", + "@npmcli/run-script": "^6.0.0", + "abbrev": "^2.0.0", + "archy": "~1.0.0", + "cacache": "^17.0.3", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.3", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "graceful-fs": "^4.2.10", + "hosted-git-info": "^6.1.1", + "ini": "^3.0.1", + "init-package-json": "^4.0.1", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^3.0.0", + "libnpmaccess": "^7.0.1", + "libnpmdiff": "^5.0.6", + "libnpmexec": "^5.0.6", + "libnpmfund": "^4.0.6", + "libnpmhook": "^9.0.1", + "libnpmorg": "^5.0.1", + "libnpmpack": "^5.0.6", + "libnpmpublish": "^7.0.6", + "libnpmsearch": "^6.0.1", + "libnpmteam": "^5.0.1", + "libnpmversion": "^4.0.1", + "make-fetch-happen": "^11.0.2", + "minimatch": "^5.1.1", + "minipass": "^4.0.0", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "ms": "^2.1.2", + "node-gyp": "^9.3.0", + "nopt": "^7.0.0", + "npm-audit-report": "^4.0.0", + "npm-install-checks": "^6.0.0", + "npm-package-arg": "^10.1.0", + "npm-pick-manifest": "^8.0.1", + "npm-profile": "^7.0.1", + "npm-registry-fetch": "^14.0.3", + "npm-user-validate": "^1.0.1", + "npmlog": "^7.0.1", + "p-map": "^4.0.0", + "pacote": "^15.0.7", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.8", + "ssri": "^10.0.1", + "tar": "^6.1.13", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.0", + "which": "^3.0.0", + "write-file-atomic": "^5.0.0" + }, + "dependencies": { + "@colors/colors": { + "version": "1.5.0", + "bundled": true, + "optional": true + }, + "@gar/promisify": { + "version": "1.1.3", + "bundled": true + }, + "@isaacs/string-locale-compare": { + "version": "1.1.0", + "bundled": true + }, + "@npmcli/arborist": { + "version": "6.1.5", + "bundled": true, + "requires": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^3.1.0", + "@npmcli/installed-package-contents": "^2.0.0", + "@npmcli/map-workspaces": "^3.0.0", + "@npmcli/metavuln-calculator": "^5.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^3.0.0", + "@npmcli/query": "^3.0.0", + "@npmcli/run-script": "^6.0.0", + "bin-links": "^4.0.1", + "cacache": "^17.0.3", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^6.1.1", + "json-parse-even-better-errors": "^3.0.0", + "json-stringify-nice": "^1.1.4", + "minimatch": "^5.1.1", + "nopt": "^7.0.0", + "npm-install-checks": "^6.0.0", + "npm-package-arg": "^10.1.0", + "npm-pick-manifest": "^8.0.1", + "npm-registry-fetch": "^14.0.3", + "npmlog": "^7.0.1", + "pacote": "^15.0.7", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^3.0.1", + "semver": "^7.3.7", + "ssri": "^10.0.1", + "treeverse": "^3.0.0", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/config": { + "version": "6.1.0", + "bundled": true, + "requires": { + "@npmcli/map-workspaces": "^3.0.0", + "ini": "^3.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "read-package-json-fast": "^3.0.0", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "3.0.0", + "bundled": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/fs": { + "version": "3.1.0", + "bundled": true, + "requires": { + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "4.0.3", + "bundled": true, + "requires": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + } + }, + "@npmcli/installed-package-contents": { + "version": "2.0.1", + "bundled": true, + "requires": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + } + }, + "@npmcli/map-workspaces": { + "version": "3.0.0", + "bundled": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^3.0.0" + } + }, + "@npmcli/metavuln-calculator": { + "version": "5.0.0", + "bundled": true, + "requires": { + "cacache": "^17.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^15.0.0", + "semver": "^7.3.5" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true + }, + "@npmcli/node-gyp": { + "version": "3.0.0", + "bundled": true + }, + "@npmcli/package-json": { + "version": "3.0.0", + "bundled": true, + "requires": { + "json-parse-even-better-errors": "^3.0.0" + } + }, + "@npmcli/promise-spawn": { + "version": "6.0.1", + "bundled": true, + "requires": { + "which": "^3.0.0" + } + }, + "@npmcli/query": { + "version": "3.0.0", + "bundled": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "@npmcli/run-script": { + "version": "6.0.0", + "bundled": true, + "requires": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "bundled": true + }, + "abbrev": { + "version": "2.0.0", + "bundled": true + }, + "abort-controller": { + "version": "3.0.0", + "bundled": true, + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "agent-base": { + "version": "6.0.2", + "bundled": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.1", + "bundled": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "bundled": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.1", + "bundled": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "aproba": { + "version": "2.0.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "4.0.0", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^4.1.0" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "bundled": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "readable-stream": { + "version": "4.2.0", + "bundled": true, + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + } + } + } + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true + }, + "base64-js": { + "version": "1.5.1", + "bundled": true + }, + "bin-links": { + "version": "4.0.1", + "bundled": true, + "requires": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "bundled": true + }, + "brace-expansion": { + "version": "2.0.1", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "builtins": { + "version": "5.0.1", + "bundled": true, + "requires": { + "semver": "^7.0.0" + } + }, + "cacache": { + "version": "17.0.3", + "bundled": true, + "requires": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + } + }, + "chalk": { + "version": "4.1.2", + "bundled": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chownr": { + "version": "2.0.0", + "bundled": true + }, + "ci-info": { + "version": "3.7.0", + "bundled": true + }, + "cidr-regex": { + "version": "3.1.1", + "bundled": true, + "requires": { + "ip-regex": "^4.1.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "bundled": true + }, + "cli-columns": { + "version": "4.0.0", + "bundled": true, + "requires": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + } + }, + "cli-table3": { + "version": "0.6.3", + "bundled": true, + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "6.0.0", + "bundled": true + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true + }, + "color-support": { + "version": "1.1.3", + "bundled": true + }, + "columnify": { + "version": "1.6.0", + "bundled": true, + "requires": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + } + }, + "common-ancestor-path": { + "version": "1.0.1", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "cssesc": { + "version": "3.0.0", + "bundled": true + }, + "debug": { + "version": "4.3.4", + "bundled": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "bundled": true + } + } + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "depd": { + "version": "1.1.2", + "bundled": true + }, + "diff": { + "version": "5.1.0", + "bundled": true + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true + }, + "encoding": { + "version": "0.1.13", + "bundled": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "bundled": true + }, + "err-code": { + "version": "2.0.3", + "bundled": true + }, + "event-target-shim": { + "version": "5.0.1", + "bundled": true + }, + "events": { + "version": "3.3.0", + "bundled": true + }, + "fastest-levenshtein": { + "version": "1.0.16", + "bundled": true + }, + "fs-minipass": { + "version": "2.1.0", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true + }, + "gauge": { + "version": "5.0.0", + "bundled": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "glob": { + "version": "8.0.3", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "bundled": true + }, + "has": { + "version": "1.0.3", + "bundled": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hosted-git-info": { + "version": "6.1.1", + "bundled": true, + "requires": { + "lru-cache": "^7.5.1" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "bundled": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "bundled": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ieee754": { + "version": "1.2.1", + "bundled": true + }, + "ignore-walk": { + "version": "6.0.0", + "bundled": true, + "requires": { + "minimatch": "^5.0.1" + } + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "indent-string": { + "version": "4.0.0", + "bundled": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "ini": { + "version": "3.0.1", + "bundled": true + }, + "init-package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "npm-package-arg": "^10.0.0", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + } + }, + "ip": { + "version": "2.0.0", + "bundled": true + }, + "ip-regex": { + "version": "4.3.0", + "bundled": true + }, + "is-cidr": { + "version": "4.0.2", + "bundled": true, + "requires": { + "cidr-regex": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.10.0", + "bundled": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true + }, + "is-lambda": { + "version": "1.0.1", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "json-parse-even-better-errors": { + "version": "3.0.0", + "bundled": true + }, + "json-stringify-nice": { + "version": "1.1.4", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "just-diff": { + "version": "5.1.1", + "bundled": true + }, + "just-diff-apply": { + "version": "5.4.1", + "bundled": true + }, + "libnpmaccess": { + "version": "7.0.1", + "bundled": true, + "requires": { + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3" + } + }, + "libnpmdiff": { + "version": "5.0.6", + "bundled": true, + "requires": { + "@npmcli/arborist": "^6.1.5", + "@npmcli/disparity-colors": "^3.0.0", + "@npmcli/installed-package-contents": "^2.0.0", + "binary-extensions": "^2.2.0", + "diff": "^5.1.0", + "minimatch": "^5.1.1", + "npm-package-arg": "^10.1.0", + "pacote": "^15.0.7", + "tar": "^6.1.13" + } + }, + "libnpmexec": { + "version": "5.0.6", + "bundled": true, + "requires": { + "@npmcli/arborist": "^6.1.5", + "@npmcli/run-script": "^6.0.0", + "chalk": "^4.1.0", + "ci-info": "^3.7.0", + "npm-package-arg": "^10.1.0", + "npmlog": "^7.0.1", + "pacote": "^15.0.7", + "proc-log": "^3.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^3.0.1", + "semver": "^7.3.7", + "walk-up-path": "^1.0.0" + } + }, + "libnpmfund": { + "version": "4.0.6", + "bundled": true, + "requires": { + "@npmcli/arborist": "^6.1.5" + } + }, + "libnpmhook": { + "version": "9.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^14.0.3" + } + }, + "libnpmorg": { + "version": "5.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^14.0.3" + } + }, + "libnpmpack": { + "version": "5.0.6", + "bundled": true, + "requires": { + "@npmcli/arborist": "^6.1.5", + "@npmcli/run-script": "^6.0.0", + "npm-package-arg": "^10.1.0", + "pacote": "^15.0.7" + } + }, + "libnpmpublish": { + "version": "7.0.6", + "bundled": true, + "requires": { + "normalize-package-data": "^5.0.0", + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3", + "semver": "^7.3.7", + "ssri": "^10.0.1" + } + }, + "libnpmsearch": { + "version": "6.0.1", + "bundled": true, + "requires": { + "npm-registry-fetch": "^14.0.3" + } + }, + "libnpmteam": { + "version": "5.0.1", + "bundled": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^14.0.3" + } + }, + "libnpmversion": { + "version": "4.0.1", + "bundled": true, + "requires": { + "@npmcli/git": "^4.0.1", + "@npmcli/run-script": "^6.0.0", + "json-parse-even-better-errors": "^3.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7" + } + }, + "lru-cache": { + "version": "7.13.2", + "bundled": true + }, + "make-fetch-happen": { + "version": "11.0.2", + "bundled": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + } + }, + "minimatch": { + "version": "5.1.1", + "bundled": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "4.0.0", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-fetch": { + "version": "3.0.0", + "bundled": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-flush": { + "version": "1.0.5", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "bundled": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minipass-sized": { + "version": "1.0.3", + "bundled": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "minizlib": { + "version": "2.1.2", + "bundled": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mkdirp": { + "version": "1.0.4", + "bundled": true + }, + "ms": { + "version": "2.1.3", + "bundled": true + }, + "mute-stream": { + "version": "0.0.8", + "bundled": true + }, + "negotiator": { + "version": "0.6.3", + "bundled": true + }, + "node-gyp": { + "version": "9.3.0", + "bundled": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "@npmcli/fs": { + "version": "2.1.2", + "bundled": true, + "requires": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + } + }, + "@npmcli/move-file": { + "version": "2.0.1", + "bundled": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "are-we-there-yet": { + "version": "3.0.1", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "cacache": { + "version": "16.1.3", + "bundled": true, + "requires": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.0.3", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.0", + "bundled": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "gauge": { + "version": "4.0.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "glob": { + "version": "7.2.3", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "make-fetch-happen": { + "version": "10.2.1", + "bundled": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.3.6", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-fetch": { + "version": "2.1.2", + "bundled": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + } + }, + "nopt": { + "version": "6.0.0", + "bundled": true, + "requires": { + "abbrev": "^1.0.0" + } + }, + "npmlog": { + "version": "6.0.2", + "bundled": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "ssri": { + "version": "9.0.1", + "bundled": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "unique-filename": { + "version": "2.0.1", + "bundled": true, + "requires": { + "unique-slug": "^3.0.0" + } + }, + "unique-slug": { + "version": "3.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "which": { + "version": "2.0.2", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "nopt": { + "version": "7.0.0", + "bundled": true, + "requires": { + "abbrev": "^2.0.0" + } + }, + "normalize-package-data": { + "version": "5.0.0", + "bundled": true, + "requires": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + } + }, + "npm-audit-report": { + "version": "4.0.0", + "bundled": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "npm-bundled": { + "version": "3.0.0", + "bundled": true, + "requires": { + "npm-normalize-package-bin": "^3.0.0" + } + }, + "npm-install-checks": { + "version": "6.0.0", + "bundled": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "3.0.0", + "bundled": true + }, + "npm-package-arg": { + "version": "10.1.0", + "bundled": true, + "requires": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + } + }, + "npm-packlist": { + "version": "7.0.4", + "bundled": true, + "requires": { + "ignore-walk": "^6.0.0" + } + }, + "npm-pick-manifest": { + "version": "8.0.1", + "bundled": true, + "requires": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", + "semver": "^7.3.5" + } + }, + "npm-profile": { + "version": "7.0.1", + "bundled": true, + "requires": { + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0" + } + }, + "npm-registry-fetch": { + "version": "14.0.3", + "bundled": true, + "requires": { + "make-fetch-happen": "^11.0.0", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true + }, + "npmlog": { + "version": "7.0.1", + "bundled": true, + "requires": { + "are-we-there-yet": "^4.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "p-map": { + "version": "4.0.0", + "bundled": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "pacote": { + "version": "15.0.7", + "bundled": true, + "requires": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^2.1.0", + "minipass": "^4.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + } + }, + "parse-conflict-json": { + "version": "3.0.0", + "bundled": true, + "requires": { + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "postcss-selector-parser": { + "version": "6.0.10", + "bundled": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "proc-log": { + "version": "3.0.0", + "bundled": true + }, + "process": { + "version": "0.11.10", + "bundled": true + }, + "promise-all-reject-late": { + "version": "1.0.1", + "bundled": true + }, + "promise-call-limit": { + "version": "1.0.1", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "2.0.1", + "bundled": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "4.0.0", + "bundled": true + }, + "read-package-json": { + "version": "6.0.0", + "bundled": true, + "requires": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + } + }, + "read-package-json-fast": { + "version": "3.0.1", + "bundled": true, + "requires": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "3.0.2", + "bundled": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "semver": { + "version": "7.3.8", + "bundled": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.7", + "bundled": true + }, + "smart-buffer": { + "version": "4.2.0", + "bundled": true + }, + "socks": { + "version": "2.7.0", + "bundled": true, + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "7.0.0", + "bundled": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "spdx-correct": { + "version": "3.1.1", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "bundled": true + }, + "ssri": { + "version": "10.0.1", + "bundled": true, + "requires": { + "minipass": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "bundled": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tar": { + "version": "6.1.13", + "bundled": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "treeverse": { + "version": "3.0.0", + "bundled": true + }, + "unique-filename": { + "version": "3.0.0", + "bundled": true, + "requires": { + "unique-slug": "^4.0.0" + } + }, + "unique-slug": { + "version": "4.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "5.0.0", + "bundled": true, + "requires": { + "builtins": "^5.0.0" + } + }, + "walk-up-path": { + "version": "1.0.0", + "bundled": true + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "3.0.0", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "bundled": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "5.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true + } + } + }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", diff --git a/website/package.json b/website/package.json index 2bd531a0..c1d0c3d2 100644 --- a/website/package.json +++ b/website/package.json @@ -22,6 +22,7 @@ "@dnd-kit/core": "^6.0.6", "@dnd-kit/modifiers": "^6.0.1", "@dnd-kit/sortable": "^7.0.1", + "@dnd-kit/utilities": "^3.2.1", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@headlessui/react": "^1.7.7", @@ -39,9 +40,11 @@ "eslint-plugin-simple-import-sort": "^8.0.0", "focus-visible": "^5.2.0", "framer-motion": "^6.5.1", + "install": "^0.13.0", "next": "13.0.6", "next-auth": "^4.18.6", "nodemailer": "^6.8.0", + "npm": "^9.2.0", "postcss-focus-visible": "^7.1.0", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/website/src/components/AuthLayout.tsx b/website/src/components/AuthLayout.tsx index 3e45dd65..5136e43e 100644 --- a/website/src/components/AuthLayout.tsx +++ b/website/src/components/AuthLayout.tsx @@ -1,11 +1,11 @@ export function AuthLayout({ children }) { return ( -
+
{children}
-
+ ); } diff --git a/website/src/components/CallToAction.tsx b/website/src/components/CallToAction.tsx index 2fd91c79..ee6827eb 100644 --- a/website/src/components/CallToAction.tsx +++ b/website/src/components/CallToAction.tsx @@ -1,16 +1,48 @@ -import { CircleBackground } from "./CircleBackground"; +import { useColorMode } from "@chakra-ui/react"; +import { useId } from "react"; + import { Container } from "./Container"; -export function CallToAction() { +function CircleBackground({ width = 558, height = 558, ...props }) { + const id = useId(); + + const { colorMode } = useColorMode(); + const baseRingColor = colorMode === "light" ? "#777" : "#000"; + const gradStopColor = colorMode === "light" ? "#fff" : "#000"; + return ( -
+ + ); +} + +export function CallToAction() { + const { colorMode } = useColorMode(); + const bgColorClass = colorMode === "light" ? "bg-gray-900" : "bg-gray-50"; + const headingColorClass = colorMode === "light" ? "text-white" : "text-black"; + const textColorClass = colorMode === "light" ? "text-gray-300" : "text-black"; + + return ( +
- +
-

Join Us

-

+

Join Us

+

All open source projects begin with people like you. Open source is the belief that if we collaborate we can together gift our knowledge and technology to the world for the benefit of humanity. Are you in? Find us here: diff --git a/website/src/components/CircleBackground.tsx b/website/src/components/CircleBackground.tsx deleted file mode 100644 index 5851d4ab..00000000 --- a/website/src/components/CircleBackground.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useId } from "react"; - -export function CircleBackground({ color, width = 558, height = 558, ...props }) { - const id = useId(); - - return ( - - ); -} diff --git a/website/src/components/Faq.tsx b/website/src/components/Faq.tsx index 8f42b920..b8e5e8f8 100644 --- a/website/src/components/Faq.tsx +++ b/website/src/components/Faq.tsx @@ -1,3 +1,5 @@ +import { useColorMode } from "@chakra-ui/react"; + import { Container } from "./Container"; const faqs = [ @@ -25,11 +27,16 @@ const faqs = [ ]; export function Faq() { + const { colorMode } = useColorMode(); + + const headingColorClass = colorMode === "light" ? "text-gray-900" : "text-white"; + const textColorClass = colorMode === "light" ? "text-gray-700" : "text-gray-100"; + return (

-

+

Frequently Asked Questions

{/*

@@ -52,8 +59,8 @@ export function Faq() {

    {column.map((faq, faqIndex) => (
  • -

    {faq.question}

    -

    {faq.answer}

    +

    {faq.question}

    +

    {faq.answer}

  • ))}
diff --git a/website/src/components/Footer.tsx b/website/src/components/Footer.tsx index 5c774398..fc88368e 100644 --- a/website/src/components/Footer.tsx +++ b/website/src/components/Footer.tsx @@ -1,71 +1,70 @@ +import { useColorMode } from "@chakra-ui/react"; import Image from "next/image"; import Link from "next/link"; -import { Container } from "./Container"; - export function Footer() { - return ( -
-
- -
-
- - logo - + const { colorMode } = useColorMode(); + const bgColorClass = colorMode === "light" ? "bg-transparent" : "bg-gray-800"; + const borderClass = colorMode === "light" ? "border-slate-200" : "border-transparent"; -
-

Open Assistant

-

Conversational AI for everyone.

+ return ( +
+
+
+ + logo + + +
+

Open Assistant

+

Conversational AI for everyone.

+
+
+ +
- -
+
+ + {/*
*/} + + ); } diff --git a/website/src/components/Header/Header.stories.jsx b/website/src/components/Header/Header.stories.jsx index 6a8a3866..c3c61018 100644 --- a/website/src/components/Header/Header.stories.jsx +++ b/website/src/components/Header/Header.stories.jsx @@ -22,4 +22,15 @@ const Template = (args) => { }; export const Default = Template.bind({}); -Default.args = { session: { data: { user: { name: "StoryBook user" } }, status: "authenticated" }, transparent: false }; +Default.args = { + session: { + data: { + user: { + name: "StoryBook user", + }, + }, + status: "authenticated", + }, + transparent: false, + borderClass: undefined, +}; diff --git a/website/src/components/Header/Header.tsx b/website/src/components/Header/Header.tsx index e4965807..8b8c4663 100644 --- a/website/src/components/Header/Header.tsx +++ b/website/src/components/Header/Header.tsx @@ -1,14 +1,12 @@ -import { Button } from "@chakra-ui/react"; +import { Box, Button, useColorMode } from "@chakra-ui/react"; import { Popover } from "@headlessui/react"; -import clsx from "clsx"; import { AnimatePresence, motion } from "framer-motion"; import Image from "next/image"; import Link from "next/link"; import { useSession } from "next-auth/react"; import { FaUser } from "react-icons/fa"; -import { Container } from "src/components/Container"; -import { NavLinks } from "./NavLinks"; +import { ColorModeIconToggle } from "../UI/ColorModeIconToggle"; import { UserMenu } from "./UserMenu"; function MenuIcon(props) { @@ -55,63 +53,72 @@ function AccountButton() { } export function Header(props) { - const transparent = props.transparent ?? false; + const { colorMode } = useColorMode(); + const borderClass = props.transparent + ? "" + : colorMode === "light" + ? "border-b border-gray-400" + : "border-b border-zinc-800"; + return ( -
- -
+ ); } diff --git a/website/src/components/Header/NavLinks.tsx b/website/src/components/Header/NavLinks.tsx index 3903c8b6..4f559e7e 100644 --- a/website/src/components/Header/NavLinks.tsx +++ b/website/src/components/Header/NavLinks.tsx @@ -1,9 +1,15 @@ +import { useColorMode } from "@chakra-ui/react"; import { AnimatePresence, motion } from "framer-motion"; import Link from "next/link"; import { useState } from "react"; export function NavLinks(): JSX.Element { const [hoveredIndex, setHoveredIndex] = useState(null); + const { colorMode } = useColorMode(); + + const linkColor = colorMode === "light" ? "text-gray-700 hover:text-gray-900" : "text-gray-50 hover:text-white"; + + const hoverBgColor = colorMode === "light" ? "bg-gray-100" : "bg-gray-800"; return ( <> @@ -14,14 +20,14 @@ export function NavLinks(): JSX.Element { setHoveredIndex(index)} onMouseLeave={() => setHoveredIndex(null)} > {hoveredIndex === index && ( ; @@ -26,7 +28,7 @@ export function UserMenu() { {({ open }) => ( <> -
+
Profile Picture {open && ( - <> + -
+ {accountOptions.map((item) => ( Sign Out

-
+ - + )} diff --git a/website/src/components/Hero.tsx b/website/src/components/Hero.tsx index 3ddbc194..1e6b296f 100644 --- a/website/src/components/Hero.tsx +++ b/website/src/components/Hero.tsx @@ -1,3 +1,4 @@ +import { useColorMode } from "@chakra-ui/react"; import Image from "next/image"; import { useId } from "react"; @@ -6,6 +7,10 @@ import { Container } from "./Container"; function BackgroundIllustration(props) { const id = useId(); + const { colorMode } = useColorMode(); + const baseRingColor = colorMode === "light" ? "#d4d4d4" : "#005a69"; + const gradStopColor = colorMode === "light" ? "#06b6d4" : "#00f2ff"; + return (
- - + + @@ -35,14 +40,14 @@ function BackgroundIllustration(props) { > - - + + @@ -51,17 +56,24 @@ function BackgroundIllustration(props) { } export function Hero() { + const { colorMode } = useColorMode(); + const pTextColor = colorMode === "light" ? "text-gray-600" : "text-white"; + const fancyTextGradientClasses = + colorMode === "light" ? "from-blue-600 via-sky-400 to-blue-700" : "from-blue-500 via-sky-300 to-blue-400"; + return (
-

Open Assistant

-

+

Open Assistant

+

Conversational AI for everyone.

-

We believe we can create a revolution.

-

+

We believe we can create a revolution.

+

In the same way that Stable Diffusion helped the world make art and images in new ways, we want to improve the world by providing amazing conversational AI.

diff --git a/website/src/components/Loading/LoadingScreen.jsx b/website/src/components/Loading/LoadingScreen.jsx index 57323f8c..02aabe7a 100644 --- a/website/src/components/Loading/LoadingScreen.jsx +++ b/website/src/components/Loading/LoadingScreen.jsx @@ -1,12 +1,18 @@ import { Progress } from "@chakra-ui/react"; +import { useColorMode } from "@chakra-ui/react"; -export const LoadingScreen = ({ text }) => ( -
- - {text && ( -
-
{text}
-
- )} -
-); +export const LoadingScreen = ({ text }) => { + const { colorMode } = useColorMode(); + const mainClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; + + return ( +
+ + {text && ( +
+
{text}
+
+ )} +
+ ); +}; diff --git a/website/src/components/Messages.tsx b/website/src/components/Messages.tsx index 5b77cd26..d3d7b3b8 100644 --- a/website/src/components/Messages.tsx +++ b/website/src/components/Messages.tsx @@ -1,4 +1,6 @@ import { Grid } from "@chakra-ui/react"; +import { useColorMode } from "@chakra-ui/react"; + import { FlaggableElement } from "./FlaggableElement"; export interface Message { @@ -6,13 +8,24 @@ export interface Message { is_assistant: boolean; } -const getColor = (isAssistant: boolean) => (isAssistant ? "bg-slate-800" : "bg-sky-900"); +const getBgColor = (isAssistant: boolean, colorMode: "light" | "dark") => { + if (colorMode === "light") { + return isAssistant ? "bg-slate-800" : "bg-sky-900"; + } else { + return isAssistant ? "bg-black" : "bg-sky-900"; + } +}; export const Messages = ({ messages, post_id }: { messages: Message[]; post_id: string }) => { + const { colorMode } = useColorMode(); + const items = messages.map(({ text, is_assistant }: Message, i: number) => { return ( -
+
{text}
diff --git a/website/src/components/Sortable/Sortable.tsx b/website/src/components/Sortable/Sortable.tsx index 615b0853..2f63ff27 100644 --- a/website/src/components/Sortable/Sortable.tsx +++ b/website/src/components/Sortable/Sortable.tsx @@ -2,9 +2,9 @@ import { Flex } from "@chakra-ui/react"; import { closestCenter, DndContext, + KeyboardSensor, PointerSensor, TouchSensor, - KeyboardSensor, useSensor, useSensors, } from "@dnd-kit/core"; @@ -23,6 +23,7 @@ import { SortableItem } from "./SortableItem"; export interface SortableProps { items: ReactNode[]; onChange: (newSortedIndices: number[]) => void; + className?: string; } interface SortableItems { @@ -31,18 +32,18 @@ interface SortableItems { item: ReactNode; } -export const Sortable = ({ items, onChange }: SortableProps) => { +export const Sortable = (props: SortableProps) => { const [itemsWithIds, setItemsWithIds] = useState([]); useEffect(() => { setItemsWithIds( - items.map((item, idx) => ({ + props.items.map((item, idx) => ({ item, id: idx + 1, // +1 because dndtoolkit has problem with "falsy" ids originalIndex: idx, })) ); - }, [items]); + }, [props.items]); const sensors = useSensors( useSensor(PointerSensor), @@ -50,6 +51,8 @@ export const Sortable = ({ items, onChange }: SortableProps) => { useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); + const extraClasses = props.className || ""; + return ( { modifiers={[restrictToVerticalAxis]} > - + {itemsWithIds.map(({ id, item }) => ( {item} @@ -78,7 +81,7 @@ export const Sortable = ({ items, onChange }: SortableProps) => { const oldIndex = items.findIndex((x) => x.id === active.id); const newIndex = items.findIndex((x) => x.id === over.id); const newArray = arrayMove(items, oldIndex, newIndex); - onChange(newArray.map((item) => item.originalIndex)); + props.onChange(newArray.map((item) => item.originalIndex)); return newArray; }); } diff --git a/website/src/components/Sortable/SortableItem.tsx b/website/src/components/Sortable/SortableItem.tsx index 834a854f..da691de3 100644 --- a/website/src/components/Sortable/SortableItem.tsx +++ b/website/src/components/Sortable/SortableItem.tsx @@ -1,8 +1,9 @@ import { Button } from "@chakra-ui/react"; +import { useColorMode } from "@chakra-ui/react"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; -import { RxDragHandleDots2 } from "react-icons/rx"; import { PropsWithChildren } from "react"; +import { RxDragHandleDots2 } from "react-icons/rx"; export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }>) => { const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); @@ -13,9 +14,15 @@ export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }> touchAction: "none", }; + const { colorMode } = useColorMode(); + const themedClasses = + colorMode === "light" + ? "bg-slate-600 hover:bg-slate-500 text-white" + : "bg-black hover:bg-slate-900 text-white ring-1 ring-white/30 ring-inset hover:ring-slate-200/50"; + return (
  • diff --git a/website/src/components/Survey/SurveyCard.tsx b/website/src/components/Survey/SurveyCard.tsx new file mode 100644 index 00000000..25699c3f --- /dev/null +++ b/website/src/components/Survey/SurveyCard.tsx @@ -0,0 +1,20 @@ +import { useColorMode } from "@chakra-ui/react"; + +interface SurveyCardProps { + className?: string; + children: React.ReactNode; +} + +export const SurveyCard = (props: SurveyCardProps) => { + const extraClases = props.className || ""; + const { colorMode } = useColorMode(); + + const baseCardClasses = "rounded-lg h-full block p-6"; + const cardClases = + colorMode === "light" + ? `${baseCardClasses} bg-slate-50 text-gray-800 shadow-lg ${extraClases}` + : // `${baseCardClasses} bg-slate-800 text-white shadow-xl${extraClases}`; + `${baseCardClasses} bg-slate-800 text-slate-400 shadow-xl ring-1 ring-white/10 ring-inset ${extraClases}`; + + return
    {props.children}
    ; +}; diff --git a/website/src/components/Survey/TaskControls.tsx b/website/src/components/Survey/TaskControls.tsx new file mode 100644 index 00000000..7847c452 --- /dev/null +++ b/website/src/components/Survey/TaskControls.tsx @@ -0,0 +1,40 @@ +import { useColorMode } from "@chakra-ui/react"; +import { Flex } from "@chakra-ui/react"; +import { SkipButton } from "src/components/Buttons/Skip"; +import { SubmitButton } from "src/components/Buttons/Submit"; +import { TaskInfo } from "src/components/TaskInfo/TaskInfo"; + +interface TaskControlsProps { + // we need a task type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + tasks: any[]; + className?: string; + onSubmitResponse: (task: { id: string }) => void; + onSkip: () => void; +} + +export const TaskControls = (props: TaskControlsProps) => { + const extraClases = props.className || ""; + const { colorMode } = useColorMode(); + + const baseClasses = "flex flex-row justify-items-stretch mb-8 p-4 rounded-lg max-w-7xl mx-auto"; + const taskControlClases = + colorMode === "light" + ? `${baseClasses} bg-white text-gray-800 shadow-lg ${extraClases}` + : `${baseClasses} bg-slate-800 text-slate-400 shadow-xl ring-1 ring-white/10 ring-inset ${extraClases}`; + + const endTask = props.tasks[props.tasks.length - 1]; + return ( +
    + + + Skip + {endTask.task.type !== "task_done" ? ( + props.onSubmitResponse(props.tasks[0])}>Submit + ) : ( + Next Task + )} + +
    + ); +}; diff --git a/website/src/components/Survey/TwoColumnsWithCards.tsx b/website/src/components/Survey/TwoColumnsWithCards.tsx new file mode 100644 index 00000000..55f787ea --- /dev/null +++ b/website/src/components/Survey/TwoColumnsWithCards.tsx @@ -0,0 +1,16 @@ +import { SurveyCard } from "src/components/Survey/SurveyCard"; + +export const TwoColumnsWithCards = ({ children }: { children: React.ReactNode[] }) => { + if (!Array.isArray(children) || children.length !== 2) { + throw new Error("TwoColumns expects 2 children"); + } + + const [first, second] = children; + + return ( +
    + {first} + {second} +
    + ); +}; diff --git a/website/src/components/TaskInfo/TaskInfo.tsx b/website/src/components/TaskInfo/TaskInfo.tsx index d32f7d9d..86fd2d96 100644 --- a/website/src/components/TaskInfo/TaskInfo.tsx +++ b/website/src/components/TaskInfo/TaskInfo.tsx @@ -1,6 +1,6 @@ export const TaskInfo = ({ id, output }: { id: string; output: string }) => { return ( -
    +
    Prompt {id} Output diff --git a/website/src/components/TaskSelection/TaskSelection.tsx b/website/src/components/TaskSelection/TaskSelection.tsx index 7cb216c1..f258a90b 100644 --- a/website/src/components/TaskSelection/TaskSelection.tsx +++ b/website/src/components/TaskSelection/TaskSelection.tsx @@ -1,12 +1,24 @@ import { Flex } from "@chakra-ui/react"; +import { useColorMode } from "@chakra-ui/react"; import React from "react"; import { TaskOption } from "./TaskOption"; import { TaskOptions } from "./TaskOptions"; export const TaskSelection = () => { + const { colorMode } = useColorMode(); + const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; + return ( - + {/* { - if (!Array.isArray(children) || children.length !== 2) { - throw new Error("TwoColumns expects 2 children"); - } - - const [first, second] = children; - - return ( -
    -
    {first}
    -
    {second}
    -
    - ); -}; diff --git a/website/src/components/UI/ColorModeIconToggle.tsx b/website/src/components/UI/ColorModeIconToggle.tsx new file mode 100644 index 00000000..0b846e0b --- /dev/null +++ b/website/src/components/UI/ColorModeIconToggle.tsx @@ -0,0 +1,23 @@ +import { useColorMode } from "@chakra-ui/react"; +import { CiDark } from "react-icons/ci"; +import { CiLight } from "react-icons/ci"; + +export function ColorModeIconToggle(props) { + const { colorMode, toggleColorMode } = useColorMode(); + const propsClassName = props.className ?? ""; + + return ( + + ); +} diff --git a/website/src/components/UI/ColorModeSwitch.tsx b/website/src/components/UI/ColorModeSwitch.tsx new file mode 100644 index 00000000..05c9bde3 --- /dev/null +++ b/website/src/components/UI/ColorModeSwitch.tsx @@ -0,0 +1,16 @@ +import { Switch, useColorMode } from "@chakra-ui/react"; +import React from "react"; + +const ColorModeSwitch = () => { + const { colorMode, toggleColorMode } = useColorMode(); + return ( + + ); +}; + +export default ColorModeSwitch; diff --git a/website/src/pages/_app.tsx b/website/src/pages/_app.tsx index b9cffba1..ab7655cd 100644 --- a/website/src/pages/_app.tsx +++ b/website/src/pages/_app.tsx @@ -1,48 +1,25 @@ import "../styles/globals.css"; import "focus-visible"; -import { ChakraProvider } from "@chakra-ui/react"; -import { extendTheme } from "@chakra-ui/react"; -import { Inter } from "@next/font/google"; import type { AppProps } from "next/app"; import { SessionProvider } from "next-auth/react"; import { getDefaultLayout, NextPageWithLayout } from "src/components/Layout"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const inter = Inter({ - subsets: ["latin"], - variable: "--font-inter", -}); - -const theme = extendTheme({ - styles: { - global: { - body: { - bg: "white", - }, - main: { - fontFamily: "Inter", - }, - header: { - fontFamily: "Inter", - }, - }, - }, -}); +import { Chakra, getServerSideProps } from "../styles/Chakra"; type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout; }; -function MyApp({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLayout) { +function MyApp({ Component, pageProps: { session, cookies, ...pageProps } }: AppPropsWithLayout) { const getLayout = Component.getLayout ?? getDefaultLayout; const page = getLayout(); return ( - + {page} - + ); } - +export { getServerSideProps }; export default MyApp; diff --git a/website/src/pages/api/new_task/[task_type].ts b/website/src/pages/api/new_task/[task_type].ts index 2a2a0215..50f0b4e2 100644 --- a/website/src/pages/api/new_task/[task_type].ts +++ b/website/src/pages/api/new_task/[task_type].ts @@ -53,7 +53,7 @@ const handler = async (req, res) => { }); // Update the backend with our Task ID - const ackRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, { + await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, { method: "POST", headers: { "X-API-Key": process.env.FASTAPI_KEY, diff --git a/website/src/pages/api/update_task.ts b/website/src/pages/api/update_task.ts index ef0147df..9582040b 100644 --- a/website/src/pages/api/update_task.ts +++ b/website/src/pages/api/update_task.ts @@ -7,8 +7,7 @@ import prisma from "src/lib/prismadb"; * This implicity does a few things: * 1) Stores the answer with the Task Backend. * 2) Records the new task in our local database. - * 3) (TODO) Acks the new task with our local task ID to the Task Backend. - * 4) Returns the newly created task to the client. + * 3) Returns the newly created task to the client. */ const handler = async (req, res) => { const token = await getToken({ req }); @@ -69,9 +68,6 @@ const handler = async (req, res) => { }, }); - // TODO: Ack the task with the Task Backend using the newly created local - // task ID. - // Send the next task in the sequence to the client. res.status(200).json(newRegisteredTask); }; diff --git a/website/src/pages/auth/signin.tsx b/website/src/pages/auth/signin.tsx index 5bfeeb3b..936a3dbf 100644 --- a/website/src/pages/auth/signin.tsx +++ b/website/src/pages/auth/signin.tsx @@ -1,13 +1,16 @@ import { Button, Input, Stack } from "@chakra-ui/react"; +import { useColorMode } from "@chakra-ui/react"; import Head from "next/head"; import Link from "next/link"; import { getCsrfToken, getProviders, signIn } from "next-auth/react"; import React, { useRef } from "react"; import { FaBug, FaDiscord, FaEnvelope, FaGithub } from "react-icons/fa"; import { AuthLayout } from "src/components/AuthLayout"; +import { Footer } from "src/components/Footer"; +import { Header } from "src/components/Header"; // eslint-disable-next-line @typescript-eslint/no-unused-vars -export default function Signin({ csrfToken, providers }) { +function Signin({ csrfToken, providers }) { const { discord, email, github, credentials } = providers; const emailEl = useRef(null); const signinWithEmail = (ev: React.FormEvent) => { @@ -21,8 +24,14 @@ export default function Signin({ csrfToken, providers }) { signIn(credentials.id, { callbackUrl: "/", username: debugUsernameEl.current.value }); } + const { colorMode } = useColorMode(); + const bgColorClass = colorMode === "light" ? "bg-gray-50" : "bg-chakra-gray-900"; + const buttonBgColor = colorMode === "light" ? "#2563eb" : "#2563eb"; + + const buttonColorScheme = colorMode === "light" ? "blue" : "dark-blue-btn"; + return ( - <> +
    Sign Up - Open Assistant @@ -30,11 +39,11 @@ export default function Signin({ csrfToken, providers }) { {credentials && ( -
    - For Debugging Only + + For Debugging Only - @@ -43,13 +52,13 @@ export default function Signin({ csrfToken, providers }) { {email && ( - + @@ -58,7 +67,7 @@ export default function Signin({ csrfToken, providers }) { )} {discord && (
    ); } -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export async function getServerSideProps(context) { +Signin.getLayout = (page) => ( +
    +
    + {page} +
    +
    +); + +export default Signin; + +export async function getServerSideProps() { const csrfToken = await getCsrfToken(); const providers = await getProviders(); return { diff --git a/website/src/pages/create/assistant_reply.tsx b/website/src/pages/create/assistant_reply.tsx index 4941c106..9a9c062e 100644 --- a/website/src/pages/create/assistant_reply.tsx +++ b/website/src/pages/create/assistant_reply.tsx @@ -1,11 +1,10 @@ -import { Flex, Textarea } from "@chakra-ui/react"; +import { Container, Textarea } from "@chakra-ui/react"; +import { useColorMode } from "@chakra-ui/react"; import { useRef, useState } from "react"; -import { SkipButton } from "src/components/Buttons/Skip"; -import { SubmitButton } from "src/components/Buttons/Submit"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; import { Messages } from "src/components/Messages"; -import { TaskInfo } from "src/components/TaskInfo/TaskInfo"; -import { TwoColumns } from "src/components/TwoColumns"; +import { TaskControls } from "src/components/Survey/TaskControls"; +import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards"; import fetcher from "src/lib/fetcher"; import poster from "src/lib/poster"; import useSWRImmutable from "swr/immutable"; @@ -45,43 +44,31 @@ const AssistantReply = () => { mutate(); }; + const { colorMode } = useColorMode(); + const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; + if (isLoading) { return ; } if (tasks.length == 0) { - return
    No tasks found...
    ; + return No tasks found...; } const task = tasks[0].task; - const endTask = tasks[tasks.length - 1]; + return ( -
    - +
    + <>
    Reply as the assistant

    Given the following conversation, provide an adequate reply

    -