From 4a21b9f2cd215292da25dad3aa7e6079e4ad2ff1 Mon Sep 17 00:00:00 2001 From: Ravi Ranjan Date: Sat, 7 Mar 2026 15:25:51 +0530 Subject: [PATCH 1/3] FIX: Enable version command without login and redirect errors to stderr #28 --- cbrain_cli/main.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/cbrain_cli/main.py b/cbrain_cli/main.py index 41e2d07..d38b073 100644 --- a/cbrain_cli/main.py +++ b/cbrain_cli/main.py @@ -399,23 +399,20 @@ def main(): # MARK: Setup CLI args = parser.parse_args() + if args.command == "version": + return handle_errors(version_info)(args) if not args.command: - parser.print_help() - return - + parser.print_help(file=sys.stderr) + return 1 + # Handle session commands (no authentication needed for login, version, and whoami). if args.command == "login": return handle_errors(create_session)(args) - elif args.command == "version": - return handle_errors(version_info)(args) + elif args.command == "whoami": return handle_errors(whoami_user)(args) - # All other commands require authentication. - if not is_authenticated(): - return 1 - # Handle authenticated commands. if args.command == "logout": return handle_errors(logout_session)(args) @@ -433,23 +430,23 @@ def main(): if not hasattr(args, "action") or not args.action: # Show help for the specific model command. if args.command == "file": - file_parser.print_help() + file_parser.print_help(file=sys.stderr) elif args.command == "dataprovider": - dataprovider_parser.print_help() + dataprovider_parser.print_help(file=sys.stderr) elif args.command == "project": - project_parser.print_help() + project_parser.print_help(file=sys.stderr) elif args.command == "tool": - tool_parser.print_help() + tool_parser.print_help(file=sys.stderr) elif args.command == "tool-config": - tool_configs_parser.print_help() + tool_configs_parser.print_help(file=sys.stderr) elif args.command == "tag": - tag_parser.print_help() + tag_parser.print_help(file=sys.stderr) elif args.command == "background": - background_parser.print_help() + background_parser.print_help(file=sys.stderr) elif args.command == "task": - task_parser.print_help() + task_parser.print_help(file=sys.stderr) elif args.command == "remote-resource": - remote_resource_parser.print_help() + remote_resource_parser.print_help(file=sys.stderr) return 1 else: # Execute the function associated with the command. From e45acb66c978c00cab24a1c98b916a31b2b280a0 Mon Sep 17 00:00:00 2001 From: Ravi Ranjan Date: Sat, 7 Mar 2026 15:34:27 +0530 Subject: [PATCH 2/3] FIX: Allow version without login, redirect errors to stderr, and add default upload type #28 --- cbrain_cli/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cbrain_cli/main.py b/cbrain_cli/main.py index d38b073..4236f08 100644 --- a/cbrain_cli/main.py +++ b/cbrain_cli/main.py @@ -111,6 +111,7 @@ def main(): "--data-provider", type=int, required=True, help="Data provider ID" ) file_upload_parser.add_argument("--group-id", type=int, help="Group ID") + file_upload_parser.add_argument("--file-type", type=str, default="SingleFile", help="File type (default: SingleFile)") file_upload_parser.set_defaults(func=handle_errors(handle_file_upload)) From 9bba3cbdf3e4cb31c31cab2c1d71f656bdef731b Mon Sep 17 00:00:00 2001 From: Ravi Ranjan Date: Sun, 8 Mar 2026 15:39:58 +0530 Subject: [PATCH 3/3] FEAT: Implement project switch-all and add project-id aliases #22 #28 --- cbrain_cli/data/projects.py | 40 ++++++++++++++++++++++++++++++++++--- cbrain_cli/handlers.py | 8 +++++--- cbrain_cli/main.py | 8 ++++---- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/cbrain_cli/data/projects.py b/cbrain_cli/data/projects.py index 58ff27b..de27047 100644 --- a/cbrain_cli/data/projects.py +++ b/cbrain_cli/data/projects.py @@ -27,9 +27,8 @@ def switch_project(args): return None # Handle the special case of "all" - if group_id == "all": - print("Project switch 'all' not yet implemented as of Aug 2025") - return None + if group_id.lower() == "all": + return _perform_project_unswitch() # Convert to integer for regular group IDs try: @@ -164,3 +163,38 @@ def list_projects(args): projects_data = json.loads(data) return projects_data + + +def _perform_project_unswitch(): + """Internal helper to clear project keys from the credentials file.""" + try: + from cbrain_cli.config import CREDENTIALS_FILE + + # 1. Check if the file even exists + if not CREDENTIALS_FILE.exists(): + print("No active session found. You are already viewing 'All Projects'.") + return {"name": "All Projects"} + + # 2. Read the file + with open(CREDENTIALS_FILE, "r") as f: + credentials = json.load(f) + + # 3. Clear the keys + credentials.pop("current_group_id", None) + credentials.pop("current_group_name", None) + credentials.pop("current_project_id", None) + + # 4. Save back to file + with open(CREDENTIALS_FILE, "w") as f: + json.dump(credentials, f, indent=2) + + print("Successfully unswitched. Now viewing 'All Projects'.") + return {"name": "All Projects"} + + except Exception as e: + print(f"Error during unswitch: {e}") + return None + +def unswitch_project(args): + """Handler for the 'cbrain project unswitch' command.""" + return _perform_project_unswitch() \ No newline at end of file diff --git a/cbrain_cli/handlers.py b/cbrain_cli/handlers.py index 83c0ccf..5aaa7a7 100644 --- a/cbrain_cli/handlers.py +++ b/cbrain_cli/handlers.py @@ -24,7 +24,7 @@ show_file, upload_file, ) -from cbrain_cli.data.projects import list_projects, show_project, switch_project +from cbrain_cli.data.projects import list_projects, show_project, switch_project, unswitch_project from cbrain_cli.data.remote_resources import list_remote_resources, show_remote_resource from cbrain_cli.data.tags import create_tag, delete_tag, list_tags, show_tag, update_tag from cbrain_cli.data.tasks import list_tasks, show_task @@ -174,8 +174,10 @@ def handle_project_show(args): def handle_project_unswitch(args): - """Unswitch from current project context.""" - print("Project Unswitch 'all' not yet implemented as of Aug 2025") + """Unswitch from current project context by reverting to 'All Projects'.""" + result = unswitch_project(args) + if result: + print_current_project(result) # Tool command handlers diff --git a/cbrain_cli/main.py b/cbrain_cli/main.py index 4236f08..7850292 100644 --- a/cbrain_cli/main.py +++ b/cbrain_cli/main.py @@ -88,7 +88,7 @@ def main(): # file list file_list_parser = file_subparsers.add_parser("list", help="List files") - file_list_parser.add_argument("--group-id", type=int, help="Filter files by group ID") + file_list_parser.add_argument("--group-id", "--project-id", type=int, dest="group_id", help="Filter files by Group/Project ID") file_list_parser.add_argument("--dp-id", type=int, help="Filter files by data provider ID") file_list_parser.add_argument("--user-id", type=int, help="Filter files by user ID") file_list_parser.add_argument("--parent-id", type=int, help="Filter files by parent ID") @@ -110,7 +110,7 @@ def main(): file_upload_parser.add_argument( "--data-provider", type=int, required=True, help="Data provider ID" ) - file_upload_parser.add_argument("--group-id", type=int, help="Group ID") + file_upload_parser.add_argument("--group-id", "--project-id", type=int, dest="group_id", help="Group/Project ID") file_upload_parser.add_argument("--file-type", type=str, default="SingleFile", help="File type (default: SingleFile)") file_upload_parser.set_defaults(func=handle_errors(handle_file_upload)) @@ -304,7 +304,7 @@ def main(): tag_create_parser = tag_subparsers.add_parser("create", help="Create a new tag") tag_create_parser.add_argument("--name", type=str, required=True, help="Tag name") tag_create_parser.add_argument("--user-id", type=int, required=True, help="User ID") - tag_create_parser.add_argument("--group-id", type=int, required=True, help="Group ID") + tag_create_parser.add_argument("--group-id", "--project-id", type=int, required=True, dest="group_id", help="Group/Project ID") tag_create_parser.set_defaults(func=handle_errors(handle_tag_create)) # tag update @@ -316,7 +316,7 @@ def main(): ) tag_update_parser.add_argument("--name", type=str, required=True, help="Tag name") tag_update_parser.add_argument("--user-id", type=int, required=True, help="User ID") - tag_update_parser.add_argument("--group-id", type=int, required=True, help="Group ID") + tag_update_parser.add_argument("--group-id", "--project-id", type=int, required=True, dest="group_id", help="Group/Project ID") tag_update_parser.set_defaults(func=handle_errors(handle_tag_update)) # tag delete