We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
bug: `flask` not found when running `meltano-cloud` via `pipx`
### Meltano Version

N/A

### Python Version

3.11

### Bug scope

CLI (options, error messages, logging, etc.)

### Operating System

Python 3.11 Docker container

### Description

```sh
pip install pipx
pipx install 'git+https://github.com/meltano/meltano.git@cloud#subdirectory=src/cloud-cli'`
pipx ensurepath
bash
meltano-cloud login
```

Results in:
```
FileNotFoundError: [Errno 2] No such file or directory: 'flask'
```

This is because `pipx` only exposes the CLI entrypoints of our package, and not those of its dependencies like `flask`. We can resolve this by using the precise path to the `flask` binary in the active environment rather than searching for it on `$PATH`.

### Code

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/cloud-cli/meltano/cloud/api/auth/auth.py`
Content:
```
1 """Authentication for Meltano Cloud."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 import os
7 import subprocess
8 import sys
9 import typing as t
10 import webbrowser
11 from contextlib import contextmanager
12 from pathlib import Path
13 from urllib.parse import urlencode, urljoin
14 
15 import aiohttp
16 import click
17 
18 from meltano.cloud.api.config import MeltanoCloudConfig
19 
20 if sys.version_info <= (3, 8):
21     from cached_property import cached_property
22 else:
23     from functools import cached_property
24 
25 LOGIN_STATUS_CHECK_DELAY_SECONDS = 0.2
26 
27 
28 class MeltanoCloudAuthError(Exception):
29     """Raised when an API call returns a 403."""
30 
31 
32 class MeltanoCloudAuth:  # noqa: WPS214
33     """Authentication methods for Meltano Cloud."""
34 
35     def __init__(self, config: MeltanoCloudConfig | None = None):
36         """Initialize a MeltanoCloudAuth instance.
37 
38         Args:
39             config: the MeltanoCloudConfig to use
40         """
41         self.config = config or MeltanoCloudConfig.find()
42         self.base_url = self.config.base_auth_url
43         self.client_id = self.config.app_client_id
44 
45     @cached_property
46     def login_url(self) -> str:
47         """Get the oauth2 authorization URL.
48 
49         Returns:
50             the oauth2 authorization URL.
51         """
52         query_params = urlencode(
53             {
54                 "client_id": self.client_id,
55                 "response_type": "token",
56                 "scope": "email openid profile",
57                 "redirect_uri": f"http://localhost:{self.config.auth_callback_port}",
58             }
59         )
60         return f"{self.base_url}/oauth2/authorize?{query_params}"
61 
62     @cached_property
63     def logout_url(self) -> str:
64         """Get the Meltano Cloud logout URL.
65 
66         Returns:
67             the Meltano Cloud logout URL.
68         """
69         params = urlencode(
70             {
71                 "client_id": self.client_id,
72                 "logout_uri": f"http://localhost:{self.config.auth_callback_port}/logout",  # noqa: E501)
73             }
74         )
75         return urljoin(self.base_url, f"logout?{params}")
76 
77     @contextmanager
78     def callback_server(self) -> t.Iterator[None]:
79         """Context manager to run callback server locally.
80 
81         Yields:
82             None
83         """
84         server = None
85         try:
86             server = subprocess.Popen(  # noqa: S607
87                 ("flask", "run", f"--port={self.config.auth_callback_port}"),
88                 env={
89                     **os.environ,
90                     "FLASK_APP": "callback_server.py",
91                     "MELTANO_CLOUD_CONFIG_PATH": str(self.config.config_path),
92                 },
93                 cwd=Path(__file__).parent,
94                 stdout=subprocess.DEVNULL,
95                 stderr=subprocess.STDOUT,
96             )
97             yield
98         finally:
99             if server:
100                 server.kill()
101 
102     async def login(self) -> None:
103         """Take user through login flow and get auth and id tokens."""
104         if await self.logged_in():
105             return
106         with self.callback_server():
107             click.echo("Logging in to Meltano Cloud.")
108             click.echo("You will be directed to a web browser to complete login.")
109             click.echo("If a web browser does not open, open the following link:")
110             click.secho(self.login_url, fg="green")
111             webbrowser.open_new_tab(self.login_url)
112             while not await self.logged_in():
113                 self.config.refresh()
114                 await asyncio.sleep(LOGIN_STATUS_CHECK_DELAY_SECONDS)
115 
116     async def logout(self) -> None:  # noqa: WPS213
117         """Log out."""
118         if not await self.logged_in():
119             click.secho("Not logged in.", fg="green")
120             return
121         with self.callback_server():
122             click.echo("Logging out of Meltano Cloud.")
123             click.echo("You will be directed to a web browser to complete logout.")
124             click.echo("If a web browser does not open, open the following link:")
125             click.secho(self.logout_url, fg="green")
126             webbrowser.open_new_tab(self.logout_url)
127             while await self.logged_in():
128                 self.config.refresh()
129                 await asyncio.sleep(LOGIN_STATUS_CHECK_DELAY_SECONDS)
130         click.secho("Successfully logged out.", fg="green")
131 
132     def get_auth_header(self) -> dict[str, str]:
133         """Get the authorization header.
134 
135         Used for authenticating to cloud API endpoints.
136 
137         Returns:
138             Authorization header using ID token as bearer token.
139 
140         """
141         return {"Authorization": f"Bearer {self.config.id_token}"}
142 
143     def get_access_token_header(self) -> dict[str, str]:
144         """Get the access token header.
145 
146         Used for authenticating to auth endpoints.
147 
148         Returns:
149             Authorization header using access token as bearer token.
150         """
151         return {"Authorization": f"Bearer {self.config.access_token}"}
152 
153     async def get_user_info_response(self) -> aiohttp.ClientResponse:
154         """Get user info.
155 
156         Returns:
157             User info response
158         """
159         async with aiohttp.ClientSession() as session:
160             async with session.get(
161                 urljoin(self.base_url, "oauth2/userInfo"),
162                 headers=self.get_access_token_header(),
163             ) as response:
164                 return response
165 
166     async def get_user_info_json(self) -> dict:
167         """Get user info as dict.
168 
169         Returns:
170             User info json
171         """
172         async with aiohttp.ClientSession() as session:
173             async with session.get(
174                 urljoin(self.base_url, "oauth2/userInfo"),
175                 headers=self.get_access_token_header(),
176             ) as response:
177                 return await response.json()
178 
179     async def logged_in(self) -> bool:
180         """Check if this instance is currently logged in.
181 
182         Returns:
183             True if logged in, else False
184         """
185         user_info_resp = await self.get_user_info_response()
186         return bool(
187             self.config.access_token and self.config.id_token and user_info_resp.ok
188         )
189
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/cloud-cli/meltano/cloud/api/auth/auth.py b/src/cloud-cli/meltano/cloud/api/auth/auth.py
--- a/src/cloud-cli/meltano/cloud/api/auth/auth.py
+++ b/src/cloud-cli/meltano/cloud/api/auth/auth.py
@@ -84,7 +84,11 @@
         server = None
         try:
             server = subprocess.Popen(  # noqa: S607
-                ("flask", "run", f"--port={self.config.auth_callback_port}"),
+                (
+                    str(Path(sys.prefix) / "bin" / "flask"),
+                    "run",
+                    f"--port={self.config.auth_callback_port}",
+                ),
                 env={
                     **os.environ,
                     "FLASK_APP": "callback_server.py",

{"golden_diff": "diff --git a/src/cloud-cli/meltano/cloud/api/auth/auth.py b/src/cloud-cli/meltano/cloud/api/auth/auth.py\n--- a/src/cloud-cli/meltano/cloud/api/auth/auth.py\n+++ b/src/cloud-cli/meltano/cloud/api/auth/auth.py\n@@ -84,7 +84,11 @@\n         server = None\n         try:\n             server = subprocess.Popen(  # noqa: S607\n-                (\"flask\", \"run\", f\"--port={self.config.auth_callback_port}\"),\n+                (\n+                    str(Path(sys.prefix) / \"bin\" / \"flask\"),\n+                    \"run\",\n+                    f\"--port={self.config.auth_callback_port}\",\n+                ),\n                 env={\n                     **os.environ,\n                     \"FLASK_APP\": \"callback_server.py\",\n", "issue": "bug: `flask` not found when running `meltano-cloud` via `pipx`\n### Meltano Version\n\nN/A\n\n### Python Version\n\n3.11\n\n### Bug scope\n\nCLI (options, error messages, logging, etc.)\n\n### Operating System\n\nPython 3.11 Docker container\n\n### Description\n\n```sh\r\npip install pipx\r\npipx install 'git+https://github.com/meltano/meltano.git@cloud#subdirectory=src/cloud-cli'`\r\npipx ensurepath\r\nbash\r\nmeltano-cloud login\r\n```\r\n\r\nResults in:\r\n```\r\nFileNotFoundError: [Errno 2] No such file or directory: 'flask'\r\n```\r\n\r\nThis is because `pipx` only exposes the CLI entrypoints of our package, and not those of its dependencies like `flask`. We can resolve this by using the precise path to the `flask` binary in the active environment rather than searching for it on `$PATH`.\n\n### Code\n\n_No response_\n", "before_files": [{"content": "\"\"\"Authentication for Meltano Cloud.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nimport os\nimport subprocess\nimport sys\nimport typing as t\nimport webbrowser\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom urllib.parse import urlencode, urljoin\n\nimport aiohttp\nimport click\n\nfrom meltano.cloud.api.config import MeltanoCloudConfig\n\nif sys.version_info <= (3, 8):\n    from cached_property import cached_property\nelse:\n    from functools import cached_property\n\nLOGIN_STATUS_CHECK_DELAY_SECONDS = 0.2\n\n\nclass MeltanoCloudAuthError(Exception):\n    \"\"\"Raised when an API call returns a 403.\"\"\"\n\n\nclass MeltanoCloudAuth:  # noqa: WPS214\n    \"\"\"Authentication methods for Meltano Cloud.\"\"\"\n\n    def __init__(self, config: MeltanoCloudConfig | None = None):\n        \"\"\"Initialize a MeltanoCloudAuth instance.\n\n        Args:\n            config: the MeltanoCloudConfig to use\n        \"\"\"\n        self.config = config or MeltanoCloudConfig.find()\n        self.base_url = self.config.base_auth_url\n        self.client_id = self.config.app_client_id\n\n    @cached_property\n    def login_url(self) -> str:\n        \"\"\"Get the oauth2 authorization URL.\n\n        Returns:\n            the oauth2 authorization URL.\n        \"\"\"\n        query_params = urlencode(\n            {\n                \"client_id\": self.client_id,\n                \"response_type\": \"token\",\n                \"scope\": \"email openid profile\",\n                \"redirect_uri\": f\"http://localhost:{self.config.auth_callback_port}\",\n            }\n        )\n        return f\"{self.base_url}/oauth2/authorize?{query_params}\"\n\n    @cached_property\n    def logout_url(self) -> str:\n        \"\"\"Get the Meltano Cloud logout URL.\n\n        Returns:\n            the Meltano Cloud logout URL.\n        \"\"\"\n        params = urlencode(\n            {\n                \"client_id\": self.client_id,\n                \"logout_uri\": f\"http://localhost:{self.config.auth_callback_port}/logout\",  # noqa: E501)\n            }\n        )\n        return urljoin(self.base_url, f\"logout?{params}\")\n\n    @contextmanager\n    def callback_server(self) -> t.Iterator[None]:\n        \"\"\"Context manager to run callback server locally.\n\n        Yields:\n            None\n        \"\"\"\n        server = None\n        try:\n            server = subprocess.Popen(  # noqa: S607\n                (\"flask\", \"run\", f\"--port={self.config.auth_callback_port}\"),\n                env={\n                    **os.environ,\n                    \"FLASK_APP\": \"callback_server.py\",\n                    \"MELTANO_CLOUD_CONFIG_PATH\": str(self.config.config_path),\n                },\n                cwd=Path(__file__).parent,\n                stdout=subprocess.DEVNULL,\n                stderr=subprocess.STDOUT,\n            )\n            yield\n        finally:\n            if server:\n                server.kill()\n\n    async def login(self) -> None:\n        \"\"\"Take user through login flow and get auth and id tokens.\"\"\"\n        if await self.logged_in():\n            return\n        with self.callback_server():\n            click.echo(\"Logging in to Meltano Cloud.\")\n            click.echo(\"You will be directed to a web browser to complete login.\")\n            click.echo(\"If a web browser does not open, open the following link:\")\n            click.secho(self.login_url, fg=\"green\")\n            webbrowser.open_new_tab(self.login_url)\n            while not await self.logged_in():\n                self.config.refresh()\n                await asyncio.sleep(LOGIN_STATUS_CHECK_DELAY_SECONDS)\n\n    async def logout(self) -> None:  # noqa: WPS213\n        \"\"\"Log out.\"\"\"\n        if not await self.logged_in():\n            click.secho(\"Not logged in.\", fg=\"green\")\n            return\n        with self.callback_server():\n            click.echo(\"Logging out of Meltano Cloud.\")\n            click.echo(\"You will be directed to a web browser to complete logout.\")\n            click.echo(\"If a web browser does not open, open the following link:\")\n            click.secho(self.logout_url, fg=\"green\")\n            webbrowser.open_new_tab(self.logout_url)\n            while await self.logged_in():\n                self.config.refresh()\n                await asyncio.sleep(LOGIN_STATUS_CHECK_DELAY_SECONDS)\n        click.secho(\"Successfully logged out.\", fg=\"green\")\n\n    def get_auth_header(self) -> dict[str, str]:\n        \"\"\"Get the authorization header.\n\n        Used for authenticating to cloud API endpoints.\n\n        Returns:\n            Authorization header using ID token as bearer token.\n\n        \"\"\"\n        return {\"Authorization\": f\"Bearer {self.config.id_token}\"}\n\n    def get_access_token_header(self) -> dict[str, str]:\n        \"\"\"Get the access token header.\n\n        Used for authenticating to auth endpoints.\n\n        Returns:\n            Authorization header using access token as bearer token.\n        \"\"\"\n        return {\"Authorization\": f\"Bearer {self.config.access_token}\"}\n\n    async def get_user_info_response(self) -> aiohttp.ClientResponse:\n        \"\"\"Get user info.\n\n        Returns:\n            User info response\n        \"\"\"\n        async with aiohttp.ClientSession() as session:\n            async with session.get(\n                urljoin(self.base_url, \"oauth2/userInfo\"),\n                headers=self.get_access_token_header(),\n            ) as response:\n                return response\n\n    async def get_user_info_json(self) -> dict:\n        \"\"\"Get user info as dict.\n\n        Returns:\n            User info json\n        \"\"\"\n        async with aiohttp.ClientSession() as session:\n            async with session.get(\n                urljoin(self.base_url, \"oauth2/userInfo\"),\n                headers=self.get_access_token_header(),\n            ) as response:\n                return await response.json()\n\n    async def logged_in(self) -> bool:\n        \"\"\"Check if this instance is currently logged in.\n\n        Returns:\n            True if logged in, else False\n        \"\"\"\n        user_info_resp = await self.get_user_info_response()\n        return bool(\n            self.config.access_token and self.config.id_token and user_info_resp.ok\n        )\n", "path": "src/cloud-cli/meltano/cloud/api/auth/auth.py"}], "after_files": [{"content": "\"\"\"Authentication for Meltano Cloud.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nimport os\nimport subprocess\nimport sys\nimport typing as t\nimport webbrowser\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom urllib.parse import urlencode, urljoin\n\nimport aiohttp\nimport click\n\nfrom meltano.cloud.api.config import MeltanoCloudConfig\n\nif sys.version_info <= (3, 8):\n    from cached_property import cached_property\nelse:\n    from functools import cached_property\n\nLOGIN_STATUS_CHECK_DELAY_SECONDS = 0.2\n\n\nclass MeltanoCloudAuthError(Exception):\n    \"\"\"Raised when an API call returns a 403.\"\"\"\n\n\nclass MeltanoCloudAuth:  # noqa: WPS214\n    \"\"\"Authentication methods for Meltano Cloud.\"\"\"\n\n    def __init__(self, config: MeltanoCloudConfig | None = None):\n        \"\"\"Initialize a MeltanoCloudAuth instance.\n\n        Args:\n            config: the MeltanoCloudConfig to use\n        \"\"\"\n        self.config = config or MeltanoCloudConfig.find()\n        self.base_url = self.config.base_auth_url\n        self.client_id = self.config.app_client_id\n\n    @cached_property\n    def login_url(self) -> str:\n        \"\"\"Get the oauth2 authorization URL.\n\n        Returns:\n            the oauth2 authorization URL.\n        \"\"\"\n        query_params = urlencode(\n            {\n                \"client_id\": self.client_id,\n                \"response_type\": \"token\",\n                \"scope\": \"email openid profile\",\n                \"redirect_uri\": f\"http://localhost:{self.config.auth_callback_port}\",\n            }\n        )\n        return f\"{self.base_url}/oauth2/authorize?{query_params}\"\n\n    @cached_property\n    def logout_url(self) -> str:\n        \"\"\"Get the Meltano Cloud logout URL.\n\n        Returns:\n            the Meltano Cloud logout URL.\n        \"\"\"\n        params = urlencode(\n            {\n                \"client_id\": self.client_id,\n                \"logout_uri\": f\"http://localhost:{self.config.auth_callback_port}/logout\",  # noqa: E501)\n            }\n        )\n        return urljoin(self.base_url, f\"logout?{params}\")\n\n    @contextmanager\n    def callback_server(self) -> t.Iterator[None]:\n        \"\"\"Context manager to run callback server locally.\n\n        Yields:\n            None\n        \"\"\"\n        server = None\n        try:\n            server = subprocess.Popen(  # noqa: S607\n                (\n                    str(Path(sys.prefix) / \"bin\" / \"flask\"),\n                    \"run\",\n                    f\"--port={self.config.auth_callback_port}\",\n                ),\n                env={\n                    **os.environ,\n                    \"FLASK_APP\": \"callback_server.py\",\n                    \"MELTANO_CLOUD_CONFIG_PATH\": str(self.config.config_path),\n                },\n                cwd=Path(__file__).parent,\n                stdout=subprocess.DEVNULL,\n                stderr=subprocess.STDOUT,\n            )\n            yield\n        finally:\n            if server:\n                server.kill()\n\n    async def login(self) -> None:\n        \"\"\"Take user through login flow and get auth and id tokens.\"\"\"\n        if await self.logged_in():\n            return\n        with self.callback_server():\n            click.echo(\"Logging in to Meltano Cloud.\")\n            click.echo(\"You will be directed to a web browser to complete login.\")\n            click.echo(\"If a web browser does not open, open the following link:\")\n            click.secho(self.login_url, fg=\"green\")\n            webbrowser.open_new_tab(self.login_url)\n            while not await self.logged_in():\n                self.config.refresh()\n                await asyncio.sleep(LOGIN_STATUS_CHECK_DELAY_SECONDS)\n\n    async def logout(self) -> None:  # noqa: WPS213\n        \"\"\"Log out.\"\"\"\n        if not await self.logged_in():\n            click.secho(\"Not logged in.\", fg=\"green\")\n            return\n        with self.callback_server():\n            click.echo(\"Logging out of Meltano Cloud.\")\n            click.echo(\"You will be directed to a web browser to complete logout.\")\n            click.echo(\"If a web browser does not open, open the following link:\")\n            click.secho(self.logout_url, fg=\"green\")\n            webbrowser.open_new_tab(self.logout_url)\n            while await self.logged_in():\n                self.config.refresh()\n                await asyncio.sleep(LOGIN_STATUS_CHECK_DELAY_SECONDS)\n        click.secho(\"Successfully logged out.\", fg=\"green\")\n\n    def get_auth_header(self) -> dict[str, str]:\n        \"\"\"Get the authorization header.\n\n        Used for authenticating to cloud API endpoints.\n\n        Returns:\n            Authorization header using ID token as bearer token.\n\n        \"\"\"\n        return {\"Authorization\": f\"Bearer {self.config.id_token}\"}\n\n    def get_access_token_header(self) -> dict[str, str]:\n        \"\"\"Get the access token header.\n\n        Used for authenticating to auth endpoints.\n\n        Returns:\n            Authorization header using access token as bearer token.\n        \"\"\"\n        return {\"Authorization\": f\"Bearer {self.config.access_token}\"}\n\n    async def get_user_info_response(self) -> aiohttp.ClientResponse:\n        \"\"\"Get user info.\n\n        Returns:\n            User info response\n        \"\"\"\n        async with aiohttp.ClientSession() as session:\n            async with session.get(\n                urljoin(self.base_url, \"oauth2/userInfo\"),\n                headers=self.get_access_token_header(),\n            ) as response:\n                return response\n\n    async def get_user_info_json(self) -> dict:\n        \"\"\"Get user info as dict.\n\n        Returns:\n            User info json\n        \"\"\"\n        async with aiohttp.ClientSession() as session:\n            async with session.get(\n                urljoin(self.base_url, \"oauth2/userInfo\"),\n                headers=self.get_access_token_header(),\n            ) as response:\n                return await response.json()\n\n    async def logged_in(self) -> bool:\n        \"\"\"Check if this instance is currently logged in.\n\n        Returns:\n            True if logged in, else False\n        \"\"\"\n        user_info_resp = await self.get_user_info_response()\n        return bool(\n            self.config.access_token and self.config.id_token and user_info_resp.ok\n        )\n", "path": "src/cloud-cli/meltano/cloud/api/auth/auth.py"}]}