diff --git a/README.md b/README.md index 48ff316..f784bbd 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,8 @@ export ARTDAG_L2=https://artdag.rose-ash.com # Using asset name ./artdag.py run dog cat -# Using content hash -./artdag.py run dog 33268b6e167deaf018cc538de12dbe562612b33e89a749391cef855b320a269b +# Using CID +./artdag.py run dog Qm33268b6e167deaf018cc538de12dbe562612b33e # Wait for completion ./artdag.py run dog cat --wait @@ -112,13 +112,13 @@ export ARTDAG_L2=https://artdag.rose-ash.com ### View/Download Cached Content ```bash # Show info -./artdag.py view +./artdag.py view # Download to file -./artdag.py view -o output.mkv +./artdag.py view -o output.mkv # Pipe to mpv (use -o - for stdout) -./artdag.py view -o - | mpv - +./artdag.py view -o - | mpv - ``` ### Import Local File to Cache @@ -128,10 +128,10 @@ export ARTDAG_L2=https://artdag.rose-ash.com ### Delete Cached Content ```bash -./artdag.py delete-cache +./artdag.py delete-cache # Skip confirmation -./artdag.py delete-cache -f +./artdag.py delete-cache -f ``` Note: Items that are inputs/outputs of runs, or published to L2, cannot be deleted. @@ -151,7 +151,7 @@ Configs are reusable DAG definitions with fixed and variable inputs. ./artdag.py config # Run a config with variable inputs -./artdag.py run-config -i node_id:content_hash --wait +./artdag.py run-config -i node_id:cid --wait # Delete a config ./artdag.py delete-config @@ -173,5 +173,5 @@ Configs are reusable DAG definitions with fixed and variable inputs. ./artdag.py runs # Download the output -./artdag.py view -o result.mkv +./artdag.py view -o result.mkv ``` diff --git a/artdag.py b/artdag.py index 564a8a0..b1e16fa 100755 --- a/artdag.py +++ b/artdag.py @@ -308,7 +308,7 @@ def run(recipe, input_hash, name, wait): if status["status"] == "completed": click.echo(f"Completed!") - click.echo(f"Output: {status['output_hash']}") + click.echo(f"Output: {status['output_cid']}") else: click.echo(f"Failed: {status.get('error', 'Unknown error')}") @@ -327,7 +327,7 @@ def list_runs(limit): click.echo("-" * 80) for run in runs[:limit]: - output = run.get("output_hash", "")[:16] + "..." if run.get("output_hash") else "-" + output = run.get("output_cid", "")[:16] + "..." if run.get("output_cid") else "-" click.echo(f"{run['run_id']} {run['status']:<10} {run['recipe']:<10} {output}") @@ -353,8 +353,8 @@ def status(run_id): if run.get("completed_at"): click.echo(f"Completed: {run['completed_at']}") - if run.get("output_hash"): - click.echo(f"Output Hash: {run['output_hash']}") + if run.get("output_cid"): + click.echo(f"Output Hash: {run['output_cid']}") if run.get("error"): click.echo(f"Error: {run['error']}") @@ -411,12 +411,12 @@ def delete_run(run_id, force): @cli.command("delete-cache") -@click.argument("content_hash") +@click.argument("cid") @click.option("--force", "-f", is_flag=True, help="Skip confirmation") -def delete_cache(content_hash, force): +def delete_cache(cid, force): """Delete a cached item. Requires login. - CONTENT_HASH: The content hash to delete + CID: The content identifier (IPFS CID) to delete """ token_data = load_token() if not token_data.get("access_token"): @@ -424,14 +424,14 @@ def delete_cache(content_hash, force): sys.exit(1) if not force: - click.echo(f"Content hash: {content_hash}") + click.echo(f"CID: {cid}") if not click.confirm("Delete this cached item?"): click.echo("Cancelled.") return try: headers = {"Authorization": f"Bearer {token_data['access_token']}"} - resp = requests.delete(f"{get_server()}/cache/{content_hash}", headers=headers) + 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) sys.exit(1) @@ -439,14 +439,14 @@ def delete_cache(content_hash, force): click.echo("Access denied", err=True) sys.exit(1) if resp.status_code == 404: - click.echo(f"Content not found: {content_hash}", err=True) + click.echo(f"Content not found: {cid}", err=True) sys.exit(1) resp.raise_for_status() except requests.RequestException as e: click.echo(f"Failed to delete cache item: {e}", err=True) sys.exit(1) - click.echo(f"Deleted: {content_hash}") + click.echo(f"Deleted: {cid}") @cli.command() @@ -465,14 +465,14 @@ def cache(limit): @cli.command() -@click.argument("content_hash") +@click.argument("cid") @click.option("--output", "-o", type=click.Path(), help="Save to file (use - for stdout)") -def view(content_hash, output): +def view(cid, output): """View or download cached content. - Use -o - to pipe to stdout, e.g.: artdag view -o - | mpv - + Use -o - to pipe to stdout, e.g.: artdag view -o - | mpv - """ - url = f"{get_server()}/cache/{content_hash}" + url = f"{get_server()}/cache/{cid}" try: if output == "-": @@ -495,14 +495,14 @@ def view(content_hash, output): resp.raise_for_status() size = resp.headers.get("content-length", "unknown") content_type = resp.headers.get("content-type", "unknown") - click.echo(f"Hash: {content_hash}") + click.echo(f"CID: {cid}") click.echo(f"Size: {size} bytes") click.echo(f"Type: {content_type}") click.echo(f"URL: {url}") resp.close() except requests.HTTPError as e: if e.response.status_code == 404: - click.echo(f"Not found: {content_hash}", err=True) + click.echo(f"Not found: {cid}", err=True) else: raise @@ -513,7 +513,7 @@ def import_file(filepath): """Import a local file to cache (local server only).""" path = str(Path(filepath).resolve()) result = api_post("/cache/import", params={"path": path}) - click.echo(f"Imported: {result['content_hash']}") + click.echo(f"Imported: {result['cid']}") @cli.command() @@ -588,14 +588,14 @@ def publish(run_id, output_name): result = resp.json() click.echo(f"Published to L2!") click.echo(f"Asset: {result['asset']['name']}") - click.echo(f"Hash: {result['asset']['content_hash']}") + click.echo(f"CID: {result['asset']['cid']}") click.echo(f"Activity: {result['activity']['activity_id']}") # ============ Metadata Commands ============ @cli.command() -@click.argument("content_hash") +@click.argument("cid") @click.option("--origin", type=click.Choice(["self", "external"]), help="Set origin type") @click.option("--origin-url", help="Set external origin URL") @click.option("--origin-note", help="Note about the origin") @@ -607,7 +607,7 @@ def publish(run_id, output_name): @click.option("--publish", "publish_name", help="Publish to L2 with given asset name") @click.option("--publish-type", default="image", help="Asset type for publishing (image, video)") @click.option("--republish", is_flag=True, help="Re-sync with L2 after metadata changes") -def meta(content_hash, origin, origin_url, origin_note, description, tags, folder, add_collection, remove_collection, publish_name, publish_type, republish): +def meta(cid, origin, origin_url, origin_note, description, tags, folder, add_collection, remove_collection, publish_name, publish_type, republish): """View or update metadata for a cached item. With no options, displays current metadata. @@ -627,7 +627,7 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde if publish_name: try: resp = requests.post( - f"{get_server()}/cache/{content_hash}/publish", + f"{get_server()}/cache/{cid}/publish", json={"asset_name": publish_name, "asset_type": publish_type}, headers=headers ) @@ -635,7 +635,7 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde click.echo(f"Error: {resp.json().get('detail', 'Bad request')}", err=True) sys.exit(1) if resp.status_code == 404: - click.echo(f"Content not found: {content_hash}", err=True) + click.echo(f"Content not found: {cid}", err=True) sys.exit(1) resp.raise_for_status() result = resp.json() @@ -651,14 +651,14 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde if republish: try: resp = requests.patch( - f"{get_server()}/cache/{content_hash}/republish", + f"{get_server()}/cache/{cid}/republish", headers=headers ) if resp.status_code == 400: click.echo(f"Error: {resp.json().get('detail', 'Bad request')}", err=True) sys.exit(1) if resp.status_code == 404: - click.echo(f"Content not found: {content_hash}", err=True) + click.echo(f"Content not found: {cid}", err=True) sys.exit(1) resp.raise_for_status() result = resp.json() @@ -675,9 +675,9 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde if not has_updates: # GET metadata try: - resp = requests.get(f"{get_server()}/cache/{content_hash}/meta", headers=headers) + resp = requests.get(f"{get_server()}/cache/{cid}/meta", headers=headers) if resp.status_code == 404: - click.echo(f"Content not found: {content_hash}", err=True) + click.echo(f"Content not found: {cid}", err=True) sys.exit(1) if resp.status_code == 403: click.echo("Access denied", err=True) @@ -688,7 +688,7 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde click.echo(f"Failed to get metadata: {e}", err=True) sys.exit(1) - click.echo(f"Content Hash: {content_hash}") + click.echo(f"Content Hash: {cid}") click.echo(f"Uploader: {meta.get('uploader', 'unknown')}") click.echo(f"Uploaded: {meta.get('uploaded_at', 'unknown')}") if meta.get("origin"): @@ -715,7 +715,7 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde if origin or origin_url or origin_note: # Get current origin first try: - resp = requests.get(f"{get_server()}/cache/{content_hash}/meta", headers=headers) + resp = requests.get(f"{get_server()}/cache/{cid}/meta", headers=headers) resp.raise_for_status() current = resp.json() current_origin = current.get("origin", {}) @@ -740,7 +740,7 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde if add_collection or remove_collection: # Get current collections try: - resp = requests.get(f"{get_server()}/cache/{content_hash}/meta", headers=headers) + resp = requests.get(f"{get_server()}/cache/{cid}/meta", headers=headers) resp.raise_for_status() current = resp.json() collections = set(current.get("collections", [])) @@ -756,12 +756,12 @@ def meta(content_hash, origin, origin_url, origin_note, description, tags, folde # PATCH metadata try: resp = requests.patch( - f"{get_server()}/cache/{content_hash}/meta", + f"{get_server()}/cache/{cid}/meta", json=update, headers=headers ) if resp.status_code == 404: - click.echo(f"Content not found: {content_hash}", err=True) + click.echo(f"Content not found: {cid}", err=True) sys.exit(1) if resp.status_code == 400: click.echo(f"Error: {resp.json().get('detail', 'Bad request')}", err=True) @@ -1088,7 +1088,7 @@ def list_effects(limit): for effect in effects: meta = effect.get("meta", {}) click.echo(f" {meta.get('name', 'unknown')} v{meta.get('version', '?')}") - click.echo(f" Hash: {effect['content_hash'][:32]}...") + click.echo(f" Hash: {effect['cid'][:32]}...") click.echo(f" Temporal: {meta.get('temporal', False)}") if meta.get('params'): click.echo(f" Params: {', '.join(p['name'] for p in meta['params'])}") @@ -1155,12 +1155,12 @@ def show_recipe(recipe_id): if recipe.get("fixed_inputs"): click.echo("\nFixed Inputs:") for inp in recipe["fixed_inputs"]: - click.echo(f" - {inp['asset']}: {inp['content_hash'][:16]}...") + click.echo(f" - {inp['asset']}: {inp['cid'][:16]}...") @cli.command("run-recipe") @click.argument("recipe_id") -@click.option("--input", "-i", "inputs", multiple=True, help="Input as node_id:content_hash") +@click.option("--input", "-i", "inputs", multiple=True, help="Input as node_id:cid") @click.option("--wait", "-w", is_flag=True, help="Wait for completion") def run_recipe(recipe_id, inputs, wait): """Run a recipe with variable inputs. Requires login. @@ -1178,10 +1178,10 @@ def run_recipe(recipe_id, inputs, wait): input_dict = {} for inp in inputs: if ":" not in inp: - click.echo(f"Invalid input format: {inp} (expected node_id:content_hash)", err=True) + click.echo(f"Invalid input format: {inp} (expected node_id:cid)", err=True) sys.exit(1) - node_id, content_hash = inp.split(":", 1) - input_dict[node_id] = content_hash + node_id, cid = inp.split(":", 1) + input_dict[node_id] = cid # Run try: @@ -1225,7 +1225,7 @@ def run_recipe(recipe_id, inputs, wait): continue if run["status"] == "completed": - click.echo(f"Completed! Output: {run.get('output_hash', 'N/A')}") + click.echo(f"Completed! Output: {run.get('output_cid', 'N/A')}") break elif run["status"] == "failed": click.echo(f"Failed: {run.get('error', 'Unknown error')}", err=True) @@ -1272,7 +1272,7 @@ def delete_recipe(recipe_id, force): @cli.command("plan") @click.argument("recipe_file", type=click.Path(exists=True)) -@click.option("--input", "-i", "inputs", multiple=True, help="Input as name:content_hash") +@click.option("--input", "-i", "inputs", multiple=True, help="Input as name:cid") @click.option("--features", "-f", multiple=True, help="Features to extract (default: beats, energy)") @click.option("--output", "-o", type=click.Path(), help="Save plan JSON to file") def generate_plan(recipe_file, inputs, features, output): @@ -1297,10 +1297,10 @@ def generate_plan(recipe_file, inputs, features, output): input_hashes = {} for inp in inputs: if ":" not in inp: - click.echo(f"Invalid input format: {inp} (expected name:content_hash)", err=True) + click.echo(f"Invalid input format: {inp} (expected name:cid)", err=True) sys.exit(1) - name, content_hash = inp.split(":", 1) - input_hashes[name] = content_hash + name, cid = inp.split(":", 1) + input_hashes[name] = cid # Build request request_data = { @@ -1401,7 +1401,7 @@ def execute_plan(plan_file, wait): @cli.command("run-v2") @click.argument("recipe_file", type=click.Path(exists=True)) -@click.option("--input", "-i", "inputs", multiple=True, help="Input as name:content_hash") +@click.option("--input", "-i", "inputs", multiple=True, help="Input as name:cid") @click.option("--features", "-f", multiple=True, help="Features to extract (default: beats, energy)") @click.option("--wait", "-w", is_flag=True, help="Wait for completion") def run_recipe_v2(recipe_file, inputs, features, wait): @@ -1433,10 +1433,10 @@ def run_recipe_v2(recipe_file, inputs, features, wait): input_hashes = {} for inp in inputs: if ":" not in inp: - click.echo(f"Invalid input format: {inp} (expected name:content_hash)", err=True) + click.echo(f"Invalid input format: {inp} (expected name:cid)", err=True) sys.exit(1) - name, content_hash = inp.split(":", 1) - input_hashes[name] = content_hash + name, cid = inp.split(":", 1) + input_hashes[name] = cid # Build request request_data = { @@ -1473,8 +1473,8 @@ def run_recipe_v2(recipe_file, inputs, features, wait): click.echo(f"Run ID: {run_id}") click.echo(f"Status: {result['status']}") - if result.get("output_hash"): - click.echo(f"Output: {result['output_hash']}") + if result.get("output_cid"): + click.echo(f"Output: {result['output_cid']}") if result.get("output_ipfs_cid"): click.echo(f"IPFS CID: {result['output_ipfs_cid']}") return @@ -1505,8 +1505,8 @@ def _wait_for_v2_run(token_data: dict, run_id: str): if status == "completed": click.echo(f"\nCompleted!") - if run.get("output_hash"): - click.echo(f"Output: {run['output_hash']}") + if run.get("output_cid"): + click.echo(f"Output: {run['output_cid']}") if run.get("output_ipfs_cid"): click.echo(f"IPFS CID: {run['output_ipfs_cid']}") if run.get("cached"): @@ -1555,8 +1555,8 @@ def run_status_v2(run_id): click.echo(f"Recipe: {run['recipe']}") if run.get("plan_id"): click.echo(f"Plan ID: {run['plan_id'][:16]}...") - if run.get("output_hash"): - click.echo(f"Output: {run['output_hash']}") + if run.get("output_cid"): + click.echo(f"Output: {run['output_cid']}") if run.get("output_ipfs_cid"): click.echo(f"IPFS CID: {run['output_ipfs_cid']}") if run.get("cached") is not None: