mirror of
https://github.com/not-lucky/GeminiKeyManagement.git
synced 2025-12-06 08:44:01 +05:30
using json database; jsonschema for validation
This commit is contained in:
256
main.py
256
main.py
@@ -1,7 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import json
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
import jsonschema
|
||||||
import google.auth
|
import google.auth
|
||||||
from google.oauth2.credentials import Credentials
|
from google.oauth2.credentials import Credentials
|
||||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||||
@@ -13,12 +15,26 @@ from google.auth.transport import requests
|
|||||||
CREDENTIALS_DIR = "credentials"
|
CREDENTIALS_DIR = "credentials"
|
||||||
EMAILS_FILE = "emails.txt"
|
EMAILS_FILE = "emails.txt"
|
||||||
CLIENT_SECRETS_FILE = "credentials.json"
|
CLIENT_SECRETS_FILE = "credentials.json"
|
||||||
|
API_KEYS_DATABASE_FILE = "api_keys_database.json"
|
||||||
|
API_KEYS_SCHEMA_FILE = os.path.join("schemas", "v1", "api_keys_database.schema.json")
|
||||||
# ---------------------
|
# ---------------------
|
||||||
|
|
||||||
SCOPES = [
|
SCOPES = [
|
||||||
"https://www.googleapis.com/auth/cloud-platform",
|
"https://www.googleapis.com/auth/cloud-platform",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def load_schema(filename):
|
||||||
|
"""Loads a JSON schema from a file."""
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
print(f"Error: Schema file not found at '{filename}'")
|
||||||
|
sys.exit(1)
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
try:
|
||||||
|
return json.load(f)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f"Error: Could not decode JSON schema from {filename}.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def load_emails_from_file(filename):
|
def load_emails_from_file(filename):
|
||||||
"""Loads a list of emails from a text file, ignoring comments."""
|
"""Loads a list of emails from a text file, ignoring comments."""
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
@@ -29,23 +45,48 @@ def load_emails_from_file(filename):
|
|||||||
# Ignore empty lines and lines starting with #
|
# Ignore empty lines and lines starting with #
|
||||||
return [line.strip() for line in f if line.strip() and not line.startswith("#")]
|
return [line.strip() for line in f if line.strip() and not line.startswith("#")]
|
||||||
|
|
||||||
def load_existing_api_keys(email):
|
def load_keys_database(filename, schema):
|
||||||
"""Loads API keys from the user's key file."""
|
"""Loads and validates the JSON database of API keys."""
|
||||||
filename = f"{email}.keys.txt"
|
if not os.path.exists(filename):
|
||||||
keys = set()
|
return {
|
||||||
if os.path.exists(filename):
|
"schema_version": "1.0.0",
|
||||||
with open(filename, "r") as f:
|
"accounts": []
|
||||||
content = f.read()
|
}
|
||||||
# Find all occurrences of "Key: <key_string>"
|
with open(filename, "r") as f:
|
||||||
found_keys = re.findall(r"Key:\s*(.+)", content)
|
try:
|
||||||
keys.update(found_keys)
|
data = json.load(f)
|
||||||
return keys
|
jsonschema.validate(instance=data, schema=schema)
|
||||||
|
return data
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f"Warning: Could not decode JSON from {filename}. Starting fresh.")
|
||||||
|
except jsonschema.ValidationError as e:
|
||||||
|
print(f"Warning: Database file '{filename}' is not valid. {e.message}. Starting fresh.")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"schema_version": "1.0.0",
|
||||||
|
"accounts": []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def save_keys_to_json(data, filename, schema):
|
||||||
|
"""Validates and saves the API key data to a single JSON file."""
|
||||||
|
data["generation_timestamp_utc"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
try:
|
||||||
|
jsonschema.validate(instance=data, schema=schema)
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
print(f"--- Database saved to {filename} ---")
|
||||||
|
except jsonschema.ValidationError as e:
|
||||||
|
print(f"Error: Data to be saved is invalid. Could not write to '{filename}'.")
|
||||||
|
print(f"Validation Error: {e.message}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main function to orchestrate API key creation or deletion."""
|
"""Main function to orchestrate API key creation or deletion."""
|
||||||
parser = argparse.ArgumentParser(description="Manage Gemini API keys in Google Cloud projects.")
|
parser = argparse.ArgumentParser(description="Manage Gemini API keys in Google Cloud projects.")
|
||||||
parser.add_argument("action", choices=['create', 'delete'], help="The action to perform: 'create' or 'delete' API keys.")
|
parser.add_argument("action", choices=['create', 'delete'], help="The action to perform: 'create' or 'delete' API keys.")
|
||||||
parser.add_argument("--email", help="Specify a single email address to process. Required for 'delete' action. If not provided for 'create', emails will be read from emails.txt.")
|
parser.add_argument("--email", help="Specify a single email address to process. If not provided for 'create', emails will be read from emails.txt.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if not os.path.exists(CLIENT_SECRETS_FILE):
|
if not os.path.exists(CLIENT_SECRETS_FILE):
|
||||||
@@ -56,11 +97,14 @@ def main():
|
|||||||
if not os.path.exists(CREDENTIALS_DIR):
|
if not os.path.exists(CREDENTIALS_DIR):
|
||||||
os.makedirs(CREDENTIALS_DIR)
|
os.makedirs(CREDENTIALS_DIR)
|
||||||
|
|
||||||
|
schema = load_schema(API_KEYS_SCHEMA_FILE)
|
||||||
|
api_keys_data = load_keys_database(API_KEYS_DATABASE_FILE, schema)
|
||||||
|
|
||||||
emails_to_process = []
|
emails_to_process = []
|
||||||
if args.email:
|
if args.email:
|
||||||
emails_to_process.append(args.email)
|
emails_to_process.append(args.email)
|
||||||
elif args.action == 'delete':
|
elif args.action == 'delete':
|
||||||
print("Error: The 'delete' action requires the --email argument.")
|
print("Error: The 'delete' action requires the --email argument to specify which account's keys to delete.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else: # action is 'create' and no email provided
|
else: # action is 'create' and no email provided
|
||||||
emails_to_process = load_emails_from_file(EMAILS_FILE)
|
emails_to_process = load_emails_from_file(EMAILS_FILE)
|
||||||
@@ -69,9 +113,11 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
for email in emails_to_process:
|
for email in emails_to_process:
|
||||||
process_account(email, args.action)
|
process_account(email, args.action, api_keys_data)
|
||||||
|
|
||||||
def process_account(email, action):
|
save_keys_to_json(api_keys_data, API_KEYS_DATABASE_FILE, schema)
|
||||||
|
|
||||||
|
def process_account(email, action, api_keys_data):
|
||||||
"""Processes a single account for the given action."""
|
"""Processes a single account for the given action."""
|
||||||
print(f"--- Processing account: {email} for action: {action} ---")
|
print(f"--- Processing account: {email} for action: {action} ---")
|
||||||
creds = get_credentials_for_email(email)
|
creds = get_credentials_for_email(email)
|
||||||
@@ -79,12 +125,19 @@ def process_account(email, action):
|
|||||||
print(f"Could not get credentials for {email}. Skipping.")
|
print(f"Could not get credentials for {email}. Skipping.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Load existing keys if we are creating
|
account_entry = next((acc for acc in api_keys_data["accounts"] if acc.get("account_details", {}).get("email") == email), None)
|
||||||
existing_keys = set()
|
if not account_entry:
|
||||||
if action == 'create':
|
account_entry = {
|
||||||
existing_keys = load_existing_api_keys(email)
|
"account_details": {
|
||||||
if existing_keys:
|
"email": email,
|
||||||
print(f"Loaded {len(existing_keys)} existing keys from file.")
|
"authentication_details": {
|
||||||
|
"token_file": os.path.join(CREDENTIALS_DIR, f"{email}.json"),
|
||||||
|
"scopes": SCOPES
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projects": []
|
||||||
|
}
|
||||||
|
api_keys_data["accounts"].append(account_entry)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resource_manager = resourcemanager_v3.ProjectsClient(credentials=creds)
|
resource_manager = resourcemanager_v3.ProjectsClient(credentials=creds)
|
||||||
@@ -97,20 +150,21 @@ def process_account(email, action):
|
|||||||
print(f"Found {len(projects)} projects. Processing...")
|
print(f"Found {len(projects)} projects. Processing...")
|
||||||
for project in projects:
|
for project in projects:
|
||||||
project_id = project.project_id
|
project_id = project.project_id
|
||||||
print(f"- Project: {project_id}")
|
print(f"- Project: {project_id} ({project.display_name})")
|
||||||
|
|
||||||
if action == 'create':
|
if action == 'create':
|
||||||
# Check if a key with the specific display name already exists
|
|
||||||
if project_has_gemini_key(project_id, creds):
|
if project_has_gemini_key(project_id, creds):
|
||||||
print(" 'Gemini API Key' already exists in this project. Skipping creation.")
|
print(" 'Gemini API Key' already exists in this project. Skipping creation.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if enable_api(project_id, creds):
|
if enable_api(project_id, creds):
|
||||||
key = create_api_key(project_id, creds)
|
key_object = create_api_key(project_id, creds)
|
||||||
if key:
|
if key_object:
|
||||||
save_api_key(email, project_id, key.key_string)
|
add_key_to_database(account_entry, project, key_object)
|
||||||
elif action == 'delete':
|
elif action == 'delete':
|
||||||
delete_api_keys(project_id, creds)
|
deleted_keys_uids = delete_api_keys(project_id, creds)
|
||||||
|
if deleted_keys_uids:
|
||||||
|
remove_keys_from_database(account_entry, project_id, deleted_keys_uids)
|
||||||
|
|
||||||
except google_exceptions.PermissionDenied as err:
|
except google_exceptions.PermissionDenied as err:
|
||||||
print(f"Permission denied for account {email}. Check IAM roles.")
|
print(f"Permission denied for account {email}. Check IAM roles.")
|
||||||
@@ -118,6 +172,66 @@ def process_account(email, action):
|
|||||||
except google_exceptions.GoogleAPICallError as err:
|
except google_exceptions.GoogleAPICallError as err:
|
||||||
print(f"An API error occurred while processing account {email}: {err}")
|
print(f"An API error occurred while processing account {email}: {err}")
|
||||||
|
|
||||||
|
def add_key_to_database(account_entry, project, key_object):
|
||||||
|
"""Adds a new API key's details to the data structure."""
|
||||||
|
project_id = project.project_id
|
||||||
|
|
||||||
|
project_entry = next((p for p in account_entry["projects"] if p.get("project_info", {}).get("project_id") == project_id), None)
|
||||||
|
if not project_entry:
|
||||||
|
project_entry = {
|
||||||
|
"project_info": {
|
||||||
|
"project_id": project_id,
|
||||||
|
"project_name": project.display_name,
|
||||||
|
"project_number": project.name.split('/')[-1],
|
||||||
|
"state": str(project.state)
|
||||||
|
},
|
||||||
|
"api_keys": []
|
||||||
|
}
|
||||||
|
account_entry["projects"].append(project_entry)
|
||||||
|
|
||||||
|
api_targets = []
|
||||||
|
if key_object.restrictions and key_object.restrictions.api_targets:
|
||||||
|
for target in key_object.restrictions.api_targets:
|
||||||
|
api_targets.append({"service": target.service, "methods": []})
|
||||||
|
|
||||||
|
new_key_entry = {
|
||||||
|
"key_details": {
|
||||||
|
"key_string": key_object.key_string,
|
||||||
|
"key_id": key_object.uid,
|
||||||
|
"key_name": key_object.name,
|
||||||
|
"display_name": key_object.display_name,
|
||||||
|
"creation_timestamp_utc": key_object.create_time.isoformat(),
|
||||||
|
"last_updated_timestamp_utc": key_object.update_time.isoformat(),
|
||||||
|
},
|
||||||
|
"restrictions": {
|
||||||
|
"api_targets": api_targets
|
||||||
|
},
|
||||||
|
"state": "ACTIVE"
|
||||||
|
}
|
||||||
|
|
||||||
|
existing_key = next((k for k in project_entry["api_keys"] if k.get("key_details", {}).get("key_id") == key_object.uid), None)
|
||||||
|
if not existing_key:
|
||||||
|
project_entry["api_keys"].append(new_key_entry)
|
||||||
|
print(f" Added key {key_object.uid} to local database for project {project_id}")
|
||||||
|
else:
|
||||||
|
print(f" Key {key_object.uid} already exists in local database for project {project_id}")
|
||||||
|
|
||||||
|
def remove_keys_from_database(account_entry, project_id, deleted_keys_uids):
|
||||||
|
"""Removes deleted API keys from the data structure."""
|
||||||
|
project_entry = next((p for p in account_entry["projects"] if p.get("project_info", {}).get("project_id") == project_id), None)
|
||||||
|
if not project_entry:
|
||||||
|
return
|
||||||
|
|
||||||
|
initial_key_count = len(project_entry["api_keys"])
|
||||||
|
project_entry["api_keys"] = [
|
||||||
|
key for key in project_entry["api_keys"]
|
||||||
|
if key.get("key_details", {}).get("key_id") not in deleted_keys_uids
|
||||||
|
]
|
||||||
|
final_key_count = len(project_entry["api_keys"])
|
||||||
|
num_removed = initial_key_count - final_key_count
|
||||||
|
if num_removed > 0:
|
||||||
|
print(f" Removed {num_removed} key(s) from local database for project {project_id}")
|
||||||
|
|
||||||
def project_has_gemini_key(project_id, credentials):
|
def project_has_gemini_key(project_id, credentials):
|
||||||
"""Checks if a project already has a key named 'Gemini API Key'."""
|
"""Checks if a project already has a key named 'Gemini API Key'."""
|
||||||
try:
|
try:
|
||||||
@@ -129,10 +243,9 @@ def project_has_gemini_key(project_id, credentials):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
except google_exceptions.GoogleAPICallError as err:
|
except google_exceptions.GoogleAPICallError as err:
|
||||||
print(f" Could not list keys in project {project_id}. Assuming no key exists. Error: {err}")
|
print(f" Could not list keys in project {project_id}. Error: {err}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_credentials_for_email(email):
|
def get_credentials_for_email(email):
|
||||||
"""Handles the OAuth2 flow for a given email."""
|
"""Handles the OAuth2 flow for a given email."""
|
||||||
token_file = os.path.join(CREDENTIALS_DIR, f"{email}.json")
|
token_file = os.path.join(CREDENTIALS_DIR, f"{email}.json")
|
||||||
@@ -150,7 +263,6 @@ def get_credentials_for_email(email):
|
|||||||
flow = InstalledAppFlow.from_client_secrets_file(
|
flow = InstalledAppFlow.from_client_secrets_file(
|
||||||
CLIENT_SECRETS_FILE, SCOPES
|
CLIENT_SECRETS_FILE, SCOPES
|
||||||
)
|
)
|
||||||
# Use login_hint to pre-fill the email address
|
|
||||||
creds = flow.run_local_server(port=0, login_hint=email)
|
creds = flow.run_local_server(port=0, login_hint=email)
|
||||||
with open(token_file, "w") as token:
|
with open(token_file, "w") as token:
|
||||||
token.write(creds.to_json())
|
token.write(creds.to_json())
|
||||||
@@ -159,65 +271,63 @@ def get_credentials_for_email(email):
|
|||||||
print(f"An unexpected error occurred during authentication: {e}")
|
print(f"An unexpected error occurred during authentication: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def enable_api(project_id, credentials):
|
def enable_api(project_id, credentials):
|
||||||
"""Enables the Generative Language API for a project."""
|
"""Enables the Generative Language API if it's not already enabled."""
|
||||||
|
service_name = "generativelanguage.googleapis.com"
|
||||||
|
service_path = f"projects/{project_id}/services/{service_name}"
|
||||||
|
service_usage_client = service_usage_v1.ServiceUsageClient(credentials=credentials)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
service_usage_client = service_usage_v1.ServiceUsageClient(credentials=credentials)
|
service_request = service_usage_v1.GetServiceRequest(name=service_path)
|
||||||
service_name = "generativelanguage.googleapis.com"
|
service = service_usage_client.get_service(request=service_request)
|
||||||
request = service_usage_v1.EnableServiceRequest(
|
|
||||||
name=f"projects/{project_id}/services/{service_name}"
|
if service.state == service_usage_v1.State.ENABLED:
|
||||||
)
|
print(f" Generative Language API is already enabled for project {project_id}")
|
||||||
operation = service_usage_client.enable_service(request=request)
|
return True
|
||||||
print("Waiting for API enablement to complete...")
|
|
||||||
operation.result() # Wait for the operation to complete
|
print(f" API is not enabled. Attempting to enable...")
|
||||||
print(f"Enabled Generative Language API for project {project_id}")
|
enable_request = service_usage_v1.EnableServiceRequest(name=service_path)
|
||||||
|
operation = service_usage_client.enable_service(request=enable_request)
|
||||||
|
print(" Waiting for API enablement to complete...")
|
||||||
|
operation.result()
|
||||||
|
print(f" Successfully enabled Generative Language API for project {project_id}")
|
||||||
return True
|
return True
|
||||||
except google_exceptions.PermissionDenied as err:
|
|
||||||
print(f"Permission denied to enable API for project {project_id}. Skipping.")
|
except google_exceptions.PermissionDenied:
|
||||||
|
print(f" Permission denied to check or enable API for project {project_id}. Skipping.")
|
||||||
return False
|
return False
|
||||||
except google_exceptions.GoogleAPICallError as err:
|
except google_exceptions.GoogleAPICallError as err:
|
||||||
# Check if the error is that the API is already enabled
|
print(f" Error checking or enabling API for project {project_id}: {err}")
|
||||||
if "already been enabled" in str(err):
|
|
||||||
print(f"Generative Language API is already enabled for project {project_id}")
|
|
||||||
return True
|
|
||||||
print(f"Error enabling API for project {project_id}: {err}")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def create_api_key(project_id, credentials):
|
def create_api_key(project_id, credentials):
|
||||||
"""Creates a new, restricted API key."""
|
"""Creates a new, restricted API key."""
|
||||||
try:
|
try:
|
||||||
api_keys_client = api_keys_v2.ApiKeysClient(credentials=credentials)
|
api_keys_client = api_keys_v2.ApiKeysClient(credentials=credentials)
|
||||||
|
|
||||||
# Define the API target for restrictions
|
|
||||||
api_target = api_keys_v2.ApiTarget(service="generativelanguage.googleapis.com")
|
api_target = api_keys_v2.ApiTarget(service="generativelanguage.googleapis.com")
|
||||||
|
|
||||||
# Define the key with restrictions
|
|
||||||
key = api_keys_v2.Key(
|
key = api_keys_v2.Key(
|
||||||
display_name="Gemini API Key",
|
display_name="Gemini API Key",
|
||||||
restrictions=api_keys_v2.Restrictions(
|
restrictions=api_keys_v2.Restrictions(api_targets=[api_target]),
|
||||||
api_targets=[api_target]
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
request = api_keys_v2.CreateKeyRequest(
|
request = api_keys_v2.CreateKeyRequest(
|
||||||
parent=f"projects/{project_id}/locations/global",
|
parent=f"projects/{project_id}/locations/global",
|
||||||
key=key,
|
key=key,
|
||||||
)
|
)
|
||||||
print("Creating API key...")
|
print(" Creating API key...")
|
||||||
operation = api_keys_client.create_key(request=request)
|
operation = api_keys_client.create_key(request=request)
|
||||||
result = operation.result() # Wait for the operation to complete
|
result = operation.result()
|
||||||
print(f"Successfully created restricted API key for project {project_id}")
|
print(f" Successfully created restricted API key for project {project_id}")
|
||||||
return result
|
return result
|
||||||
except google_exceptions.PermissionDenied as err:
|
except google_exceptions.PermissionDenied:
|
||||||
print(f"Permission denied to create API key for project {project_id}. Skipping.")
|
print(f" Permission denied to create API key for project {project_id}. Skipping.")
|
||||||
return None
|
return None
|
||||||
except google_exceptions.GoogleAPICallError as err:
|
except google_exceptions.GoogleAPICallError as err:
|
||||||
print(f"Error creating API key for project {project_id}: {err}")
|
print(f" Error creating API key for project {project_id}: {err}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def delete_api_keys(project_id, credentials):
|
def delete_api_keys(project_id, credentials):
|
||||||
"""Deletes all API keys with the display name 'Gemini API Key'."""
|
"""Deletes all API keys with the display name 'Gemini API Key' and returns their UIDs."""
|
||||||
|
deleted_keys_uids = []
|
||||||
try:
|
try:
|
||||||
api_keys_client = api_keys_v2.ApiKeysClient(credentials=credentials)
|
api_keys_client = api_keys_v2.ApiKeysClient(credentials=credentials)
|
||||||
parent = f"projects/{project_id}/locations/global"
|
parent = f"projects/{project_id}/locations/global"
|
||||||
@@ -227,30 +337,24 @@ def delete_api_keys(project_id, credentials):
|
|||||||
|
|
||||||
if not keys_to_delete:
|
if not keys_to_delete:
|
||||||
print(f" No 'Gemini API Key' found to delete.")
|
print(f" No 'Gemini API Key' found to delete.")
|
||||||
return
|
return []
|
||||||
|
|
||||||
print(f" Found {len(keys_to_delete)} key(s) with display name 'Gemini API Key'. Deleting...")
|
print(f" Found {len(keys_to_delete)} key(s) with display name 'Gemini API Key'. Deleting...")
|
||||||
for key in keys_to_delete:
|
for key in keys_to_delete:
|
||||||
try:
|
try:
|
||||||
request = api_keys_v2.DeleteKeyRequest(name=key.name)
|
request = api_keys_v2.DeleteKeyRequest(name=key.name)
|
||||||
operation = api_keys_client.delete_key(request=request)
|
operation = api_keys_client.delete_key(request=request)
|
||||||
operation.result() # Wait for completion
|
operation.result()
|
||||||
print(f" Successfully deleted key: {key.uid}")
|
print(f" Successfully deleted key: {key.uid}")
|
||||||
|
deleted_keys_uids.append(key.uid)
|
||||||
except google_exceptions.GoogleAPICallError as err:
|
except google_exceptions.GoogleAPICallError as err:
|
||||||
print(f" Error deleting key {key.uid}: {err}")
|
print(f" Error deleting key {key.uid}: {err}")
|
||||||
|
return deleted_keys_uids
|
||||||
except google_exceptions.PermissionDenied as err:
|
except google_exceptions.PermissionDenied:
|
||||||
print(f" Permission denied to list or delete API keys for project {project_id}. Skipping.")
|
print(f" Permission denied to list or delete API keys for project {project_id}. Skipping.")
|
||||||
except google_exceptions.GoogleAPICallError as err:
|
except google_exceptions.GoogleAPICallError as err:
|
||||||
print(f" An API error occurred while deleting keys for project {project_id}: {err}")
|
print(f" An API error occurred while deleting keys for project {project_id}: {err}")
|
||||||
|
return []
|
||||||
|
|
||||||
def save_api_key(email, project_id, api_key):
|
|
||||||
"""Saves the API key to a file, including the project ID."""
|
|
||||||
filename = f"{email}.keys.txt"
|
|
||||||
with open(filename, "a") as f:
|
|
||||||
f.write(f"Project: {project_id}\nKey: {api_key}\n\n")
|
|
||||||
print(f"Saved API key to {filename}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
@@ -9,4 +9,5 @@ dependencies = [
|
|||||||
"google-cloud-api-keys>=0.5.17",
|
"google-cloud-api-keys>=0.5.17",
|
||||||
"google-cloud-resource-manager>=1.14.2",
|
"google-cloud-resource-manager>=1.14.2",
|
||||||
"google-cloud-service-usage>=1.13.1",
|
"google-cloud-service-usage>=1.13.1",
|
||||||
|
"jsonschema>=4.25.1",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ google-auth-oauthlib>=1.2.2
|
|||||||
google-cloud-api-keys>=0.5.17
|
google-cloud-api-keys>=0.5.17
|
||||||
google-cloud-resource-manager>=1.14.2
|
google-cloud-resource-manager>=1.14.2
|
||||||
google-cloud-service-usage>=1.13.1
|
google-cloud-service-usage>=1.13.1
|
||||||
|
jsonschema>=4.22.0
|
||||||
140
schemas/v1/api_keys_database.schema.json
Normal file
140
schemas/v1/api_keys_database.schema.json
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Gemini API Keys Database Schema",
|
||||||
|
"description": "Schema for the api_keys_database.json file.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"generation_timestamp_utc": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"accounts": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"Account": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"account_details": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"email": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "email"
|
||||||
|
},
|
||||||
|
"authentication_details": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"token_file": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"scopes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Project": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project_info": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"project_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"project_number": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"api_keys": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/ApiKey"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ApiKey": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"key_details": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"key_string": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"key_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"key_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"display_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"creation_timestamp_utc": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"last_updated_timestamp_utc": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"restrictions": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"api_targets": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"service": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
142
uv.lock
generated
142
uv.lock
generated
@@ -6,6 +6,15 @@ resolution-markers = [
|
|||||||
"python_full_version < '3.13'",
|
"python_full_version < '3.13'",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "attrs"
|
||||||
|
version = "25.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cachetools"
|
name = "cachetools"
|
||||||
version = "5.5.2"
|
version = "5.5.2"
|
||||||
@@ -75,6 +84,7 @@ dependencies = [
|
|||||||
{ name = "google-cloud-api-keys" },
|
{ name = "google-cloud-api-keys" },
|
||||||
{ name = "google-cloud-resource-manager" },
|
{ name = "google-cloud-resource-manager" },
|
||||||
{ name = "google-cloud-service-usage" },
|
{ name = "google-cloud-service-usage" },
|
||||||
|
{ name = "jsonschema" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
@@ -83,6 +93,7 @@ requires-dist = [
|
|||||||
{ name = "google-cloud-api-keys", specifier = ">=0.5.17" },
|
{ name = "google-cloud-api-keys", specifier = ">=0.5.17" },
|
||||||
{ name = "google-cloud-resource-manager", specifier = ">=1.14.2" },
|
{ name = "google-cloud-resource-manager", specifier = ">=1.14.2" },
|
||||||
{ name = "google-cloud-service-usage", specifier = ">=1.13.1" },
|
{ name = "google-cloud-service-usage", specifier = ">=1.13.1" },
|
||||||
|
{ name = "jsonschema", specifier = ">=4.25.1" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -262,6 +273,33 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonschema"
|
||||||
|
version = "4.25.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "attrs" },
|
||||||
|
{ name = "jsonschema-specifications" },
|
||||||
|
{ name = "referencing" },
|
||||||
|
{ name = "rpds-py" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonschema-specifications"
|
||||||
|
version = "2025.4.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "referencing" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oauthlib"
|
name = "oauthlib"
|
||||||
version = "3.3.1"
|
version = "3.3.1"
|
||||||
@@ -318,6 +356,20 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
|
{ url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "referencing"
|
||||||
|
version = "0.36.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "attrs" },
|
||||||
|
{ name = "rpds-py" },
|
||||||
|
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.32.5"
|
version = "2.32.5"
|
||||||
@@ -346,6 +398,87 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" },
|
{ url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpds-py"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1e/d9/991a0dee12d9fc53ed027e26a26a64b151d77252ac477e22666b9688bc16/rpds_py-0.27.0.tar.gz", hash = "sha256:8b23cf252f180cda89220b378d917180f29d313cd6a07b2431c0d3b776aae86f", size = 27420, upload-time = "2025-08-07T08:26:39.624Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/17/e67309ca1ac993fa1888a0d9b2f5ccc1f67196ace32e76c9f8e1dbbbd50c/rpds_py-0.27.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:19c990fdf5acecbf0623e906ae2e09ce1c58947197f9bced6bbd7482662231c4", size = 362611, upload-time = "2025-08-07T08:23:44.773Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/2e/28c2fb84aa7aa5d75933d1862d0f7de6198ea22dfd9a0cca06e8a4e7509e/rpds_py-0.27.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c27a7054b5224710fcfb1a626ec3ff4f28bcb89b899148c72873b18210e446b", size = 347680, upload-time = "2025-08-07T08:23:46.014Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/3e/9834b4c8f4f5fe936b479e623832468aa4bd6beb8d014fecaee9eac6cdb1/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09965b314091829b378b60607022048953e25f0b396c2b70e7c4c81bcecf932e", size = 384600, upload-time = "2025-08-07T08:23:48Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/78/744123c7b38865a965cd9e6f691fde7ef989a00a256fa8bf15b75240d12f/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:14f028eb47f59e9169bfdf9f7ceafd29dd64902141840633683d0bad5b04ff34", size = 400697, upload-time = "2025-08-07T08:23:49.407Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/97/3c3d32fe7daee0a1f1a678b6d4dfb8c4dcf88197fa2441f9da7cb54a8466/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6168af0be75bba990a39f9431cdfae5f0ad501f4af32ae62e8856307200517b8", size = 517781, upload-time = "2025-08-07T08:23:50.557Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/be/28f0e3e733680aa13ecec1212fc0f585928a206292f14f89c0b8a684cad1/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab47fe727c13c09d0e6f508e3a49e545008e23bf762a245b020391b621f5b726", size = 406449, upload-time = "2025-08-07T08:23:51.732Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/ae/5d15c83e337c082d0367053baeb40bfba683f42459f6ebff63a2fd7e5518/rpds_py-0.27.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa01b3d5e3b7d97efab65bd3d88f164e289ec323a8c033c5c38e53ee25c007e", size = 386150, upload-time = "2025-08-07T08:23:52.822Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/65/944e95f95d5931112829e040912b25a77b2e7ed913ea5fe5746aa5c1ce75/rpds_py-0.27.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:6c135708e987f46053e0a1246a206f53717f9fadfba27174a9769ad4befba5c3", size = 406100, upload-time = "2025-08-07T08:23:54.339Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/21/a4/1664b83fae02894533cd11dc0b9f91d673797c2185b7be0f7496107ed6c5/rpds_py-0.27.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc327f4497b7087d06204235199daf208fd01c82d80465dc5efa4ec9df1c5b4e", size = 421345, upload-time = "2025-08-07T08:23:55.832Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/26/b7303941c2b0823bfb34c71378249f8beedce57301f400acb04bb345d025/rpds_py-0.27.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e57906e38583a2cba67046a09c2637e23297618dc1f3caddbc493f2be97c93f", size = 561891, upload-time = "2025-08-07T08:23:56.951Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/c8/48623d64d4a5a028fa99576c768a6159db49ab907230edddc0b8468b998b/rpds_py-0.27.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f4f69d7a4300fbf91efb1fb4916421bd57804c01ab938ab50ac9c4aa2212f03", size = 591756, upload-time = "2025-08-07T08:23:58.146Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/51/18f62617e8e61cc66334c9fb44b1ad7baae3438662098efbc55fb3fda453/rpds_py-0.27.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b4c4fbbcff474e1e5f38be1bf04511c03d492d42eec0babda5d03af3b5589374", size = 557088, upload-time = "2025-08-07T08:23:59.6Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/4c/e84c3a276e2496a93d245516be6b49e20499aa8ca1c94d59fada0d79addc/rpds_py-0.27.0-cp312-cp312-win32.whl", hash = "sha256:27bac29bbbf39601b2aab474daf99dbc8e7176ca3389237a23944b17f8913d97", size = 221926, upload-time = "2025-08-07T08:24:00.695Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/89/9d0fbcef64340db0605eb0a0044f258076f3ae0a3b108983b2c614d96212/rpds_py-0.27.0-cp312-cp312-win_amd64.whl", hash = "sha256:8a06aa1197ec0281eb1d7daf6073e199eb832fe591ffa329b88bae28f25f5fe5", size = 233235, upload-time = "2025-08-07T08:24:01.846Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/b0/e177aa9f39cbab060f96de4a09df77d494f0279604dc2f509263e21b05f9/rpds_py-0.27.0-cp312-cp312-win_arm64.whl", hash = "sha256:e14aab02258cb776a108107bd15f5b5e4a1bbaa61ef33b36693dfab6f89d54f9", size = 223315, upload-time = "2025-08-07T08:24:03.337Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/d2/dfdfd42565a923b9e5a29f93501664f5b984a802967d48d49200ad71be36/rpds_py-0.27.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:443d239d02d9ae55b74015234f2cd8eb09e59fbba30bf60baeb3123ad4c6d5ff", size = 362133, upload-time = "2025-08-07T08:24:04.508Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ac/4a/0a2e2460c4b66021d349ce9f6331df1d6c75d7eea90df9785d333a49df04/rpds_py-0.27.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b8a7acf04fda1f30f1007f3cc96d29d8cf0a53e626e4e1655fdf4eabc082d367", size = 347128, upload-time = "2025-08-07T08:24:05.695Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/8d/7d1e4390dfe09d4213b3175a3f5a817514355cb3524593380733204f20b9/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d0f92b78cfc3b74a42239fdd8c1266f4715b573204c234d2f9fc3fc7a24f185", size = 384027, upload-time = "2025-08-07T08:24:06.841Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/65/78499d1a62172891c8cd45de737b2a4b84a414b6ad8315ab3ac4945a5b61/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ce4ed8e0c7dbc5b19352b9c2c6131dd23b95fa8698b5cdd076307a33626b72dc", size = 399973, upload-time = "2025-08-07T08:24:08.143Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/a1/1c67c1d8cc889107b19570bb01f75cf49852068e95e6aee80d22915406fc/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fde355b02934cc6b07200cc3b27ab0c15870a757d1a72fd401aa92e2ea3c6bfe", size = 515295, upload-time = "2025-08-07T08:24:09.711Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/df/27/700ec88e748436b6c7c4a2262d66e80f8c21ab585d5e98c45e02f13f21c0/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13bbc4846ae4c993f07c93feb21a24d8ec637573d567a924b1001e81c8ae80f9", size = 406737, upload-time = "2025-08-07T08:24:11.182Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/cc/6b0ee8f0ba3f2df2daac1beda17fde5cf10897a7d466f252bd184ef20162/rpds_py-0.27.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0744661afbc4099fef7f4e604e7f1ea1be1dd7284f357924af12a705cc7d5c", size = 385898, upload-time = "2025-08-07T08:24:12.798Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/7e/c927b37d7d33c0a0ebf249cc268dc2fcec52864c1b6309ecb960497f2285/rpds_py-0.27.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:069e0384a54f427bd65d7fda83b68a90606a3835901aaff42185fcd94f5a9295", size = 405785, upload-time = "2025-08-07T08:24:14.906Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5b/d2/8ed50746d909dcf402af3fa58b83d5a590ed43e07251d6b08fad1a535ba6/rpds_py-0.27.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4bc262ace5a1a7dc3e2eac2fa97b8257ae795389f688b5adf22c5db1e2431c43", size = 419760, upload-time = "2025-08-07T08:24:16.129Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d3/60/2b2071aee781cb3bd49f94d5d35686990b925e9b9f3e3d149235a6f5d5c1/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2fe6e18e5c8581f0361b35ae575043c7029d0a92cb3429e6e596c2cdde251432", size = 561201, upload-time = "2025-08-07T08:24:17.645Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/1f/27b67304272521aaea02be293fecedce13fa351a4e41cdb9290576fc6d81/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d93ebdb82363d2e7bec64eecdc3632b59e84bd270d74fe5be1659f7787052f9b", size = 591021, upload-time = "2025-08-07T08:24:18.999Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/9b/a2fadf823164dd085b1f894be6443b0762a54a7af6f36e98e8fcda69ee50/rpds_py-0.27.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0954e3a92e1d62e83a54ea7b3fdc9efa5d61acef8488a8a3d31fdafbfb00460d", size = 556368, upload-time = "2025-08-07T08:24:20.54Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/f3/6d135d46a129cda2e3e6d4c5e91e2cc26ea0428c6cf152763f3f10b6dd05/rpds_py-0.27.0-cp313-cp313-win32.whl", hash = "sha256:2cff9bdd6c7b906cc562a505c04a57d92e82d37200027e8d362518df427f96cd", size = 221236, upload-time = "2025-08-07T08:24:22.144Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/44/65d7494f5448ecc755b545d78b188440f81da98b50ea0447ab5ebfdf9bd6/rpds_py-0.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc79d192fb76fc0c84f2c58672c17bbbc383fd26c3cdc29daae16ce3d927e8b2", size = 232634, upload-time = "2025-08-07T08:24:23.642Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/d9/23852410fadab2abb611733933401de42a1964ce6600a3badae35fbd573e/rpds_py-0.27.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b3a5c8089eed498a3af23ce87a80805ff98f6ef8f7bdb70bd1b7dae5105f6ac", size = 222783, upload-time = "2025-08-07T08:24:25.098Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/15/75/03447917f78512b34463f4ef11066516067099a0c466545655503bed0c77/rpds_py-0.27.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:90fb790138c1a89a2e58c9282fe1089638401f2f3b8dddd758499041bc6e0774", size = 359154, upload-time = "2025-08-07T08:24:26.249Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/fc/4dac4fa756451f2122ddaf136e2c6aeb758dc6fdbe9ccc4bc95c98451d50/rpds_py-0.27.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010c4843a3b92b54373e3d2291a7447d6c3fc29f591772cc2ea0e9f5c1da434b", size = 343909, upload-time = "2025-08-07T08:24:27.405Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/81/723c1ed8e6f57ed9d8c0c07578747a2d3d554aaefc1ab89f4e42cfeefa07/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9ce7a9e967afc0a2af7caa0d15a3e9c1054815f73d6a8cb9225b61921b419bd", size = 379340, upload-time = "2025-08-07T08:24:28.714Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/16/7e3740413de71818ce1997df82ba5f94bae9fff90c0a578c0e24658e6201/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa0bf113d15e8abdfee92aa4db86761b709a09954083afcb5bf0f952d6065fdb", size = 391655, upload-time = "2025-08-07T08:24:30.223Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/63/2a9f510e124d80660f60ecce07953f3f2d5f0b96192c1365443859b9c87f/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb91d252b35004a84670dfeafadb042528b19842a0080d8b53e5ec1128e8f433", size = 513017, upload-time = "2025-08-07T08:24:31.446Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/4e/cf6ff311d09776c53ea1b4f2e6700b9d43bb4e99551006817ade4bbd6f78/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db8a6313dbac934193fc17fe7610f70cd8181c542a91382531bef5ed785e5615", size = 402058, upload-time = "2025-08-07T08:24:32.613Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/11/5e36096d474cb10f2a2d68b22af60a3bc4164fd8db15078769a568d9d3ac/rpds_py-0.27.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce96ab0bdfcef1b8c371ada2100767ace6804ea35aacce0aef3aeb4f3f499ca8", size = 383474, upload-time = "2025-08-07T08:24:33.767Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/a2/3dff02805b06058760b5eaa6d8cb8db3eb3e46c9e452453ad5fc5b5ad9fe/rpds_py-0.27.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:7451ede3560086abe1aa27dcdcf55cd15c96b56f543fb12e5826eee6f721f858", size = 400067, upload-time = "2025-08-07T08:24:35.021Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/87/eed7369b0b265518e21ea836456a4ed4a6744c8c12422ce05bce760bb3cf/rpds_py-0.27.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:32196b5a99821476537b3f7732432d64d93a58d680a52c5e12a190ee0135d8b5", size = 412085, upload-time = "2025-08-07T08:24:36.267Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/48/f50b2ab2fbb422fbb389fe296e70b7a6b5ea31b263ada5c61377e710a924/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a029be818059870664157194e46ce0e995082ac49926f1423c1f058534d2aaa9", size = 555928, upload-time = "2025-08-07T08:24:37.573Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/41/b18eb51045d06887666c3560cd4bbb6819127b43d758f5adb82b5f56f7d1/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3841f66c1ffdc6cebce8aed64e36db71466f1dc23c0d9a5592e2a782a3042c79", size = 585527, upload-time = "2025-08-07T08:24:39.391Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/03/a3dd6470fc76499959b00ae56295b76b4bdf7c6ffc60d62006b1217567e1/rpds_py-0.27.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:42894616da0fc0dcb2ec08a77896c3f56e9cb2f4b66acd76fc8992c3557ceb1c", size = 554211, upload-time = "2025-08-07T08:24:40.6Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/d1/ee5fd1be395a07423ac4ca0bcc05280bf95db2b155d03adefeb47d5ebf7e/rpds_py-0.27.0-cp313-cp313t-win32.whl", hash = "sha256:b1fef1f13c842a39a03409e30ca0bf87b39a1e2a305a9924deadb75a43105d23", size = 216624, upload-time = "2025-08-07T08:24:42.204Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/94/4814c4c858833bf46706f87349c37ca45e154da7dbbec9ff09f1abeb08cc/rpds_py-0.27.0-cp313-cp313t-win_amd64.whl", hash = "sha256:183f5e221ba3e283cd36fdfbe311d95cd87699a083330b4f792543987167eff1", size = 230007, upload-time = "2025-08-07T08:24:43.329Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/a5/8fffe1c7dc7c055aa02df310f9fb71cfc693a4d5ccc5de2d3456ea5fb022/rpds_py-0.27.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:f3cd110e02c5bf17d8fb562f6c9df5c20e73029d587cf8602a2da6c5ef1e32cb", size = 362595, upload-time = "2025-08-07T08:24:44.478Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/c7/4e4253fd2d4bb0edbc0b0b10d9f280612ca4f0f990e3c04c599000fe7d71/rpds_py-0.27.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8d0e09cf4863c74106b5265c2c310f36146e2b445ff7b3018a56799f28f39f6f", size = 347252, upload-time = "2025-08-07T08:24:45.678Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f3/c8/3d1a954d30f0174dd6baf18b57c215da03cf7846a9d6e0143304e784cddc/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f689ab822f9b5eb6dfc69893b4b9366db1d2420f7db1f6a2adf2a9ca15ad64", size = 384886, upload-time = "2025-08-07T08:24:46.86Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/52/3c5835f2df389832b28f9276dd5395b5a965cea34226e7c88c8fbec2093c/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e36c80c49853b3ffda7aa1831bf175c13356b210c73128c861f3aa93c3cc4015", size = 399716, upload-time = "2025-08-07T08:24:48.174Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/73/176e46992461a1749686a2a441e24df51ff86b99c2d34bf39f2a5273b987/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6de6a7f622860af0146cb9ee148682ff4d0cea0b8fd3ad51ce4d40efb2f061d0", size = 517030, upload-time = "2025-08-07T08:24:49.52Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/2a/7266c75840e8c6e70effeb0d38922a45720904f2cd695e68a0150e5407e2/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4045e2fc4b37ec4b48e8907a5819bdd3380708c139d7cc358f03a3653abedb89", size = 408448, upload-time = "2025-08-07T08:24:50.727Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/5f/a7efc572b8e235093dc6cf39f4dbc8a7f08e65fdbcec7ff4daeb3585eef1/rpds_py-0.27.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da162b718b12c4219eeeeb68a5b7552fbc7aadedf2efee440f88b9c0e54b45d", size = 387320, upload-time = "2025-08-07T08:24:52.004Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/eb/9ff6bc92efe57cf5a2cb74dee20453ba444b6fdc85275d8c99e0d27239d1/rpds_py-0.27.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:0665be515767dc727ffa5f74bd2ef60b0ff85dad6bb8f50d91eaa6b5fb226f51", size = 407414, upload-time = "2025-08-07T08:24:53.664Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/bd/3b9b19b00d5c6e1bd0f418c229ab0f8d3b110ddf7ec5d9d689ef783d0268/rpds_py-0.27.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:203f581accef67300a942e49a37d74c12ceeef4514874c7cede21b012613ca2c", size = 420766, upload-time = "2025-08-07T08:24:55.917Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/17/6b/521a7b1079ce16258c70805166e3ac6ec4ee2139d023fe07954dc9b2d568/rpds_py-0.27.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7873b65686a6471c0037139aa000d23fe94628e0daaa27b6e40607c90e3f5ec4", size = 562409, upload-time = "2025-08-07T08:24:57.17Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/bf/65db5bfb14ccc55e39de8419a659d05a2a9cd232f0a699a516bb0991da7b/rpds_py-0.27.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:249ab91ceaa6b41abc5f19513cb95b45c6f956f6b89f1fe3d99c81255a849f9e", size = 590793, upload-time = "2025-08-07T08:24:58.388Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/b8/82d368b378325191ba7aae8f40f009b78057b598d4394d1f2cdabaf67b3f/rpds_py-0.27.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2f184336bc1d6abfaaa1262ed42739c3789b1e3a65a29916a615307d22ffd2e", size = 558178, upload-time = "2025-08-07T08:24:59.756Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f6/ff/f270bddbfbc3812500f8131b1ebbd97afd014cd554b604a3f73f03133a36/rpds_py-0.27.0-cp314-cp314-win32.whl", hash = "sha256:d3c622c39f04d5751408f5b801ecb527e6e0a471b367f420a877f7a660d583f6", size = 222355, upload-time = "2025-08-07T08:25:01.027Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/20/fdab055b1460c02ed356a0e0b0a78c1dd32dc64e82a544f7b31c9ac643dc/rpds_py-0.27.0-cp314-cp314-win_amd64.whl", hash = "sha256:cf824aceaeffff029ccfba0da637d432ca71ab21f13e7f6f5179cd88ebc77a8a", size = 234007, upload-time = "2025-08-07T08:25:02.268Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/a8/694c060005421797a3be4943dab8347c76c2b429a9bef68fb2c87c9e70c7/rpds_py-0.27.0-cp314-cp314-win_arm64.whl", hash = "sha256:86aca1616922b40d8ac1b3073a1ead4255a2f13405e5700c01f7c8d29a03972d", size = 223527, upload-time = "2025-08-07T08:25:03.45Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/f9/77f4c90f79d2c5ca8ce6ec6a76cb4734ee247de6b3a4f337e289e1f00372/rpds_py-0.27.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:341d8acb6724c0c17bdf714319c393bb27f6d23d39bc74f94221b3e59fc31828", size = 359469, upload-time = "2025-08-07T08:25:04.648Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/22/b97878d2f1284286fef4172069e84b0b42b546ea7d053e5fb7adb9ac6494/rpds_py-0.27.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6b96b0b784fe5fd03beffff2b1533dc0d85e92bab8d1b2c24ef3a5dc8fac5669", size = 343960, upload-time = "2025-08-07T08:25:05.863Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/b0/dfd55b5bb480eda0578ae94ef256d3061d20b19a0f5e18c482f03e65464f/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c431bfb91478d7cbe368d0a699978050d3b112d7f1d440a41e90faa325557fd", size = 380201, upload-time = "2025-08-07T08:25:07.513Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/22/e1fa64e50d58ad2b2053077e3ec81a979147c43428de9e6de68ddf6aff4e/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20e222a44ae9f507d0f2678ee3dd0c45ec1e930f6875d99b8459631c24058aec", size = 392111, upload-time = "2025-08-07T08:25:09.149Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/f9/43ab7a43e97aedf6cea6af70fdcbe18abbbc41d4ae6cdec1bfc23bbad403/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:184f0d7b342967f6cda94a07d0e1fae177d11d0b8f17d73e06e36ac02889f303", size = 515863, upload-time = "2025-08-07T08:25:10.431Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/38/9b/9bd59dcc636cd04d86a2d20ad967770bf348f5eb5922a8f29b547c074243/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a00c91104c173c9043bc46f7b30ee5e6d2f6b1149f11f545580f5d6fdff42c0b", size = 402398, upload-time = "2025-08-07T08:25:11.819Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/bf/f099328c6c85667aba6b66fa5c35a8882db06dcd462ea214be72813a0dd2/rpds_py-0.27.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7a37dd208f0d658e0487522078b1ed68cd6bce20ef4b5a915d2809b9094b410", size = 384665, upload-time = "2025-08-07T08:25:13.194Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/c5/9c1f03121ece6634818490bd3c8be2c82a70928a19de03467fb25a3ae2a8/rpds_py-0.27.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:92f3b3ec3e6008a1fe00b7c0946a170f161ac00645cde35e3c9a68c2475e8156", size = 400405, upload-time = "2025-08-07T08:25:14.417Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/b8/e25d54af3e63ac94f0c16d8fe143779fe71ff209445a0c00d0f6984b6b2c/rpds_py-0.27.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1b3db5fae5cbce2131b7420a3f83553d4d89514c03d67804ced36161fe8b6b2", size = 413179, upload-time = "2025-08-07T08:25:15.664Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/d1/406b3316433fe49c3021546293a04bc33f1478e3ec7950215a7fce1a1208/rpds_py-0.27.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5355527adaa713ab693cbce7c1e0ec71682f599f61b128cf19d07e5c13c9b1f1", size = 556895, upload-time = "2025-08-07T08:25:17.061Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/bc/3697c0c21fcb9a54d46ae3b735eb2365eea0c2be076b8f770f98e07998de/rpds_py-0.27.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fcc01c57ce6e70b728af02b2401c5bc853a9e14eb07deda30624374f0aebfe42", size = 585464, upload-time = "2025-08-07T08:25:18.406Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/09/ee1bb5536f99f42c839b177d552f6114aa3142d82f49cef49261ed28dbe0/rpds_py-0.27.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3001013dae10f806380ba739d40dee11db1ecb91684febb8406a87c2ded23dae", size = 555090, upload-time = "2025-08-07T08:25:20.461Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/2c/363eada9e89f7059199d3724135a86c47082cbf72790d6ba2f336d146ddb/rpds_py-0.27.0-cp314-cp314t-win32.whl", hash = "sha256:0f401c369186a5743694dd9fc08cba66cf70908757552e1f714bfc5219c655b5", size = 218001, upload-time = "2025-08-07T08:25:21.761Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/3f/d6c216ed5199c9ef79e2a33955601f454ed1e7420a93b89670133bca5ace/rpds_py-0.27.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8a1dca5507fa1337f75dcd5070218b20bc68cf8844271c923c1b79dfcbc20391", size = 230993, upload-time = "2025-08-07T08:25:23.34Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "4.9.1"
|
version = "4.9.1"
|
||||||
@@ -358,6 +491,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
|
{ url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.14.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user