Repository: TobikoData/sqlmesh
Pull request #4211 (2025-04-22 04:17:11)

Feat!: First-class support for calling python macros from jinja context
This allows users to write Python macros and then easily call them from Jinja, including Jinja macros.


Patch:
diff --git a/docs/guides/multi_engine.md b/docs/guides/multi_engine.md
index ad4a8a3c..f22187f2 100644
--- a/docs/guides/multi_engine.md
+++ b/docs/guides/multi_engine.md
@@ -137,106 +137,106 @@ First, add the connections to your configuration and set the `gateway_managed_vi
 
 === "YAML"
 
-```yaml linenums="1" hl_lines="30"
-gateways:
-  redshift:
-    connection:
-      type: redshift
-      user: <redshift_user>
-      password: <redshift_password>
-      host: <redshift_host>
-      database: <redshift_database>
-    variables:
-      gw_var: 'redshift'
-  athena:
-    connection:
-      type: athena
-      aws_access_key_id: <athena_aws_access_key_id>
-      aws_secret_access_key: <athena_aws_secret_access_key>
-      s3_warehouse_location: <athena_s3_warehouse_location>
-    variables:
-      gw_var: 'athena'
-  snowflake:
-    connection:
-      type: snowflake
-      account: <snowflake_account>
-      user: <snowflake_user>
-      database: <snowflake_database>
-      warehouse: <snowflake_warehouse>
-    variables:
-      gw_var: 'snowflake'
+    ```yaml linenums="1" hl_lines="30"
+    gateways:
+      redshift:
+        connection:
+          type: redshift
+          user: <redshift_user>
+          password: <redshift_password>
+          host: <redshift_host>
+          database: <redshift_database>
+        variables:
+          gw_var: 'redshift'
+      athena:
+        connection:
+          type: athena
+          aws_access_key_id: <athena_aws_access_key_id>
+          aws_secret_access_key: <athena_aws_secret_access_key>
+          s3_warehouse_location: <athena_s3_warehouse_location>
+        variables:
+          gw_var: 'athena'
+      snowflake:
+        connection:
+          type: snowflake
+          account: <snowflake_account>
+          user: <snowflake_user>
+          database: <snowflake_database>
+          warehouse: <snowflake_warehouse>
+        variables:
+          gw_var: 'snowflake'
 
-default_gateway: redshift
-gateway_managed_virtual_layer: true
+    default_gateway: redshift
+    gateway_managed_virtual_layer: true
 
-variables:
-  gw_var: 'global'
-  global_var: 5
-```
+    variables:
+      gw_var: 'global'
+      global_var: 5
+    ```
 
 === "Python"
 
-```python linenums="1" hl_lines="48"
-from sqlmesh.core.config import (
-    Config,
-    ModelDefaultsConfig,
-    GatewayConfig,
-    RedshiftConnectionConfig,
-    AthenaConnectionConfig,
-    SnowflakeConnectionConfig,
-)
-
-config = Config(
-    model_defaults=ModelDefaultsConfig(dialect="redshift"),
-    gateways={
-        "redshift": GatewayConfig(
-            connection=RedshiftConnectionConfig(
-                user="<redshift_user>",
-                password="<redshift_password>",
-                host="<redshift_host>",
-                database="<redshift_database>",
+    ```python linenums="1" hl_lines="48"
+    from sqlmesh.core.config import (
+        Config,
+        ModelDefaultsConfig,
+        GatewayConfig,
+        RedshiftConnectionConfig,
+        AthenaConnectionConfig,
+        SnowflakeConnectionConfig,
+    )
+
+    config = Config(
+        model_defaults=ModelDefaultsConfig(dialect="redshift"),
+        gateways={
+            "redshift": GatewayConfig(
+                connection=RedshiftConnectionConfig(
+                    user="<redshift_user>",
+                    password="<redshift_password>",
+                    host="<redshift_host>",
+                    database="<redshift_database>",
+                ),
+                variables={
+                    "gw_var": "redshift"
+                },
             ),
-            variables={
-                "gw_var": "redshift"
-            },
-        ),
-        "athena": GatewayConfig(
-            connection=AthenaConnectionConfig(
-                aws_access_key_id="<athena_aws_access_key_id>",
-                aws_secret_access_key="<athena_aws_secret_access_key>",
-                region_name="<athena_region_name>",
-                s3_warehouse_location="<athena_s3_warehouse_location>",
+            "athena": GatewayConfig(
+                connection=AthenaConnectionConfig(
+                    aws_access_key_id="<athena_aws_access_key_id>",
+                    aws_secret_access_key="<athena_aws_secret_access_key>",
+                    region_name="<athena_region_name>",
+                    s3_warehouse_location="<athena_s3_warehouse_location>",
+                ),
+                variables={
+                    "gw_var": "athena"
+                },
             ),
-            variables={
-                "gw_var": "athena"
-            },
-        ),
-        "snowflake": GatewayConfig(
-            connection=SnowflakeConnectionConfig(
-                account="<snowflake_account>",
-                user="<snowflake_user>",
-                database="<snowflake_database>",
-                warehouse="<snowflake_warehouse>",
+            "snowflake": GatewayConfig(
+                connection=SnowflakeConnectionConfig(
+                    account="<snowflake_account>",
+                    user="<snowflake_user>",
+                    database="<snowflake_database>",
+                    warehouse="<snowflake_warehouse>",
+                ),
+                variables={
+                    "gw_var": "snowflake"
+                },
             ),
-            variables={
-                "gw_var": "snowflake"
-            },
-        ),
-    },
-    default_gateway="redshift",
-    gateway_managed_virtual_layer=True,
-    variables={
-        "gw_var": "global",
-        "global_var": 5,
-    },
-)
-```
+        },
+        default_gateway="redshift",
+        gateway_managed_virtual_layer=True,
+        variables={
+            "gw_var": "global",
+            "global_var": 5,
+        },
+    )
+    ```
 
 Note that gateway-specific variables take precedence over global ones. In the example above, the `gw_var` used in a model will resolve to the value specified in the model's gateway.
 
 For further customization, you can also enable [gateway-specific model defaults](../guides/configuration.md#gateway-specific-model-defaults). This allows you to define custom behaviors, such as specifying a dialect with case-insensitivity normalization.
 
-The default gateway is `redshift` In the example configuration above, so all models without a `gateway` specification will run on redshift, as in this `order_dates` model:
+In the example configuration above the default gateway is `redshift`, so all models without a `gateway` specification will run on redshift, as in this `order_dates` model:
 
 ```sql linenums="1"
 MODEL (
diff --git a/pyproject.toml b/pyproject.toml
index c1485a77..4c484c49 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,7 +21,7 @@ dependencies = [
     "requests",
     "rich[jupyter]",
     "ruamel.yaml",
-    "sqlglot[rs]~=26.13.2",
+    "sqlglot[rs]~=26.16.0",
     "tenacity",
     "time-machine",
     "json-stream"
diff --git a/sqlmesh/core/macros.py b/sqlmesh/core/macros.py
index beca4ea7..88728457 100644
--- a/sqlmesh/core/macros.py
+++ b/sqlmesh/core/macros.py
@@ -150,10 +150,9 @@ class MacroEvaluator:
         self,
         dialect: DialectType = "",
         python_env: t.Optional[t.Dict[str, Executable]] = None,
-        jinja_env: t.Optional[Environment] = None,
         schema: t.Optional[MappingSchema] = None,
         runtime_stage: RuntimeStage = RuntimeStage.LOADING,
-        resolve_table: t.Optional[t.Callable[[str | exp.Expression], str]] = None,
+        resolve_table: t.Optional[t.Callable[[str | exp.Table], str]] = None,
         resolve_tables: t.Optional[t.Callable[[exp.Expression], exp.Expression]] = None,
         snapshots: t.Optional[t.Dict[str, Snapshot]] = None,
         default_catalog: t.Optional[str] = None,
@@ -177,7 +176,7 @@ class MacroEvaluator:
         self.columns_to_types_called = False
         self.default_catalog = default_catalog
 
-        self._jinja_env: t.Optional[Environment] = jinja_env
+        self._jinja_env: t.Optional[Environment] = None
         self._schema = schema
         self._resolve_table = resolve_table
         self._resolve_tables = resolve_tables
@@ -431,7 +430,7 @@ class MacroEvaluator:
             )
         )
 
-    def resolve_table(self, table: str | exp.Expression) -> str:
+    def resolve_table(self, table: str | exp.Table) -> str:
         """Gets the physical table name for a given model."""
         if not self._resolve_table:
             raise SQLMeshError(
diff --git a/sqlmesh/core/model/definition.py b/sqlmesh/core/model/definition.py
index ffd6b4e4..1b6ac7c5 100644
--- a/sqlmesh/core/model/definition.py
+++ b/sqlmesh/core/model/definition.py
@@ -2476,9 +2476,7 @@ def _create_model(
     for _, kwargs in model.signals:
         statements.extend((signal_kwarg, True) for signal_kwarg in kwargs.values())
 
-    python_env = python_env or {}
-
-    make_python_env(
+    python_env = make_python_env(
         statements,
         jinja_macro_references,
         module_path,
diff --git a/sqlmesh/core/renderer.py b/sqlmesh/core/renderer.py
index 316e0d5e..fee9dcb5 100644
--- a/sqlmesh/core/renderer.py
+++ b/sqlmesh/core/renderer.py
@@ -3,6 +3,7 @@ from __future__ import annotations
 import logging
 import typing as t
 from contextlib import contextmanager
+from functools import partial
 from pathlib import Path
 
 from sqlglot import exp, parse
@@ -134,7 +135,35 @@ class BaseExpressionRenderer:
         if this_snapshot and (kind := this_snapshot.model_kind_name):
             kwargs["model_kind_name"] = kind.name
 
-        expressions = [self._expression]
+        def _resolve_table(table: str | exp.Table) -> str:
+            return self._resolve_table(
+                d.normalize_model_name(table, self._default_catalog, self._dialect),
+                snapshots=snapshots,
+                table_mapping=table_mapping,
+                deployability_index=deployability_index,
+            ).sql(dialect=self._dialect, identify=True, comments=False)
+
+        macro_evaluator = MacroEvaluator(
+            self._dialect,
+            python_env=self._python_env,
+            schema=self.schema,
+            runtime_stage=runtime_stage,
+            resolve_table=_resolve_table,
+            resolve_tables=lambda e: self._resolve_tables(
+                e,
+                snapshots=snapshots,
+                table_mapping=table_mapping,
+                deployability_index=deployability_index,
+                start=start,
+                end=end,
+                execution_time=execution_time,
+                runtime_stage=runtime_stage,
+            ),
+            snapshots=snapshots,
+            default_catalog=self._default_catalog,
+            path=self._path,
+            environment_naming_info=environment_naming_info,
+        )
 
         start_time, end_time = (
             make_inclusive(start or c.EPOCH, end or c.EPOCH, self._dialect)
@@ -153,18 +182,17 @@ class BaseExpressionRenderer:
 
         variables = kwargs.pop("variables", {})
         jinja_env_kwargs = {
-            **{**render_kwargs, **prepare_env(self._python_env), **variables},
+            **{
+                **render_kwargs,
+                **_prepare_python_env_for_jinja(macro_evaluator, self._python_env),
+                **variables,
+            },
             "snapshots": snapshots or {},
             "table_mapping": table_mapping,
             "deployability_index": deployability_index,
             "default_catalog": self._default_catalog,
             "runtime_stage": runtime_stage.value,
-            "resolve_table": lambda table: self._resolve_table(
-                d.normalize_model_name(table, self._default_catalog, self._dialect),
-                snapshots=snapshots,
-                table_mapping=table_mapping,
-                deployability_index=deployability_index,
-            ).sql(dialect=self._dialect, identify=True, comments=False),
+            "resolve_table": _resolve_table,
         }
         if this_model:
             render_kwargs["this_model"] = this_model
@@ -174,6 +202,7 @@ class BaseExpressionRenderer:
 
         jinja_env = self._jinja_macro_registry.build_environment(**jinja_env_kwargs)
 
+        expressions = [self._expression]
         if isinstance(self._expression, d.Jinja):
             try:
                 expressions = []
@@ -190,29 +219,6 @@ class BaseExpressionRenderer:
                     f"Could not render or parse jinja at '{self._path}'.\n{ex}"
                 ) from ex
 
-        macro_evaluator = MacroEvaluator(
-            self._dialect,
-            python_env=self._python_env,
-            jinja_env=jinja_env,
-            schema=self.schema,
-            runtime_stage=runtime_stage,
-            resolve_table=jinja_env.globals["resolve_table"],  # type: ignore
-            resolve_tables=lambda e: self._resolve_tables(
-                e,
-                snapshots=snapshots,
-                table_mapping=table_mapping,
-                deployability_index=deployability_index,
-                start=start,
-                end=end,
-                execution_time=execution_time,
-                runtime_stage=runtime_stage,
-            ),
-            snapshots=snapshots,
-            default_catalog=self._default_catalog,
-            path=self._path,
-            environment_naming_info=environment_naming_info,
-        )
-
         macro_evaluator.locals.update(render_kwargs)
 
         if variables:
@@ -637,3 +643,15 @@ class QueryRenderer(BaseExpressionRenderer):
                 annotate_types(select)
 
         return query
+
+
+def _prepare_python_env_for_jinja(
+    evaluator: MacroEvaluator,
+    python_env: t.Dict[str, Executable],
+) -> t.Dict[str, t.Any]:
+    prepared_env = prepare_env(python_env)
+    # Pass the evaluator to all macro functions
+    return {
+        key: partial(value, evaluator) if callable(value) else value
+        for key, value in prepared_env.items()
+    }
diff --git a/vscode/extension/eslint.config.mjs b/vscode/extension/eslint.config.mjs
index 5b14c4d2..d8d2c998 100644
--- a/vscode/extension/eslint.config.mjs
+++ b/vscode/extension/eslint.config.mjs
@@ -20,9 +20,9 @@ export default [{
             format: ["camelCase", "PascalCase"],
         }],
 
-        curly: "warn",
-        eqeqeq: "warn",
-        "no-throw-literal": "warn",
+        curly: "error",
+        eqeqeq: "error",
+        "no-throw-literal": "error",
         semi: ["error", "never"]
     },
 }];
\ No newline at end of file
diff --git a/vscode/extension/package-lock.json b/vscode/extension/package-lock.json
index 16e4ac0d..4756a10b 100644
--- a/vscode/extension/package-lock.json
+++ b/vscode/extension/package-lock.json
@@ -127,9 +127,9 @@
       }
     },
     "node_modules/@azure/identity": {
-      "version": "4.8.0",
-      "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.8.0.tgz",
-      "integrity": "sha512-l9ALUGHtFB/JfsqmA+9iYAp2a+cCwdNO/cyIr2y7nJLJsz1aae6qVP8XxT7Kbudg0IQRSIMXj0+iivFdbD1xPA==",
+      "version": "4.9.1",
+      "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.9.1.tgz",
+      "integrity": "sha512-986D7Cf1AOwYqSDtO/FnMAyk/Jc8qpftkGsxuehoh4F85MhQ4fICBGX/44+X1y78lN4Sqib3Bsoaoh/FvOGgmg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -141,11 +141,8 @@
         "@azure/core-util": "^1.11.0",
         "@azure/logger": "^1.0.0",
         "@azure/msal-browser": "^4.2.0",
-        "@azure/msal-node": "^3.2.3",
-        "events": "^3.0.0",
-        "jws": "^4.0.0",
+        "@azure/msal-node": "^3.5.0",
         "open": "^10.1.0",
-        "stoppable": "^1.1.0",
         "tslib": "^2.2.0"
       },
       "engines": {
@@ -166,22 +163,22 @@
       }
     },
     "node_modules/@azure/msal-browser": {
-      "version": "4.10.0",
-      "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.10.0.tgz",
-      "integrity": "sha512-48X2VwOtHk8A1CI00E8tAqko0+3qQh53u5bOPySzdojL3T/Ad4GgRnN0c0oLJ1/PcTm4D4QybHYG3LBOX0l3/g==",
+      "version": "4.11.0",
+      "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.11.0.tgz",
+      "integrity": "sha512-0p5Ut3wORMP+975AKvaSPIO4UytgsfAvJ7RxaTx+nkP+Hpkmm93AuiMkBWKI2x9tApU/SLgIyPz/ZwLYUIWb5Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@azure/msal-common": "15.5.0"
+        "@azure/msal-common": "15.5.1"
       },
       "engines": {
         "node": ">=0.8.0"
       }
     },
     "node_modules/@azure/msal-common": {
-      "version": "15.5.0",
-      "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.5.0.tgz",
-      "integrity": "sha512-u97AJ6m4PB24/Plms9e9iydRcOaxxrHWkan1px5GeWGJfakY1D/r1DmY1+Typ8zWC/5JbNzH1GYpXrorPymz5g==",
+      "version": "15.5.1",
+      "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.5.1.tgz",
+      "integrity": "sha512-oxK0khbc4Bg1bKQnqDr7ikULhVL2OHgSrIq0Vlh4b6+hm4r0lr6zPMQE8ZvmacJuh+ZZGKBM5iIObhF1q1QimQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -189,13 +186,13 @@
       }
     },
     "node_modules/@azure/msal-node": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.5.0.tgz",
-      "integrity": "sha512-9cLUmcOZ5FODz3uAhS2C9A1U7xDUTCHVcaNQBYpOd5qCKdKM6ft/ydAfw27vEntuaDgnh5jytOAKsEzEbtoQ1Q==",
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.5.1.tgz",
+      "integrity": "sha512-dkgMYM5B6tI88r/oqf5bYd93WkenQpaWwiszJDk7avVjso8cmuKRTW97dA1RMi6RhihZFLtY1VtWxU9+sW2T5g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@azure/msal-common": "15.5.0",
+        "@azure/msal-common": "15.5.1",
         "jsonwebtoken": "^9.0.0",
         "uuid": "^8.3.0"
       },
@@ -636,9 +633,9 @@
       }
     },
     "node_modules/@eslint-community/eslint-utils": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz",
-      "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==",
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz",
+      "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -714,9 +711,9 @@
       }
     },
     "node_modules/@eslint/core": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
-      "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+      "version": "0.13.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
+      "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
       "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
@@ -775,9 +772,9 @@
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.24.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz",
-      "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==",
+      "version": "9.25.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
+      "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -808,19 +805,6 @@
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
-    "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
-      "version": "0.13.0",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
-      "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@types/json-schema": "^7.0.15"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      }
-    },
     "node_modules/@humanfs/core": {
       "version": "0.19.1",
       "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -1119,17 +1103,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz",
-      "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz",
+      "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.30.1",
-        "@typescript-eslint/type-utils": "8.30.1",
-        "@typescript-eslint/utils": "8.30.1",
-        "@typescript-eslint/visitor-keys": "8.30.1",
+        "@typescript-eslint/scope-manager": "8.31.0",
+        "@typescript-eslint/type-utils": "8.31.0",
+        "@typescript-eslint/utils": "8.31.0",
+        "@typescript-eslint/visitor-keys": "8.31.0",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
@@ -1149,16 +1133,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz",
-      "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz",
+      "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.30.1",
-        "@typescript-eslint/types": "8.30.1",
-        "@typescript-eslint/typescript-estree": "8.30.1",
-        "@typescript-eslint/visitor-keys": "8.30.1",
+        "@typescript-eslint/scope-manager": "8.31.0",
+        "@typescript-eslint/types": "8.31.0",
+        "@typescript-eslint/typescript-estree": "8.31.0",
+        "@typescript-eslint/visitor-keys": "8.31.0",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -1174,14 +1158,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz",
-      "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz",
+      "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.30.1",
-        "@typescript-eslint/visitor-keys": "8.30.1"
+        "@typescript-eslint/types": "8.31.0",
+        "@typescript-eslint/visitor-keys": "8.31.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1192,14 +1176,14 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz",
-      "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz",
+      "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.30.1",
-        "@typescript-eslint/utils": "8.30.1",
+        "@typescript-eslint/typescript-estree": "8.31.0",
+        "@typescript-eslint/utils": "8.31.0",
         "debug": "^4.3.4",
         "ts-api-utils": "^2.0.1"
       },
@@ -1216,9 +1200,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz",
-      "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz",
+      "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -1230,14 +1214,14 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz",
-      "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz",
+      "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.30.1",
-        "@typescript-eslint/visitor-keys": "8.30.1",
+        "@typescript-eslint/types": "8.31.0",
+        "@typescript-eslint/visitor-keys": "8.31.0",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
@@ -1257,16 +1241,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz",
-      "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz",
+      "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.30.1",
-        "@typescript-eslint/types": "8.30.1",
-        "@typescript-eslint/typescript-estree": "8.30.1"
+        "@typescript-eslint/scope-manager": "8.31.0",
+        "@typescript-eslint/types": "8.31.0",
+        "@typescript-eslint/typescript-estree": "8.31.0"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1281,13 +1265,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.30.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz",
-      "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==",
+      "version": "8.31.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz",
+      "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.30.1",
+        "@typescript-eslint/types": "8.31.0",
         "eslint-visitor-keys": "^4.2.0"
       },
       "engines": {
@@ -1549,82 +1533,6 @@
         "win32"
       ]
     },
-    "node_modules/@vscode/vsce/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@vscode/vsce/node_modules/azure-devops-node-api": {
-      "version": "12.5.0",
-      "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz",
-      "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "tunnel": "0.0.6",
-        "typed-rest-client": "^1.8.4"
-      }
-    },
-    "node_modules/@vscode/vsce/node_modules/chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@vscode/vsce/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/@vscode/vsce/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vscode/vsce/node_modules/commander": {
-      "version": "12.1.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
-      "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      }
-    },
-    "node_modules/@vscode/vsce/node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
     "node_modules/@vscode/vsce/node_modules/glob": {
       "version": "11.0.1",
       "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
@@ -1665,16 +1573,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/@vscode/vsce/node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/@vscode/vsce/node_modules/jackspeak": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz",
@@ -1691,16 +1589,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/@vscode/vsce/node_modules/linkify-it": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
-      "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "uc.micro": "^2.0.0"
-      }
-    },
     "node_modules/@vscode/vsce/node_modules/lru-cache": {
       "version": "11.1.0",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
@@ -1711,31 +1599,6 @@
         "node": "20 || >=22"
       }
     },
-    "node_modules/@vscode/vsce/node_modules/markdown-it": {
-      "version": "14.1.0",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
-      "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "argparse": "^2.0.1",
-        "entities": "^4.4.0",
-        "linkify-it": "^5.0.0",
-        "mdurl": "^2.0.0",
-        "punycode.js": "^2.3.1",
-        "uc.micro": "^2.1.0"
-      },
-      "bin": {
-        "markdown-it": "bin/markdown-it.mjs"
-      }
-    },
-    "node_modules/@vscode/vsce/node_modules/mdurl": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
-      "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@vscode/vsce/node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -1777,33 +1640,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/@vscode/vsce/node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/@vscode/vsce/node_modules/uc.micro": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
-      "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/@vscode/vsce/node_modules/url-join": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
-      "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@@ -2115,19 +1951,16 @@
       }
     },
     "node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "color-convert": "^2.0.1"
+        "color-convert": "^1.9.0"
       },
       "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+        "node": ">=4"
       }
     },
     "node_modules/anymatch": {
@@ -2158,6 +1991,17 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/azure-devops-node-api": {
+      "version": "12.5.0",
+      "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz",
+      "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tunnel": "0.0.6",
+        "typed-rest-client": "^1.8.4"
+      }
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -2446,9 +2290,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001713",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz",
-      "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==",
+      "version": "1.0.30001715",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz",
+      "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==",
       "dev": true,
       "funding": [
         {
@@ -2468,33 +2312,31 @@
       "peer": true
     },
     "node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
       },
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
+        "node": ">=4"
       }
     },
     "node_modules/chalk/node_modules/supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "has-flag": "^4.0.0"
+        "has-flag": "^3.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=4"
       }
     },
     "node_modules/cheerio": {
@@ -2639,6 +2481,42 @@
         "node": ">=8"
       }
     },
+    "node_modules/cliui/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/cliui/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/cliui/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/cliui/node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -2703,22 +2581,19 @@
       }
     },
     "node_modules/color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "color-name": "~1.1.4"
-      },
-      "engines": {
-        "node": ">=7.0.0"
+        "color-name": "1.1.3"
       }
     },
     "node_modules/color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
       "dev": true,
       "license": "MIT"
     },
@@ -2736,12 +2611,14 @@
       }
     },
     "node_modules/commander": {
-      "version": "2.20.3",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+      "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
       "dev": true,
       "license": "MIT",
-      "peer": true
+      "engines": {
+        "node": ">=18"
+      }
     },
     "node_modules/concat-map": {
       "version": "0.0.1",
@@ -3041,9 +2918,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.137",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz",
-      "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==",
+      "version": "1.5.140",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz",
+      "integrity": "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==",
       "dev": true,
       "license": "ISC",
       "peer": true
@@ -3216,33 +3093,30 @@
       }
     },
     "node_modules/escape-string-regexp": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
-      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": ">=0.8.0"
       }
     },
     "node_modules/eslint": {
-      "version": "9.24.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz",
-      "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==",
+      "version": "9.25.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz",
+      "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.12.1",
         "@eslint/config-array": "^0.20.0",
-        "@eslint/config-helpers": "^0.2.0",
-        "@eslint/core": "^0.12.0",
+        "@eslint/config-helpers": "^0.2.1",
+        "@eslint/core": "^0.13.0",
         "@eslint/eslintrc": "^3.3.1",
-        "@eslint/js": "9.24.0",
-        "@eslint/plugin-kit": "^0.2.7",
+        "@eslint/js": "9.25.1",
+        "@eslint/plugin-kit": "^0.2.8",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
         "@humanwhocodes/retry": "^0.4.2",
@@ -3319,6 +3193,22 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
     "node_modules/eslint/node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3330,6 +3220,56 @@
         "concat-map": "0.0.1"
       }
     },
+    "node_modules/eslint/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/eslint/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/eslint/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/eslint/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/eslint/node_modules/eslint-visitor-keys": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
@@ -3356,6 +3296,16 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/eslint/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/eslint/node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3369,6 +3319,19 @@
         "node": "*"
       }
     },
+    "node_modules/eslint/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/espree": {
       "version": "10.3.0",
       "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
@@ -3452,6 +3415,7 @@
       "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
       "dev": true,
       "license": "MIT",
+      "peer": true,
       "engines": {
         "node": ">=0.8.x"
       }
@@ -3856,13 +3820,13 @@
       "license": "MIT"
     },
     "node_modules/has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
       "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=8"
+        "node": ">=4"
       }
     },
     "node_modules/has-symbols": {
@@ -3930,19 +3894,6 @@
         "node": ">=10"
       }
     },
-    "node_modules/hosted-git-info/node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/html-escaper": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -4286,6 +4237,16 @@
         "node": ">=10"
       }
     },
+    "node_modules/istanbul-lib-report/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/istanbul-lib-report/node_modules/supports-color": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -4345,6 +4306,17 @@
         "node": ">= 10.13.0"
       }
     },
+    "node_modules/jest-worker/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/jest-worker/node_modules/supports-color": {
       "version": "8.1.1",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@@ -4446,29 +4418,6 @@
         "npm": ">=6"
       }
     },
-    "node_modules/jsonwebtoken/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==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "buffer-equal-constant-time": "1.0.1",
-        "ecdsa-sig-formatter": "1.0.11",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "node_modules/jsonwebtoken/node_modules/jws": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
-      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "jwa": "^1.4.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
     "node_modules/jszip": {
       "version": "3.10.1",
       "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -4483,9 +4432,9 @@
       }
     },
     "node_modules/jwa": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
-      "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4495,13 +4444,13 @@
       }
     },
     "node_modules/jws": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
-      "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "jwa": "^2.0.0",
+        "jwa": "^1.4.1",
         "safe-buffer": "^5.0.1"
       }
     },
@@ -4562,6 +4511,16 @@
         "immediate": "~3.0.5"
       }
     },
+    "node_modules/linkify-it": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+      "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "uc.micro": "^2.0.0"
+      }
+    },
     "node_modules/loader-runner": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@@ -4638,36 +4597,118 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/lodash.once": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
-      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+    "node_modules/lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-symbols/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/log-symbols/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/log-symbols/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/log-symbols/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/log-symbols/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
     },
-    "node_modules/log-symbols": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
-      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+    "node_modules/log-symbols/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "chalk": "^4.1.0",
-        "is-unicode-supported": "^0.1.0"
+        "has-flag": "^4.0.0"
       },
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "node": ">=8"
       }
     },
     "node_modules/lru-cache": {
-      "version": "10.4.3",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
-      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
       "dev": true,
-      "license": "ISC"
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
     },
     "node_modules/make-dir": {
       "version": "4.0.0",
@@ -4685,6 +4726,24 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/markdown-it": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+      "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1",
+        "entities": "^4.4.0",
+        "linkify-it": "^5.0.0",
+        "mdurl": "^2.0.0",
+        "punycode.js": "^2.3.1",
+        "uc.micro": "^2.1.0"
+      },
+      "bin": {
+        "markdown-it": "bin/markdown-it.mjs"
+      }
+    },
     "node_modules/math-intrinsics": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -4695,6 +4754,13 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/mdurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+      "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -4881,6 +4947,22 @@
         "node": ">=8"
       }
     },
+    "node_modules/mocha/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
     "node_modules/mocha/node_modules/cliui": {
       "version": "7.0.4",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@@ -4893,6 +4975,26 @@
         "wrap-ansi": "^7.0.0"
       }
     },
+    "node_modules/mocha/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/mocha/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/mocha/node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -4900,6 +5002,19 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/mocha/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/mocha/node_modules/glob": {
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
@@ -4921,6 +5036,16 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/mocha/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/mocha/node_modules/minimatch": {
       "version": "5.1.6",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
@@ -5155,9 +5280,9 @@
       }
     },
     "node_modules/open": {
-      "version": "10.1.0",
-      "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz",
-      "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==",
+      "version": "10.1.1",
+      "resolved": "https://registry.npmjs.org/open/-/open-10.1.1.tgz",
+      "integrity": "sha512-zy1wx4+P3PfhXSEPJNtZmJXfhkkIaxU1VauWIrDZw1O7uJRDRJtKr9n3Ic4NgbA16KyOxOXO2ng9gYwCdXuSXA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -5462,6 +5587,13 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/path-scurry/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/pend": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@@ -6098,17 +6230,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/stoppable": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
-      "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4",
-        "npm": ">=6"
-      }
-    },
     "node_modules/string_decoder": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -6363,6 +6484,14 @@
         }
       }
     },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/test-exclude": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -6481,6 +6610,82 @@
         "webpack": "^5.0.0"
       }
     },
+    "node_modules/ts-loader/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/ts-loader/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/ts-loader/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/ts-loader/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ts-loader/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ts-loader/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/tslib": {
       "version": "2.8.1",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -6551,6 +6756,13 @@
         "node": ">=14.17"
       }
     },
+    "node_modules/uc.micro": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+      "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/underscore": {
       "version": "1.13.7",
       "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz",
@@ -6625,6 +6837,13 @@
         "punycode": "^2.1.0"
       }
     },
+    "node_modules/url-join": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
+      "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -6724,9 +6943,9 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.99.5",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz",
-      "integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==",
+      "version": "5.99.6",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz",
+      "integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==",
       "dev": true,
       "license": "MIT",
       "peer": true,
@@ -6911,6 +7130,42 @@
         "node": ">=8"
       }
     },
+    "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
diff --git a/vscode/extension/src/auth/auth.ts b/vscode/extension/src/auth/auth.ts
index e4c7159e..0f9e124e 100644
--- a/vscode/extension/src/auth/auth.ts
+++ b/vscode/extension/src/auth/auth.ts
@@ -27,9 +27,10 @@ const tokenSchema = z.object({
   exp: z.number(),
   email: z.string(),
 })
+
 const statusResponseSchema = z.object({
   is_logged_in: z.boolean(),
-  id_token: tokenSchema,
+  id_token: tokenSchema.optional().nullable(),
 })
 
 type StatusResponse = z.infer<typeof statusResponseSchema>;
@@ -96,6 +97,9 @@ export class AuthenticationProviderTobikoCloud
       return []
     }
     const token = statusResponse.id_token
+    if (!token) {
+      throw new Error("Invalid state from tcloud, failed to get token.")
+    }
     const session = {
       id: token.email,
       account: {
@@ -119,6 +123,9 @@ export class AuthenticationProviderTobikoCloud
       throw new Error("Failed to login to tcloud")
     }
     const token = statusResponse.id_token
+    if (!token) {
+      throw new Error("Failed to get tcloud token")
+    }
     const session: AuthenticationSession = {
       id: token.email,
       account: {
@@ -298,7 +305,9 @@ export class AuthenticationProviderTobikoCloud
       )
 
       if (messageResult === "Open browser") {
-        await env.openExternal(Uri.parse(deviceCodeResponse.verification_uri_complete))
+        await env.openExternal(
+          Uri.parse(deviceCodeResponse.verification_uri_complete)
+        )
       }
       if (messageResult === "Cancel") {
         ac.abort()
@@ -332,3 +341,18 @@ export class AuthenticationProviderTobikoCloud
     }
   }
 }
+
+/**
+ * Checks if the user is currently signed into Tobiko Cloud.
+ * @returns A promise that resolves to true if the user is signed in, false otherwise.
+ */
+export async function isSignedIntoTobikoCloud(): Promise<boolean> {
+  try {
+    const authProvider = new AuthenticationProviderTobikoCloud()
+    const sessions = await authProvider.getSessions()
+    return sessions.length > 0
+  } catch (error) {
+    traceError(`Error checking authentication status: ${error}`)
+    return false
+  }
+}
diff --git a/vscode/extension/src/commands/format.ts b/vscode/extension/src/commands/format.ts
index ebedd4c4..777335c5 100644
--- a/vscode/extension/src/commands/format.ts
+++ b/vscode/extension/src/commands/format.ts
@@ -1,36 +1,45 @@
-import { traceError, traceLog } from "../utilities/common/log"
+import { traceLog } from "../utilities/common/log"
 import { execSync } from "child_process"
 import { sqlmesh_exec } from "../utilities/sqlmesh/sqlmesh"
-import { isErr } from "../utilities/functional/result"
+import { err, isErr, ok, Result } from "../utilities/functional/result"
 import * as vscode from "vscode"
+import { ErrorType, handleNotSginedInError } from "../utilities/errors"
+import { AuthenticationProviderTobikoCloud } from "../auth/auth"
 
-export const format = async () => {
-  traceLog("Calling format")
-  const out = await internalFormat()
-  if (out === 0) {
+export const format =
+  (authProvider: AuthenticationProviderTobikoCloud) => async () => {
+    traceLog("Calling format")
+    const out = await internalFormat()
+    if (isErr(out)) {
+      if (out.error.type === "not_signed_in") {
+        handleNotSginedInError(authProvider)
+        return
+      } else {
+        vscode.window.showErrorMessage(
+          `Project format failed: ${out.error.message}`
+        )
+        return
+      }
+    }
     vscode.window.showInformationMessage("Project formatted successfully")
-  } else {
-    vscode.window.showErrorMessage("Project format failed")
   }
-}
 
-const internalFormat = async (): Promise<number> => {
+const internalFormat = async (): Promise<Result<number, ErrorType>> => {
   try {
     const exec = await sqlmesh_exec()
     if (isErr(exec)) {
-      traceError(exec.error)
-      return 1
+      return exec
     }
     execSync(`${exec.value.bin} format`, {
       encoding: "utf-8",
       cwd: exec.value.workspacePath,
       env: exec.value.env,
     })
-    return 0
+    return ok(0)
   } catch (error: any) {
-    traceError("Error executing sqlmesh format:", error.message)
-    traceError(error.stdout)
-    traceError(error.stderr)
-    return error.status || 1
+    return err({
+      type: "generic",
+      message: `Error executing sqlmesh format: ${error.message}`,
+    })
   }
 }
diff --git a/vscode/extension/src/extension.ts b/vscode/extension/src/extension.ts
index d7fa8840..f4e88d5c 100644
--- a/vscode/extension/src/extension.ts
+++ b/vscode/extension/src/extension.ts
@@ -1,5 +1,5 @@
-import * as vscode from "vscode"
 import { format } from "./commands/format"
+import * as vscode from "vscode"
 import {
   createOutputChannel,
   onDidChangeConfiguration,
@@ -16,6 +16,8 @@ import { AuthenticationProviderTobikoCloud } from "./auth/auth"
 import { signOut } from "./commands/signout"
 import { signIn } from "./commands/signin"
 import { signInSpecifyFlow } from "./commands/signinSpecifyFlow"
+import { isErr } from "./utilities/functional/result"
+import { handleNotSginedInError } from "./utilities/errors"
 
 let lspClient: LSPClient | undefined
 
@@ -44,7 +46,6 @@ export async function activate(context: vscode.ExtensionContext) {
   context.subscriptions.push(
     vscode.commands.registerCommand("sqlmesh.signin", signIn(authProvider))
   )
-
   context.subscriptions.push(
     vscode.commands.registerCommand(
       "sqlmesh.signinSpecifyFlow",
@@ -55,19 +56,26 @@ export async function activate(context: vscode.ExtensionContext) {
     vscode.commands.registerCommand("sqlmesh.signout", signOut(authProvider))
   )
   context.subscriptions.push(
-    vscode.commands.registerCommand("sqlmesh.format", format)
+    vscode.commands.registerCommand("sqlmesh.format", format(authProvider))
   )
 
   lspClient = new LSPClient()
-  await lspClient.start()
+  const result = await lspClient.start()
+  if (isErr(result)) {
+    handleNotSginedInError(authProvider)
+  }
   context.subscriptions.push(lspClient)
 
   const restart = async () => {
     if (lspClient) {
       traceVerbose("Restarting LSP client")
-      await lspClient.restart()
+      const result = await lspClient.restart()
+      if (isErr(result)) {
+        handleNotSginedInError(authProvider)
+      }
     }
   }
+
   context.subscriptions.push(
     onDidChangePythonInterpreter(async () => {
       await restart()
diff --git a/vscode/extension/src/lsp/lsp.ts b/vscode/extension/src/lsp/lsp.ts
index 6085b4a1..bdeea352 100644
--- a/vscode/extension/src/lsp/lsp.ts
+++ b/vscode/extension/src/lsp/lsp.ts
@@ -4,6 +4,7 @@ import { sqlmesh_lsp_exec } from "../utilities/sqlmesh/sqlmesh"
 import { err, isErr, ok, Result } from "../utilities/functional/result"
 import { getWorkspaceFolders } from "../utilities/common/vscodeapi"
 import { traceError } from "../utilities/common/log"
+import { ErrorType } from "../utilities/errors"
 
 let outputChannel: OutputChannel | undefined
 
@@ -14,7 +15,7 @@ export class LSPClient implements Disposable {
         this.client = undefined
     }
 
-    public async start(): Promise<Result<undefined, string>> {
+    public async start(): Promise<Result<undefined, ErrorType>> {
         if (!outputChannel) {
             outputChannel = window.createOutputChannel('sqlmesh-lsp')
         }
@@ -22,12 +23,15 @@ export class LSPClient implements Disposable {
         const sqlmesh = await sqlmesh_lsp_exec()
         if (isErr(sqlmesh)) {
             traceError(`Failed to get sqlmesh_lsp_exec: ${sqlmesh.error}`)
-            return sqlmesh
+            return sqlmesh 
         }
         const workspaceFolders = getWorkspaceFolders()
         if (workspaceFolders.length !== 1) {
             traceError(`Invalid number of workspace folders: ${workspaceFolders.length}`)
-            return err("Invalid number of workspace folders")
+            return err({
+                type: "generic",
+                message: "Invalid number of workspace folders",
+            })
         }
     
         let folder = workspaceFolders[0]
@@ -67,9 +71,9 @@ export class LSPClient implements Disposable {
         return ok(undefined)
     }
 
-    public async restart() {
+    public async restart(): Promise<Result<undefined, ErrorType>> {
         await this.stop()
-        await this.start()
+        return await this.start()
     }
 
     public async stop() {
diff --git a/vscode/extension/src/utilities/errors.ts b/vscode/extension/src/utilities/errors.ts
new file mode 100644
index 00000000..29ab5813
--- /dev/null
+++ b/vscode/extension/src/utilities/errors.ts
@@ -0,0 +1,28 @@
+import { window } from "vscode"
+import { AuthenticationProviderTobikoCloud } from "../auth/auth"
+import { signIn } from "../commands/signin"
+import { traceInfo } from "./common/log"
+
+/**
+ * Represents different types of errors that can occur in the application.
+ */
+export type ErrorType =
+  | { type: "generic"; message: string }
+  | { type: "not_signed_in" };
+
+/**
+ * Handles the case where the user is not signed in to Tobiko Cloud.
+ * @param authProvider - The authentication provider to use for signing in.
+ */
+export const handleNotSginedInError = async (
+  authProvider: AuthenticationProviderTobikoCloud
+): Promise<void> => {
+  traceInfo("handleNotSginedInError")
+  const result = await window.showInformationMessage(
+    "Please sign in to Tobiko Cloud to use SQLMesh",
+    "Sign In"
+  )
+  if (result === "Sign In") {
+    await signIn(authProvider)()
+  }
+}
diff --git a/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts b/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts
index e0456992..7b1fd25b 100644
--- a/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts
+++ b/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts
@@ -7,6 +7,8 @@ import { execFile } from "child_process"
 import { promisify } from "util"
 import { isPythonModuleInstalled } from "../python"
 import fs from "fs"
+import { ErrorType } from "../errors"
+import { isSignedIntoTobikoCloud } from "../../auth/auth"
 
 export type sqlmesh_exec = {
   workspacePath: string;
@@ -51,7 +53,7 @@ export const get_tcloud_bin = async (): Promise<Result<string, string>> => {
  *
  * @returns The sqlmesh executable for the current workspace.
  */
-export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, string>> => {
+export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, ErrorType>> => {
   const projectRoot = await getProjectRoot()
   const workspacePath = projectRoot.uri.fsPath
   const interpreterDetails = await getInterpreterDetails()
@@ -65,14 +67,26 @@ export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, string>> => {
   }
   if (interpreterDetails.isVirtualEnvironment) {
     traceLog("Using virtual environment")
-    const tcloudInstalled = await isTcloudProject()
-    if (isErr(tcloudInstalled)) {
-      return tcloudInstalled
+    const isTcloudInstalled = await isTcloudProject()
+    if (isErr(isTcloudInstalled)) {
+      return err({
+        type: "generic",
+        message: isTcloudInstalled.error,
+      }) 
     }
-    if (tcloudInstalled.value) {
+    if (isTcloudInstalled.value) {
       const tcloudBin = await get_tcloud_bin()
       if (isErr(tcloudBin)) {
-        return tcloudBin
+        return err({
+          type: "generic",
+          message: tcloudBin.error,
+        })
+      }
+      const isSignedIn = await isSignedIntoTobikoCloud()
+      if (!isSignedIn) {
+        return err({
+          type: "not_signed_in",
+        })
       }
       return ok({
         bin: `${tcloudBin.value} sqlmesh`,
@@ -113,7 +127,7 @@ export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, string>> => {
  * @returns The sqlmesh_lsp executable for the current workspace.
  */
 export const sqlmesh_lsp_exec = async (): Promise<
-  Result<sqlmesh_exec, string>
+  Result<sqlmesh_exec, ErrorType>
 > => {
   const projectRoot = await getProjectRoot()
   const workspacePath = projectRoot.uri.fsPath
@@ -130,13 +144,25 @@ export const sqlmesh_lsp_exec = async (): Promise<
     traceLog("Using virtual environment")
     const tcloudInstalled = await isTcloudProject()
     if (isErr(tcloudInstalled)) {
-      return tcloudInstalled
+      return err({
+        type: "generic",
+        message: tcloudInstalled.error,
+      })
     }
     if (tcloudInstalled.value) {
       traceLog("Tcloud installed, installing sqlmesh")
       const tcloudBin = await get_tcloud_bin()
       if (isErr(tcloudBin)) {
-        return tcloudBin
+        return err({
+          type: "generic",
+          message: tcloudBin.error,
+        })
+      }
+      const isSignedIn = await isSignedIntoTobikoCloud()
+      if (!isSignedIn) {
+        return err({
+          type: "not_signed_in",
+        })
       }
       const execFileAsync = promisify(execFile)
       await execFileAsync(tcloudBin.value, ["install_sqlmesh"], {
diff --git a/web/server/openapi.py b/web/server/openapi.py
index b7f2e561..302688dd 100644
--- a/web/server/openapi.py
+++ b/web/server/openapi.py
@@ -11,6 +11,13 @@ def generate_openapi_spec(app: FastAPI, path: str) -> None:
 
 
 if __name__ == "__main__":
-    app = create_app()
+    import argparse
+
+    parser = argparse.ArgumentParser(description="Generate OpenAPI specification")
+    parser.add_argument(
+        "--output", default="web/client/openapi.json", help="Path to output OpenAPI spec file"
+    )
+    args = parser.parse_args()
 
-    generate_openapi_spec(app, "web/client/openapi.json")
+    app = create_app()
+    generate_openapi_spec(app, args.output)


Test patch:
diff --git a/tests/core/test_model.py b/tests/core/test_model.py
index cd8c29d4..270414db 100644
--- a/tests/core/test_model.py
+++ b/tests/core/test_model.py
@@ -2803,7 +2803,7 @@ def test_parse_expression_list_with_jinja():
 
 def test_no_depends_on_runtime_jinja_query():
     @macro()
-    def runtime_macro(**kwargs) -> None:
+    def runtime_macro(evaluator, **kwargs) -> None:
         from sqlmesh.utils.errors import ParsetimeAdapterCallError
 
         raise ParsetimeAdapterCallError("")
@@ -9051,3 +9051,40 @@ def test_formatting_flag_serde():
 
     deserialized_model = SqlModel.parse_raw(model_json)
     assert deserialized_model.dict() == model.dict()
+
+
+def test_call_python_macro_from_jinja():
+    def noop() -> None:
+        print("noop")
+
+    @macro()
+    def test_runtime_stage(evaluator):
+        noop()
+        return evaluator.runtime_stage
+
+    expressions = d.parse(
+        """
+        MODEL (
+            name db.table,
+            dialect spark,
+            owner owner_name,
+        );
+
+        JINJA_QUERY_BEGIN;
+        SELECT '{{ test_runtime_stage() }}' AS a, '{{ test_runtime_stage_jinja('bla') }}' AS b;
+        JINJA_END;
+    """
+    )
+
+    jinja_macros = JinjaMacroRegistry(
+        root_macros={
+            "test_runtime_stage_jinja": MacroInfo(
+                definition="{% macro test_runtime_stage_jinja(value) %}{{ test_runtime_stage() }}_{{ value }}{% endmacro %}",
+                depends_on=[],
+            )
+        }
+    )
+
+    model = load_sql_based_model(expressions, jinja_macros=jinja_macros)
+    assert model.render_query().sql() == "SELECT 'loading' AS a, 'loading_bla' AS b"
+    assert set(model.python_env) == {"noop", "test_runtime_stage"}