We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
bug: Cloud project ID of `00000000000000000000000000` is used as real ID
### Meltano Version

cloud branch

### Python Version

NA

### Bug scope

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

### Operating System

N/A

### Description

[Context on Slack.](https://meltano.slack.com/archives/C04SHQJB95F/p1686101768535459?thread_ts=1686096202.419359&cid=C04SHQJB95F)

[Datadog logs for the provisioner API error.](https://app.datadoghq.com/logs?query=service%3Aprovisioner-function-e012747%20&cols=host%2Cservice&event=AgAAAYiTLKwO9bi1IQAAAAAAAAAYAAAAAEFZaVRMTkEyQUFDdlZ2eVphdGk2SEFBRAAAACQAAAAAMDE4ODkzMmYtNDcwNy00YWY4LTgzM2ItNTY4MmQ5MjEwMGFm&index=%2A&messageDisplay=inline&saved-view-id=1860006&stream_sort=desc&viz=stream&from_ts=1686087280625&to_ts=1686101680625&live=true)

The project ID of `00000000000000000000000000` should never be used as a real Cloud project ID. It is merely a placeholder that is returned as part of the custom Cognito attribute during login because historically a real project ID was obtained via Cognito.

If a project ID of `00000000000000000000000000` is found, it should be replaced by `None` (i.e. `null` in the config JSON document), rather than used for any command.

### 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/config.py`
Content:
```
1 """Configuration for Meltano Cloud."""
2 
3 from __future__ import annotations
4 
5 import json
6 import os
7 import sys
8 import typing as t
9 from contextlib import suppress
10 from pathlib import Path
11 
12 import jwt
13 import platformdirs
14 
15 if sys.version_info <= (3, 8):
16     from cached_property import cached_property
17 else:
18     from functools import cached_property
19 
20 MELTANO_CLOUD_BASE_URL = "https://internal.api.meltano.cloud/"
21 MELTANO_CLOUD_BASE_AUTH_URL = "https://auth.meltano.cloud"
22 MELTANO_CLOUD_APP_CLIENT_ID = "45rpn5ep3g4qjut8jd3s4iq872"
23 
24 USER_RW_FILE_MODE = 0o600
25 
26 
27 class InvalidMeltanoCloudConfigError(Exception):
28     """Provided configuration is invalid."""
29 
30 
31 class MeltanoCloudConfigFileNotFoundError(Exception):
32     """Meltano Cloud config file is missing."""
33 
34 
35 class MeltanoCloudTenantAmbiguityError(Exception):
36     """Logged in user has access to multiple tenants."""
37 
38     def __init__(self) -> None:
39         """Initialize exception with message."""
40         super().__init__(
41             "Logged in Meltano user has multiple tenant resource keys. "
42             "Set MELTANO_CLOUD_TENANT_RESOURCE_KEY to select one.",
43         )
44 
45 
46 class NoMeltanoCloudTenantResourceKeyError(Exception):
47     """Logged in user does not have access to any tenants."""
48 
49     def __init__(self) -> None:
50         """Initialize exception with message."""
51         super().__init__(
52             "Logged in Meltano user has no tenant resource keys. "
53             "Reach out to Meltano support to be assigned a tenant resource key.",
54         )
55 
56 
57 class MeltanoCloudProjectAmbiguityError(Exception):
58     """Logged in user has access to multiple projects."""
59 
60     def __init__(self) -> None:
61         """Initialize exception with message."""
62         super().__init__(
63             "Logged in Meltano user has multiple projects. "
64             "Set one as the default project using the "
65             "`meltano cloud project use` command.",
66         )
67 
68 
69 class NoMeltanoCloudProjectIDError(Exception):
70     """Logged in user does not have any projects."""
71 
72     def __init__(self) -> None:
73         """Initialize exception with message."""
74         super().__init__(
75             "Logged in Meltano user has no projects. "
76             "Set one as the default project using the "
77             "`meltano cloud project use` command.",
78         )
79 
80 
81 class MeltanoCloudConfig:  # noqa: WPS214 WPS230
82     """Configuration for Meltano Cloud client."""
83 
84     env_var_prefix = "MELTANO_CLOUD_"
85 
86     def __init__(
87         self,
88         *,
89         auth_callback_port: int = 9999,
90         base_url: str = MELTANO_CLOUD_BASE_URL,
91         base_auth_url: str = MELTANO_CLOUD_BASE_AUTH_URL,
92         app_client_id: str = MELTANO_CLOUD_APP_CLIENT_ID,
93         id_token: str | None = None,
94         access_token: str | None = None,
95         config_path: os.PathLike | str | None = None,
96         default_project_id: str | None = None,
97         default_deployment_name: str | None = None,
98     ):
99         """Initialize a MeltanoCloudConfig instance.
100 
101         The configuration file is by default stored in the `meltano-cloud`
102         directory in the user's config directory. The directory can be
103         overridden by setting the `MELTANO_CLOUD_CONFIG_DIR` environment
104         variable.
105 
106         Args:
107             auth_callback_port: Port to run auth callback server at.
108             base_url: Base URL for Meltano API to interact with.
109             base_auth_url: Base URL for the Meltano Auth API to authenticate with.
110             app_client_id: Client ID for oauth2 endpoints.
111             id_token: ID token for use in authentication.
112             access_token: Access token for use in authentication.
113             config_path: Path to the config file to use.
114             default_project_id: The ID of the default Meltano Cloud project.
115             default_deployment_name: The name of the default Meltano Cloud
116                 deployment.
117         """
118         self.auth_callback_port = auth_callback_port
119         self.base_url = base_url
120         self.base_auth_url = base_auth_url
121         self.app_client_id = app_client_id
122         self.id_token = id_token
123         self.access_token = access_token
124         self.config_path = (
125             Path(config_path).resolve() if config_path else self.user_config_path()
126         )
127         self.default_project_id = default_project_id
128         self.default_deployment_name = default_deployment_name
129 
130     def __getattribute__(self, name: str) -> str | None:
131         """Get config attribute.
132 
133         Args:
134             name: name of the attribute to get
135 
136         Returns:
137             the attribute value, using env var if set
138         """
139         return os.environ.get(
140             f"{MeltanoCloudConfig.env_var_prefix}{name.upper()}",
141         ) or super().__getattribute__(name)
142 
143     @staticmethod
144     def decode_jwt(token: str) -> dict[str, t.Any]:
145         """Decode a JWT without verifying.
146 
147         Args:
148             token: the jwt to decode
149 
150         Returns:
151             The decoded jwt.
152         """
153         return jwt.decode(token, options={"verify_signature": False})
154 
155     @cached_property
156     def _trks_and_pids(self) -> list[str]:
157         """Get tenant resource keys and project ids from id token.
158 
159         Returns:
160             List of trks and pids in the form '<trk>::<jpid>`
161 
162         """
163         with suppress(jwt.DecodeError):
164             decoded = self.decode_jwt(self.id_token)  # type: ignore
165             trks_and_pids = decoded.get("custom:trk_and_pid")
166             if trks_and_pids:
167                 return [perm.strip() for perm in trks_and_pids.split(",")]
168         return []
169 
170     @property
171     def tenant_resource_keys(self) -> set[str]:
172         """Get the tenant resource keys from the ID token.
173 
174         Returns:
175             The tenant resource keys found in the ID token.
176 
177         """
178         return {perm.split("::")[0] for perm in self._trks_and_pids}
179 
180     @property
181     def internal_project_ids(self) -> set[str]:
182         """Get the internal project IDs from the ID token.
183 
184         Returns:
185             The internal project IDs found in the ID token.
186         """
187         return {perm.split("::")[1] for perm in self._trks_and_pids}
188 
189     @property
190     def internal_project_id(self) -> str:
191         """Get the default Meltano Cloud project ID.
192 
193         Returns:
194             The default Meltano Cloud project ID.
195 
196         Raises:
197             NoMeltanoCloudProjectIDError: when ID token includes no project IDs.
198             MeltanoCloudProjectAmbiguityError: when ID token includes more
199                 than one project ID.
200         """
201         if self.default_project_id:
202             return self.default_project_id
203         if len(self.internal_project_ids) > 1:
204             raise MeltanoCloudProjectAmbiguityError
205         try:
206             pid = next(iter(self.internal_project_ids))
207             self.internal_project_id = pid
208             return pid
209         except StopIteration:  # noqa: WPS329
210             raise NoMeltanoCloudProjectIDError from None
211 
212     @internal_project_id.setter
213     def internal_project_id(self, project_id: str) -> None:
214         """Set the default Meltano Cloud project ID.
215 
216         Args:
217             project_id: The Meltano Cloud project ID that should be used.
218         """
219         self.default_project_id = project_id
220         self.write_to_file()
221 
222     @property
223     def tenant_resource_key(self) -> str:
224         """Get the tenant resource key.
225 
226         Returns:
227             The tenant resource key.
228 
229         Raises:
230             NoMeltanoCloudTenantResourceKeyError: when ID token includes no
231                 tenant resource keys.
232             MeltanoCloudTenantAmbiguityError: when ID token includes more
233                 than one tenant resource key.
234         """
235         if len(self.tenant_resource_keys) > 1:
236             raise MeltanoCloudTenantAmbiguityError
237         try:
238             return next(iter(self.tenant_resource_keys))
239         except StopIteration:  # noqa: WPS329
240             raise NoMeltanoCloudTenantResourceKeyError from None
241 
242     @staticmethod
243     def user_config_path() -> Path:  # noqa: WPS605
244         """Find the path to meltano config file.
245 
246         Returns:
247             The path to the first meltano cloud config file found.
248         """
249         config_dir = Path(platformdirs.user_config_dir("meltano-cloud")).resolve()
250         config_dir.mkdir(parents=True, exist_ok=True)
251         return config_dir / "config.json"
252 
253     @classmethod
254     def from_config_file(cls, config_path: os.PathLike | str) -> MeltanoCloudConfig:
255         """Initialize the configuration from a config file.
256 
257         Args:
258             config_path: Path to the config file.
259 
260         Returns:
261             A MeltanoCloudConfig
262         """
263         with Path(config_path).open(encoding="utf-8") as config_file:
264             return cls(**json.load(config_file), config_path=config_path)
265 
266     @classmethod
267     def find(cls, config_path: os.PathLike | str | None = None) -> MeltanoCloudConfig:
268         """Initialize config from the first config file found.
269 
270         If no config file is found, one with default setting values
271         is created in the user config directory.
272 
273         Args:
274             config_path: the path to the config file to use, if any.
275 
276         Returns:
277             A MeltanoCloudConfig
278         """
279         try:
280             return cls.from_config_file(config_path or cls.user_config_path())
281         except (FileNotFoundError, json.JSONDecodeError):
282             config = cls()
283             config.write_to_file()
284             return config
285 
286     def refresh(self) -> None:
287         """Reread config from config file."""
288         with Path(self.config_path).open(encoding="utf-8") as config_f:
289             self.__dict__.update(json.load(config_f))
290 
291     def write_to_file(self, config_path: Path | None = None) -> None:
292         """Persist this config to its config file.
293 
294         Args:
295             config_path: Path to write config to. Uses self.config_path if not provided.
296         """
297         path_to_write = config_path or self.config_path
298         config_to_write = {
299             key: val
300             for key, val in self.__dict__.items()
301             if not key.startswith("_") and key != "config_path"
302         }
303         # Write the config file with the same permissions that an SSH private
304         # key would typically have, since it contains secrets.
305         with open(
306             path_to_write,
307             "w",
308             opener=lambda path, flags: os.open(path, flags, USER_RW_FILE_MODE),
309         ) as config:
310             json.dump(config_to_write, config, indent=2)
311
```
--- 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/config.py b/src/cloud-cli/meltano/cloud/api/config.py
--- a/src/cloud-cli/meltano/cloud/api/config.py
+++ b/src/cloud-cli/meltano/cloud/api/config.py
@@ -184,7 +184,11 @@
         Returns:
             The internal project IDs found in the ID token.
         """
-        return {perm.split("::")[1] for perm in self._trks_and_pids}
+        # NOTE: The ID 00000000000000000000000000 is used as a placeholder
+        # within the Cognito custom attribute trk_and_pid, because the trk
+        # part is the only significant part.
+        ignore_ids = {"00000000000000000000000000"}
+        return {perm.split("::")[1] for perm in self._trks_and_pids} - ignore_ids
 
     @property
     def internal_project_id(self) -> str:

{"golden_diff": "diff --git a/src/cloud-cli/meltano/cloud/api/config.py b/src/cloud-cli/meltano/cloud/api/config.py\n--- a/src/cloud-cli/meltano/cloud/api/config.py\n+++ b/src/cloud-cli/meltano/cloud/api/config.py\n@@ -184,7 +184,11 @@\n         Returns:\n             The internal project IDs found in the ID token.\n         \"\"\"\n-        return {perm.split(\"::\")[1] for perm in self._trks_and_pids}\n+        # NOTE: The ID 00000000000000000000000000 is used as a placeholder\n+        # within the Cognito custom attribute trk_and_pid, because the trk\n+        # part is the only significant part.\n+        ignore_ids = {\"00000000000000000000000000\"}\n+        return {perm.split(\"::\")[1] for perm in self._trks_and_pids} - ignore_ids\n \n     @property\n     def internal_project_id(self) -> str:\n", "issue": "bug: Cloud project ID of `00000000000000000000000000` is used as real ID\n### Meltano Version\n\ncloud branch\n\n### Python Version\n\nNA\n\n### Bug scope\n\nCLI (options, error messages, logging, etc.)\n\n### Operating System\n\nN/A\n\n### Description\n\n[Context on Slack.](https://meltano.slack.com/archives/C04SHQJB95F/p1686101768535459?thread_ts=1686096202.419359&cid=C04SHQJB95F)\r\n\r\n[Datadog logs for the provisioner API error.](https://app.datadoghq.com/logs?query=service%3Aprovisioner-function-e012747%20&cols=host%2Cservice&event=AgAAAYiTLKwO9bi1IQAAAAAAAAAYAAAAAEFZaVRMTkEyQUFDdlZ2eVphdGk2SEFBRAAAACQAAAAAMDE4ODkzMmYtNDcwNy00YWY4LTgzM2ItNTY4MmQ5MjEwMGFm&index=%2A&messageDisplay=inline&saved-view-id=1860006&stream_sort=desc&viz=stream&from_ts=1686087280625&to_ts=1686101680625&live=true)\r\n\r\nThe project ID of `00000000000000000000000000` should never be used as a real Cloud project ID. It is merely a placeholder that is returned as part of the custom Cognito attribute during login because historically a real project ID was obtained via Cognito.\r\n\r\nIf a project ID of `00000000000000000000000000` is found, it should be replaced by `None` (i.e. `null` in the config JSON document), rather than used for any command.\n\n### Code\n\n_No response_\n", "before_files": [{"content": "\"\"\"Configuration for Meltano Cloud.\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport os\nimport sys\nimport typing as t\nfrom contextlib import suppress\nfrom pathlib import Path\n\nimport jwt\nimport platformdirs\n\nif sys.version_info <= (3, 8):\n    from cached_property import cached_property\nelse:\n    from functools import cached_property\n\nMELTANO_CLOUD_BASE_URL = \"https://internal.api.meltano.cloud/\"\nMELTANO_CLOUD_BASE_AUTH_URL = \"https://auth.meltano.cloud\"\nMELTANO_CLOUD_APP_CLIENT_ID = \"45rpn5ep3g4qjut8jd3s4iq872\"\n\nUSER_RW_FILE_MODE = 0o600\n\n\nclass InvalidMeltanoCloudConfigError(Exception):\n    \"\"\"Provided configuration is invalid.\"\"\"\n\n\nclass MeltanoCloudConfigFileNotFoundError(Exception):\n    \"\"\"Meltano Cloud config file is missing.\"\"\"\n\n\nclass MeltanoCloudTenantAmbiguityError(Exception):\n    \"\"\"Logged in user has access to multiple tenants.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has multiple tenant resource keys. \"\n            \"Set MELTANO_CLOUD_TENANT_RESOURCE_KEY to select one.\",\n        )\n\n\nclass NoMeltanoCloudTenantResourceKeyError(Exception):\n    \"\"\"Logged in user does not have access to any tenants.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has no tenant resource keys. \"\n            \"Reach out to Meltano support to be assigned a tenant resource key.\",\n        )\n\n\nclass MeltanoCloudProjectAmbiguityError(Exception):\n    \"\"\"Logged in user has access to multiple projects.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has multiple projects. \"\n            \"Set one as the default project using the \"\n            \"`meltano cloud project use` command.\",\n        )\n\n\nclass NoMeltanoCloudProjectIDError(Exception):\n    \"\"\"Logged in user does not have any projects.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has no projects. \"\n            \"Set one as the default project using the \"\n            \"`meltano cloud project use` command.\",\n        )\n\n\nclass MeltanoCloudConfig:  # noqa: WPS214 WPS230\n    \"\"\"Configuration for Meltano Cloud client.\"\"\"\n\n    env_var_prefix = \"MELTANO_CLOUD_\"\n\n    def __init__(\n        self,\n        *,\n        auth_callback_port: int = 9999,\n        base_url: str = MELTANO_CLOUD_BASE_URL,\n        base_auth_url: str = MELTANO_CLOUD_BASE_AUTH_URL,\n        app_client_id: str = MELTANO_CLOUD_APP_CLIENT_ID,\n        id_token: str | None = None,\n        access_token: str | None = None,\n        config_path: os.PathLike | str | None = None,\n        default_project_id: str | None = None,\n        default_deployment_name: str | None = None,\n    ):\n        \"\"\"Initialize a MeltanoCloudConfig instance.\n\n        The configuration file is by default stored in the `meltano-cloud`\n        directory in the user's config directory. The directory can be\n        overridden by setting the `MELTANO_CLOUD_CONFIG_DIR` environment\n        variable.\n\n        Args:\n            auth_callback_port: Port to run auth callback server at.\n            base_url: Base URL for Meltano API to interact with.\n            base_auth_url: Base URL for the Meltano Auth API to authenticate with.\n            app_client_id: Client ID for oauth2 endpoints.\n            id_token: ID token for use in authentication.\n            access_token: Access token for use in authentication.\n            config_path: Path to the config file to use.\n            default_project_id: The ID of the default Meltano Cloud project.\n            default_deployment_name: The name of the default Meltano Cloud\n                deployment.\n        \"\"\"\n        self.auth_callback_port = auth_callback_port\n        self.base_url = base_url\n        self.base_auth_url = base_auth_url\n        self.app_client_id = app_client_id\n        self.id_token = id_token\n        self.access_token = access_token\n        self.config_path = (\n            Path(config_path).resolve() if config_path else self.user_config_path()\n        )\n        self.default_project_id = default_project_id\n        self.default_deployment_name = default_deployment_name\n\n    def __getattribute__(self, name: str) -> str | None:\n        \"\"\"Get config attribute.\n\n        Args:\n            name: name of the attribute to get\n\n        Returns:\n            the attribute value, using env var if set\n        \"\"\"\n        return os.environ.get(\n            f\"{MeltanoCloudConfig.env_var_prefix}{name.upper()}\",\n        ) or super().__getattribute__(name)\n\n    @staticmethod\n    def decode_jwt(token: str) -> dict[str, t.Any]:\n        \"\"\"Decode a JWT without verifying.\n\n        Args:\n            token: the jwt to decode\n\n        Returns:\n            The decoded jwt.\n        \"\"\"\n        return jwt.decode(token, options={\"verify_signature\": False})\n\n    @cached_property\n    def _trks_and_pids(self) -> list[str]:\n        \"\"\"Get tenant resource keys and project ids from id token.\n\n        Returns:\n            List of trks and pids in the form '<trk>::<jpid>`\n\n        \"\"\"\n        with suppress(jwt.DecodeError):\n            decoded = self.decode_jwt(self.id_token)  # type: ignore\n            trks_and_pids = decoded.get(\"custom:trk_and_pid\")\n            if trks_and_pids:\n                return [perm.strip() for perm in trks_and_pids.split(\",\")]\n        return []\n\n    @property\n    def tenant_resource_keys(self) -> set[str]:\n        \"\"\"Get the tenant resource keys from the ID token.\n\n        Returns:\n            The tenant resource keys found in the ID token.\n\n        \"\"\"\n        return {perm.split(\"::\")[0] for perm in self._trks_and_pids}\n\n    @property\n    def internal_project_ids(self) -> set[str]:\n        \"\"\"Get the internal project IDs from the ID token.\n\n        Returns:\n            The internal project IDs found in the ID token.\n        \"\"\"\n        return {perm.split(\"::\")[1] for perm in self._trks_and_pids}\n\n    @property\n    def internal_project_id(self) -> str:\n        \"\"\"Get the default Meltano Cloud project ID.\n\n        Returns:\n            The default Meltano Cloud project ID.\n\n        Raises:\n            NoMeltanoCloudProjectIDError: when ID token includes no project IDs.\n            MeltanoCloudProjectAmbiguityError: when ID token includes more\n                than one project ID.\n        \"\"\"\n        if self.default_project_id:\n            return self.default_project_id\n        if len(self.internal_project_ids) > 1:\n            raise MeltanoCloudProjectAmbiguityError\n        try:\n            pid = next(iter(self.internal_project_ids))\n            self.internal_project_id = pid\n            return pid\n        except StopIteration:  # noqa: WPS329\n            raise NoMeltanoCloudProjectIDError from None\n\n    @internal_project_id.setter\n    def internal_project_id(self, project_id: str) -> None:\n        \"\"\"Set the default Meltano Cloud project ID.\n\n        Args:\n            project_id: The Meltano Cloud project ID that should be used.\n        \"\"\"\n        self.default_project_id = project_id\n        self.write_to_file()\n\n    @property\n    def tenant_resource_key(self) -> str:\n        \"\"\"Get the tenant resource key.\n\n        Returns:\n            The tenant resource key.\n\n        Raises:\n            NoMeltanoCloudTenantResourceKeyError: when ID token includes no\n                tenant resource keys.\n            MeltanoCloudTenantAmbiguityError: when ID token includes more\n                than one tenant resource key.\n        \"\"\"\n        if len(self.tenant_resource_keys) > 1:\n            raise MeltanoCloudTenantAmbiguityError\n        try:\n            return next(iter(self.tenant_resource_keys))\n        except StopIteration:  # noqa: WPS329\n            raise NoMeltanoCloudTenantResourceKeyError from None\n\n    @staticmethod\n    def user_config_path() -> Path:  # noqa: WPS605\n        \"\"\"Find the path to meltano config file.\n\n        Returns:\n            The path to the first meltano cloud config file found.\n        \"\"\"\n        config_dir = Path(platformdirs.user_config_dir(\"meltano-cloud\")).resolve()\n        config_dir.mkdir(parents=True, exist_ok=True)\n        return config_dir / \"config.json\"\n\n    @classmethod\n    def from_config_file(cls, config_path: os.PathLike | str) -> MeltanoCloudConfig:\n        \"\"\"Initialize the configuration from a config file.\n\n        Args:\n            config_path: Path to the config file.\n\n        Returns:\n            A MeltanoCloudConfig\n        \"\"\"\n        with Path(config_path).open(encoding=\"utf-8\") as config_file:\n            return cls(**json.load(config_file), config_path=config_path)\n\n    @classmethod\n    def find(cls, config_path: os.PathLike | str | None = None) -> MeltanoCloudConfig:\n        \"\"\"Initialize config from the first config file found.\n\n        If no config file is found, one with default setting values\n        is created in the user config directory.\n\n        Args:\n            config_path: the path to the config file to use, if any.\n\n        Returns:\n            A MeltanoCloudConfig\n        \"\"\"\n        try:\n            return cls.from_config_file(config_path or cls.user_config_path())\n        except (FileNotFoundError, json.JSONDecodeError):\n            config = cls()\n            config.write_to_file()\n            return config\n\n    def refresh(self) -> None:\n        \"\"\"Reread config from config file.\"\"\"\n        with Path(self.config_path).open(encoding=\"utf-8\") as config_f:\n            self.__dict__.update(json.load(config_f))\n\n    def write_to_file(self, config_path: Path | None = None) -> None:\n        \"\"\"Persist this config to its config file.\n\n        Args:\n            config_path: Path to write config to. Uses self.config_path if not provided.\n        \"\"\"\n        path_to_write = config_path or self.config_path\n        config_to_write = {\n            key: val\n            for key, val in self.__dict__.items()\n            if not key.startswith(\"_\") and key != \"config_path\"\n        }\n        # Write the config file with the same permissions that an SSH private\n        # key would typically have, since it contains secrets.\n        with open(\n            path_to_write,\n            \"w\",\n            opener=lambda path, flags: os.open(path, flags, USER_RW_FILE_MODE),\n        ) as config:\n            json.dump(config_to_write, config, indent=2)\n", "path": "src/cloud-cli/meltano/cloud/api/config.py"}], "after_files": [{"content": "\"\"\"Configuration for Meltano Cloud.\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport os\nimport sys\nimport typing as t\nfrom contextlib import suppress\nfrom pathlib import Path\n\nimport jwt\nimport platformdirs\n\nif sys.version_info <= (3, 8):\n    from cached_property import cached_property\nelse:\n    from functools import cached_property\n\nMELTANO_CLOUD_BASE_URL = \"https://internal.api.meltano.cloud/\"\nMELTANO_CLOUD_BASE_AUTH_URL = \"https://auth.meltano.cloud\"\nMELTANO_CLOUD_APP_CLIENT_ID = \"45rpn5ep3g4qjut8jd3s4iq872\"\n\nUSER_RW_FILE_MODE = 0o600\n\n\nclass InvalidMeltanoCloudConfigError(Exception):\n    \"\"\"Provided configuration is invalid.\"\"\"\n\n\nclass MeltanoCloudConfigFileNotFoundError(Exception):\n    \"\"\"Meltano Cloud config file is missing.\"\"\"\n\n\nclass MeltanoCloudTenantAmbiguityError(Exception):\n    \"\"\"Logged in user has access to multiple tenants.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has multiple tenant resource keys. \"\n            \"Set MELTANO_CLOUD_TENANT_RESOURCE_KEY to select one.\",\n        )\n\n\nclass NoMeltanoCloudTenantResourceKeyError(Exception):\n    \"\"\"Logged in user does not have access to any tenants.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has no tenant resource keys. \"\n            \"Reach out to Meltano support to be assigned a tenant resource key.\",\n        )\n\n\nclass MeltanoCloudProjectAmbiguityError(Exception):\n    \"\"\"Logged in user has access to multiple projects.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has multiple projects. \"\n            \"Set one as the default project using the \"\n            \"`meltano cloud project use` command.\",\n        )\n\n\nclass NoMeltanoCloudProjectIDError(Exception):\n    \"\"\"Logged in user does not have any projects.\"\"\"\n\n    def __init__(self) -> None:\n        \"\"\"Initialize exception with message.\"\"\"\n        super().__init__(\n            \"Logged in Meltano user has no projects. \"\n            \"Set one as the default project using the \"\n            \"`meltano cloud project use` command.\",\n        )\n\n\nclass MeltanoCloudConfig:  # noqa: WPS214 WPS230\n    \"\"\"Configuration for Meltano Cloud client.\"\"\"\n\n    env_var_prefix = \"MELTANO_CLOUD_\"\n\n    def __init__(\n        self,\n        *,\n        auth_callback_port: int = 9999,\n        base_url: str = MELTANO_CLOUD_BASE_URL,\n        base_auth_url: str = MELTANO_CLOUD_BASE_AUTH_URL,\n        app_client_id: str = MELTANO_CLOUD_APP_CLIENT_ID,\n        id_token: str | None = None,\n        access_token: str | None = None,\n        config_path: os.PathLike | str | None = None,\n        default_project_id: str | None = None,\n        default_deployment_name: str | None = None,\n    ):\n        \"\"\"Initialize a MeltanoCloudConfig instance.\n\n        The configuration file is by default stored in the `meltano-cloud`\n        directory in the user's config directory. The directory can be\n        overridden by setting the `MELTANO_CLOUD_CONFIG_DIR` environment\n        variable.\n\n        Args:\n            auth_callback_port: Port to run auth callback server at.\n            base_url: Base URL for Meltano API to interact with.\n            base_auth_url: Base URL for the Meltano Auth API to authenticate with.\n            app_client_id: Client ID for oauth2 endpoints.\n            id_token: ID token for use in authentication.\n            access_token: Access token for use in authentication.\n            config_path: Path to the config file to use.\n            default_project_id: The ID of the default Meltano Cloud project.\n            default_deployment_name: The name of the default Meltano Cloud\n                deployment.\n        \"\"\"\n        self.auth_callback_port = auth_callback_port\n        self.base_url = base_url\n        self.base_auth_url = base_auth_url\n        self.app_client_id = app_client_id\n        self.id_token = id_token\n        self.access_token = access_token\n        self.config_path = (\n            Path(config_path).resolve() if config_path else self.user_config_path()\n        )\n        self.default_project_id = default_project_id\n        self.default_deployment_name = default_deployment_name\n\n    def __getattribute__(self, name: str) -> str | None:\n        \"\"\"Get config attribute.\n\n        Args:\n            name: name of the attribute to get\n\n        Returns:\n            the attribute value, using env var if set\n        \"\"\"\n        return os.environ.get(\n            f\"{MeltanoCloudConfig.env_var_prefix}{name.upper()}\",\n        ) or super().__getattribute__(name)\n\n    @staticmethod\n    def decode_jwt(token: str) -> dict[str, t.Any]:\n        \"\"\"Decode a JWT without verifying.\n\n        Args:\n            token: the jwt to decode\n\n        Returns:\n            The decoded jwt.\n        \"\"\"\n        return jwt.decode(token, options={\"verify_signature\": False})\n\n    @cached_property\n    def _trks_and_pids(self) -> list[str]:\n        \"\"\"Get tenant resource keys and project ids from id token.\n\n        Returns:\n            List of trks and pids in the form '<trk>::<jpid>`\n\n        \"\"\"\n        with suppress(jwt.DecodeError):\n            decoded = self.decode_jwt(self.id_token)  # type: ignore\n            trks_and_pids = decoded.get(\"custom:trk_and_pid\")\n            if trks_and_pids:\n                return [perm.strip() for perm in trks_and_pids.split(\",\")]\n        return []\n\n    @property\n    def tenant_resource_keys(self) -> set[str]:\n        \"\"\"Get the tenant resource keys from the ID token.\n\n        Returns:\n            The tenant resource keys found in the ID token.\n\n        \"\"\"\n        return {perm.split(\"::\")[0] for perm in self._trks_and_pids}\n\n    @property\n    def internal_project_ids(self) -> set[str]:\n        \"\"\"Get the internal project IDs from the ID token.\n\n        Returns:\n            The internal project IDs found in the ID token.\n        \"\"\"\n        # NOTE: The ID 00000000000000000000000000 is used as a placeholder\n        # within the Cognito custom attribute trk_and_pid, because the trk\n        # part is the only significant part.\n        ignore_ids = {\"00000000000000000000000000\"}\n        return {perm.split(\"::\")[1] for perm in self._trks_and_pids} - ignore_ids\n\n    @property\n    def internal_project_id(self) -> str:\n        \"\"\"Get the default Meltano Cloud project ID.\n\n        Returns:\n            The default Meltano Cloud project ID.\n\n        Raises:\n            NoMeltanoCloudProjectIDError: when ID token includes no project IDs.\n            MeltanoCloudProjectAmbiguityError: when ID token includes more\n                than one project ID.\n        \"\"\"\n        if self.default_project_id:\n            return self.default_project_id\n        if len(self.internal_project_ids) > 1:\n            raise MeltanoCloudProjectAmbiguityError\n        try:\n            pid = next(iter(self.internal_project_ids))\n            self.internal_project_id = pid\n            return pid\n        except StopIteration:  # noqa: WPS329\n            raise NoMeltanoCloudProjectIDError from None\n\n    @internal_project_id.setter\n    def internal_project_id(self, project_id: str) -> None:\n        \"\"\"Set the default Meltano Cloud project ID.\n\n        Args:\n            project_id: The Meltano Cloud project ID that should be used.\n        \"\"\"\n        self.default_project_id = project_id\n        self.write_to_file()\n\n    @property\n    def tenant_resource_key(self) -> str:\n        \"\"\"Get the tenant resource key.\n\n        Returns:\n            The tenant resource key.\n\n        Raises:\n            NoMeltanoCloudTenantResourceKeyError: when ID token includes no\n                tenant resource keys.\n            MeltanoCloudTenantAmbiguityError: when ID token includes more\n                than one tenant resource key.\n        \"\"\"\n        if len(self.tenant_resource_keys) > 1:\n            raise MeltanoCloudTenantAmbiguityError\n        try:\n            return next(iter(self.tenant_resource_keys))\n        except StopIteration:  # noqa: WPS329\n            raise NoMeltanoCloudTenantResourceKeyError from None\n\n    @staticmethod\n    def user_config_path() -> Path:  # noqa: WPS605\n        \"\"\"Find the path to meltano config file.\n\n        Returns:\n            The path to the first meltano cloud config file found.\n        \"\"\"\n        config_dir = Path(platformdirs.user_config_dir(\"meltano-cloud\")).resolve()\n        config_dir.mkdir(parents=True, exist_ok=True)\n        return config_dir / \"config.json\"\n\n    @classmethod\n    def from_config_file(cls, config_path: os.PathLike | str) -> MeltanoCloudConfig:\n        \"\"\"Initialize the configuration from a config file.\n\n        Args:\n            config_path: Path to the config file.\n\n        Returns:\n            A MeltanoCloudConfig\n        \"\"\"\n        with Path(config_path).open(encoding=\"utf-8\") as config_file:\n            return cls(**json.load(config_file), config_path=config_path)\n\n    @classmethod\n    def find(cls, config_path: os.PathLike | str | None = None) -> MeltanoCloudConfig:\n        \"\"\"Initialize config from the first config file found.\n\n        If no config file is found, one with default setting values\n        is created in the user config directory.\n\n        Args:\n            config_path: the path to the config file to use, if any.\n\n        Returns:\n            A MeltanoCloudConfig\n        \"\"\"\n        try:\n            return cls.from_config_file(config_path or cls.user_config_path())\n        except (FileNotFoundError, json.JSONDecodeError):\n            config = cls()\n            config.write_to_file()\n            return config\n\n    def refresh(self) -> None:\n        \"\"\"Reread config from config file.\"\"\"\n        with Path(self.config_path).open(encoding=\"utf-8\") as config_f:\n            self.__dict__.update(json.load(config_f))\n\n    def write_to_file(self, config_path: Path | None = None) -> None:\n        \"\"\"Persist this config to its config file.\n\n        Args:\n            config_path: Path to write config to. Uses self.config_path if not provided.\n        \"\"\"\n        path_to_write = config_path or self.config_path\n        config_to_write = {\n            key: val\n            for key, val in self.__dict__.items()\n            if not key.startswith(\"_\") and key != \"config_path\"\n        }\n        # Write the config file with the same permissions that an SSH private\n        # key would typically have, since it contains secrets.\n        with open(\n            path_to_write,\n            \"w\",\n            opener=lambda path, flags: os.open(path, flags, USER_RW_FILE_MODE),\n        ) as config:\n            json.dump(config_to_write, config, indent=2)\n", "path": "src/cloud-cli/meltano/cloud/api/config.py"}]}