diff --git a/.gitignore b/.gitignore index a7bcceada..e2bffa3af 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,4 @@ package-lock.json dataEval.json # Ignore generated credentials from google-github-actions/auth -gha-creds-*.json \ No newline at end of file +gha-creds-*.json diff --git a/FeatureFlags.js b/FeatureFlags.js index 5b46c9633..e1746c5f7 100644 --- a/FeatureFlags.js +++ b/FeatureFlags.js @@ -45,6 +45,9 @@ module.exports = { // Whether the Chatbot UserInterface and its functionality should be enabled enableChatbot: false, + // Chatbot for creating and modifying BPMN in modeler + enableBPMNChatbot: true, + // ----------------------------------------------------------------------------- // Chopping Block // diff --git a/README.md b/README.md index 9518d2651..1f874221e 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,12 @@ yarn dev-engine-web **Management System:** +Before using the BPMN chat bot you need to create an API key and add it as an environment variable. If you are not interesting in using the chat bot ignore the following steps. + +1. Go to https://ai.google.dev. +2. Log in and generate an API key. +3. Copy the API key and create an enviroment variable named GEMINI_API_KEY and paste the copied API key as value. + ``` // NextJS frontend yarn dev-ms diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler-toolbar.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler-toolbar.tsx index e80917c1f..a3a36c684 100644 --- a/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler-toolbar.tsx +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler-toolbar.tsx @@ -10,9 +10,8 @@ import Icon, { UndoOutlined, RedoOutlined, ArrowUpOutlined, - ArrowDownOutlined, - FullscreenOutlined, FilePdfOutlined, + RobotOutlined, } from '@ant-design/icons'; import { SvgXML } from '@/components/svg'; import PropertiesPanel from './properties-panel'; @@ -28,6 +27,9 @@ import ModelerShareModalButton from './modeler-share-modal'; import { ProcessExportTypes } from '@/components/process-export'; import { spaceURL } from '@/lib/utils'; import { generateSharedViewerUrl } from '@/lib/sharing/process-sharing'; +import ChatbotDialog from '@/components/bpmn-chatbot'; +import { enableBPMNChatbot } from 'FeatureFlags'; +import { BPMNCanvasRef } from '@/components/bpmn-canvas'; const LATEST_VERSION = { version: -1, name: 'Latest Version', description: '' }; @@ -37,6 +39,7 @@ type ModelerToolbarProps = { canUndo: boolean; canRedo: boolean; versions: { version: number; name: string; description: string }[]; + modeler: BPMNCanvasRef | null; }; const ModelerToolbar = ({ processId, @@ -58,6 +61,7 @@ const ModelerToolbar = ({ const query = useSearchParams(); const subprocessId = query.get('subprocess'); + const [showChatbotDialog, setShowChatbotDialog] = useState(false); const modeler = useModelerStateStore((state) => state.modeler); const selectedElementId = useModelerStateStore((state) => state.selectedElementId); @@ -275,6 +279,15 @@ const ModelerToolbar = ({ )} + + {enableBPMNChatbot && ( + + + + )} {showPropertiesPanel && selectedElement && ( @@ -284,6 +297,10 @@ const ModelerToolbar = ({ selectedElement={selectedElement} /> )} + + {enableBPMNChatbot && ( + + )} diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler.tsx index c14d7d0f1..8de16fe06 100644 --- a/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler.tsx +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/processes/[processId]/modeler.tsx @@ -253,7 +253,7 @@ const Modeler = ({ versionName, process, versions, ...divProps }: ModelerProps) setXmlEditorBpmn(undefined); }; - const handleXmlEditorSave = async (bpmn: string) => { + const handleXmlSave = async (bpmn: string) => { if (modeler.current) { await modeler.current.loadBPMN(bpmn); // If the bpmn contains unexpected content (text content for an element @@ -283,6 +283,7 @@ const Modeler = ({ versionName, process, versions, ...divProps }: ModelerProps) versions={versions} canRedo={canRedo} canUndo={canUndo} + modeler={modeler.current} /> )} {selectedVersionId && !showMobileView && } @@ -292,7 +293,7 @@ const Modeler = ({ versionName, process, versions, ...divProps }: ModelerProps) bpmn={xmlEditorBpmn} canSave={!selectedVersionId} onClose={handleCloseXmlEditor} - onSaveXml={handleXmlEditorSave} + onSaveXml={handleXmlSave} process={process} versionName={versionName} /> diff --git a/src/management-system-v2/components/bpmn-canvas.tsx b/src/management-system-v2/components/bpmn-canvas.tsx index fc65db561..9d17cb0d1 100644 --- a/src/management-system-v2/components/bpmn-canvas.tsx +++ b/src/management-system-v2/components/bpmn-canvas.tsx @@ -17,6 +17,7 @@ import schema from '@/lib/schema'; import { copyProcessImage } from '@/lib/process-export/copy-process-image'; import Modeling, { CommandStack } from 'bpmn-js/lib/features/modeling/Modeling'; import { Root, Element } from 'bpmn-js/lib/model/Types'; +import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory'; // Conditionally load the BPMN modeler only on the client, because it uses // "window" reference. It won't be included in the initial bundle, but will be @@ -81,6 +82,7 @@ export interface BPMNCanvasRef { getModeling: () => Modeling; getFactory: () => BpmnFactory; loadBPMN: (bpmn: string) => Promise; + getElementFactory: () => ElementFactory; } const BPMNCanvas = forwardRef( @@ -159,6 +161,9 @@ const BPMNCanvas = forwardRef( await modeler.current!.importXML(bpmn); fitViewport(modeler.current!); }, + getElementFactory: () => { + return modeler.current!.get('elementFactory'); + }, })); const [Modeler, Viewer] = use(BPMNJs); diff --git a/src/management-system-v2/components/bpmn-chatbot-response.tsx b/src/management-system-v2/components/bpmn-chatbot-response.tsx new file mode 100644 index 000000000..05d55c9b9 --- /dev/null +++ b/src/management-system-v2/components/bpmn-chatbot-response.tsx @@ -0,0 +1,37 @@ +import { FunctionCall } from '@google/generative-ai'; +import { Modal } from 'antd'; +import Title from 'antd/es/typography/Title'; +import React from 'react'; + +type ChatbotResponseModalProps = { + open: boolean; + onClose: () => void; + chatbotInteraction: ChatbotInteraction; +}; + +export type ChatbotInteraction = { + userPrompt: string; + bpmnProcess: string; + chatbotResponse: FunctionCall[] | undefined; +}; + +const ChatbotResponseModal: React.FC = ({ + open, + onClose, + chatbotInteraction, +}) => { + return ( + + User Prompt +

{chatbotInteraction.userPrompt}

+ BPMN Process Element +

{chatbotInteraction.bpmnProcess}

+ Response Content +

+ {JSON.stringify(chatbotInteraction.chatbotResponse)} +

+
+ ); +}; + +export default ChatbotResponseModal; diff --git a/src/management-system-v2/components/bpmn-chatbot-tools.json b/src/management-system-v2/components/bpmn-chatbot-tools.json new file mode 100644 index 000000000..808722357 --- /dev/null +++ b/src/management-system-v2/components/bpmn-chatbot-tools.json @@ -0,0 +1,67 @@ +[ + { + "name": "append_element", + "description": "Create a BPMN element and connect it to an already existing element. The element can either be a task, event or gateway. This automatically places the element and creates a connection from the given source element.", + "parameters": { + "type": "object", + "properties": { + "bpmn_type": { + "type": "string", + "description": "The BPMN type of the element. Possible types are: 'Task','StartEvent','EndEvent','ExclusiveGateway','InclusiveGateway','ParallelGateway'." + }, + "name": { + "type": "string", + "description": "The name of the new element." + }, + "source_element_id_or_name": { + "type": "string", + "description": "The id or name of the source element which to append to. Use the id if the element already exists in the process, otherwise the name." + }, + "label": { + "type": "string", + "description": "The label of the connection which is automatically created." + } + }, + "required": ["bpmn_type", "name", "source_element_id_or_name"] + } + }, + { + "name": "create_connection", + "description": "Create a connection between two elements. Only needed if the connection has not been created yet.", + "parameters": { + "type": "object", + "properties": { + "source_element_id_or_name": { + "type": "string", + "description": "The id or name of the source element of the connection. User the id if the element already exists in the process, otherwise the name." + }, + "target_element_id_or_name": { + "type": "string", + "description": "The id or name of the target element of the connection. User the id if the element already exists in the process, otherwise the name." + }, + "label": { + "type": "string", + "description": "The optional label of the connection." + } + }, + "required": ["source_element_id_or_name", "target_element_id_or_name"] + } + }, + { + "name": "remove_elements", + "description": "Remove BPMN elements. Removing an element with exactly one incomming and one outgoing connection merges the two connections into one. If the deleted element has more than one incomming or outgoing connection all associated connections will be removed. Connections are also considered elements and can be seperately removed too by including their id here.", + "parameters": { + "type": "object", + "properties": { + "element_ids": { + "type": "array", + "description": "The ids of the elements to be removed.", + "items": { + "type": "string" + } + } + }, + "required": ["element_ids"] + } + } +] diff --git a/src/management-system-v2/components/bpmn-chatbot.tsx b/src/management-system-v2/components/bpmn-chatbot.tsx new file mode 100644 index 000000000..39d3faab9 --- /dev/null +++ b/src/management-system-v2/components/bpmn-chatbot.tsx @@ -0,0 +1,206 @@ +import { BPMNCanvasRef } from '@/components/bpmn-canvas'; +import { Button, Card, Form, Input, List, Space, Tooltip } from 'antd'; +import React, { useState } from 'react'; +import { getNewShapePosition } from 'bpmn-js/lib/features/auto-place/BpmnAutoPlaceUtil'; +import { Element, Shape } from 'bpmn-js/lib/model/Types'; +import { MessageOutlined } from '@ant-design/icons'; +import ChatbotResponseModal, { ChatbotInteraction } from './bpmn-chatbot-response'; +import { sendToAPI } from '@/lib/bpmn-chatbot/bpmnChatbotAPIcommunication'; + +type ChatbotDialogProps = { + show: boolean; + modeler: BPMNCanvasRef | null; +}; + +type FieldType = { + prompt: string; +}; + +const ChatbotDialog: React.FC = ({ show, modeler }) => { + const [lastPrompts, setLastPrompts] = useState([]); + const [waitForResponse, setWaitForResponse] = useState(false); + const root = modeler!.getCurrentRoot(); + const modeling = modeler!.getModeling(); + const elementFactory = modeler!.getElementFactory(); + const [showChatbotResponseModal, setShowChatbotResponseModal] = useState(false); + const [chatbotInteraction, setChatbotInteraction] = useState(); + + function onPrompt({ prompt }: FieldType) { + setWaitForResponse(true); + getProcessXml().then((process) => { + sendToAPI(prompt, process) + .then((res) => { + if (res) { + processResponse(res); + } + setLastPrompts( + lastPrompts.concat({ + userPrompt: prompt, + bpmnProcess: process, + chatbotResponse: res, + }) + ); + }) + .finally(() => setWaitForResponse(false)); + }); + } + + //see tools definitions + function append_shape( + bpmn_type: string, + new_element_name: string, + source_element_id_or_name: string, + created: { name: string; shape: Shape }[], + label: string + ): Shape { + let source = modeler?.getElement(source_element_id_or_name) as Shape; + if (!source) { + console.log(created); + source = created.find((e) => e.name == source_element_id_or_name)!.shape; + } + let shape = elementFactory.createShape({ type: 'bpmn:' + bpmn_type }); + const position = getNewShapePosition(source, shape); + shape = modeling.createShape({ type: 'bpmn:' + bpmn_type }, position, root!); + const connection = modeling.createConnection( + source, + shape, + { type: 'bpmn:SequenceFlow' }, + root! + ); + modeling.updateLabel(connection, label); + modeling.updateLabel(shape, new_element_name); + return shape; + } + function create_connection( + source_element_id_or_name: string, + target_element_id_or_name: string, + created: { name: string; shape: Shape }[], + label?: string + ): void { + let source = modeler?.getElement(source_element_id_or_name) as Shape; + if (!source) { + source = created.find((e) => e.name == source_element_id_or_name)!.shape; + } + let target = modeler?.getElement(target_element_id_or_name) as Shape; + if (!target) { + target = created.find((e) => e.name == target_element_id_or_name)!.shape; + } + const connection = modeling.createConnection( + source, + target, + { type: 'bpmn:SequenceFlow' }, + root! + ); + if (label) { + modeling.updateLabel(connection, label); + } + } + function remove_elements(element_ids: string[]): void { + const elements: Element[] = []; + element_ids.forEach((e) => { + const element = modeler?.getElement(e); + if (element) { + elements.push(element); + } + }); + + modeling.removeElements(elements); + } + + //parsing tool uses listed in response + function processResponse(response: any[]) { + const created: { name: string; shape: Shape }[] = []; + response.forEach((res) => { + if (res.name == 'create_connection') { + create_connection( + res.args.source_element_id_or_name, + res.args.target_element_id_or_name, + created, + res.args.label + ); + } else if (res.name == 'remove_elements') { + remove_elements(res.args.element_ids); + } else if (res.name == 'append_element') { + const shape = append_shape( + res.args.bpmn_type, + res.args.name, + res.args.source_element_id_or_name, + created, + res.args.label + ); + created.push({ name: res.args.name, shape: shape }); + } + }); + } + + //get current xml of the ... part only + function getProcessXml(): Promise { + return modeler!.getXML().then((res) => { + if (res) { + const startIndex = res.indexOf('', startIndex); + if (endIndex == -1) { + return ''; + } + return res.slice(startIndex, endIndex) + ''; + } else { + return ''; + } + }); + } + + return ( + <> + + {chatbotInteraction && ( + setShowChatbotResponseModal(false)} + chatbotInteraction={chatbotInteraction} + > + )} + + ); +}; +export default ChatbotDialog; diff --git a/src/management-system-v2/lib/bpmn-chatbot/bpmn-chatbot-tools.ts b/src/management-system-v2/lib/bpmn-chatbot/bpmn-chatbot-tools.ts new file mode 100644 index 000000000..b5916c95d --- /dev/null +++ b/src/management-system-v2/lib/bpmn-chatbot/bpmn-chatbot-tools.ts @@ -0,0 +1,76 @@ +import { FunctionDeclaration, SchemaType } from '@google/generative-ai'; + +export const tools: FunctionDeclaration[] = [ + { + name: 'append_element', + description: + 'Create a BPMN element and connect it to an already existing element. The element can either be a task, event or gateway. This automatically places the element and creates a connection from the given source element.', + parameters: { + type: SchemaType.OBJECT, + properties: { + bpmn_type: { + type: SchemaType.STRING, + description: + "The BPMN type of the element. Possible types are: 'Task','StartEvent','EndEvent','ExclusiveGateway','InclusiveGateway','ParallelGateway'.", + }, + name: { + type: SchemaType.STRING, + description: 'The name of the new element.', + }, + source_element_id_or_name: { + type: SchemaType.STRING, + description: + 'The id or name of the source element which to append to. Use the id if the element already exists in the process, otherwise the name.', + }, + label: { + type: SchemaType.STRING, + description: 'The label of the connection which is automatically created.', + }, + }, + required: ['bpmn_type', 'name', 'source_element_id_or_name'], + }, + }, + { + name: 'create_connection', + description: + 'Create a connection between two elements. Only needed if the connection has not been created yet.', + parameters: { + type: SchemaType.OBJECT, + properties: { + source_element_id_or_name: { + type: SchemaType.STRING, + description: + 'The id or name of the source element of the connection. User the id if the element already exists in the process, otherwise the name.', + }, + target_element_id_or_name: { + type: SchemaType.STRING, + description: + 'The id or name of the target element of the connection. User the id if the element already exists in the process, otherwise the name.', + }, + label: { + type: SchemaType.STRING, + description: 'The optional label of the connection.', + }, + }, + required: ['source_element_id_or_name', 'target_element_id_or_name'], + }, + }, + { + name: 'remove_elements', + description: + 'Remove BPMN elements. Removing an element with exactly one incomming and one outgoing connection merges the two connections into one. If the deleted element has more than one incomming or outgoing connection all associated connections will be removed. Connections are also considered elements and can be seperately removed too by including their id here.', + parameters: { + type: SchemaType.OBJECT, + properties: { + element_ids: { + type: SchemaType.ARRAY, + description: 'The ids of the elements to be removed.', + items: { + type: SchemaType.STRING, + }, + }, + }, + required: ['element_ids'], + }, + }, +]; diff --git a/src/management-system-v2/lib/bpmn-chatbot/bpmnChatbotAPIcommunication.ts b/src/management-system-v2/lib/bpmn-chatbot/bpmnChatbotAPIcommunication.ts new file mode 100644 index 000000000..c4c8a0fe6 --- /dev/null +++ b/src/management-system-v2/lib/bpmn-chatbot/bpmnChatbotAPIcommunication.ts @@ -0,0 +1,22 @@ +'use server'; + +import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai'; +import { tools } from './bpmn-chatbot-tools'; + +if (!process.env.GEMINI_API_KEY) { + throw Error('Environment variable GEMINI_API_KEY not set.'); +} + +const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY); + +export async function sendToAPI(userPrompt: string, processXml: string) { + const model = genAI.getGenerativeModel({ + model: 'gemini-1.5-pro', + tools: [{ functionDeclarations: tools }], + }); + const finalPrompt = + '' + userPrompt + ' Use the following process as a basis: ' + processXml; + const result = await model.generateContent(finalPrompt); + const result_1 = result.response; + return result_1.functionCalls(); +} diff --git a/src/management-system-v2/package.json b/src/management-system-v2/package.json index 6314c0177..3c7bdb4e8 100644 --- a/src/management-system-v2/package.json +++ b/src/management-system-v2/package.json @@ -18,45 +18,46 @@ }, "dependencies": { "@casl/ability": "6.7.1", + "@dnd-kit/core": "6.1.0", + "@dnd-kit/modifiers": "7.0.0", + "@google/generative-ai": "^0.21.0", "@monaco-editor/react": "4.6.0", "@proceed/bpmn-helper": "1.0.0", - "@toast-ui/react-editor": "3.2.3", + "@react-email/components": "^0.0.15", "@tanstack/react-query": "5.29.2", + "@toast-ui/react-editor": "3.2.3", + "@types/jsonwebtoken": "^9.0.6", "antd": "5.16.2", "bcryptjs": "^2.4.3", "bpmn-js": "17.2.1", "bpmn-js-differ": "2.0.2", "classnames": "2.5.1", + "conf": "6.2.4", "env-paths": "^2.2.0", "file-type": "19.0.0", "fs-extra": "^10.1.0", - "conf": "6.2.4", - "@types/jsonwebtoken": "^9.0.6", "fuse.js": "7.0.0", "immer": "10.0.4", "js-md5": "^0.7.3", + "jsonwebtoken": "^8.5.1", "jspdf": "2.5.1", "jszip": "3.10.1", "lru-cache": "^10.1.0", "monaco-editor": "0.47.0", "next": "14.2.2", "next-auth": "4.24.7", + "nodemailer": "6.9.13", "openapi-fetch": "0.8.2", "react": "18.2.0", "react-dom": "18.2.0", - "jsonwebtoken": "^8.5.1", + "react-icons": "5.1.0", "server-only": "0.0.1", "svg2pdf.js": "2.2.3", "uuid": "9.0.1", "winston": "^3.3.3", "yup": "^0.32.9", "zod": "3.22.4", - "zustand": "4.5.2", - "react-icons": "5.1.0", - "nodemailer": "6.9.13", - "@dnd-kit/core": "6.1.0", - "@dnd-kit/modifiers": "7.0.0", - "@react-email/components": "^0.0.15" + "zustand": "4.5.2" }, "devDependencies": { "@tanstack/eslint-plugin-query": "5.28.11", diff --git a/yarn.lock b/yarn.lock index cb7e11252..1f17fae6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1350,7 +1350,7 @@ htm "^3.1.1" preact "^10.11.2" -"@casl/ability-v6@npm:@casl/ability@^6.5.0": +"@casl/ability-v6@npm:@casl/ability@^6.5.0", "@casl/ability@6.7.1": version "6.7.1" resolved "https://registry.yarnpkg.com/@casl/ability/-/ability-6.7.1.tgz#89691083aafd1cfc4ae9519ffbcb0e7cb77ac201" integrity sha512-e+Vgrehd1/lzOSwSqKHtmJ6kmIuZbGBlM2LBS5IuYGGKmVHuhUuyh3XgTn1VIw9+TO4gqU+uptvxfIRBUEdJuw== @@ -1364,13 +1364,6 @@ dependencies: "@ucast/mongo2js" "^1.3.0" -"@casl/ability@6.7.1": - version "6.7.1" - resolved "https://registry.yarnpkg.com/@casl/ability/-/ability-6.7.1.tgz#89691083aafd1cfc4ae9519ffbcb0e7cb77ac201" - integrity sha512-e+Vgrehd1/lzOSwSqKHtmJ6kmIuZbGBlM2LBS5IuYGGKmVHuhUuyh3XgTn1VIw9+TO4gqU+uptvxfIRBUEdJuw== - dependencies: - "@ucast/mongo2js" "^1.3.0" - "@casl/vue@1.x": version "1.2.3" resolved "https://registry.yarnpkg.com/@casl/vue/-/vue-1.2.3.tgz#ba835f71746334cddc6a97aa879e7b77d7472451" @@ -1562,6 +1555,11 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@google/generative-ai@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@google/generative-ai/-/generative-ai-0.21.0.tgz#a5011aab9e6082e706937b26ef23445933fa0d15" + integrity sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg== + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -19731,7 +19729,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -19766,15 +19764,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -19864,7 +19853,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -19892,13 +19881,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -21985,7 +21967,7 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -22020,15 +22002,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"