From 9613750fd1ff48343e8d80ce5154733a4dbc55d7 Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Mon, 5 Jun 2023 12:09:13 -0400
Subject: Enter button on user input

---
 extension/react-app/src/tabs/gui.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index a08698a4..7dd30acb 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -38,11 +38,11 @@ function GUI(props: GUIProps) {
   const [waitingForSteps, setWaitingForSteps] = useState(false);
   const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
   const [history, setHistory] = useState<History | undefined>();
-  // {
+  //   {
   //   timeline: [
   //     {
   //       step: {
-  //         name: "RunCodeStep",
+  //         name: "Waiting for user input",
   //         cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",
   //         description:
   //           "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`",
@@ -189,6 +189,8 @@ function GUI(props: GUIProps) {
     if (mainTextInputRef.current) {
       if (!client) return;
       let input = mainTextInputRef.current.value;
+      if (input.trim() === "") return;
+
       setWaitingForSteps(true);
       client.sendMainInput(input);
       setUserInputQueue((queue) => {
-- 
cgit v1.2.3-70-g09d2


From 60eaf08df63b77ce31ce8afaa77fdd6b357c8a8a Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Tue, 6 Jun 2023 00:21:15 -0400
Subject: many design improvements

---
 continuedev/src/continuedev/core/autopilot.py      | 34 +++++++---
 continuedev/src/continuedev/core/main.py           | 30 ++++++---
 continuedev/src/continuedev/core/policy.py         |  7 +-
 continuedev/src/continuedev/core/sdk.py            | 21 ++++--
 .../recipes/CreatePipelineRecipe/main.py           |  2 +-
 .../recipes/CreatePipelineRecipe/steps.py          | 62 +++++++++++++-----
 continuedev/src/continuedev/server/ide.py          | 11 ++++
 continuedev/src/continuedev/server/ide_protocol.py |  8 +++
 continuedev/src/continuedev/steps/core/core.py     | 27 ++++----
 extension/package.json                             |  4 +-
 extension/react-app/src/components/DebugPanel.tsx  | 15 ++---
 .../react-app/src/components/StepContainer.tsx     | 52 ++++++++-------
 extension/react-app/src/components/index.ts        | 11 +++-
 extension/react-app/src/tabs/chat/MessageDiv.tsx   |  1 -
 extension/react-app/src/tabs/gui.tsx               | 74 +++++++++++++++++-----
 extension/src/continueIdeClient.ts                 | 39 +++++++++++-
 16 files changed, 286 insertions(+), 112 deletions(-)

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index db06c975..d55e521b 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -1,13 +1,13 @@
 import traceback
 import time
-from typing import Callable, Coroutine, List
+from typing import Any, Callable, Coroutine, Dict, List
 from ..models.filesystem_edit import FileEditWithFullContents
 from ..libs.llm import LLM
 from .observation import Observation, InternalErrorObservation
 from ..server.ide_protocol import AbstractIdeProtocolServer
 from ..libs.util.queue import AsyncSubscriptionQueue
 from ..models.main import ContinueBaseModel
-from .main import Policy, History, FullState, Step, HistoryNode
+from .main import Context, ContinueCustomException, Policy, History, FullState, Step, HistoryNode
 from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep
 from ..libs.util.telemetry import capture_event
 from .sdk import ContinueSDK
@@ -18,6 +18,7 @@ class Autopilot(ContinueBaseModel):
     policy: Policy
     ide: AbstractIdeProtocolServer
     history: History = History.from_empty()
+    context: Context = Context()
     _on_update_callbacks: List[Callable[[FullState], None]] = []
 
     _active: bool = False
@@ -26,6 +27,10 @@ class Autopilot(ContinueBaseModel):
 
     _user_input_queue = AsyncSubscriptionQueue()
 
+    @property
+    def continue_sdk(self) -> ContinueSDK:
+        return ContinueSDK(self)
+
     class Config:
         arbitrary_types_allowed = True
 
@@ -60,7 +65,7 @@ class Autopilot(ContinueBaseModel):
                 current_step = self.history.get_current().step
                 self.history.step_back()
                 if issubclass(current_step.__class__, ReversibleStep):
-                    await current_step.reverse(ContinueSDK(self))
+                    await current_step.reverse(self.continue_sdk)
 
                 await self.update_subscribers()
         except Exception as e:
@@ -105,7 +110,16 @@ class Autopilot(ContinueBaseModel):
         self._step_depth += 1
 
         try:
-            observation = await step(ContinueSDK(self))
+            observation = await step(self.continue_sdk)
+        except ContinueCustomException as e:
+            # Attach an InternalErrorObservation to the step and unhide it.
+            error_string = e.message
+            print(
+                f"\n{error_string}\n{e}")
+
+            observation = InternalErrorObservation(
+                error=error_string)
+            step.hide = False
         except Exception as e:
             # Attach an InternalErrorObservation to the step and unhide it.
             error_string = '\n\n'.join(
@@ -125,11 +139,13 @@ class Autopilot(ContinueBaseModel):
         await self.update_subscribers()
 
         # Update its description
-        async def update_description():
-            step._set_description(await step.describe(ContinueSDK(self).models))
-            # Update subscribers with new description
-            await self.update_subscribers()
-        asyncio.create_task(update_description())
+        if step.description is None:
+            async def update_description():
+                step.description = await step.describe(self.continue_sdk.models)
+                # Update subscribers with new description
+                await self.update_subscribers()
+
+            asyncio.create_task(update_description())
 
         return observation
 
diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py
index b2b97bae..17b30e96 100644
--- a/continuedev/src/continuedev/core/main.py
+++ b/continuedev/src/continuedev/core/main.py
@@ -105,7 +105,7 @@ class Policy(ContinueBaseModel):
 class Step(ContinueBaseModel):
     name: str = None
     hide: bool = False
-    _description: Union[str, None] = None
+    description: Union[str, None] = None
 
     system_message: Union[str, None] = None
 
@@ -113,17 +113,14 @@ class Step(ContinueBaseModel):
         copy_on_model_validation = False
 
     async def describe(self, models: Models) -> Coroutine[str, None, None]:
-        if self._description is not None:
-            return self._description
+        if self.description is not None:
+            return self.description
         return "Running step: " + self.name
 
-    def _set_description(self, description: str):
-        self._description = description
-
     def dict(self, *args, **kwargs):
         d = super().dict(*args, **kwargs)
-        if self._description is not None:
-            d["description"] = self._description
+        if self.description is not None:
+            d["description"] = self.description
         else:
             d["description"] = "`Description loading...`"
         return d
@@ -173,4 +170,21 @@ class Validator(Step):
         raise NotImplementedError
 
 
+class Context:
+    key_value: Dict[str, str] = {}
+
+    def set(self, key: str, value: str):
+        self.key_value[key] = value
+
+    def get(self, key: str) -> str:
+        return self.key_value[key]
+
+
+class ContinueCustomException(Exception):
+    message: str
+
+    def __init__(self, message: str):
+        self.message = message
+
+
 HistoryNode.update_forward_refs()
diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py
index e65b6c9d..4934497d 100644
--- a/continuedev/src/continuedev/core/policy.py
+++ b/continuedev/src/continuedev/core/policy.py
@@ -17,9 +17,10 @@ class DemoPolicy(Policy):
     def next(self, history: History) -> Step:
         # At the very start, run initial Steps spcecified in the config
         if history.get_current() is None:
-            return (MessageStep(message="Welcome to Continue!") >>
-                    SetupContinueWorkspaceStep() >>
-                    CreateCodebaseIndexChroma() >>
+            return (
+                    MessageStep(name="Welcome to Continue!", message="") >>
+                    # SetupContinueWorkspaceStep() >>
+                    # CreateCodebaseIndexChroma() >>
                     StepsOnStartupStep())
 
         observation = history.get_current().observation
diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py
index 8317a3d1..690949f1 100644
--- a/continuedev/src/continuedev/core/sdk.py
+++ b/continuedev/src/continuedev/core/sdk.py
@@ -11,7 +11,7 @@ from ..libs.llm.hf_inference_api import HuggingFaceInferenceAPI
 from ..libs.llm.openai import OpenAI
 from .observation import Observation
 from ..server.ide_protocol import AbstractIdeProtocolServer
-from .main import History, Step
+from .main import Context, ContinueCustomException, History, Step
 from ..steps.core.core import *
 
 
@@ -44,6 +44,7 @@ class ContinueSDK(AbstractContinueSDK):
     ide: AbstractIdeProtocolServer
     steps: ContinueSDKSteps
     models: Models
+    context: Context
     __autopilot: Autopilot
 
     def __init__(self, autopilot: Autopilot):
@@ -51,6 +52,7 @@ class ContinueSDK(AbstractContinueSDK):
         self.__autopilot = autopilot
         self.steps = ContinueSDKSteps(self)
         self.models = Models(self)
+        self.context = autopilot.context
 
     @property
     def history(self) -> History:
@@ -64,8 +66,8 @@ class ContinueSDK(AbstractContinueSDK):
     async def run_step(self, step: Step) -> Coroutine[Observation, None, None]:
         return await self.__autopilot._run_singular_step(step)
 
-    async def apply_filesystem_edit(self, edit: FileSystemEdit):
-        return await self.run_step(FileSystemEditStep(edit=edit))
+    async def apply_filesystem_edit(self, edit: FileSystemEdit, name: str = None, description: str = None):
+        return await self.run_step(FileSystemEditStep(edit=edit, description=description, **({'name': name} if name else {})))
 
     async def wait_for_user_input(self) -> str:
         return await self.__autopilot.wait_for_user_input()
@@ -73,18 +75,20 @@ class ContinueSDK(AbstractContinueSDK):
     async def wait_for_user_confirmation(self, prompt: str):
         return await self.run_step(WaitForUserConfirmationStep(prompt=prompt))
 
-    async def run(self, commands: Union[List[str], str], cwd: str = None):
+    async def run(self, commands: Union[List[str], str], cwd: str = None, name: str = None, description: str = None):
         commands = commands if isinstance(commands, List) else [commands]
-        return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd))
+        return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd, description=description, **({'name': name} if name else {})))
 
-    async def edit_file(self, filename: str, prompt: str):
+    async def edit_file(self, filename: str, prompt: str, name: str = None, description: str = None):
         filepath = await self._ensure_absolute_path(filename)
 
         await self.ide.setFileOpen(filepath)
         contents = await self.ide.readFile(filepath)
         await self.run_step(EditCodeStep(
             range_in_files=[RangeInFile.from_entire_file(filepath, contents)],
-            prompt=f'Here is the code before:\n\n{{code}}\n\nHere is the user request:\n\n{prompt}\n\nHere is the code edited to perfectly solve the user request:\n\n'
+            prompt=f'Here is the code before:\n\n{{code}}\n\nHere is the user request:\n\n{prompt}\n\nHere is the code edited to perfectly solve the user request:\n\n',
+            description=description,
+            **({'name': name} if name else {})
         ))
 
     async def append_to_file(self, filename: str, content: str):
@@ -126,3 +130,6 @@ class ContinueSDK(AbstractContinueSDK):
     def set_loading_message(self, message: str):
         # self.__autopilot.set_loading_message(message)
         raise NotImplementedError()
+
+    def raise_exception(self, message: str):
+        raise ContinueCustomException(message)
diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
index 55c25da4..1206db0e 100644
--- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
+++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
@@ -12,7 +12,7 @@ class CreatePipelineRecipe(Step):
 
     async def run(self, sdk: ContinueSDK):
         text_observation = await sdk.run_step(
-            MessageStep(message=dedent("""\
+            MessageStep(name="Building your first dlt pipeline", message=dedent("""\
                 This recipe will walk you through the process of creating a dlt pipeline for your chosen data source. With the help of Continue, you will:
                 - Create a Python virtual environment with dlt installed
                 - Run `dlt init` to generate a pipeline template
diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
index ef5e3b43..3c8277c0 100644
--- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
+++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
@@ -1,8 +1,12 @@
+import os
+import subprocess
 from textwrap import dedent
 
+from ...models.main import Range
+from ...models.filesystem import RangeInFile
 from ...steps.main import MessageStep
 from ...core.sdk import Models
-from ...core.observation import DictObservation
+from ...core.observation import DictObservation, InternalErrorObservation
 from ...models.filesystem_edit import AddFile
 from ...core.main import Step
 from ...core.sdk import ContinueSDK
@@ -33,45 +37,69 @@ class SetupPipelineStep(Step):
             f'dlt init {source_name} duckdb',
             'Y',
             'pip install -r requirements.txt'
-        ])
-
+        ], description=dedent(f"""\
+            Running the following commands:
+            - `python3 -m venv env`: Create a Python virtual environment
+            - `source env/bin/activate`: Activate the virtual environment
+            - `pip install dlt`: Install dlt
+            - `dlt init {source_name} duckdb`: Create a new dlt pipeline called {source_name} that loads data into a local DuckDB instance
+            - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline"""))
+
+        await sdk.wait_for_user_confirmation("Wait for the commands to finish running, then press `Continue`")
         # editing the resource function to call the requested API
+        await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 30, 0)), "#00ff0022")
+
         await sdk.edit_file(
             filename=filename,
-            prompt=f'Edit the resource function to call the API described by this: {self.api_description}'
+            prompt=f'Edit the resource function to call the API described by this: {self.api_description}',
+            name="Edit the resource function to call the API"
         )
 
         # wait for user to put API key in secrets.toml
         await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml")
         await sdk.wait_for_user_confirmation("If this service requires an API key, please add it to the `secrets.toml` file and then press `Continue`")
-        return DictObservation(values={"source_name": source_name})
+
+        sdk.context.set("source_name", source_name)
 
 
 class ValidatePipelineStep(Step):
     hide: bool = True
 
     async def run(self, sdk: ContinueSDK):
-        source_name = sdk.history.last_observation().values["source_name"]
+        workspace_dir = await sdk.ide.getWorkspaceDirectory()
+        source_name = sdk.context.get("source_name")
         filename = f'{source_name}.py'
 
-        await sdk.run_step(MessageStep(message=dedent("""\
-                This step will validate that your dlt pipeline is working as expected:
-                - Test that the API call works
-                - Load the data into a local DuckDB instance
-                - Write a query to view the data
-                """)))
+        # await sdk.run_step(MessageStep(name="Validate the pipeline", message=dedent("""\
+        #         Next, we will validate that your dlt pipeline is working as expected:
+        #         - Test that the API call works
+        #         - Load the data into a local DuckDB instance
+        #         - Write a query to view the data
+        #         """)))
 
         # test that the API call works
-        await sdk.run(f'python3 {filename}')
+
+        p = subprocess.run(
+            ['python3', f'{filename}'], capture_output=True, text=True, cwd=workspace_dir)
+        err = p.stderr
+
+        # If it fails, return the error
+        if err is not None and err != "":
+            sdk.raise_exception(
+                f"Error while running pipeline. Fix the resource function in {filename} and rerun this step: \n\n" + err)
+
+        await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API")
 
         # remove exit() from the main main function
         await sdk.edit_file(
             filename=filename,
-            prompt='Remove exit() from the main function'
+            prompt='Remove exit() from the main function',
+            name="Remove early exit() from main function",
+            description="Remove the `exit()` call from the main function in the pipeline file so that the data is loaded into DuckDB"
         )
 
         # load the data into the DuckDB instance
-        await sdk.run(f'python3 {filename}')
+        await sdk.run(f'python3 {filename}', name="Load data into DuckDB", description=f"Running python3 {filename} to load data into DuckDB")
 
         table_name = f"{source_name}.{source_name}_resource"
         tables_query_code = dedent(f'''\
@@ -89,5 +117,5 @@ class ValidatePipelineStep(Step):
         ''')
 
         query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py"
-        await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code))
-        await sdk.run('env/bin/python3 query.py')
+        await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code), name="Add query.py file", description="Adding a file called `query.py` to the workspace that will run a test query on the DuckDB instance")
+        await sdk.run('env/bin/python3 query.py', name="Run test query", description="Running `env/bin/python3 query.py` to test that the data was loaded into DuckDB as expected")
diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py
index eec5b607..073e1dba 100644
--- a/continuedev/src/continuedev/server/ide.py
+++ b/continuedev/src/continuedev/server/ide.py
@@ -133,6 +133,17 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
             "sessionId": session_id
         })
 
+    async def highlightCode(self, range_in_file: RangeInFile, color: str):
+        await self._send_json("highlightCode", {
+            "rangeInFile": range_in_file.dict(),
+            "color": color
+        })
+
+    async def runCommand(self, command: str):
+        await self._send_json("runCommand", {
+            "command": command
+        })
+
     async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool:
         ids = [str(uuid.uuid4()) for _ in suggestions]
         for i in range(len(suggestions)):
diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py
index 8f155415..f42de68f 100644
--- a/continuedev/src/continuedev/server/ide_protocol.py
+++ b/continuedev/src/continuedev/server/ide_protocol.py
@@ -82,3 +82,11 @@ class AbstractIdeProtocolServer(ABC):
     @abstractmethod
     async def getUserSecret(self, key: str):
         """Get a user secret"""
+
+    @abstractmethod
+    async def highlightCode(self, range_in_file: RangeInFile, color: str):
+        """Highlight code"""
+
+    @abstractmethod
+    async def runCommand(self, command: str):
+        """Run a command"""
diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py
index ad468595..99786b00 100644
--- a/continuedev/src/continuedev/steps/core/core.py
+++ b/continuedev/src/continuedev/steps/core/core.py
@@ -55,18 +55,21 @@ class ShellCommandsStep(Step):
     async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]:
         cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd
 
-        process = subprocess.Popen(
-            '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd)
+        for cmd in self.cmds:
+            await sdk.ide.runCommand(cmd)
 
-        stdin_input = "\n".join(self.cmds)
-        out, err = process.communicate(stdin_input.encode())
+        # process = subprocess.Popen(
+        #     '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd)
 
-        # If it fails, return the error
-        if err is not None and err != "":
-            self._err_text = err
-            return TextObservation(text=err)
+        # stdin_input = "\n".join(self.cmds)
+        # out, err = process.communicate(stdin_input.encode())
 
-        return None
+        # # If it fails, return the error
+        # if err is not None and err != "":
+        #     self._err_text = err
+        #     return TextObservation(text=err)
+
+        # return None
 
 
 class EditCodeStep(Step):
@@ -197,10 +200,10 @@ class WaitForUserInputStep(Step):
         if self._response is None:
             return self.prompt
         else:
-            return self.prompt + "\n\n" + self._response
+            return f"{self.prompt}\n\n`{self._response}`"
 
     async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]:
-        self._description = self.prompt
+        self.description = self.prompt
         resp = await sdk.wait_for_user_input()
         self._response = resp
         return TextObservation(text=resp)
@@ -214,7 +217,7 @@ class WaitForUserConfirmationStep(Step):
         return self.prompt
 
     async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]:
-        self._description = self.prompt
+        self.description = self.prompt
         resp = await sdk.wait_for_user_input()
         self.hide = True
         return TextObservation(text=resp)
diff --git a/extension/package.json b/extension/package.json
index 7bd48f98..c834f402 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -42,12 +42,12 @@
           "description": "The URL of the Continue server to use."
         },
         "continue.OPENAI_API_KEY": {
-          "type": "string",
+          "type": "password",
           "default": "",
           "description": "The OpenAI API key to use for code generation."
         },
         "continue.HUGGING_FACE_TOKEN": {
-          "type": "string",
+          "type": "password",
           "default": "",
           "description": "The Hugging Face API token to use for code generation."
         }
diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx
index 9dacc624..11ec2fe2 100644
--- a/extension/react-app/src/components/DebugPanel.tsx
+++ b/extension/react-app/src/components/DebugPanel.tsx
@@ -9,7 +9,7 @@ import {
 } from "../redux/slices/configSlice";
 import { setHighlightedCode } from "../redux/slices/miscSlice";
 import { updateFileSystem } from "../redux/slices/debugContexSlice";
-import { buttonColor, defaultBorderRadius, vscBackground } from ".";
+import { defaultBorderRadius, secondaryDark, vscBackground } from ".";
 interface DebugPanelProps {
   tabs: {
     element: React.ReactElement;
@@ -19,14 +19,15 @@ interface DebugPanelProps {
 
 const GradientContainer = styled.div`
   // Uncomment to get gradient border
-  background: linear-gradient(
+  /* background: linear-gradient(
     101.79deg,
     #12887a 0%,
     #87245c 37.64%,
     #e12637 65.98%,
     #ffb215 110.45%
-  );
+  ); */
   /* padding: 10px; */
+  background-color: ${secondaryDark};
   margin: 0;
   height: 100%;
   /* border: 1px solid white; */
@@ -36,11 +37,8 @@ const GradientContainer = styled.div`
 const MainDiv = styled.div`
   height: 100%;
   border-radius: ${defaultBorderRadius};
-  overflow-y: scroll;
-  scrollbar-gutter: stable both-edges;
   scrollbar-base-color: transparent;
-  /* background: ${vscBackground}; */
-  background-color: #1e1e1ede;
+  background-color: ${vscBackground};
 `;
 
 const TabBar = styled.div<{ numTabs: number }>`
@@ -105,9 +103,6 @@ function DebugPanel(props: DebugPanelProps) {
               <div
                 key={index}
                 hidden={index !== currentTab}
-                className={
-                  tab.title === "Chat" ? "overflow-hidden" : "overflow-scroll"
-                }
                 style={{ scrollbarGutter: "stable both-edges" }}
               >
                 {tab.element}
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index f54d4d75..dab5a752 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -2,13 +2,11 @@ import { useCallback, useEffect, useRef, useState } from "react";
 import styled, { keyframes } from "styled-components";
 import {
   appear,
-  buttonColor,
   defaultBorderRadius,
-  MainContainerWithBorder,
-  MainTextInput,
   secondaryDark,
   vscBackground,
   GradientBorder,
+  vscBackgroundTransparent,
 } from ".";
 import { RangeInFile, FileEdit } from "../../../src/client";
 import CodeBlock from "./CodeBlock";
@@ -32,6 +30,7 @@ interface StepContainerProps {
   onRefinement: (input: string) => void;
   onUserInput: (input: string) => void;
   onRetry: () => void;
+  open?: boolean;
 }
 
 const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>`
@@ -44,17 +43,25 @@ const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>`
 `;
 
 const StepContainerDiv = styled.div<{ open: boolean }>`
-  background-color: ${(props) => (props.open ? vscBackground : secondaryDark)};
-  border-radius: ${defaultBorderRadius};
-  padding: 8px;
+  /* background-color: ${(props) =>
+    props.open ? vscBackground : secondaryDark}; */
+  /* border-radius: ${defaultBorderRadius}; */
+  /* padding: 8px; */
 `;
 
 const HeaderDiv = styled.div`
+  background-color: ${vscBackgroundTransparent};
   display: grid;
   grid-template-columns: 1fr auto;
   align-items: center;
 `;
 
+const ContentDiv = styled.div`
+  padding: 8px;
+  padding-left: 16px;
+  background-color: ${vscBackground};
+`;
+
 const HeaderButton = styled.button`
   background-color: transparent;
   border: 1px solid white;
@@ -75,12 +82,10 @@ const OnHoverDiv = styled.div`
   animation: ${appear} 0.3s ease-in-out;
 `;
 
-const NaturalLanguageInput = styled(MainTextInput)`
-  width: 80%;
-`;
-
 function StepContainer(props: StepContainerProps) {
-  const [open, setOpen] = useState(false);
+  const [open, setOpen] = useState(
+    typeof props.open === "undefined" ? true : props.open
+  );
   const [isHovered, setIsHovered] = useState(false);
   const naturalLanguageInputRef = useRef<HTMLTextAreaElement>(null);
   const userInputRef = useRef<HTMLInputElement>(null);
@@ -116,11 +121,11 @@ function StepContainer(props: StepContainerProps) {
       }}
       hidden={props.historyNode.step.hide as any}
     >
-      <GradientBorder
-        className="m-2 overflow-hidden cursor-pointer"
-        onClick={() => setOpen((prev) => !prev)}
-      >
-        <StepContainerDiv open={open}>
+      <StepContainerDiv open={open}>
+        <GradientBorder
+          className="overflow-hidden cursor-pointer"
+          onClick={() => setOpen((prev) => !prev)}
+        >
           <HeaderDiv>
             <h4 className="m-2">
               {open ? (
@@ -128,7 +133,7 @@ function StepContainer(props: StepContainerProps) {
               ) : (
                 <ChevronRight size="1.4em" />
               )}
-              {props.historyNode.step.name as any}:
+              {props.historyNode.step.name as any}
             </h4>
             {/* <HeaderButton
               onClick={(e) => {
@@ -152,8 +157,9 @@ function StepContainer(props: StepContainerProps) {
               <></>
             )}
           </HeaderDiv>
-
-          {open && (
+        </GradientBorder>
+        <ContentDiv hidden={!open}>
+          {open && false && (
             <>
               <pre className="overflow-scroll">
                 Step Details:
@@ -177,7 +183,7 @@ function StepContainer(props: StepContainerProps) {
             </ReactMarkdown>
           )}
 
-          {props.historyNode.step.name === "Waiting for user input" && (
+          {/* {props.historyNode.step.name === "Waiting for user input" && (
             <InputAndButton
               onUserInput={(value) => {
                 props.onUserInput(value);
@@ -202,9 +208,9 @@ function StepContainer(props: StepContainerProps) {
                 value="Confirm"
               />
             </>
-          )}
-        </StepContainerDiv>
-      </GradientBorder>
+          )} */}
+        </ContentDiv>
+      </StepContainerDiv>
 
       {/* <OnHoverDiv hidden={!open}>
         <NaturalLanguageInput
diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts
index 7ba60467..ac5faa41 100644
--- a/extension/react-app/src/components/index.ts
+++ b/extension/react-app/src/components/index.ts
@@ -3,6 +3,7 @@ import styled, { keyframes } from "styled-components";
 export const defaultBorderRadius = "5px";
 export const secondaryDark = "rgb(37 37 38)";
 export const vscBackground = "rgb(30 30 30)";
+export const vscBackgroundTransparent = "#1e1e1ede";
 export const buttonColor = "rgb(113 28 59)";
 export const buttonColorHover = "rgb(113 28 59 0.67)";
 
@@ -94,9 +95,13 @@ export const Loader = styled.div`
   margin: auto;
 `;
 
-export const GradientBorder = styled.div<{ borderWidth?: string }>`
-  border-radius: ${defaultBorderRadius};
-  padding: ${(props) => props.borderWidth || "1px"};
+export const GradientBorder = styled.div<{
+  borderWidth?: string;
+  borderRadius?: string;
+}>`
+  border-radius: ${(props) => props.borderRadius || "0"};
+  padding-top: ${(props) => props.borderWidth || "1px"};
+  padding-bottom: ${(props) => props.borderWidth || "1px"};
   background: linear-gradient(
     101.79deg,
     #12887a 0%,
diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx
index ad81f5e9..1d7bb5f5 100644
--- a/extension/react-app/src/tabs/chat/MessageDiv.tsx
+++ b/extension/react-app/src/tabs/chat/MessageDiv.tsx
@@ -21,7 +21,6 @@ const Container = styled.div`
   width: fit-content;
   max-width: 75%;
   overflow-y: scroll;
-  scrollbar-gutter: stable both-edges;
   word-wrap: break-word;
   -ms-word-wrap: break-word;
   height: fit-content;
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index 7dd30acb..c66172a9 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -1,25 +1,21 @@
 import styled from "styled-components";
 import {
-  Button,
   defaultBorderRadius,
   vscBackground,
-  MainTextInput,
   Loader,
+  MainTextInput,
 } from "../components";
 import ContinueButton from "../components/ContinueButton";
 import { useCallback, useEffect, useRef, useState } from "react";
 import { History } from "../../../schema/History";
 import { HistoryNode } from "../../../schema/HistoryNode";
 import StepContainer from "../components/StepContainer";
-import { useSelector } from "react-redux";
-import { RootStore } from "../redux/store";
-import useContinueWebsocket from "../hooks/useWebsocket";
 import useContinueGUIProtocol from "../hooks/useWebsocket";
 
 let TopGUIDiv = styled.div`
   display: grid;
   grid-template-columns: 1fr;
-  overflow: scroll;
+  background-color: ${vscBackground};
 `;
 
 let UserInputQueueItem = styled.div`
@@ -156,8 +152,20 @@ function GUI(props: GUIProps) {
   //   current_index: 0,
   // } as any);
 
+  const topGuiDivRef = useRef<HTMLDivElement>(null);
   const client = useContinueGUIProtocol();
 
+  const scrollToBottom = useCallback(() => {
+    if (topGuiDivRef.current) {
+      setTimeout(() => {
+        window.scrollTo({
+          top: window.outerHeight,
+          behavior: "smooth",
+        });
+      }, 100);
+    }
+  }, [topGuiDivRef.current]);
+
   useEffect(() => {
     console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate);
     client?.onStateUpdate((state) => {
@@ -165,9 +173,15 @@ function GUI(props: GUIProps) {
       setWaitingForSteps(state.active);
       setHistory(state.history);
       setUserInputQueue(state.user_input_queue);
+
+      scrollToBottom();
     });
   }, [client]);
 
+  useEffect(() => {
+    scrollToBottom();
+  }, [waitingForSteps]);
+
   const mainTextInputRef = useRef<HTMLTextAreaElement>(null);
 
   useEffect(() => {
@@ -189,16 +203,33 @@ function GUI(props: GUIProps) {
     if (mainTextInputRef.current) {
       if (!client) return;
       let input = mainTextInputRef.current.value;
-      if (input.trim() === "") return;
 
-      setWaitingForSteps(true);
-      client.sendMainInput(input);
-      setUserInputQueue((queue) => {
-        return [...queue, input];
-      });
-      mainTextInputRef.current.value = "";
-      mainTextInputRef.current.style.height = "";
+      if (
+        history &&
+        history.timeline[history.current_index].step.name ===
+          "Waiting for user input"
+      ) {
+        if (input.trim() === "") return;
+        onStepUserInput(input, history!.current_index);
+      } else if (
+        history &&
+        history.timeline[history.current_index].step.name ===
+          "Waiting for user confirmation"
+      ) {
+        onStepUserInput("ok", history!.current_index);
+      } else {
+        if (input.trim() === "") return;
+
+        client.sendMainInput(input);
+        setUserInputQueue((queue) => {
+          return [...queue, input];
+        });
+        mainTextInputRef.current.value = "";
+        mainTextInputRef.current.style.height = "";
+      }
     }
+
+    setWaitingForSteps(true);
   };
 
   const onStepUserInput = (input: string, index: number) => {
@@ -209,7 +240,14 @@ function GUI(props: GUIProps) {
 
   // const iterations = useSelector(selectIterations);
   return (
-    <TopGUIDiv>
+    <TopGUIDiv
+      ref={topGuiDivRef}
+      onKeyDown={(e) => {
+        if (e.key === "Enter" && e.ctrlKey) {
+          onMainTextInput();
+        }
+      }}
+    >
       {typeof client === "undefined" && (
         <>
           <Loader></Loader>
@@ -249,6 +287,12 @@ function GUI(props: GUIProps) {
       </div>
 
       <MainTextInput
+        disabled={
+          history
+            ? history.timeline[history.current_index].step.name ===
+              "Waiting for user confirmation"
+            : false
+        }
         ref={mainTextInputRef}
         onKeyDown={(e) => {
           if (e.key === "Enter") {
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index a5a1c5dc..25287d32 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -96,6 +96,12 @@ class IdeProtocolClient {
           fileEdit,
         });
         break;
+      case "highlightCode":
+        this.highlightCode(data.rangeInFile, data.color);
+        break;
+      case "runCommand":
+        this.runCommand(data.command);
+        break;
       case "saveFile":
         this.saveFile(data.filepath);
         break;
@@ -117,6 +123,28 @@ class IdeProtocolClient {
   // ------------------------------------ //
   // On message handlers
 
+  async highlightCode(rangeInFile: RangeInFile, color: string) {
+    const range = new vscode.Range(
+      rangeInFile.range.start.line,
+      rangeInFile.range.start.character,
+      rangeInFile.range.end.line,
+      rangeInFile.range.end.character
+    );
+    const editor = await openEditorAndRevealRange(
+      rangeInFile.filepath,
+      range,
+      vscode.ViewColumn.One
+    );
+    if (editor) {
+      editor.setDecorations(
+        vscode.window.createTextEditorDecorationType({
+          backgroundColor: color,
+        }),
+        [range]
+      );
+    }
+  }
+
   showSuggestion(edit: FileEdit) {
     // showSuggestion already exists
     showSuggestion(
@@ -289,7 +317,16 @@ class IdeProtocolClient {
   }
 
   runCommand(command: string) {
-    vscode.window.terminals[0].sendText(command, true);
+    if (vscode.window.terminals.length === 0) {
+      const terminal = vscode.window.createTerminal();
+      terminal.show();
+      terminal.sendText("bash", true);
+      terminal.sendText(command, true);
+      return;
+    }
+    const terminal = vscode.window.terminals[0];
+    terminal.show();
+    terminal.sendText(command, true);
     // But need to know when it's done executing...
   }
 }
-- 
cgit v1.2.3-70-g09d2


From 881718c4c7f58837a8a208930e7d2c69b9433fd7 Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Tue, 6 Jun 2023 17:50:50 -0400
Subject: trying to reliably capture terminal output in vsc

---
 continuedev/src/continuedev/core/autopilot.py      |    4 +-
 continuedev/src/continuedev/core/main.py           |    4 +-
 continuedev/src/continuedev/core/observation.py    |    1 +
 continuedev/src/continuedev/core/sdk.py            |    8 +-
 .../recipes/CreatePipelineRecipe/main.py           |    2 +-
 .../recipes/CreatePipelineRecipe/steps.py          |   17 +-
 continuedev/src/continuedev/server/ide.py          |   12 +-
 continuedev/src/continuedev/server/ide_protocol.py |    2 +-
 continuedev/src/continuedev/steps/core/core.py     |    6 +-
 extension/esbuild.mjs                              |   14 +
 extension/package-lock.json                        | 1132 +-------------------
 extension/package.json                             |    5 +-
 .../react-app/src/components/StepContainer.tsx     |   12 +-
 .../react-app/src/components/ToggleErrorDiv.tsx    |   41 +
 extension/react-app/src/tabs/gui.tsx               |    4 +
 extension/src/continueIdeClient.ts                 |   24 +-
 extension/src/terminal/terminalEmulator.ts         |  301 +++---
 17 files changed, 277 insertions(+), 1312 deletions(-)
 create mode 100644 extension/esbuild.mjs
 create mode 100644 extension/react-app/src/components/ToggleErrorDiv.tsx

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index d55e521b..b227570e 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -118,7 +118,7 @@ class Autopilot(ContinueBaseModel):
                 f"\n{error_string}\n{e}")
 
             observation = InternalErrorObservation(
-                error=error_string)
+                error=error_string, title=e.title)
             step.hide = False
         except Exception as e:
             # Attach an InternalErrorObservation to the step and unhide it.
@@ -128,7 +128,7 @@ class Autopilot(ContinueBaseModel):
                 f"Error while running step: \n{error_string}\n{e}")
 
             observation = InternalErrorObservation(
-                error=error_string)
+                error=error_string, title=e.__repr__())
             step.hide = False
 
         self._step_depth -= 1
diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py
index 17b30e96..33e25c93 100644
--- a/continuedev/src/continuedev/core/main.py
+++ b/continuedev/src/continuedev/core/main.py
@@ -181,10 +181,12 @@ class Context:
 
 
 class ContinueCustomException(Exception):
+    title: str
     message: str
 
-    def __init__(self, message: str):
+    def __init__(self, message: str, title: str = "Error while running step:"):
         self.message = message
+        self.title = title
 
 
 HistoryNode.update_forward_refs()
diff --git a/continuedev/src/continuedev/core/observation.py b/continuedev/src/continuedev/core/observation.py
index b6117236..126cf19e 100644
--- a/continuedev/src/continuedev/core/observation.py
+++ b/continuedev/src/continuedev/core/observation.py
@@ -36,4 +36,5 @@ class TextObservation(Observation):
 
 
 class InternalErrorObservation(Observation):
+    title: str
     error: str
diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py
index 690949f1..f4aa2b35 100644
--- a/continuedev/src/continuedev/core/sdk.py
+++ b/continuedev/src/continuedev/core/sdk.py
@@ -75,9 +75,9 @@ class ContinueSDK(AbstractContinueSDK):
     async def wait_for_user_confirmation(self, prompt: str):
         return await self.run_step(WaitForUserConfirmationStep(prompt=prompt))
 
-    async def run(self, commands: Union[List[str], str], cwd: str = None, name: str = None, description: str = None):
+    async def run(self, commands: Union[List[str], str], cwd: str = None, name: str = None, description: str = None) -> Coroutine[str, None, None]:
         commands = commands if isinstance(commands, List) else [commands]
-        return await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd, description=description, **({'name': name} if name else {})))
+        return (await self.run_step(ShellCommandsStep(cmds=commands, cwd=cwd, description=description, **({'name': name} if name else {})))).text
 
     async def edit_file(self, filename: str, prompt: str, name: str = None, description: str = None):
         filepath = await self._ensure_absolute_path(filename)
@@ -131,5 +131,5 @@ class ContinueSDK(AbstractContinueSDK):
         # self.__autopilot.set_loading_message(message)
         raise NotImplementedError()
 
-    def raise_exception(self, message: str):
-        raise ContinueCustomException(message)
+    def raise_exception(self, message: str, title: str):
+        raise ContinueCustomException(message, title)
diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
index 1206db0e..428ac9cc 100644
--- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
+++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/main.py
@@ -22,7 +22,7 @@ class CreatePipelineRecipe(Step):
                 - Load the data into a local DuckDB instance
                 - Write a query to view the data""")) >>
             WaitForUserInputStep(
-                prompt="What API do you want to load data from?")
+                prompt="What API do you want to load data from? (e.g. weatherapi.com, chess.com)")
         )
         await sdk.run_step(
             SetupPipelineStep(api_description=text_observation.text) >>
diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
index 3c8277c0..9bee4c95 100644
--- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
+++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
@@ -34,8 +34,7 @@ class SetupPipelineStep(Step):
             'python3 -m venv env',
             'source env/bin/activate',
             'pip install dlt',
-            f'dlt init {source_name} duckdb',
-            'Y',
+            f'dlt init {source_name} duckdb\n\rY',
             'pip install -r requirements.txt'
         ], description=dedent(f"""\
             Running the following commands:
@@ -43,9 +42,8 @@ class SetupPipelineStep(Step):
             - `source env/bin/activate`: Activate the virtual environment
             - `pip install dlt`: Install dlt
             - `dlt init {source_name} duckdb`: Create a new dlt pipeline called {source_name} that loads data into a local DuckDB instance
-            - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline"""))
+            - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline"""), name="Setup Python environment")
 
-        await sdk.wait_for_user_confirmation("Wait for the commands to finish running, then press `Continue`")
         # editing the resource function to call the requested API
         await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 30, 0)), "#00ff0022")
 
@@ -78,17 +76,12 @@ class ValidatePipelineStep(Step):
         #         """)))
 
         # test that the API call works
-
-        p = subprocess.run(
-            ['python3', f'{filename}'], capture_output=True, text=True, cwd=workspace_dir)
-        err = p.stderr
+        output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API")
 
         # If it fails, return the error
-        if err is not None and err != "":
+        if "Traceback" in output:
             sdk.raise_exception(
-                f"Error while running pipeline. Fix the resource function in {filename} and rerun this step: \n\n" + err)
-
-        await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API")
+                title="Error while running pipeline.\nFix the resource function in {filename} and rerun this step", description=output)
 
         # remove exit() from the main main function
         await sdk.edit_file(
diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py
index 073e1dba..007eb2b4 100644
--- a/continuedev/src/continuedev/server/ide.py
+++ b/continuedev/src/continuedev/server/ide.py
@@ -76,6 +76,10 @@ class GetUserSecretResponse(BaseModel):
     value: str
 
 
+class RunCommandResponse(BaseModel):
+    output: str
+
+
 T = TypeVar("T", bound=BaseModel)
 
 
@@ -110,7 +114,7 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
             fileEdits = list(
                 map(lambda d: FileEditWithFullContents.parse_obj(d), data["fileEdits"]))
             self.onFileEdits(fileEdits)
-        elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret"]:
+        elif message_type in ["highlightedCode", "openFiles", "readFile", "editFile", "workspaceDirectory", "getUserSecret", "runCommand"]:
             self.sub_queue.post(message_type, data)
         else:
             raise ValueError("Unknown message type", message_type)
@@ -139,10 +143,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
             "color": color
         })
 
-    async def runCommand(self, command: str):
-        await self._send_json("runCommand", {
-            "command": command
-        })
+    async def runCommand(self, command: str) -> str:
+        return (await self._send_and_receive_json({"command": command}, RunCommandResponse, "runCommand")).output
 
     async def showSuggestionsAndWait(self, suggestions: List[FileEdit]) -> bool:
         ids = [str(uuid.uuid4()) for _ in suggestions]
diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py
index f42de68f..4622d6ff 100644
--- a/continuedev/src/continuedev/server/ide_protocol.py
+++ b/continuedev/src/continuedev/server/ide_protocol.py
@@ -88,5 +88,5 @@ class AbstractIdeProtocolServer(ABC):
         """Highlight code"""
 
     @abstractmethod
-    async def runCommand(self, command: str):
+    async def runCommand(self, command: str) -> str:
         """Run a command"""
diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py
index 99786b00..c6dc7c04 100644
--- a/continuedev/src/continuedev/steps/core/core.py
+++ b/continuedev/src/continuedev/steps/core/core.py
@@ -56,7 +56,9 @@ class ShellCommandsStep(Step):
         cwd = await sdk.ide.getWorkspaceDirectory() if self.cwd is None else self.cwd
 
         for cmd in self.cmds:
-            await sdk.ide.runCommand(cmd)
+            output = await sdk.ide.runCommand(cmd)
+
+        return TextObservation(text=output)
 
         # process = subprocess.Popen(
         #     '/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd)
@@ -205,7 +207,7 @@ class WaitForUserInputStep(Step):
     async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]:
         self.description = self.prompt
         resp = await sdk.wait_for_user_input()
-        self._response = resp
+        self.description = f"{self.prompt}\n\n`{resp}`"
         return TextObservation(text=resp)
 
 
diff --git a/extension/esbuild.mjs b/extension/esbuild.mjs
new file mode 100644
index 00000000..bc1b3e5f
--- /dev/null
+++ b/extension/esbuild.mjs
@@ -0,0 +1,14 @@
+import * as esbuild from "esbuild";
+// esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node
+await esbuild.build({
+  entryPoints: ["src/extension.ts"],
+  bundle: true,
+  outfile: "out/extension.js",
+  external: ["vscode"],
+  format: "cjs",
+  platform: "node",
+  sourcemap: true,
+  loader: {
+    ".node": "file",
+  },
+});
diff --git a/extension/package-lock.json b/extension/package-lock.json
index fa09527e..a20be756 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -16,11 +16,10 @@
         "@vitejs/plugin-react-swc": "^3.3.2",
         "axios": "^1.2.5",
         "highlight.js": "^11.7.0",
-        "octokit": "^2.0.11",
         "posthog-js": "^1.63.3",
         "react-markdown": "^8.0.7",
         "react-redux": "^8.0.5",
-        "strip-ansi": "^7.0.1",
+        "strip-ansi": "^7.1.0",
         "tailwindcss": "^3.3.2",
         "vite": "^4.3.9",
         "vscode-languageclient": "^8.0.2",
@@ -1120,457 +1119,6 @@
         "npm": ">=5.0.0"
       }
     },
-    "node_modules/@octokit/app": {
-      "version": "13.1.2",
-      "resolved": "https://registry.npmjs.org/@octokit/app/-/app-13.1.2.tgz",
-      "integrity": "sha512-Kf+h5sa1SOI33hFsuHvTsWj1jUrjp1x4MuiJBq7U/NicfEGa6nArPUoDnyfP/YTmcQ5cQ5yvOgoIBkbwPg6kzQ==",
-      "dependencies": {
-        "@octokit/auth-app": "^4.0.8",
-        "@octokit/auth-unauthenticated": "^3.0.0",
-        "@octokit/core": "^4.0.0",
-        "@octokit/oauth-app": "^4.0.7",
-        "@octokit/plugin-paginate-rest": "^6.0.0",
-        "@octokit/types": "^9.0.0",
-        "@octokit/webhooks": "^10.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/app/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/app/node_modules/@octokit/plugin-paginate-rest": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz",
-      "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==",
-      "dependencies": {
-        "@octokit/types": "^9.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=4"
-      }
-    },
-    "node_modules/@octokit/app/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/auth-app": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-4.0.8.tgz",
-      "integrity": "sha512-miI7y9FfS/fL1bSPsDaAfCGSxQ04iGLyisI2GA8N7P6eB6AkCOt+F1XXapJKRnAubQubvYF0dqxoTZYyKk93NQ==",
-      "dependencies": {
-        "@octokit/auth-oauth-app": "^5.0.0",
-        "@octokit/auth-oauth-user": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^8.0.0",
-        "@types/lru-cache": "^5.1.0",
-        "deprecation": "^2.3.1",
-        "lru-cache": "^6.0.0",
-        "universal-github-app-jwt": "^1.1.1",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/auth-oauth-app": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.5.tgz",
-      "integrity": "sha512-UPX1su6XpseaeLVCi78s9droxpGtBWIgz9XhXAx9VXabksoF0MyI5vaa1zo1njyYt6VaAjFisC2A2Wchcu2WmQ==",
-      "dependencies": {
-        "@octokit/auth-oauth-device": "^4.0.0",
-        "@octokit/auth-oauth-user": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^9.0.0",
-        "@types/btoa-lite": "^1.0.0",
-        "btoa-lite": "^1.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/auth-oauth-device": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.3.tgz",
-      "integrity": "sha512-KPTx5nMntKjNZzzltO3X4T68v22rd7Cp/TcLJXQE2U8aXPcZ9LFuww9q9Q5WUNSu3jwi3lRwzfkPguRfz1R8Vg==",
-      "dependencies": {
-        "@octokit/oauth-methods": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^8.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/auth-oauth-user": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.0.tgz",
-      "integrity": "sha512-TC2Mj8NkSy9uAnZLYX+FKB/IH6uDe+qYNzHzH8l13JlzsrTE7GKkcqtXdSGGN4tncyROAB4/KS5rDPRCEnWHlA==",
-      "dependencies": {
-        "@octokit/auth-oauth-device": "^4.0.0",
-        "@octokit/oauth-methods": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^8.0.0",
-        "btoa-lite": "^1.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/auth-token": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz",
-      "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==",
-      "dependencies": {
-        "@octokit/types": "^9.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/auth-token/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/auth-unauthenticated": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.4.tgz",
-      "integrity": "sha512-AT74XGBylcLr4lmUp1s6mjSUgphGdlse21Qjtv5DzpX1YOl5FXKwvNcZWESdhyBbpDT8VkVyLFqa/7a7eqpPNw==",
-      "dependencies": {
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/core": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz",
-      "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==",
-      "dependencies": {
-        "@octokit/auth-token": "^3.0.0",
-        "@octokit/graphql": "^5.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0",
-        "before-after-hook": "^2.2.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/core/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/endpoint": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.4.tgz",
-      "integrity": "sha512-hXJP43VT2IrUxBCNIahta8qawpIzLvCjHLCuDDsdIPbd6+jPwsc3KGl/kdQ37mLd+sdiJm6c9qKI7k5CjE0Z9A==",
-      "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "is-plain-object": "^5.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/endpoint/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/graphql": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz",
-      "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==",
-      "dependencies": {
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^9.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/graphql/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/oauth-app": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-4.2.0.tgz",
-      "integrity": "sha512-gyGclT77RQMkVUEW3YBeAKY+LBSc5u3eC9Wn/Uwt3WhuKuu9mrV18EnNpDqmeNll+mdV02yyBROU29Tlili6gg==",
-      "dependencies": {
-        "@octokit/auth-oauth-app": "^5.0.0",
-        "@octokit/auth-oauth-user": "^2.0.0",
-        "@octokit/auth-unauthenticated": "^3.0.0",
-        "@octokit/core": "^4.0.0",
-        "@octokit/oauth-authorization-url": "^5.0.0",
-        "@octokit/oauth-methods": "^2.0.0",
-        "@types/aws-lambda": "^8.10.83",
-        "fromentries": "^1.3.1",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/oauth-authorization-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz",
-      "integrity": "sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg==",
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/oauth-methods": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-2.0.4.tgz",
-      "integrity": "sha512-RDSa6XL+5waUVrYSmOlYROtPq0+cfwppP4VaQY/iIei3xlFb0expH6YNsxNrZktcLhJWSpm9uzeom+dQrXlS3A==",
-      "dependencies": {
-        "@octokit/oauth-authorization-url": "^5.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^8.0.0",
-        "btoa-lite": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/openapi-types": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
-      "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
-    },
-    "node_modules/@octokit/plugin-paginate-rest": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
-      "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
-      "dependencies": {
-        "@octokit/types": "^8.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=4"
-      }
-    },
-    "node_modules/@octokit/plugin-rest-endpoint-methods": {
-      "version": "6.8.1",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz",
-      "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==",
-      "dependencies": {
-        "@octokit/types": "^8.1.1",
-        "deprecation": "^2.3.1"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=3"
-      }
-    },
-    "node_modules/@octokit/plugin-retry": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-4.0.4.tgz",
-      "integrity": "sha512-d7qGFLR3AH+WbNEDUvBPgMc7wRCxU40FZyNXFFqs8ISw75ZYS5/P3ScggzU13dCoY0aywYDxKugGstQTwNgppA==",
-      "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "bottleneck": "^2.15.3"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=3"
-      }
-    },
-    "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/plugin-throttling": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-4.3.2.tgz",
-      "integrity": "sha512-ZaCK599h3tzcoy0Jtdab95jgmD7X9iAk59E2E7hYKCAmnURaI4WpzwL9vckImilybUGrjY1JOWJapDs2N2D3vw==",
-      "dependencies": {
-        "@octokit/types": "^8.0.0",
-        "bottleneck": "^2.15.3"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": "^4.0.0"
-      }
-    },
-    "node_modules/@octokit/request": {
-      "version": "6.2.2",
-      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
-      "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
-      "dependencies": {
-        "@octokit/endpoint": "^7.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^8.0.0",
-        "is-plain-object": "^5.0.0",
-        "node-fetch": "^2.6.7",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/request-error": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
-      "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
-      "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "deprecation": "^2.0.0",
-        "once": "^1.4.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-      "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-    },
-    "node_modules/@octokit/request-error/node_modules/@octokit/types": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-      "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^16.0.0"
-      }
-    },
-    "node_modules/@octokit/types": {
-      "version": "8.2.1",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz",
-      "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==",
-      "dependencies": {
-        "@octokit/openapi-types": "^14.0.0"
-      }
-    },
-    "node_modules/@octokit/webhooks": {
-      "version": "10.7.0",
-      "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-10.7.0.tgz",
-      "integrity": "sha512-zZBbQMpXXnK/ki/utrFG/TuWv9545XCSLibfDTxrYqR1PmU6zel02ebTOrA7t5XIGHzlEOc/NgISUIBUe7pMFA==",
-      "dependencies": {
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/webhooks-methods": "^3.0.0",
-        "@octokit/webhooks-types": "6.10.0",
-        "aggregate-error": "^3.1.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/webhooks-methods": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-3.0.2.tgz",
-      "integrity": "sha512-Vlnv5WBscf07tyAvfDbp7pTkMZUwk7z7VwEF32x6HqI+55QRwBTcT+D7DDjZXtad/1dU9E32x0HmtDlF9VIRaQ==",
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/webhooks-types": {
-      "version": "6.10.0",
-      "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.10.0.tgz",
-      "integrity": "sha512-lDNv83BeEyxxukdQ0UttiUXawk9+6DkdjjFtm2GFED+24IQhTVaoSbwV9vWWKONyGLzRmCQqZmoEWkDhkEmPlw=="
-    },
     "node_modules/@openapitools/openapi-generator-cli": {
       "version": "2.6.0",
       "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.6.0.tgz",
@@ -2028,16 +1576,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/@types/aws-lambda": {
-      "version": "8.10.109",
-      "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.109.tgz",
-      "integrity": "sha512-/ME92FneNyXQzrAfcnQQlW1XkCZGPDlpi2ao1MJwecN+6SbeonKeggU8eybv1DfKli90FAVT1MlIZVXfwVuCyg=="
-    },
-    "node_modules/@types/btoa-lite": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz",
-      "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg=="
-    },
     "node_modules/@types/cacheable-request": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
@@ -2095,14 +1633,6 @@
       "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
-    "node_modules/@types/jsonwebtoken": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz",
-      "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==",
-      "dependencies": {
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/keyv": {
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
@@ -2117,11 +1647,6 @@
       "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
       "dev": true
     },
-    "node_modules/@types/lru-cache": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
-      "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
-    },
     "node_modules/@types/mdast": {
       "version": "3.0.11",
       "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz",
@@ -2760,11 +2285,6 @@
         }
       ]
     },
-    "node_modules/before-after-hook": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
-      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
-    },
     "node_modules/big-integer": {
       "version": "1.6.51",
       "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
@@ -2830,11 +2350,6 @@
       "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
       "dev": true
     },
-    "node_modules/bottleneck": {
-      "version": "2.19.5",
-      "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
-      "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
-    },
     "node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2861,11 +2376,6 @@
       "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
       "dev": true
     },
-    "node_modules/btoa-lite": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
-      "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA=="
-    },
     "node_modules/buffer": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -2898,11 +2408,6 @@
         "node": "*"
       }
     },
-    "node_modules/buffer-equal-constant-time": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
-      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
-    },
     "node_modules/buffer-indexof-polyfill": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
@@ -3643,11 +3148,6 @@
         "node": ">= 0.6"
       }
     },
-    "node_modules/deprecation": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
-      "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
-    },
     "node_modules/dequal": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -3787,14 +3287,6 @@
         "wcwidth": ">=1.0.1"
       }
     },
-    "node_modules/ecdsa-sig-formatter": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
-      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
-      "dependencies": {
-        "safe-buffer": "^5.0.1"
-      }
-    },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -4443,25 +3935,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/fromentries": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
-      "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ]
-    },
     "node_modules/fs-constants": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -5201,14 +4674,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/is-plain-object": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
-      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/is-promise": {
       "version": "2.2.2",
       "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
@@ -5410,40 +4875,6 @@
         "graceful-fs": "^4.1.6"
       }
     },
-    "node_modules/jsonwebtoken": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
-      "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
-      "dependencies": {
-        "jws": "^3.2.2",
-        "lodash": "^4.17.21",
-        "ms": "^2.1.1",
-        "semver": "^7.3.8"
-      },
-      "engines": {
-        "node": ">=12",
-        "npm": ">=6"
-      }
-    },
-    "node_modules/jwa": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
-      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
-      "dependencies": {
-        "buffer-equal-constant-time": "1.0.1",
-        "ecdsa-sig-formatter": "1.0.11",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "node_modules/jws": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
-      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
-      "dependencies": {
-        "jwa": "^1.4.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
     "node_modules/keytar": {
       "version": "7.9.0",
       "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz",
@@ -6782,24 +6213,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/octokit": {
-      "version": "2.0.11",
-      "resolved": "https://registry.npmjs.org/octokit/-/octokit-2.0.11.tgz",
-      "integrity": "sha512-Ivjapy5RXWvJfmZe0BvfMM2gnNi39rjheZV/s3SjICb7gfl83JWPDmBERe4f/l2czdRnj4NVIn4YO7Q737oLCg==",
-      "dependencies": {
-        "@octokit/app": "^13.1.1",
-        "@octokit/core": "^4.0.4",
-        "@octokit/oauth-app": "^4.0.6",
-        "@octokit/plugin-paginate-rest": "^5.0.0",
-        "@octokit/plugin-rest-endpoint-methods": "^6.0.0",
-        "@octokit/plugin-retry": "^4.0.3",
-        "@octokit/plugin-throttling": "^4.0.1",
-        "@octokit/types": "^8.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
     "node_modules/once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -7836,6 +7249,7 @@
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -8119,9 +7533,9 @@
       }
     },
     "node_modules/strip-ansi": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
-      "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+      "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
       "dependencies": {
         "ansi-regex": "^6.0.1"
       },
@@ -8758,20 +8172,6 @@
         "url": "https://opencollective.com/unified"
       }
     },
-    "node_modules/universal-github-app-jwt": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz",
-      "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==",
-      "dependencies": {
-        "@types/jsonwebtoken": "^9.0.0",
-        "jsonwebtoken": "^9.0.0"
-      }
-    },
-    "node_modules/universal-user-agent": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
-      "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
-    },
     "node_modules/universalify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@@ -10018,394 +9418,6 @@
         "node-fetch": "^2.6.1"
       }
     },
-    "@octokit/app": {
-      "version": "13.1.2",
-      "resolved": "https://registry.npmjs.org/@octokit/app/-/app-13.1.2.tgz",
-      "integrity": "sha512-Kf+h5sa1SOI33hFsuHvTsWj1jUrjp1x4MuiJBq7U/NicfEGa6nArPUoDnyfP/YTmcQ5cQ5yvOgoIBkbwPg6kzQ==",
-      "requires": {
-        "@octokit/auth-app": "^4.0.8",
-        "@octokit/auth-unauthenticated": "^3.0.0",
-        "@octokit/core": "^4.0.0",
-        "@octokit/oauth-app": "^4.0.7",
-        "@octokit/plugin-paginate-rest": "^6.0.0",
-        "@octokit/types": "^9.0.0",
-        "@octokit/webhooks": "^10.0.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/plugin-paginate-rest": {
-          "version": "6.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz",
-          "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==",
-          "requires": {
-            "@octokit/types": "^9.0.0"
-          }
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/auth-app": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-4.0.8.tgz",
-      "integrity": "sha512-miI7y9FfS/fL1bSPsDaAfCGSxQ04iGLyisI2GA8N7P6eB6AkCOt+F1XXapJKRnAubQubvYF0dqxoTZYyKk93NQ==",
-      "requires": {
-        "@octokit/auth-oauth-app": "^5.0.0",
-        "@octokit/auth-oauth-user": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^8.0.0",
-        "@types/lru-cache": "^5.1.0",
-        "deprecation": "^2.3.1",
-        "lru-cache": "^6.0.0",
-        "universal-github-app-jwt": "^1.1.1",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/auth-oauth-app": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.5.tgz",
-      "integrity": "sha512-UPX1su6XpseaeLVCi78s9droxpGtBWIgz9XhXAx9VXabksoF0MyI5vaa1zo1njyYt6VaAjFisC2A2Wchcu2WmQ==",
-      "requires": {
-        "@octokit/auth-oauth-device": "^4.0.0",
-        "@octokit/auth-oauth-user": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^9.0.0",
-        "@types/btoa-lite": "^1.0.0",
-        "btoa-lite": "^1.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/auth-oauth-device": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.3.tgz",
-      "integrity": "sha512-KPTx5nMntKjNZzzltO3X4T68v22rd7Cp/TcLJXQE2U8aXPcZ9LFuww9q9Q5WUNSu3jwi3lRwzfkPguRfz1R8Vg==",
-      "requires": {
-        "@octokit/oauth-methods": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^8.0.0",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/auth-oauth-user": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.0.tgz",
-      "integrity": "sha512-TC2Mj8NkSy9uAnZLYX+FKB/IH6uDe+qYNzHzH8l13JlzsrTE7GKkcqtXdSGGN4tncyROAB4/KS5rDPRCEnWHlA==",
-      "requires": {
-        "@octokit/auth-oauth-device": "^4.0.0",
-        "@octokit/oauth-methods": "^2.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^8.0.0",
-        "btoa-lite": "^1.0.0",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/auth-token": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.3.tgz",
-      "integrity": "sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==",
-      "requires": {
-        "@octokit/types": "^9.0.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/auth-unauthenticated": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.4.tgz",
-      "integrity": "sha512-AT74XGBylcLr4lmUp1s6mjSUgphGdlse21Qjtv5DzpX1YOl5FXKwvNcZWESdhyBbpDT8VkVyLFqa/7a7eqpPNw==",
-      "requires": {
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/core": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.0.tgz",
-      "integrity": "sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==",
-      "requires": {
-        "@octokit/auth-token": "^3.0.0",
-        "@octokit/graphql": "^5.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0",
-        "before-after-hook": "^2.2.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/endpoint": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.4.tgz",
-      "integrity": "sha512-hXJP43VT2IrUxBCNIahta8qawpIzLvCjHLCuDDsdIPbd6+jPwsc3KGl/kdQ37mLd+sdiJm6c9qKI7k5CjE0Z9A==",
-      "requires": {
-        "@octokit/types": "^9.0.0",
-        "is-plain-object": "^5.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/graphql": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz",
-      "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==",
-      "requires": {
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^9.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/oauth-app": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-4.2.0.tgz",
-      "integrity": "sha512-gyGclT77RQMkVUEW3YBeAKY+LBSc5u3eC9Wn/Uwt3WhuKuu9mrV18EnNpDqmeNll+mdV02yyBROU29Tlili6gg==",
-      "requires": {
-        "@octokit/auth-oauth-app": "^5.0.0",
-        "@octokit/auth-oauth-user": "^2.0.0",
-        "@octokit/auth-unauthenticated": "^3.0.0",
-        "@octokit/core": "^4.0.0",
-        "@octokit/oauth-authorization-url": "^5.0.0",
-        "@octokit/oauth-methods": "^2.0.0",
-        "@types/aws-lambda": "^8.10.83",
-        "fromentries": "^1.3.1",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/oauth-authorization-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz",
-      "integrity": "sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg=="
-    },
-    "@octokit/oauth-methods": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-2.0.4.tgz",
-      "integrity": "sha512-RDSa6XL+5waUVrYSmOlYROtPq0+cfwppP4VaQY/iIei3xlFb0expH6YNsxNrZktcLhJWSpm9uzeom+dQrXlS3A==",
-      "requires": {
-        "@octokit/oauth-authorization-url": "^5.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^8.0.0",
-        "btoa-lite": "^1.0.0"
-      }
-    },
-    "@octokit/openapi-types": {
-      "version": "14.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
-      "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
-    },
-    "@octokit/plugin-paginate-rest": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
-      "integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
-      "requires": {
-        "@octokit/types": "^8.0.0"
-      }
-    },
-    "@octokit/plugin-rest-endpoint-methods": {
-      "version": "6.8.1",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz",
-      "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==",
-      "requires": {
-        "@octokit/types": "^8.1.1",
-        "deprecation": "^2.3.1"
-      }
-    },
-    "@octokit/plugin-retry": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-4.0.4.tgz",
-      "integrity": "sha512-d7qGFLR3AH+WbNEDUvBPgMc7wRCxU40FZyNXFFqs8ISw75ZYS5/P3ScggzU13dCoY0aywYDxKugGstQTwNgppA==",
-      "requires": {
-        "@octokit/types": "^9.0.0",
-        "bottleneck": "^2.15.3"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/plugin-throttling": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-4.3.2.tgz",
-      "integrity": "sha512-ZaCK599h3tzcoy0Jtdab95jgmD7X9iAk59E2E7hYKCAmnURaI4WpzwL9vckImilybUGrjY1JOWJapDs2N2D3vw==",
-      "requires": {
-        "@octokit/types": "^8.0.0",
-        "bottleneck": "^2.15.3"
-      }
-    },
-    "@octokit/request": {
-      "version": "6.2.2",
-      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
-      "integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
-      "requires": {
-        "@octokit/endpoint": "^7.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^8.0.0",
-        "is-plain-object": "^5.0.0",
-        "node-fetch": "^2.6.7",
-        "universal-user-agent": "^6.0.0"
-      }
-    },
-    "@octokit/request-error": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
-      "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
-      "requires": {
-        "@octokit/types": "^9.0.0",
-        "deprecation": "^2.0.0",
-        "once": "^1.4.0"
-      },
-      "dependencies": {
-        "@octokit/openapi-types": {
-          "version": "16.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
-          "integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
-        },
-        "@octokit/types": {
-          "version": "9.0.0",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
-          "integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
-          "requires": {
-            "@octokit/openapi-types": "^16.0.0"
-          }
-        }
-      }
-    },
-    "@octokit/types": {
-      "version": "8.2.1",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz",
-      "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==",
-      "requires": {
-        "@octokit/openapi-types": "^14.0.0"
-      }
-    },
-    "@octokit/webhooks": {
-      "version": "10.7.0",
-      "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-10.7.0.tgz",
-      "integrity": "sha512-zZBbQMpXXnK/ki/utrFG/TuWv9545XCSLibfDTxrYqR1PmU6zel02ebTOrA7t5XIGHzlEOc/NgISUIBUe7pMFA==",
-      "requires": {
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/webhooks-methods": "^3.0.0",
-        "@octokit/webhooks-types": "6.10.0",
-        "aggregate-error": "^3.1.0"
-      }
-    },
-    "@octokit/webhooks-methods": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-3.0.2.tgz",
-      "integrity": "sha512-Vlnv5WBscf07tyAvfDbp7pTkMZUwk7z7VwEF32x6HqI+55QRwBTcT+D7DDjZXtad/1dU9E32x0HmtDlF9VIRaQ=="
-    },
-    "@octokit/webhooks-types": {
-      "version": "6.10.0",
-      "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.10.0.tgz",
-      "integrity": "sha512-lDNv83BeEyxxukdQ0UttiUXawk9+6DkdjjFtm2GFED+24IQhTVaoSbwV9vWWKONyGLzRmCQqZmoEWkDhkEmPlw=="
-    },
     "@openapitools/openapi-generator-cli": {
       "version": "2.6.0",
       "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.6.0.tgz",
@@ -10658,16 +9670,6 @@
       "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
       "dev": true
     },
-    "@types/aws-lambda": {
-      "version": "8.10.109",
-      "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.109.tgz",
-      "integrity": "sha512-/ME92FneNyXQzrAfcnQQlW1XkCZGPDlpi2ao1MJwecN+6SbeonKeggU8eybv1DfKli90FAVT1MlIZVXfwVuCyg=="
-    },
-    "@types/btoa-lite": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz",
-      "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg=="
-    },
     "@types/cacheable-request": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
@@ -10725,14 +9727,6 @@
       "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
-    "@types/jsonwebtoken": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz",
-      "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==",
-      "requires": {
-        "@types/node": "*"
-      }
-    },
     "@types/keyv": {
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
@@ -10747,11 +9741,6 @@
       "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
       "dev": true
     },
-    "@types/lru-cache": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
-      "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
-    },
     "@types/mdast": {
       "version": "3.0.11",
       "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz",
@@ -11217,11 +10206,6 @@
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
       "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
     },
-    "before-after-hook": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
-      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
-    },
     "big-integer": {
       "version": "1.6.51",
       "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
@@ -11277,11 +10261,6 @@
       "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
       "dev": true
     },
-    "bottleneck": {
-      "version": "2.19.5",
-      "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
-      "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
-    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -11305,11 +10284,6 @@
       "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
       "dev": true
     },
-    "btoa-lite": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
-      "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA=="
-    },
     "buffer": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -11325,11 +10299,6 @@
       "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
       "dev": true
     },
-    "buffer-equal-constant-time": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
-      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
-    },
     "buffer-indexof-polyfill": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
@@ -11862,11 +10831,6 @@
       "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
       "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
     },
-    "deprecation": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
-      "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
-    },
     "dequal": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -11970,14 +10934,6 @@
         "wcwidth": ">=1.0.1"
       }
     },
-    "ecdsa-sig-formatter": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
-      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
-      "requires": {
-        "safe-buffer": "^5.0.1"
-      }
-    },
     "emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -12488,11 +11444,6 @@
         "mime-types": "^2.1.12"
       }
     },
-    "fromentries": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
-      "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg=="
-    },
     "fs-constants": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -13030,11 +11981,6 @@
       "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
       "dev": true
     },
-    "is-plain-object": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
-      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
-    },
     "is-promise": {
       "version": "2.2.2",
       "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
@@ -13183,36 +12129,6 @@
         "universalify": "^2.0.0"
       }
     },
-    "jsonwebtoken": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
-      "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
-      "requires": {
-        "jws": "^3.2.2",
-        "lodash": "^4.17.21",
-        "ms": "^2.1.1",
-        "semver": "^7.3.8"
-      }
-    },
-    "jwa": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
-      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
-      "requires": {
-        "buffer-equal-constant-time": "1.0.1",
-        "ecdsa-sig-formatter": "1.0.11",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "jws": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
-      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
-      "requires": {
-        "jwa": "^1.4.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
     "keytar": {
       "version": "7.9.0",
       "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz",
@@ -14121,21 +13037,6 @@
       "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
       "dev": true
     },
-    "octokit": {
-      "version": "2.0.11",
-      "resolved": "https://registry.npmjs.org/octokit/-/octokit-2.0.11.tgz",
-      "integrity": "sha512-Ivjapy5RXWvJfmZe0BvfMM2gnNi39rjheZV/s3SjICb7gfl83JWPDmBERe4f/l2czdRnj4NVIn4YO7Q737oLCg==",
-      "requires": {
-        "@octokit/app": "^13.1.1",
-        "@octokit/core": "^4.0.4",
-        "@octokit/oauth-app": "^4.0.6",
-        "@octokit/plugin-paginate-rest": "^5.0.0",
-        "@octokit/plugin-rest-endpoint-methods": "^6.0.0",
-        "@octokit/plugin-retry": "^4.0.3",
-        "@octokit/plugin-throttling": "^4.0.1",
-        "@octokit/types": "^8.0.0"
-      }
-    },
     "once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -14844,7 +13745,8 @@
     "safe-buffer": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true
     },
     "safer-buffer": {
       "version": "2.1.2",
@@ -15042,9 +13944,9 @@
       }
     },
     "strip-ansi": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
-      "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+      "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
       "requires": {
         "ansi-regex": "^6.0.1"
       }
@@ -15516,20 +14418,6 @@
         "unist-util-is": "^5.0.0"
       }
     },
-    "universal-github-app-jwt": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz",
-      "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==",
-      "requires": {
-        "@types/jsonwebtoken": "^9.0.0",
-        "jsonwebtoken": "^9.0.0"
-      }
-    },
-    "universal-user-agent": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
-      "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
-    },
     "universalify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
diff --git a/extension/package.json b/extension/package.json
index c834f402..598ae778 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -169,7 +169,7 @@
   "scripts": {
     "vscode:prepublish": "npm run esbuild-base -- --minify",
     "esbuild-base": "rm -rf ./out && esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node",
-    "esbuild": "rm -rf ./out && npm run esbuild-base -- --sourcemap",
+    "esbuild": "rm -rf ./out && node esbuild.mjs",
     "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
     "test-compile": "tsc -p ./",
     "clientgen": "rm -rf src/client/ && npx @openapitools/openapi-generator-cli generate -i ../schema/openapi.json -g typescript-fetch -o src/client/ --additional-properties=supportsES6=true,npmVersion=8.19.2,typescriptThreePlus=true",
@@ -215,11 +215,10 @@
     "@vitejs/plugin-react-swc": "^3.3.2",
     "axios": "^1.2.5",
     "highlight.js": "^11.7.0",
-    "octokit": "^2.0.11",
     "posthog-js": "^1.63.3",
     "react-markdown": "^8.0.7",
     "react-redux": "^8.0.5",
-    "strip-ansi": "^7.0.1",
+    "strip-ansi": "^7.1.0",
     "tailwindcss": "^3.3.2",
     "vite": "^4.3.9",
     "vscode-languageclient": "^8.0.2",
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index dab5a752..a150e370 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -22,6 +22,7 @@ import { HistoryNode } from "../../../schema/HistoryNode";
 import ReactMarkdown from "react-markdown";
 import ContinueButton from "./ContinueButton";
 import InputAndButton from "./InputAndButton";
+import ToggleErrorDiv from "./ToggleErrorDiv";
 
 interface StepContainerProps {
   historyNode: HistoryNode;
@@ -170,13 +171,10 @@ function StepContainer(props: StepContainerProps) {
           )}
 
           {props.historyNode.observation?.error ? (
-            <>
-              Error while running step:
-              <br />
-              <pre className="overflow-scroll">
-                {props.historyNode.observation.error as string}
-              </pre>
-            </>
+            <ToggleErrorDiv
+              title={"Error while running step:"}
+              error={props.historyNode.observation.error as string}
+            />
           ) : (
             <ReactMarkdown key={1} className="overflow-scroll">
               {props.historyNode.step.description as any}
diff --git a/extension/react-app/src/components/ToggleErrorDiv.tsx b/extension/react-app/src/components/ToggleErrorDiv.tsx
new file mode 100644
index 00000000..69112ef7
--- /dev/null
+++ b/extension/react-app/src/components/ToggleErrorDiv.tsx
@@ -0,0 +1,41 @@
+import React, { useState } from "react";
+import styled from "styled-components";
+import { defaultBorderRadius } from ".";
+
+// Should be a toggleable div with red border and light red background that displays a main message and detail inside
+
+interface ToggleErrorDivProps {
+  title: string;
+  error: string;
+}
+
+const TopDiv = styled.div`
+  border: 1px solid red;
+  background-color: #ff000020;
+  padding: 8px;
+
+  border-radius: ${defaultBorderRadius};
+  cursor: pointer;
+`;
+
+const ToggleErrorDiv = (props: ToggleErrorDivProps) => {
+  const [open, setOpen] = useState(false);
+  return (
+    <TopDiv
+      onClick={() => {
+        setOpen(!open);
+      }}
+    >
+      <div className="flex flex-row">
+        <div className="flex-grow">
+          <p>
+            {open ? "▼" : "▶"} {props.title}
+          </p>
+        </div>
+      </div>
+      {open && <pre className="overflow-scroll">{props.error}</pre>}
+    </TopDiv>
+  );
+};
+
+export default ToggleErrorDiv;
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index c66172a9..308dfd57 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -43,6 +43,10 @@ function GUI(props: GUIProps) {
   //         description:
   //           "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`",
   //       },
+  //       observation: {
+  //         error:
+  //           "Traceback (most recent call last):\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n    print(sum(first, second))\n          ^^^^^^^^^^^^^^^^^^\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
+  //       },
   //       output: [
   //         {
   //           traceback: {
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index 25287d32..e84602f0 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -12,6 +12,7 @@ import { debugPanelWebview, setupDebugPanel } from "./debugPanel";
 import { FileEditWithFullContents } from "../schema/FileEditWithFullContents";
 import fs = require("fs");
 import { WebsocketMessenger } from "./util/messenger";
+import { CapturedTerminal } from "./terminal/terminalEmulator";
 
 class IdeProtocolClient {
   private messenger: WebsocketMessenger | null = null;
@@ -100,7 +101,9 @@ class IdeProtocolClient {
         this.highlightCode(data.rangeInFile, data.color);
         break;
       case "runCommand":
-        this.runCommand(data.command);
+        this.messenger?.send("runCommand", {
+          output: await this.runCommand(data.command),
+        });
         break;
       case "saveFile":
         this.saveFile(data.filepath);
@@ -316,18 +319,15 @@ class IdeProtocolClient {
     return rangeInFiles;
   }
 
-  runCommand(command: string) {
-    if (vscode.window.terminals.length === 0) {
-      const terminal = vscode.window.createTerminal();
-      terminal.show();
-      terminal.sendText("bash", true);
-      terminal.sendText(command, true);
-      return;
+  private continueTerminal: CapturedTerminal | undefined;
+
+  async runCommand(command: string) {
+    if (!this.continueTerminal) {
+      this.continueTerminal = new CapturedTerminal("Continue");
     }
-    const terminal = vscode.window.terminals[0];
-    terminal.show();
-    terminal.sendText(command, true);
-    // But need to know when it's done executing...
+
+    this.continueTerminal.show();
+    return await this.continueTerminal.runCommand(command);
   }
 }
 
diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts
index 6cf65970..8974b7e3 100644
--- a/extension/src/terminal/terminalEmulator.ts
+++ b/extension/src/terminal/terminalEmulator.ts
@@ -1,140 +1,161 @@
-// /* Terminal emulator - commented because node-pty is causing problems. */
-
-// import * as vscode from "vscode";
-// import pty = require("node-pty");
-// import os = require("os");
-// import { extensionContext } from "../activation/activate";
-// import { debugPanelWebview } from "../debugPanel"; // Need to consider having multiple panels, where to store this state.
-// import {
-//   CommandCaptureSnooper,
-//   PythonTracebackSnooper,
-//   TerminalSnooper,
-// } from "./snoopers";
-
-// export function tracebackToWebviewAction(traceback: string) {
-//   if (debugPanelWebview) {
-//     debugPanelWebview.postMessage({
-//       type: "traceback",
-//       value: traceback,
-//     });
-//   } else {
-//     vscode.commands
-//       .executeCommand("continue.openContinueGUI", extensionContext)
-//       .then(() => {
-//         // TODO: Waiting for the webview to load, but should add a hook to the onLoad message event. Same thing in autodebugTest command in commands.ts
-//         setTimeout(() => {
-//           debugPanelWebview?.postMessage({
-//             type: "traceback",
-//             value: traceback,
-//           });
-//         }, 500);
-//       });
-//   }
-// }
-
-// const DEFAULT_SNOOPERS = [
-//   new PythonTracebackSnooper(tracebackToWebviewAction),
-//   new CommandCaptureSnooper((data: string) => {
-//     if (data.trim().startsWith("pytest ")) {
-//       let fileAndFunctionSpecifier = data.split(" ")[1];
-//       vscode.commands.executeCommand(
-//         "continue.debugTest",
-//         fileAndFunctionSpecifier
-//       );
-//     }
-//   }),
-// ];
-
-// // Whenever a user opens a terminal, replace it with ours
-// vscode.window.onDidOpenTerminal((terminal) => {
-//   if (terminal.name != "Continue") {
-//     terminal.dispose();
-//     openCapturedTerminal();
-//   }
-// });
-
-// function getDefaultShell(): string {
-//   if (process.platform !== "win32") {
-//     return os.userInfo().shell;
-//   }
-//   switch (process.platform) {
-//     case "win32":
-//       return process.env.COMSPEC || "cmd.exe";
-//     // case "darwin":
-//     //   return process.env.SHELL || "/bin/zsh";
-//     // default:
-//     //   return process.env.SHELL || "/bin/sh";
-//   }
-// }
-
-// function getRootDir(): string | undefined {
-//   var isWindows = os.platform() === "win32";
-//   let cwd = isWindows ? process.env.USERPROFILE : process.env.HOME;
-//   if (
-//     vscode.workspace.workspaceFolders &&
-//     vscode.workspace.workspaceFolders.length > 0
-//   ) {
-//     cwd = vscode.workspace.workspaceFolders[0].uri.fsPath;
-//   }
-//   return cwd;
-// }
-
-// export function openCapturedTerminal(
-//   snoopers: TerminalSnooper<string>[] = DEFAULT_SNOOPERS
-// ) {
-//   // If there is another existing, non-Continue terminal, delete it
-//   let terminals = vscode.window.terminals;
-//   for (let i = 0; i < terminals.length; i++) {
-//     if (terminals[i].name != "Continue") {
-//       terminals[i].dispose();
-//     }
-//   }
-
-//   let env = { ...(process.env as any) };
-//   if (os.platform() !== "win32") {
-//     env["PATH"] += ":" + ["/opt/homebrew/bin", "/opt/homebrew/sbin"].join(":");
-//   }
-
-//   var ptyProcess = pty.spawn(getDefaultShell(), [], {
-//     name: "xterm-256color",
-//     cols: 160, // TODO: Get size of vscode terminal, and change with resize
-//     rows: 26,
-//     cwd: getRootDir(),
-//     env,
-//     useConpty: true,
-//   });
-
-//   const writeEmitter = new vscode.EventEmitter<string>();
-
-//   ptyProcess.onData((data: any) => {
-//     // Let each of the snoopers see the new data
-//     for (let snooper of snoopers) {
-//       snooper.onData(data);
-//     }
-
-//     // Pass data through to terminal
-//     writeEmitter.fire(data);
-//   });
-//   process.on("exit", () => ptyProcess.kill());
-
-//   const newPty: vscode.Pseudoterminal = {
-//     onDidWrite: writeEmitter.event,
-//     open: () => {},
-//     close: () => {},
-//     handleInput: (data) => {
-//       for (let snooper of snoopers) {
-//         snooper.onWrite(data);
-//       }
-//       ptyProcess.write(data);
-//     },
-//   };
-//   const terminal = vscode.window.createTerminal({
-//     name: "Continue",
-//     pty: newPty,
-//   });
-//   terminal.show();
-
-//   setTimeout(() => {
-//     ptyProcess.write("clear\r");
-//   }, 500);
-// }
+/* Terminal emulator - commented because node-pty is causing problems. */
+
+import * as vscode from "vscode";
+import os = require("os");
+import stripAnsi from "strip-ansi";
+
+function loadNativeModule<T>(id: string): T | null {
+  try {
+    return require(`${vscode.env.appRoot}/node_modules.asar/${id}`);
+  } catch (err) {
+    // ignore
+  }
+
+  try {
+    return require(`${vscode.env.appRoot}/node_modules/${id}`);
+  } catch (err) {
+    // ignore
+  }
+
+  return null;
+}
+
+const pty = loadNativeModule<any>("node-pty");
+
+function getDefaultShell(): string {
+  if (process.platform !== "win32") {
+    return os.userInfo().shell;
+  }
+  switch (process.platform) {
+    case "win32":
+      return process.env.COMSPEC || "cmd.exe";
+    // case "darwin":
+    //   return process.env.SHELL || "/bin/zsh";
+    // default:
+    //   return process.env.SHELL || "/bin/sh";
+  }
+}
+
+function getRootDir(): string | undefined {
+  const isWindows = os.platform() === "win32";
+  let cwd = isWindows ? process.env.USERPROFILE : process.env.HOME;
+  if (
+    vscode.workspace.workspaceFolders &&
+    vscode.workspace.workspaceFolders.length > 0
+  ) {
+    cwd = vscode.workspace.workspaceFolders[0].uri.fsPath;
+  }
+  return cwd;
+}
+
+export class CapturedTerminal {
+  private readonly terminal: vscode.Terminal;
+  private readonly shellCmd: string;
+  private readonly ptyProcess: any;
+
+  private shellPrompt: string | undefined = undefined;
+  private dataBuffer: string = "";
+
+  private onDataListeners: ((data: string) => void)[] = [];
+
+  show() {
+    this.terminal.show();
+  }
+
+  private commandQueue: [string, (output: string) => void][] = [];
+  private hasRunCommand: boolean = false;
+
+  private async waitForCommandToFinish() {
+    return new Promise<string>((resolve, reject) => {
+      this.onDataListeners.push((data: any) => {
+        const strippedData = stripAnsi(data);
+        this.dataBuffer += strippedData;
+        const lines = this.dataBuffer.split("\n");
+        if (
+          lines.length > 0 &&
+          lines[lines.length - 1].includes("bash-") &&
+          lines[lines.length - 1].trim().endsWith("$")
+        ) {
+          resolve(this.dataBuffer);
+          this.dataBuffer = "";
+          this.onDataListeners = [];
+        }
+      });
+    });
+  }
+
+  async runCommand(command: string): Promise<string> {
+    if (!this.hasRunCommand) {
+      this.hasRunCommand = true;
+      // Let the first bash- prompt appear and let python env be opened
+      await this.waitForCommandToFinish();
+    }
+
+    if (this.commandQueue.length === 0) {
+      return new Promise(async (resolve, reject) => {
+        this.commandQueue.push([command, resolve]);
+
+        while (this.commandQueue.length > 0) {
+          const [command, resolve] = this.commandQueue.shift()!;
+
+          this.terminal.sendText(command);
+          resolve(await this.waitForCommandToFinish());
+        }
+      });
+    } else {
+      return new Promise((resolve, reject) => {
+        this.commandQueue.push([command, resolve]);
+      });
+    }
+  }
+
+  private readonly writeEmitter: vscode.EventEmitter<string>;
+
+  constructor(terminalName: string) {
+    this.shellCmd = "bash"; // getDefaultShell();
+
+    const env = { ...(process.env as any) };
+    if (os.platform() !== "win32") {
+      env.PATH += `:${["/opt/homebrew/bin", "/opt/homebrew/sbin"].join(":")}`;
+    }
+
+    // Create the pseudo terminal
+    this.ptyProcess = pty.spawn(this.shellCmd, [], {
+      name: "xterm-256color",
+      cols: 160, // TODO: Get size of vscode terminal, and change with resize
+      rows: 26,
+      cwd: getRootDir(),
+      env,
+      useConpty: true,
+    });
+
+    this.writeEmitter = new vscode.EventEmitter<string>();
+
+    this.ptyProcess.onData((data: any) => {
+      // Pass data through to terminal
+      this.writeEmitter.fire(data);
+
+      for (let listener of this.onDataListeners) {
+        listener(data);
+      }
+    });
+
+    process.on("exit", () => this.ptyProcess.kill());
+
+    const newPty: vscode.Pseudoterminal = {
+      onDidWrite: this.writeEmitter.event,
+      open: () => {},
+      close: () => {},
+      handleInput: (data) => {
+        this.ptyProcess.write(data);
+      },
+    };
+
+    // Create and clear the terminal
+    this.terminal = vscode.window.createTerminal({
+      name: terminalName,
+      pty: newPty,
+    });
+    this.terminal.show();
+  }
+}
-- 
cgit v1.2.3-70-g09d2


From 6ebb5088a1363d4de8b9d2e6abaa02c49ee90f05 Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Wed, 7 Jun 2023 01:08:34 -0400
Subject: dlt stuff

---
 continuedev/src/continuedev/core/autopilot.py      | 20 ++++++-
 continuedev/src/continuedev/core/main.py           | 17 ++++--
 continuedev/src/continuedev/core/policy.py         |  8 +--
 continuedev/src/continuedev/core/sdk.py            |  4 +-
 .../recipes/CreatePipelineRecipe/steps.py          | 64 +++++++++++++++++-----
 extension/package-lock.json                        |  4 +-
 extension/package.json                             |  2 +-
 .../react-app/src/components/StepContainer.tsx     | 22 +++++---
 extension/react-app/src/components/index.ts        |  8 ++-
 extension/react-app/src/tabs/gui.tsx               | 17 +++---
 extension/src/continueIdeClient.ts                 |  1 +
 extension/src/terminal/terminalEmulator.ts         |  5 +-
 12 files changed, 124 insertions(+), 48 deletions(-)

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index b227570e..5a6bd2e7 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -83,9 +83,9 @@ class Autopilot(ContinueBaseModel):
     _step_depth: int = 0
 
     async def retry_at_index(self, index: int):
-        last_step = self.history.pop_last_step()
+        step = self.history.timeline[index].step.copy()
         await self.update_subscribers()
-        await self._run_singular_step(last_step)
+        await self._run_singular_step(step)
 
     async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]:
         capture_event(
@@ -119,7 +119,17 @@ class Autopilot(ContinueBaseModel):
 
             observation = InternalErrorObservation(
                 error=error_string, title=e.title)
+
+            # Reveal this step, but hide all of the following steps (its substeps)
             step.hide = False
+            i = self.history.get_current_index()
+            while self.history.timeline[i].step.name != step.name:
+                self.history.timeline[i].step.hide = True
+                i -= 1
+
+            if e.with_step is not None:
+                await self._run_singular_step(e.with_step)
+
         except Exception as e:
             # Attach an InternalErrorObservation to the step and unhide it.
             error_string = '\n\n'.join(
@@ -129,7 +139,13 @@ class Autopilot(ContinueBaseModel):
 
             observation = InternalErrorObservation(
                 error=error_string, title=e.__repr__())
+
+            # Reveal this step, but hide all of the following steps (its substeps)
             step.hide = False
+            i = self.history.get_current_index()
+            while self.history.timeline[i].step.name != step.name:
+                self.history.timeline[i].step.hide = True
+                i -= 1
 
         self._step_depth -= 1
 
diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py
index 33e25c93..37d80de3 100644
--- a/continuedev/src/continuedev/core/main.py
+++ b/continuedev/src/continuedev/core/main.py
@@ -67,11 +67,16 @@ class History(ContinueBaseModel):
             return None
         return state.observation
 
-    def pop_last_step(self) -> Union[HistoryNode, None]:
-        if self.current_index < 0:
+    def pop_step(self, index: int = None) -> Union[HistoryNode, None]:
+        index = index if index is not None else self.current_index
+        if index < 0 or self.current_index < 0:
             return None
-        node = self.timeline.pop(self.current_index)
-        self.current_index -= 1
+
+        node = self.timeline.pop(index)
+
+        if index <= self.current_index:
+            self.current_index -= 1
+
         return node.step
 
     @classmethod
@@ -183,10 +188,12 @@ class Context:
 class ContinueCustomException(Exception):
     title: str
     message: str
+    with_step: Union[Step, None]
 
-    def __init__(self, message: str, title: str = "Error while running step:"):
+    def __init__(self, message: str, title: str = "Error while running step:", with_step: Union[Step, None] = None):
         self.message = message
         self.title = title
+        self.with_step = with_step
 
 
 HistoryNode.update_forward_refs()
diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py
index 4934497d..91ae3c83 100644
--- a/continuedev/src/continuedev/core/policy.py
+++ b/continuedev/src/continuedev/core/policy.py
@@ -18,10 +18,10 @@ class DemoPolicy(Policy):
         # At the very start, run initial Steps spcecified in the config
         if history.get_current() is None:
             return (
-                    MessageStep(name="Welcome to Continue!", message="") >>
-                    # SetupContinueWorkspaceStep() >>
-                    # CreateCodebaseIndexChroma() >>
-                    StepsOnStartupStep())
+                # MessageStep(name="Welcome to Continue!", message="") >>
+                # SetupContinueWorkspaceStep() >>
+                # CreateCodebaseIndexChroma() >>
+                StepsOnStartupStep())
 
         observation = history.get_current().observation
         if observation is not None and isinstance(observation, UserInputObservation):
diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py
index f4aa2b35..76caef02 100644
--- a/continuedev/src/continuedev/core/sdk.py
+++ b/continuedev/src/continuedev/core/sdk.py
@@ -131,5 +131,5 @@ class ContinueSDK(AbstractContinueSDK):
         # self.__autopilot.set_loading_message(message)
         raise NotImplementedError()
 
-    def raise_exception(self, message: str, title: str):
-        raise ContinueCustomException(message, title)
+    def raise_exception(self, message: str, title: str, with_step: Union[Step, None] = None):
+        raise ContinueCustomException(message, title, with_step)
diff --git a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
index 9bee4c95..c32ae923 100644
--- a/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
+++ b/continuedev/src/continuedev/recipes/CreatePipelineRecipe/steps.py
@@ -1,16 +1,19 @@
 import os
 import subprocess
 from textwrap import dedent
+import time
 
 from ...models.main import Range
 from ...models.filesystem import RangeInFile
 from ...steps.main import MessageStep
 from ...core.sdk import Models
 from ...core.observation import DictObservation, InternalErrorObservation
-from ...models.filesystem_edit import AddFile
+from ...models.filesystem_edit import AddFile, FileEdit
 from ...core.main import Step
 from ...core.sdk import ContinueSDK
 
+AI_ASSISTED_STRING = "(✨ AI-Assisted ✨)"
+
 
 class SetupPipelineStep(Step):
     hide: bool = True
@@ -25,6 +28,8 @@ class SetupPipelineStep(Step):
         """)
 
     async def run(self, sdk: ContinueSDK):
+        sdk.context.set("api_description", self.api_description)
+
         source_name = (await sdk.models.gpt35()).complete(
             f"Write a snake_case name for the data source described by {self.api_description}: ").strip()
         filename = f'{source_name}.py'
@@ -45,14 +50,17 @@ class SetupPipelineStep(Step):
             - `pip install -r requirements.txt`: Install the Python dependencies for the pipeline"""), name="Setup Python environment")
 
         # editing the resource function to call the requested API
-        await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 30, 0)), "#00ff0022")
+        await sdk.ide.highlightCode(RangeInFile(filepath=os.path.join(await sdk.ide.getWorkspaceDirectory(), filename), range=Range.from_shorthand(15, 0, 29, 0)), "#00ff0022")
 
+        # sdk.set_loading_message("Writing code to call the API...")
         await sdk.edit_file(
             filename=filename,
-            prompt=f'Edit the resource function to call the API described by this: {self.api_description}',
-            name="Edit the resource function to call the API"
+            prompt=f'Edit the resource function to call the API described by this: {self.api_description}. Do not move or remove the exit() call in __main__.',
+            name=f"Edit the resource function to call the API {AI_ASSISTED_STRING}"
         )
 
+        time.sleep(1)
+
         # wait for user to put API key in secrets.toml
         await sdk.ide.setFileOpen(await sdk.ide.getWorkspaceDirectory() + "/.dlt/secrets.toml")
         await sdk.wait_for_user_confirmation("If this service requires an API key, please add it to the `secrets.toml` file and then press `Continue`")
@@ -76,20 +84,50 @@ class ValidatePipelineStep(Step):
         #         """)))
 
         # test that the API call works
-        output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running python3 {filename} to test loading data from the API")
+        output = await sdk.run(f'python3 {filename}', name="Test the pipeline", description=f"Running `python3 {filename}` to test loading data from the API")
 
         # If it fails, return the error
         if "Traceback" in output:
+            output = "Traceback" + output.split("Traceback")[-1]
+            file_content = await sdk.ide.readFile(os.path.join(workspace_dir, filename))
+            suggestion = (await sdk.models.gpt35()).complete(dedent(f"""\
+                ```python
+                {file_content}
+                ```
+                This above code is a dlt pipeline that loads data from an API. The function with the @resource decorator is responsible for calling the API and returning the data. While attempting to run the pipeline, the following error occurred:
+
+                ```ascii
+                {output}
+                ```
+
+                This is a brief summary of the error followed by a suggestion on how it can be fixed by editing the resource function:"""))
+
+            api_documentation_url = (await sdk.models.gpt35()).complete(dedent(f"""\
+                The API I am trying to call is the '{sdk.context.get('api_description')}'. I tried calling it in the @resource function like this:
+                ```python       
+                {file_content}
+                ```
+                What is the URL for the API documentation that will help me learn how to make this call? Please format in markdown so I can click the link."""))
+
             sdk.raise_exception(
-                title="Error while running pipeline.\nFix the resource function in {filename} and rerun this step", description=output)
+                title=f"Error while running pipeline.\nFix the resource function in {filename} and rerun this step", message=output, with_step=MessageStep(name=f"Suggestion to solve error {AI_ASSISTED_STRING}", message=dedent(f"""\
+                {suggestion}
+                
+                {api_documentation_url}
+                
+                After you've fixed the code, click the retry button at the top of the Validate Pipeline step above.""")))
 
         # remove exit() from the main main function
-        await sdk.edit_file(
-            filename=filename,
-            prompt='Remove exit() from the main function',
-            name="Remove early exit() from main function",
-            description="Remove the `exit()` call from the main function in the pipeline file so that the data is loaded into DuckDB"
-        )
+        await sdk.run_step(MessageStep(name="Remove early exit() from main function", message="Remove the early exit() from the main function now that we are done testing and want the pipeline to load the data into DuckDB."))
+
+        contents = await sdk.ide.readFile(os.path.join(workspace_dir, filename))
+        replacement = "\n".join(
+            list(filter(lambda line: line.strip() != "exit()", contents.split("\n"))))
+        await sdk.ide.applyFileSystemEdit(FileEdit(
+            filepath=os.path.join(workspace_dir, filename),
+            replacement=replacement,
+            range=Range.from_entire_file(contents)
+        ))
 
         # load the data into the DuckDB instance
         await sdk.run(f'python3 {filename}', name="Load data into DuckDB", description=f"Running python3 {filename} to load data into DuckDB")
@@ -109,6 +147,6 @@ class ValidatePipelineStep(Step):
                 print(row)
         ''')
 
-        query_filename = (await sdk.ide.getWorkspaceDirectory()) + "/query.py"
+        query_filename = os.path.join(workspace_dir, "query.py")
         await sdk.apply_filesystem_edit(AddFile(filepath=query_filename, content=tables_query_code), name="Add query.py file", description="Adding a file called `query.py` to the workspace that will run a test query on the DuckDB instance")
         await sdk.run('env/bin/python3 query.py', name="Run test query", description="Running `env/bin/python3 query.py` to test that the data was loaded into DuckDB as expected")
diff --git a/extension/package-lock.json b/extension/package-lock.json
index a20be756..0b0e063b 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "continue",
-  "version": "0.0.20",
+  "version": "0.0.23",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "continue",
-      "version": "0.0.20",
+      "version": "0.0.23",
       "license": "Apache-2.0",
       "dependencies": {
         "@electron/rebuild": "^3.2.10",
diff --git a/extension/package.json b/extension/package.json
index 598ae778..c979a435 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -14,7 +14,7 @@
   "displayName": "Continue",
   "pricing": "Free",
   "description": "Refine code 10x faster",
-  "version": "0.0.20",
+  "version": "0.0.23",
   "publisher": "Continue",
   "engines": {
     "vscode": "^1.74.0"
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index a150e370..8ea54325 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -50,8 +50,9 @@ const StepContainerDiv = styled.div<{ open: boolean }>`
   /* padding: 8px; */
 `;
 
-const HeaderDiv = styled.div`
-  background-color: ${vscBackgroundTransparent};
+const HeaderDiv = styled.div<{ error: boolean }>`
+  background-color: ${(props) =>
+    props.error ? "#522" : vscBackgroundTransparent};
   display: grid;
   grid-template-columns: 1fr auto;
   align-items: center;
@@ -124,17 +125,23 @@ function StepContainer(props: StepContainerProps) {
     >
       <StepContainerDiv open={open}>
         <GradientBorder
+          borderColor={
+            props.historyNode.observation?.error ? "#f00" : undefined
+          }
           className="overflow-hidden cursor-pointer"
           onClick={() => setOpen((prev) => !prev)}
         >
-          <HeaderDiv>
+          <HeaderDiv
+            error={props.historyNode.observation?.error ? true : false}
+          >
             <h4 className="m-2">
               {open ? (
                 <ChevronDown size="1.4em" />
               ) : (
                 <ChevronRight size="1.4em" />
               )}
-              {props.historyNode.step.name as any}
+              {props.historyNode.observation?.title ||
+                (props.historyNode.step.name as any)}
             </h4>
             {/* <HeaderButton
               onClick={(e) => {
@@ -171,10 +178,9 @@ function StepContainer(props: StepContainerProps) {
           )}
 
           {props.historyNode.observation?.error ? (
-            <ToggleErrorDiv
-              title={"Error while running step:"}
-              error={props.historyNode.observation.error as string}
-            />
+            <pre className="overflow-x-scroll">
+              {props.historyNode.observation.error as string}
+            </pre>
           ) : (
             <ReactMarkdown key={1} className="overflow-scroll">
               {props.historyNode.step.description as any}
diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts
index ac5faa41..4966f3e8 100644
--- a/extension/react-app/src/components/index.ts
+++ b/extension/react-app/src/components/index.ts
@@ -98,17 +98,21 @@ export const Loader = styled.div`
 export const GradientBorder = styled.div<{
   borderWidth?: string;
   borderRadius?: string;
+  borderColor?: string;
 }>`
   border-radius: ${(props) => props.borderRadius || "0"};
   padding-top: ${(props) => props.borderWidth || "1px"};
   padding-bottom: ${(props) => props.borderWidth || "1px"};
-  background: linear-gradient(
+  background: ${(props) =>
+    props.borderColor
+      ? props.borderColor
+      : `linear-gradient(
     101.79deg,
     #12887a 0%,
     #87245c 37.64%,
     #e12637 65.98%,
     #ffb215 110.45%
-  );
+  )`};
 `;
 
 export const MainContainerWithBorder = styled.div<{ borderWidth?: string }>`
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index 308dfd57..5c75579b 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -44,6 +44,7 @@ function GUI(props: GUIProps) {
   //           "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`",
   //       },
   //       observation: {
+  //         title: "ERROR FOUND",
   //         error:
   //           "Traceback (most recent call last):\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n    print(sum(first, second))\n          ^^^^^^^^^^^^^^^^^^\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'",
   //       },
@@ -228,9 +229,9 @@ function GUI(props: GUIProps) {
         setUserInputQueue((queue) => {
           return [...queue, input];
         });
-        mainTextInputRef.current.value = "";
-        mainTextInputRef.current.style.height = "";
       }
+      mainTextInputRef.current.value = "";
+      mainTextInputRef.current.style.height = "";
     }
 
     setWaitingForSteps(true);
@@ -307,13 +308,15 @@ function GUI(props: GUIProps) {
         }}
         rows={1}
         onChange={() => {
-          let textarea = mainTextInputRef.current!;
+          const textarea = mainTextInputRef.current!;
           textarea.style.height = ""; /* Reset the height*/
-          textarea.style.height =
-            Math.min(textarea.scrollHeight - 15, 500) + "px";
+          textarea.style.height = `${Math.min(
+            textarea.scrollHeight - 15,
+            500
+          )}px`;
         }}
-      ></MainTextInput>
-      <ContinueButton onClick={onMainTextInput}></ContinueButton>
+      />
+      <ContinueButton onClick={onMainTextInput} />
     </TopGUIDiv>
   );
 }
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index e84602f0..bbaf5f08 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -142,6 +142,7 @@ class IdeProtocolClient {
       editor.setDecorations(
         vscode.window.createTextEditorDecorationType({
           backgroundColor: color,
+          isWholeLine: true,
         }),
         [range]
       );
diff --git a/extension/src/terminal/terminalEmulator.ts b/extension/src/terminal/terminalEmulator.ts
index 8974b7e3..67b47e2f 100644
--- a/extension/src/terminal/terminalEmulator.ts
+++ b/extension/src/terminal/terminalEmulator.ts
@@ -73,8 +73,9 @@ export class CapturedTerminal {
         const lines = this.dataBuffer.split("\n");
         if (
           lines.length > 0 &&
-          lines[lines.length - 1].includes("bash-") &&
-          lines[lines.length - 1].trim().endsWith("$")
+          (lines[lines.length - 1].includes("bash-") ||
+            lines[lines.length - 1].includes("(main)")) &&
+          lines[lines.length - 1].includes("$")
         ) {
           resolve(this.dataBuffer);
           this.dataBuffer = "";
-- 
cgit v1.2.3-70-g09d2

