diff --git a/build-config/buildspec-linux.yml b/build-config/buildspec-linux.yml new file mode 100644 index 0000000000..0067def79d --- /dev/null +++ b/build-config/buildspec-linux.yml @@ -0,0 +1,47 @@ +version: 0.2 + +env: + shell: bash + +phases: + install: + run-as: root + commands: + - dnf update -y + - dnf install -y python cmake bash zsh unzip git jq + - dnf swap -y gnupg2-minimal gnupg2-full + pre_build: + commands: + - export HOME=/home/codebuild-user + - export PATH="$HOME/.local/bin:$PATH" + - mkdir -p "$HOME/.local/bin" + # Create fish config dir to prevent rustup from failing + - mkdir -p "$HOME/.config/fish/conf.d" + # Install cargo + - curl --retry 5 --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + - . "$HOME/.cargo/env" + - rustup toolchain install `cat rust-toolchain.toml | grep channel | cut -d '=' -f2 | tr -d ' "'` + # Install cross only if the musl env var is set and not null + - if [ ! -z "${AMAZON_Q_BUILD_MUSL:+x}" ]; then cargo install cross --git https://github.com/cross-rs/cross; fi + # Install python/node via mise (https://mise.jdx.dev/continuous-integration.html) + - curl --retry 5 --proto '=https' --tlsv1.2 -sSf https://mise.run | sh + - mise install + - eval "$(mise activate bash --shims)" + # Install python deps + - pip3 install -r build-scripts/requirements.txt + build: + commands: + - python3.11 build-scripts/qchatmain.py build + +artifacts: + discard-paths: "yes" + base-directory: "build" + files: + - ./*.tar.gz + - ./*.zip + # Hashes + - ./*.sha256 + # Signatures + - ./*.asc + - ./*.sig + diff --git a/build-config/buildspec-macos.yml b/build-config/buildspec-macos.yml new file mode 100644 index 0000000000..bdb9733563 --- /dev/null +++ b/build-config/buildspec-macos.yml @@ -0,0 +1,41 @@ +version: 0.2 + +phases: + pre_build: + commands: + - whoami + - echo "$HOME" + - echo "$SHELL" + - pwd + - ls + - mkdir -p "$HOME/.local/bin" + - export PATH="$HOME/.local/bin:$PATH" + # Create fish config dir to prevent rustup from failing + - mkdir -p "$HOME/.config/fish/conf.d" + # Install cargo + - export CARGO_HOME="$HOME/.cargo" + - curl --retry 5 --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + - . "$HOME/.cargo/env" + - rustup toolchain install `cat rust-toolchain.toml | grep channel | cut -d '=' -f2 | tr -d ' "'` + # Install cross only if the musl env var is set and not null + - if [ ! -z "${AMAZON_Q_BUILD_MUSL:+x}" ]; then cargo install cross --git https://github.com/cross-rs/cross; fi + # Install python/node via mise (https://mise.jdx.dev/continuous-integration.html) + - curl --retry 5 --proto '=https' --tlsv1.2 -sSf https://mise.run | sh + - mise install + - eval "$(mise activate zsh --shims)" + # Install python deps + - python3 -m venv build-scripts/.env + - source build-scripts/.env/bin/activate + - pip3 install -r build-scripts/requirements.txt + build: + commands: + - python3 build-scripts/qchatmain.py build --skip-lints --skip-tests --not-release + +artifacts: + discard-paths: "yes" + base-directory: "build" + files: + - ./*.zip + # Hashes + - ./*.sha256 + diff --git a/build-scripts/qchatbuild.py b/build-scripts/qchatbuild.py new file mode 100644 index 0000000000..4a00ca900f --- /dev/null +++ b/build-scripts/qchatbuild.py @@ -0,0 +1,587 @@ +import base64 +from dataclasses import dataclass +import json +import pathlib +from functools import cache +import os +import shutil +import time +from typing import Any, Mapping, Sequence, List, Optional +from build import generate_sha +from const import APPLE_TEAM_ID, CHAT_BINARY_NAME, CHAT_PACKAGE_NAME +from util import debug, info, isDarwin, isLinux, run_cmd, run_cmd_output, warn +from rust import cargo_cmd_name, rust_env, rust_targets +from importlib import import_module + +Args = Sequence[str | os.PathLike] +Env = Mapping[str, str | os.PathLike] +Cwd = str | os.PathLike + +BUILD_DIR_RELATIVE = pathlib.Path(os.environ.get("BUILD_DIR") or "build") +BUILD_DIR = BUILD_DIR_RELATIVE.absolute() + +CD_SIGNER_REGION = "us-west-2" +SIGNING_API_BASE_URL = "https://api.signer.builder-tools.aws.dev" + + +@dataclass +class CdSigningData: + bucket_name: str + """The bucket hosting signing artifacts accessible by CD Signer.""" + apple_notarizing_secret_arn: str + """The ARN of the secret containing the Apple ID and password, used during notarization""" + signing_role_arn: str + """The ARN of the role used by CD Signer""" + + +@dataclass +class MacOSBuildOutput: + chat_path: pathlib.Path + """The path to the chat binary""" + chat_zip_path: pathlib.Path + """The path to the chat binary zipped""" + + +def run_cargo_tests(): + args = [cargo_cmd_name()] + args.extend(["test", "--locked", "--package", CHAT_PACKAGE_NAME]) + run_cmd( + args, + env={ + **os.environ, + **rust_env(release=False), + }, + ) + + +def run_clippy(): + args = [cargo_cmd_name(), "clippy", "--locked", "--package", CHAT_PACKAGE_NAME] + run_cmd( + args, + env={ + **os.environ, + **rust_env(release=False), + }, + ) + + +def build_chat_bin( + release: bool, + output_name: str | None = None, + targets: Sequence[str] = [], +): + package = CHAT_PACKAGE_NAME + + args = [cargo_cmd_name(), "build", "--locked", "--package", package] + + for target in targets: + args.extend(["--target", target]) + + if release: + args.append("--release") + target_subdir = "release" + else: + target_subdir = "debug" + + run_cmd( + args, + env={ + **os.environ, + **rust_env(release=release), + }, + ) + + # create "universal" binary for macos + if isDarwin(): + out_path = BUILD_DIR / f"{output_name or package}-universal-apple-darwin" + args = [ + "lipo", + "-create", + "-output", + out_path, + ] + for target in targets: + args.append(pathlib.Path("target") / target / target_subdir / package) + run_cmd(args) + return out_path + else: + # linux does not cross compile arch + target = targets[0] + target_path = pathlib.Path("target") / target / target_subdir / package + out_path = BUILD_DIR / "bin" / f"{(output_name or package)}-{target}" + out_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(target_path, out_path) + return out_path + + +@cache +def get_creds(): + boto3 = import_module("boto3") + session = boto3.Session() + credentials = session.get_credentials() + creds = credentials.get_frozen_credentials() + return creds + + +def cd_signer_request(method: str, path: str, data: str | None = None): + """ + Sends a request to the CD Signer API. + """ + SigV4Auth = import_module("botocore.auth").SigV4Auth + AWSRequest = import_module("botocore.awsrequest").AWSRequest + requests = import_module("requests") + + url = f"{SIGNING_API_BASE_URL}{path}" + headers = {"Content-Type": "application/json"} + request = AWSRequest(method=method, url=url, data=data, headers=headers) + SigV4Auth(get_creds(), "signer-builder-tools", CD_SIGNER_REGION).add_auth(request) + + for i in range(1, 8): + debug(f"Sending request {method} to {url} with data: {data}") + response = requests.request(method=method, url=url, headers=dict(request.headers), data=data) + info(f"CDSigner Request ({url}): {response.status_code}") + if response.status_code == 429: + warn(f"Too many requests, backing off for {2**i} seconds") + time.sleep(2**i) + continue + return response + + raise Exception(f"Failed to request {url}") + + +def cd_signer_create_request(manifest: Any) -> str: + """ + Sends a POST request to create a new signing request. After creation, we + need to send another request to start it. + """ + response = cd_signer_request( + method="POST", + path="/signing_requests", + data=json.dumps({"manifest": manifest}), + ) + response_json = response.json() + info(f"Signing request create: {response_json}") + request_id = response_json["signingRequestId"] + return request_id + + +def cd_signer_start_request(request_id: str, source_key: str, destination_key: str, signing_data: CdSigningData): + """ + Sends a POST request to start the signing process. + """ + response_text = cd_signer_request( + method="POST", + path=f"/signing_requests/{request_id}/start", + data=json.dumps( + { + "iamRole": f"{signing_data.signing_role_arn}", + "s3Location": { + "bucket": signing_data.bucket_name, + "sourceKey": source_key, + "destinationKey": destination_key, + }, + } + ), + ).text + info(f"Signing request start: {response_text}") + + +def cd_signer_status_request(request_id: str): + response_json = cd_signer_request( + method="GET", + path=f"/signing_requests/{request_id}", + ).json() + info(f"Signing request status: {response_json}") + return response_json["signingRequest"]["status"] + + +def cd_build_signed_package(exe_path: pathlib.Path): + """ + Creates a tarball `package.tar.gz` with the following structure: + ``` + package + ├─ EXECUTABLES_TO_SIGN + | ├─ qchat + ``` + """ + # Trying a different format without manifest.yaml and placing EXECUTABLES_TO_SIGN + # at the root. + # The docs contain conflicting information, idk what to even do here + working_dir = BUILD_DIR / "package" + shutil.rmtree(working_dir, ignore_errors=True) + (BUILD_DIR / "package" / "EXECUTABLES_TO_SIGN").mkdir(parents=True) + + shutil.copy2(exe_path, working_dir / "EXECUTABLES_TO_SIGN" / exe_path.name) + exe_path.unlink() + + run_cmd(["gtar", "-czf", "artifact.gz", "EXECUTABLES_TO_SIGN"], cwd=working_dir) + run_cmd( + ["gtar", "-czf", BUILD_DIR / "package.tar.gz", "artifact.gz"], + cwd=working_dir, + ) + + return BUILD_DIR / "package.tar.gz" + + +def manifest( + identifier: str, +): + """ + Returns the manifest arguments required when creating a new CD Signer request. + """ + return { + "type": "app", + "os": "osx", + "name": "EXECUTABLES_TO_SIGN", + "outputs": [{"label": "macos", "path": "EXECUTABLES_TO_SIGN"}], + "app": { + "identifier": identifier, + "signing_requirements": { + "certificate_type": "developerIDAppDistribution", + "app_id_prefix": APPLE_TEAM_ID, + }, + }, + } + + +def sign_executable(signing_data: CdSigningData, exe_path: pathlib.Path) -> pathlib.Path: + """ + Signs an executable with CD Signer. + + Returns: + The path to the signed executable + """ + name = exe_path.name + info(f"Signing {name}") + + info("Packaging...") + package_path = cd_build_signed_package(exe_path) + + info("Uploading...") + run_cmd(["aws", "s3", "rm", "--recursive", f"s3://{signing_data.bucket_name}/signed"]) + run_cmd(["aws", "s3", "rm", "--recursive", f"s3://{signing_data.bucket_name}/pre-signed"]) + run_cmd(["aws", "s3", "cp", package_path, f"s3://{signing_data.bucket_name}/pre-signed/package.tar.gz"]) + + info("Sending request...") + request_id = cd_signer_create_request(manifest("com.amazon.codewhisperer")) + cd_signer_start_request( + request_id=request_id, + source_key="pre-signed/package.tar.gz", + destination_key="signed/signed.zip", + signing_data=signing_data, + ) + + max_duration = 180 + end_time = time.time() + max_duration + i = 1 + while True: + info(f"Checking for signed package attempt #{i}") + status = cd_signer_status_request(request_id) + info(f"Package has status: {status}") + + match status: + case "success": + break + case "created" | "processing" | "inProgress": + pass + case "failure": + raise RuntimeError("Signing request failed") + case _: + warn(f"Unexpected status, ignoring: {status}") + + if time.time() >= end_time: + raise RuntimeError("Signed package did not appear, check signer logs") + time.sleep(2) + i += 1 + + info("Signed!") + + # CD Signer should return the signed executable in a zip file containing the structure: + # "Payload/EXECUTABLES_TO_SIGN/{executable name}". + info("Downloading...") + + # Create a new directory for unzipping the signed executable. + zip_dl_path = BUILD_DIR / pathlib.Path("signed.zip") + run_cmd(["aws", "s3", "cp", f"s3://{signing_data.bucket_name}/signed/signed.zip", zip_dl_path]) + payload_path = BUILD_DIR / "signed" + shutil.rmtree(payload_path, ignore_errors=True) + run_cmd(["unzip", zip_dl_path, "-d", payload_path]) + zip_dl_path.unlink() + signed_exe_path = BUILD_DIR / "signed" / "Payload" / "EXECUTABLES_TO_SIGN" / name + # Verify that the exe is signed + run_cmd(["codesign", "--verify", "--verbose=4", signed_exe_path]) + return signed_exe_path + + +def notarize_executable(signing_data: CdSigningData, exe_path: pathlib.Path): + """ + Submits an executable to Apple notary service. + """ + # Load the Apple id and password from secrets manager. + secret_id = signing_data.apple_notarizing_secret_arn + secret_region = parse_region_from_arn(signing_data.apple_notarizing_secret_arn) + info(f"Loading secretmanager value: {secret_id}") + secret_value = run_cmd_output( + ["aws", "--region", secret_region, "secretsmanager", "get-secret-value", "--secret-id", secret_id] + ) + secret_string = json.loads(secret_value)["SecretString"] + secrets = json.loads(secret_string) + + # Submit the exe to Apple notary service. It must be zipped first. + info(f"Submitting {exe_path} to Apple notary service") + zip_path = BUILD_DIR / f"{exe_path.name}.zip" + zip_path.unlink(missing_ok=True) + run_cmd(["zip", "-j", zip_path, exe_path], cwd=BUILD_DIR) + submit_res = run_cmd_output( + [ + "xcrun", + "notarytool", + "submit", + zip_path, + "--team-id", + APPLE_TEAM_ID, + "--apple-id", + secrets["appleId"], + "--password", + secrets["appleIdPassword"], + "--wait", + "-f", + "json", + ] + ) + debug(f"Notary service response: {submit_res}") + + # Confirm notarization succeeded. + assert json.loads(submit_res)["status"] == "Accepted" + + # Cleanup + zip_path.unlink() + + +def sign_and_notarize(signing_data: CdSigningData, chat_path: pathlib.Path) -> pathlib.Path: + """ + Signs an executable with CD Signer, and verifies it with Apple notary service. + + Returns: + The path to the signed executable. + """ + # First, sign the application + chat_path = sign_executable(signing_data, chat_path) + + # Next, notarize the application + notarize_executable(signing_data, chat_path) + + return chat_path + + +def build_macos(chat_path: pathlib.Path, signing_data: CdSigningData | None): + """ + Creates a qchat.zip under the build directory. + """ + chat_dst = BUILD_DIR / CHAT_BINARY_NAME + chat_dst.unlink(missing_ok=True) + shutil.copy2(chat_path, chat_dst) + + if signing_data: + chat_dst = sign_and_notarize(signing_data, chat_dst) + + zip_path = BUILD_DIR / f"{CHAT_BINARY_NAME}.zip" + zip_path.unlink(missing_ok=True) + + info(f"Creating zip output to {zip_path}") + run_cmd(["zip", "-j", zip_path, chat_dst], cwd=BUILD_DIR) + generate_sha(zip_path) + + +class GpgSigner: + def __init__(self, gpg_id: str, gpg_secret_key: str, gpg_passphrase: str): + self.gpg_id = gpg_id + self.gpg_secret_key = gpg_secret_key + self.gpg_passphrase = gpg_passphrase + + self.gpg_home = pathlib.Path.home() / ".gnupg-tmp" + self.gpg_home.mkdir(parents=True, exist_ok=True, mode=0o700) + + # write gpg secret key to file + self.gpg_secret_key_path = self.gpg_home / "gpg_secret" + self.gpg_secret_key_path.write_bytes(base64.b64decode(gpg_secret_key)) + + self.gpg_passphrase_path = self.gpg_home / "gpg_pass" + self.gpg_passphrase_path.write_text(gpg_passphrase) + + run_cmd(["gpg", "--version"]) + + info("Importing GPG key") + run_cmd(["gpg", "--list-keys"], env=self.gpg_env()) + run_cmd( + ["gpg", *self.sign_args(), "--allow-secret-key-import", "--import", self.gpg_secret_key_path], + env=self.gpg_env(), + ) + run_cmd(["gpg", "--list-keys"], env=self.gpg_env()) + + def gpg_env(self) -> Env: + return {**os.environ, "GNUPGHOME": self.gpg_home} + + def sign_args(self) -> Args: + return [ + "--batch", + "--pinentry-mode", + "loopback", + "--no-tty", + "--yes", + "--passphrase-file", + self.gpg_passphrase_path, + ] + + def sign_file(self, path: pathlib.Path) -> List[pathlib.Path]: + info(f"Signing {path.name}") + run_cmd( + ["gpg", "--detach-sign", *self.sign_args(), "--local-user", self.gpg_id, path], + env=self.gpg_env(), + ) + run_cmd( + ["gpg", "--detach-sign", *self.sign_args(), "--armor", "--local-user", self.gpg_id, path], + env=self.gpg_env(), + ) + return [path.with_suffix(f"{path.suffix}.asc"), path.with_suffix(f"{path.suffix}.sig")] + + def clean(self): + info("Cleaning gpg keys") + shutil.rmtree(self.gpg_home, ignore_errors=True) + + +def get_secretmanager_json(secret_id: str, secret_region: str): + info(f"Loading secretmanager value: {secret_id}") + secret_value = run_cmd_output( + ["aws", "--region", secret_region, "secretsmanager", "get-secret-value", "--secret-id", secret_id] + ) + secret_string = json.loads(secret_value)["SecretString"] + return json.loads(secret_string) + + +def load_gpg_signer() -> Optional[GpgSigner]: + if gpg_id := os.getenv("TEST_PGP_ID"): + gpg_secret_key = os.getenv("TEST_PGP_SECRET_KEY") + gpg_passphrase = os.getenv("TEST_PGP_PASSPHRASE") + if gpg_secret_key is not None and gpg_passphrase is not None: + info("Using test pgp key", gpg_id) + return GpgSigner(gpg_id=gpg_id, gpg_secret_key=gpg_secret_key, gpg_passphrase=gpg_passphrase) + + pgp_secret_arn = os.getenv("SIGNING_PGP_KEY_SECRET_ARN") + info(f"SIGNING_PGP_KEY_SECRET_ARN: {pgp_secret_arn}") + if pgp_secret_arn: + pgp_secret_region = parse_region_from_arn(pgp_secret_arn) + gpg_secret_json = get_secretmanager_json(pgp_secret_arn, pgp_secret_region) + gpg_id = gpg_secret_json["gpg_id"] + gpg_secret_key = gpg_secret_json["gpg_secret_key"] + gpg_passphrase = gpg_secret_json["gpg_passphrase"] + return GpgSigner(gpg_id=gpg_id, gpg_secret_key=gpg_secret_key, gpg_passphrase=gpg_passphrase) + else: + return None + + +def parse_region_from_arn(arn: str) -> str: + # ARN format: arn:partition:service:region:account-id:resource-type/resource-id + # Check if we have enough parts and the ARN starts with "arn:" + parts = arn.split(":") + if len(parts) >= 4: + return parts[3] + + return "" + + +def build_linux(chat_path: pathlib.Path, signer: GpgSigner | None): + """ + Creates tar.gz, tar.xz, tar.zst, and zip archives under `BUILD_DIR`. + + Each archive has the following structure: + - archive/qchat + """ + archive_name = CHAT_BINARY_NAME + + archive_path = pathlib.Path(archive_name) + archive_path.mkdir(parents=True, exist_ok=True) + shutil.copy2(chat_path, archive_path / CHAT_BINARY_NAME) + + info(f"Building {archive_name}.tar.gz") + tar_gz_path = BUILD_DIR / f"{archive_name}.tar.gz" + run_cmd(["tar", "-czf", tar_gz_path, archive_path]) + generate_sha(tar_gz_path) + if signer: + signer.sign_file(tar_gz_path) + + info(f"Building {archive_name}.zip") + zip_path = BUILD_DIR / f"{archive_name}.zip" + run_cmd(["zip", "-r", zip_path, archive_path]) + generate_sha(zip_path) + if signer: + signer.sign_file(zip_path) + + # clean up + shutil.rmtree(archive_path) + if signer: + signer.clean() + + +def build( + release: bool, + stage_name: str | None = None, + run_lints: bool = True, + run_test: bool = True, +): + BUILD_DIR.mkdir(exist_ok=True) + + disable_signing = os.environ.get("DISABLE_SIGNING") + + gpg_signer = load_gpg_signer() if not disable_signing and isLinux() else None + signing_role_arn = os.environ.get("SIGNING_ROLE_ARN") + signing_bucket_name = os.environ.get("SIGNING_BUCKET_NAME") + signing_apple_notarizing_secret_arn = os.environ.get("SIGNING_APPLE_NOTARIZING_SECRET_ARN") + if ( + not disable_signing + and isDarwin() + and signing_role_arn + and signing_bucket_name + and signing_apple_notarizing_secret_arn + ): + signing_data = CdSigningData( + bucket_name=signing_bucket_name, + apple_notarizing_secret_arn=signing_apple_notarizing_secret_arn, + signing_role_arn=signing_role_arn, + ) + else: + signing_data = None + + match stage_name: + case "prod" | None: + info("Building for prod") + case "gamma": + info("Building for gamma") + case _: + raise ValueError(f"Unknown stage name: {stage_name}") + + targets = rust_targets() + + info(f"Release: {release}") + info(f"Targets: {targets}") + info(f"Signing app: {signing_data is not None or gpg_signer is not None}") + + if run_test: + info("Running cargo tests") + run_cargo_tests() + + if run_lints: + info("Running cargo clippy") + run_clippy() + + info("Building", CHAT_PACKAGE_NAME) + chat_path = build_chat_bin( + release=release, + output_name=CHAT_BINARY_NAME, + targets=targets, + ) + + if isDarwin(): + build_macos(chat_path, signing_data) + else: + build_linux(chat_path, gpg_signer) diff --git a/build-scripts/qchatmain.py b/build-scripts/qchatmain.py new file mode 100644 index 0000000000..8389c5e658 --- /dev/null +++ b/build-scripts/qchatmain.py @@ -0,0 +1,50 @@ +import argparse +from qchatbuild import build + + +class StoreIfNotEmptyAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + if values and len(values) > 0: + setattr(namespace, self.dest, values) + + +parser = argparse.ArgumentParser( + prog="build", + description="Builds the qchat binary", +) +subparsers = parser.add_subparsers(help="sub-command help", dest="subparser", required=True) + +build_subparser = subparsers.add_parser(name="build") +build_subparser.add_argument( + "--stage-name", + action=StoreIfNotEmptyAction, + help="The name of the stage", +) +build_subparser.add_argument( + "--not-release", + action="store_true", + help="Build a non-release version", +) +build_subparser.add_argument( + "--skip-tests", + action="store_true", + help="Skip running npm and rust tests", +) +build_subparser.add_argument( + "--skip-lints", + action="store_true", + help="Skip running lints", +) + +args = parser.parse_args() + +match args.subparser: + case "build": + build( + release=not args.not_release, + stage_name=args.stage_name, + run_lints=not args.skip_lints, + run_test=not args.skip_tests, + ) + case _: + raise ValueError(f"Unsupported subparser {args.subparser}") diff --git a/build-scripts/util.py b/build-scripts/util.py index f2faffb1d4..39158ebdda 100644 --- a/build-scripts/util.py +++ b/build-scripts/util.py @@ -10,7 +10,7 @@ from typing import List, Mapping, Sequence from const import DESKTOP_PACKAGE_NAME, TAURI_PRODUCT_NAME - +DEBUG = "\033[94;1m" INFO = "\033[92;1m" WARN = "\033[93;1m" FAIL = "\033[91;1m" @@ -72,6 +72,10 @@ def log(*value: object, title: str, color: str | None): print(f"{color}{title}:{ENDC}", *value, flush=True) +def debug(*value: object): + log(*value, title="DEBUG", color=DEBUG) + + def info(*value: object): log(*value, title="INFO", color=INFO) @@ -100,6 +104,8 @@ def run_cmd_output( env: Env | None = None, cwd: Cwd | None = None, ) -> str: + args_str = [str(arg) for arg in args] + print(f"+ {shlex.join(args_str)}") res = subprocess.run(args, env=env, cwd=cwd, check=True, stdout=subprocess.PIPE) return res.stdout.decode("utf-8") diff --git a/crates/chat-cli/.gitignore b/crates/chat-cli/.gitignore index 0b0c025e2a..b082f8a65e 100644 --- a/crates/chat-cli/.gitignore +++ b/crates/chat-cli/.gitignore @@ -1,2 +1,5 @@ build/ -spec.ts \ No newline at end of file +spec.ts + +# This is created by the build script for macOS +src/Info.plist diff --git a/crates/chat-cli/build.rs b/crates/chat-cli/build.rs index 3d3c7c6c43..f097298af8 100644 --- a/crates/chat-cli/build.rs +++ b/crates/chat-cli/build.rs @@ -7,6 +7,10 @@ use quote::{ quote, }; +// TODO(brandonskiser): update bundle identifier for signed builds +#[cfg(target_os = "macos")] +const MACOS_BUNDLE_IDENTIFIER: &str = "com.amazon.codewhisperer"; + const DEF: &str = include_str!("./telemetry_definitions.json"); #[derive(Debug, Clone, serde::Deserialize)] @@ -39,9 +43,49 @@ struct Def { metrics: Vec, } +/// Writes a generated Info.plist for the qchat executable under src/. +/// +/// This is required for signing the executable since we must embed the Info.plist directly within +/// the binary. +#[cfg(target_os = "macos")] +fn write_plist() { + let plist = format!( + r#" + + + + CFBundlePackageType + APPL + CFBundleIdentifier + {} + CFBundleName + {} + CFBundleVersion + {} + CFBundleShortVersionString + {} + CFBundleInfoDictionaryVersion + 6.0 + NSHumanReadableCopyright + Copyright © 2022 Amazon Q CLI Team (q-cli@amazon.com):Chay Nabors (nabochay@amazon.com):Brandon Kiser (bskiser@amazon.com) All rights reserved. + + +"#, + MACOS_BUNDLE_IDENTIFIER, + option_env!("AMAZON_Q_BUILD_HASH").unwrap_or("unknown"), + option_env!("AMAZON_Q_BUILD_DATETIME").unwrap_or("unknown"), + env!("CARGO_PKG_VERSION") + ); + + std::fs::write("src/Info.plist", plist).expect("writing the Info.plist should not fail"); +} + fn main() { println!("cargo:rerun-if-changed=def.json"); + #[cfg(target_os = "macos")] + write_plist(); + let outdir = std::env::var("OUT_DIR").unwrap(); let data = serde_json::from_str::(DEF).unwrap(); diff --git a/crates/chat-cli/src/mcp_client/client.rs b/crates/chat-cli/src/mcp_client/client.rs index 004c0623a9..14b6e2d44d 100644 --- a/crates/chat-cli/src/mcp_client/client.rs +++ b/crates/chat-cli/src/mcp_client/client.rs @@ -379,11 +379,12 @@ where let level = params .as_ref() .and_then(|p| p.get("level")) - .and_then(|v| serde_json::to_string(v).ok()); - let data = params - .as_ref() - .and_then(|p| p.get("data")) - .and_then(|v| serde_json::to_string(v).ok()); + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + let data = params.as_ref().and_then(|p| p.get("data")).map(|v| match v { + serde_json::Value::String(s) => s.clone(), + _ => serde_json::to_string_pretty(v).unwrap_or_default(), + }); if let (Some(level), Some(data)) = (level, data) { match level.to_lowercase().as_str() { "error" => { diff --git a/extensions/gnome-extension/package.json b/extensions/gnome-extension/package.json index f7ee7d1ecc..f482d6cf5d 100644 --- a/extensions/gnome-extension/package.json +++ b/extensions/gnome-extension/package.json @@ -19,7 +19,7 @@ "eslint": "9.18.0", "globals": "^16.1.0", "typescript": "^5.8.3", - "typescript-eslint": "^8.31.1" + "typescript-eslint": "^8.35.1" }, "dependencies": { "@girs/gjs": "4.0.0-beta.23", diff --git a/packages/dashboard-app/package.json b/packages/dashboard-app/package.json index c81befc195..deb8a71860 100644 --- a/packages/dashboard-app/package.json +++ b/packages/dashboard-app/package.json @@ -43,8 +43,8 @@ "@eslint/js": "^9.18.0", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", - "@typescript-eslint/eslint-plugin": "^8.31.1", - "@typescript-eslint/parser": "^8.31.1", + "@typescript-eslint/eslint-plugin": "^8.35.1", + "@typescript-eslint/parser": "^8.35.1", "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.21", "eslint": "^9.18.0", @@ -55,7 +55,7 @@ "prettier": "^3.4.2", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", - "typescript-eslint": "^8.31.1", + "typescript-eslint": "^8.35.1", "vite": "^6.3.4" } } diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index d8cb425bee..a544ac9df6 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -28,7 +28,7 @@ "@amzn/tsconfig": "workspace:^", "@eslint/js": "^9.18.0", "@types/eslint__js": "^8.42.3", - "@typescript-eslint/utils": "^8.31.1", + "@typescript-eslint/utils": "^8.35.1", "eslint-config-prettier": "^10.1.3", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.2", @@ -38,7 +38,7 @@ "eslint-plugin-unicorn": "^59.0.0", "eslint": "^9.18.0", "prettier": "^3.4.2", - "typescript-eslint": "^8.31.1", + "typescript-eslint": "^8.35.1", "typescript": "^5.8.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8d44a9cc3..db2e540929 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,8 +58,8 @@ importers: specifier: ^5.8.3 version: 5.8.3 typescript-eslint: - specifier: ^8.31.1 - version: 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + specifier: ^8.35.1 + version: 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) extensions/gnome-legacy-extension: devDependencies: @@ -577,11 +577,11 @@ importers: specifier: ^18.3.5 version: 18.3.5(@types/react@18.3.18) '@typescript-eslint/eslint-plugin': - specifier: ^8.31.1 - version: 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + specifier: ^8.35.1 + version: 8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': - specifier: ^8.31.1 - version: 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + specifier: ^8.35.1 + version: 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.3.4(vite@6.3.4(@types/node@22.15.20)(jiti@1.21.7)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)) @@ -613,8 +613,8 @@ importers: specifier: ^5.8.3 version: 5.8.3 typescript-eslint: - specifier: ^8.31.1 - version: 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + specifier: ^8.35.1 + version: 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) vite: specifier: ^6.3.4 version: 6.3.4(@types/node@22.15.20)(jiti@1.21.7)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) @@ -631,8 +631,8 @@ importers: specifier: ^8.42.3 version: 8.42.3 '@typescript-eslint/utils': - specifier: ^8.31.1 - version: 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + specifier: ^8.35.1 + version: 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.18.0 version: 9.18.0(jiti@1.21.7) @@ -664,8 +664,8 @@ importers: specifier: ^5.8.3 version: 5.8.3 typescript-eslint: - specifier: ^8.31.1 - version: 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + specifier: ^8.35.1 + version: 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) packages/fuzzysort: {} @@ -1480,28 +1480,28 @@ packages: resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} engines: {node: '>=18'} - '@csstools/css-calc@2.1.3': - resolution: {integrity: sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==} + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.4 - '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@3.0.9': - resolution: {integrity: sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==} + '@csstools/css-color-parser@3.0.10': + resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.4 - '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-parser-algorithms@3.0.4': - resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} engines: {node: '>=18'} peerDependencies: - '@csstools/css-tokenizer': ^3.0.3 + '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-tokenizer@3.0.3': - resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} '@esbuild/aix-ppc64@0.25.3': @@ -1666,6 +1666,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -2593,51 +2599,63 @@ packages: '@types/vscode@1.80.0': resolution: {integrity: sha512-qK/CmOdS2o7ry3k6YqU4zD3R2AYlJfbwBoSbKpBoP+GpXNE+0NEgJOli4n0bm0diK5kfBnchgCEj4igQz/44Hg==} - '@typescript-eslint/eslint-plugin@8.31.1': - resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==} + '@typescript-eslint/eslint-plugin@8.35.1': + resolution: {integrity: sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + '@typescript-eslint/parser': ^8.35.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.31.1': - resolution: {integrity: sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==} + '@typescript-eslint/parser@8.35.1': + resolution: {integrity: sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.31.1': - resolution: {integrity: sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==} + '@typescript-eslint/project-service@8.35.1': + resolution: {integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.35.1': + resolution: {integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.31.1': - resolution: {integrity: sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==} + '@typescript-eslint/tsconfig-utils@8.35.1': + resolution: {integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.35.1': + resolution: {integrity: sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.31.1': - resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==} + '@typescript-eslint/types@8.35.1': + resolution: {integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.31.1': - resolution: {integrity: sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==} + '@typescript-eslint/typescript-estree@8.35.1': + resolution: {integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.31.1': - resolution: {integrity: sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==} + '@typescript-eslint/utils@8.35.1': + resolution: {integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.31.1': - resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==} + '@typescript-eslint/visitor-keys@8.35.1': + resolution: {integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/analyze-trace@0.10.1': @@ -3230,8 +3248,8 @@ packages: engines: {node: '>=4'} hasBin: true - cssstyle@4.3.1: - resolution: {integrity: sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==} + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} csstype@3.1.3: @@ -3415,8 +3433,8 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - entities@6.0.0: - resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} environment@1.1.0: @@ -3563,6 +3581,10 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@9.18.0: resolution: {integrity: sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3709,8 +3731,8 @@ packages: resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} engines: {node: '>= 6'} - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} fraction.js@4.3.7: @@ -3928,6 +3950,10 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} @@ -5589,8 +5615,8 @@ packages: typed-rest-client@1.8.11: resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} - typescript-eslint@8.31.1: - resolution: {integrity: sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==} + typescript-eslint@8.35.1: + resolution: {integrity: sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -5891,8 +5917,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -5999,10 +6025,10 @@ snapshots: '@asamuzakjp/css-color@3.2.0': dependencies: - '@csstools/css-calc': 2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-color-parser': 3.0.9(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 optional: true @@ -6900,26 +6926,26 @@ snapshots: '@csstools/color-helpers@5.0.2': optional: true - '@csstools/css-calc@2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: - '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 optional: true - '@csstools/css-color-parser@3.0.9(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/color-helpers': 5.0.2 - '@csstools/css-calc': 2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) - '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 optional: true - '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': dependencies: - '@csstools/css-tokenizer': 3.0.3 + '@csstools/css-tokenizer': 3.0.4 optional: true - '@csstools/css-tokenizer@3.0.3': + '@csstools/css-tokenizer@3.0.4': optional: true '@esbuild/aix-ppc64@0.25.3': @@ -7007,6 +7033,11 @@ snapshots: eslint: 9.18.0(jiti@1.21.7) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.7.0(eslint@9.18.0(jiti@1.21.7))': + dependencies: + eslint: 9.18.0(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.1': {} '@eslint/config-array@0.19.1': @@ -8396,44 +8427,57 @@ snapshots: '@types/vscode@1.80.0': {} - '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/type-utils': 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/parser': 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/type-utils': 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.1 eslint: 9.18.0(jiti@1.21.7) graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/parser@8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 - debug: 4.4.0 + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.1 + debug: 4.4.1(supports-color@8.1.1) eslint: 9.18.0(jiti@1.21.7) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.31.1': + '@typescript-eslint/project-service@8.35.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 + debug: 4.4.1(supports-color@8.1.1) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.35.1': dependencies: - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 - '@typescript-eslint/type-utils@8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.35.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) debug: 4.4.1(supports-color@8.1.1) eslint: 9.18.0(jiti@1.21.7) ts-api-utils: 2.1.0(typescript@5.8.3) @@ -8441,12 +8485,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.31.1': {} + '@typescript-eslint/types@8.35.1': {} - '@typescript-eslint/typescript-estree@8.31.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.35.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/project-service': 8.35.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 debug: 4.4.1(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 @@ -8457,21 +8503,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/utils@8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@9.18.0(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.18.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) eslint: 9.18.0(jiti@1.21.7) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.31.1': + '@typescript-eslint/visitor-keys@8.35.1': dependencies: - '@typescript-eslint/types': 8.31.1 - eslint-visitor-keys: 4.2.0 + '@typescript-eslint/types': 8.35.1 + eslint-visitor-keys: 4.2.1 '@typescript/analyze-trace@0.10.1': dependencies: @@ -9150,7 +9196,7 @@ snapshots: cssesc@3.0.0: {} - cssstyle@4.3.1: + cssstyle@4.6.0: dependencies: '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 @@ -9320,7 +9366,7 @@ snapshots: entities@4.5.0: {} - entities@6.0.0: + entities@6.0.1: optional: true environment@1.1.0: {} @@ -9594,6 +9640,8 @@ snapshots: eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} + eslint@9.18.0(jiti@1.21.7): dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0(jiti@1.21.7)) @@ -9765,11 +9813,12 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 - form-data@4.0.2: + form-data@4.0.3: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 optional: true @@ -10018,6 +10067,8 @@ snapshots: ignore@5.3.2: {} + ignore@7.0.5: {} + immediate@3.0.6: {} import-fresh@3.3.0: @@ -10277,10 +10328,10 @@ snapshots: jsdom@24.1.0: dependencies: - cssstyle: 4.3.1 + cssstyle: 4.6.0 data-urls: 5.0.0 decimal.js: 10.5.0 - form-data: 4.0.2 + form-data: 4.0.3 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -10296,7 +10347,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.18.2 + ws: 8.18.3 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -11017,7 +11068,7 @@ snapshots: parse5@7.3.0: dependencies: - entities: 6.0.0 + entities: 6.0.1 optional: true password-prompt@1.1.3: @@ -11985,11 +12036,11 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.7 - typescript-eslint@8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3): + typescript-eslint@8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) - '@typescript-eslint/parser': 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.8.3) eslint: 9.18.0(jiti@1.21.7) typescript: 5.8.3 transitivePeerDependencies: @@ -12325,7 +12376,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.2: + ws@8.18.3: optional: true xml-name-validator@5.0.0: