Fix JSON decode error by adding Accept header to all API requests
The CLI was getting empty responses from the server because it wasn't sending Accept: application/json header. The server uses content negotiation and returns HTML for browser requests. Changes: - Updated get_auth_header() to always include Accept: application/json - Simplified all commands to use get_auth_header(require_token=True) - Removed redundant token_data checks now handled by get_auth_header Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
87
artdag.py
87
artdag.py
@@ -53,18 +53,22 @@ def clear_token():
|
||||
TOKEN_FILE.unlink()
|
||||
|
||||
|
||||
def get_auth_header() -> dict:
|
||||
"""Get Authorization header if token exists."""
|
||||
def get_auth_header(require_token: bool = False) -> dict:
|
||||
"""Get headers for API requests. Always includes Accept: application/json."""
|
||||
headers = {"Accept": "application/json"}
|
||||
token_data = load_token()
|
||||
token = token_data.get("access_token")
|
||||
if token:
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
return {}
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
elif require_token:
|
||||
click.echo("Not logged in. Use 'artdag login' first.", err=True)
|
||||
sys.exit(1)
|
||||
return headers
|
||||
|
||||
|
||||
def api_get(path: str, auth: bool = False):
|
||||
"""GET request to server."""
|
||||
headers = get_auth_header() if auth else {}
|
||||
headers = get_auth_header(require_token=auth)
|
||||
resp = requests.get(f"{get_server()}{path}", headers=headers)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
@@ -72,7 +76,7 @@ def api_get(path: str, auth: bool = False):
|
||||
|
||||
def api_post(path: str, data: dict = None, params: dict = None, auth: bool = False):
|
||||
"""POST request to server."""
|
||||
headers = get_auth_header() if auth else {}
|
||||
headers = get_auth_header(require_token=auth)
|
||||
resp = requests.post(f"{get_server()}{path}", json=data, params=params, headers=headers)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
@@ -263,7 +267,7 @@ def stats():
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.get(f"{get_server()}/api/stats", headers=headers)
|
||||
resp.raise_for_status()
|
||||
stats = resp.json()
|
||||
@@ -294,7 +298,7 @@ def clear_data(force):
|
||||
|
||||
# Show current stats first
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.get(f"{get_server()}/api/stats", headers=headers)
|
||||
resp.raise_for_status()
|
||||
stats = resp.json()
|
||||
@@ -409,13 +413,9 @@ def run(recipe, input_hash, name, wait):
|
||||
@click.option("--offset", "-o", default=0, help="Offset for pagination")
|
||||
def list_runs(limit, offset):
|
||||
"""List all runs with pagination."""
|
||||
token_data = load_token()
|
||||
if not token_data.get("access_token"):
|
||||
click.echo("Not logged in. Use 'artdag login' first.", err=True)
|
||||
sys.exit(1)
|
||||
headers = get_auth_header(require_token=True)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
resp = requests.get(f"{get_server()}/runs?offset={offset}&limit={limit}", headers=headers)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
@@ -449,10 +449,7 @@ def list_runs(limit, offset):
|
||||
@click.option("--analysis", is_flag=True, help="Show audio analysis data")
|
||||
def status(run_id, plan, artifacts, analysis):
|
||||
"""Get status of a run with optional detailed views."""
|
||||
token_data = load_token()
|
||||
headers = {}
|
||||
if token_data.get("access_token"):
|
||||
headers["Authorization"] = f"Bearer {token_data['access_token']}"
|
||||
headers = get_auth_header() # Optional auth, always has Accept header
|
||||
|
||||
try:
|
||||
resp = requests.get(f"{get_server()}/runs/{run_id}", headers=headers)
|
||||
@@ -606,7 +603,7 @@ def delete_run(run_id, force):
|
||||
return
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.delete(f"{get_server()}/runs/{run_id}", headers=headers)
|
||||
if resp.status_code == 400:
|
||||
click.echo(f"Cannot delete: {resp.json().get('detail', 'Unknown error')}", err=True)
|
||||
@@ -645,7 +642,7 @@ def delete_cache(cid, force):
|
||||
return
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.delete(f"{get_server()}/cache/{cid}", headers=headers)
|
||||
if resp.status_code == 400:
|
||||
click.echo(f"Cannot delete: {resp.json().get('detail', 'Unknown error')}", err=True)
|
||||
@@ -703,16 +700,12 @@ def matches_media_type(item: dict, media_type: str) -> bool:
|
||||
default="all", help="Filter by media type")
|
||||
def cache(limit, offset, media_type):
|
||||
"""List cached content with pagination and optional type filter."""
|
||||
token_data = load_token()
|
||||
if not token_data.get("access_token"):
|
||||
click.echo("Not logged in. Use 'artdag login' first.", err=True)
|
||||
sys.exit(1)
|
||||
headers = get_auth_header(require_token=True)
|
||||
|
||||
# Fetch more items if filtering to ensure we get enough results
|
||||
fetch_limit = limit * 3 if media_type != "all" else limit
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
resp = requests.get(f"{get_server()}/cache?offset={offset}&limit={fetch_limit}", headers=headers)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
@@ -817,7 +810,7 @@ def upload(filepath):
|
||||
try:
|
||||
with open(filepath, "rb") as f:
|
||||
files = {"file": (Path(filepath).name, f)}
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(f"{get_server()}/cache/upload", files=files, headers=headers)
|
||||
if resp.status_code == 401:
|
||||
click.echo("Authentication failed. Please login again.", err=True)
|
||||
@@ -915,7 +908,7 @@ def meta(cid, origin, origin_url, origin_note, description, tags, folder, add_co
|
||||
click.echo("Not logged in. Please run: artdag login <username>", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
|
||||
# Handle publish action
|
||||
if publish_name:
|
||||
@@ -1269,7 +1262,7 @@ def storage_list():
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.get(f"{get_server()}/storage", headers=headers)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
@@ -1322,7 +1315,7 @@ def storage_add(provider_type, name, capacity):
|
||||
|
||||
# Send to server
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
payload = {
|
||||
"provider_type": provider_type,
|
||||
"config": config,
|
||||
@@ -1355,7 +1348,7 @@ def storage_test(storage_id):
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(f"{get_server()}/storage/{storage_id}/test", headers=headers)
|
||||
if resp.status_code == 404:
|
||||
click.echo(f"Storage provider not found: {storage_id}", err=True)
|
||||
@@ -1389,7 +1382,7 @@ def storage_delete(storage_id, force):
|
||||
return
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.delete(f"{get_server()}/storage/{storage_id}", headers=headers)
|
||||
if resp.status_code == 400:
|
||||
click.echo(f"Error: {resp.json().get('detail', 'Bad request')}", err=True)
|
||||
@@ -1458,7 +1451,7 @@ def upload_recipe(filepath):
|
||||
try:
|
||||
with open(filepath, "rb") as f:
|
||||
files = {"file": (Path(filepath).name, f)}
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(f"{get_server()}/recipes/upload", files=files, headers=headers)
|
||||
if resp.status_code == 401:
|
||||
click.echo("Authentication failed. Please login again.", err=True)
|
||||
@@ -1499,7 +1492,7 @@ def upload_effect(filepath):
|
||||
try:
|
||||
with open(filepath, "rb") as f:
|
||||
files = {"file": (Path(filepath).name, f)}
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(f"{get_server()}/effects/upload", files=files, headers=headers)
|
||||
if resp.status_code == 401:
|
||||
click.echo("Authentication failed. Please login again.", err=True)
|
||||
@@ -1530,13 +1523,9 @@ def upload_effect(filepath):
|
||||
@click.option("--offset", "-o", default=0, help="Offset for pagination")
|
||||
def list_effects(limit, offset):
|
||||
"""List uploaded effects with pagination."""
|
||||
token_data = load_token()
|
||||
if not token_data.get("access_token"):
|
||||
click.echo("Not logged in. Use 'artdag login' first.", err=True)
|
||||
sys.exit(1)
|
||||
headers = get_auth_header(require_token=True)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
resp = requests.get(f"{get_server()}/effects?offset={offset}&limit={limit}", headers=headers)
|
||||
resp.raise_for_status()
|
||||
result = resp.json()
|
||||
@@ -1577,7 +1566,7 @@ def show_effect(cid, source):
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.get(f"{get_server()}/effects/{cid}", headers=headers)
|
||||
if resp.status_code == 404:
|
||||
click.echo(f"Effect not found: {cid}", err=True)
|
||||
@@ -1644,13 +1633,9 @@ def show_effect(cid, source):
|
||||
@click.option("--offset", "-o", default=0, help="Offset for pagination")
|
||||
def list_recipes(limit, offset):
|
||||
"""List uploaded recipes for the current user with pagination."""
|
||||
token_data = load_token()
|
||||
if not token_data.get("access_token"):
|
||||
click.echo("Not logged in. Use 'artdag login' first.", err=True)
|
||||
sys.exit(1)
|
||||
headers = get_auth_header(require_token=True)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
resp = requests.get(f"{get_server()}/recipes?offset={offset}&limit={limit}", headers=headers)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
@@ -1688,7 +1673,7 @@ def show_recipe(recipe_id):
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.get(f"{get_server()}/recipes/{recipe_id}", headers=headers)
|
||||
if resp.status_code == 404:
|
||||
click.echo(f"Recipe not found: {recipe_id}", err=True)
|
||||
@@ -1745,7 +1730,7 @@ def run_recipe(recipe_id, inputs, wait):
|
||||
|
||||
# Run
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(
|
||||
f"{get_server()}/recipes/{recipe_id}/run",
|
||||
json={"inputs": input_dict},
|
||||
@@ -1808,7 +1793,7 @@ def delete_recipe(recipe_id, force):
|
||||
return
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.delete(f"{get_server()}/recipes/{recipe_id}", headers=headers)
|
||||
if resp.status_code == 401:
|
||||
click.echo("Authentication failed. Please login again.", err=True)
|
||||
@@ -1872,7 +1857,7 @@ def generate_plan(recipe_file, inputs, features, output):
|
||||
|
||||
# Submit to API
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(
|
||||
f"{get_server()}/api/v2/plan",
|
||||
json=request_data,
|
||||
@@ -1933,7 +1918,7 @@ def execute_plan(plan_file, wait):
|
||||
|
||||
# Submit to API
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(
|
||||
f"{get_server()}/api/v2/execute",
|
||||
json={"plan_json": plan_json},
|
||||
@@ -2011,7 +1996,7 @@ def run_recipe_v2(recipe_file, inputs, features, wait):
|
||||
click.echo(f"Inputs: {len(input_hashes)}")
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.post(
|
||||
f"{get_server()}/api/v2/run-recipe",
|
||||
json=request_data,
|
||||
@@ -2046,7 +2031,7 @@ def run_recipe_v2(recipe_file, inputs, features, wait):
|
||||
def _wait_for_v2_run(token_data: dict, run_id: str):
|
||||
"""Poll v2 run status until completion."""
|
||||
click.echo("Waiting for completion...")
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
|
||||
while True:
|
||||
time.sleep(2)
|
||||
@@ -2094,7 +2079,7 @@ def run_status_v2(run_id):
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token_data['access_token']}"}
|
||||
headers = get_auth_header(require_token=True)
|
||||
resp = requests.get(
|
||||
f"{get_server()}/api/v2/run/{run_id}",
|
||||
headers=headers
|
||||
|
||||
Reference in New Issue
Block a user