cloudos-cli 2.68.0__tar.gz → 2.69.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/PKG-INFO +72 -1
  2. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/README.md +71 -0
  3. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/__main__.py +172 -4
  4. cloudos_cli-2.69.1/cloudos_cli/_version.py +1 -0
  5. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/clos.py +433 -1
  6. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/import_wf/import_wf.py +1 -2
  7. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli.egg-info/PKG-INFO +72 -1
  8. cloudos_cli-2.68.0/cloudos_cli/_version.py +0 -1
  9. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/LICENSE +0 -0
  10. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/__init__.py +0 -0
  11. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/configure/__init__.py +0 -0
  12. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/configure/configure.py +0 -0
  13. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/cost/__init__.py +0 -0
  14. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/cost/cost.py +0 -0
  15. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/datasets/__init__.py +0 -0
  16. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/datasets/datasets.py +0 -0
  17. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/import_wf/__init__.py +0 -0
  18. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/jobs/__init__.py +0 -0
  19. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/jobs/job.py +0 -0
  20. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/link/__init__.py +0 -0
  21. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/link/link.py +0 -0
  22. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/logging/__init__.py +0 -0
  23. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/logging/logger.py +0 -0
  24. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/procurement/__init__.py +0 -0
  25. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/procurement/images.py +0 -0
  26. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/queue/__init__.py +0 -0
  27. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/queue/queue.py +0 -0
  28. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/related_analyses/__init__.py +0 -0
  29. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/related_analyses/related_analyses.py +0 -0
  30. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/__init__.py +0 -0
  31. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/array_job.py +0 -0
  32. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/cloud.py +0 -0
  33. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/details.py +0 -0
  34. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/errors.py +0 -0
  35. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/last_wf.py +0 -0
  36. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/requests.py +0 -0
  37. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli/utils/resources.py +0 -0
  38. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli.egg-info/SOURCES.txt +0 -0
  39. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli.egg-info/dependency_links.txt +0 -0
  40. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli.egg-info/entry_points.txt +0 -0
  41. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli.egg-info/requires.txt +0 -0
  42. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/cloudos_cli.egg-info/top_level.txt +0 -0
  43. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/setup.cfg +0 -0
  44. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/setup.py +0 -0
  45. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/__init__.py +0 -0
  46. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/functions_for_pytest.py +0 -0
  47. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/test_cli_project_create.py +0 -0
  48. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/test_cost/__init__.py +0 -0
  49. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/test_cost/test_job_cost.py +0 -0
  50. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/test_logging/__init__.py +0 -0
  51. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/test_logging/test_logger.py +0 -0
  52. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/test_related_analyses/__init__.py +0 -0
  53. {cloudos_cli-2.68.0 → cloudos_cli-2.69.1}/tests/test_related_analyses/test_related_analyses.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.68.0
3
+ Version: 2.69.1
4
4
  Summary: Python package for interacting with CloudOS
5
5
  Home-page: https://github.com/lifebit-ai/cloudos-cli
6
6
  Author: David Piñeyro
@@ -669,6 +669,41 @@ You can also link all result directories to an interactive session using the `--
669
669
  cloudos job results --profile my_profile --job-id "12345678910" --link --session-id your_session_id
670
670
  ```
671
671
 
672
+ **Check Results Deletion Status**
673
+
674
+ You can check the deletion status of a job's results folder using the `--status` flag. This is useful for monitoring the deletion lifecycle of analysis results.
675
+
676
+ ```bash
677
+ cloudos job results --status --profile my_profile --job-id "12345678910"
678
+ ```
679
+
680
+ The command will display the current status of the results folder. Possible statuses include:
681
+ - **available**: Results are available and accessible
682
+ - **scheduled for deletion**: Results are scheduled to be deleted
683
+ - **deleting**: Results are currently being deleted
684
+ - **deleted**: Results have been deleted
685
+ - **failed to delete**: Deletion process failed
686
+
687
+ Example output for available results:
688
+ ```console
689
+ The results of job 1234567890 are in status: available
690
+ ```
691
+
692
+ For results in any state other than available, the output includes additional information about when the status changed and who initiated the change:
693
+ ```console
694
+ The results of job 6912036aa6ed001148c96018 are in status: scheduled for deletion
695
+ Status changed at: 2025-11-11T14:43:44.416Z
696
+ User: Leila Mansouri (leila.mansouri@lifebit.ai)
697
+ ```
698
+
699
+ Use the `--verbose` flag to see detailed information including the results folder name, folder ID, creation and update timestamps:
700
+ ```bash
701
+ cloudos job results --status --profile my_profile --job-id "12345678910" --verbose
702
+ ```
703
+
704
+ > [!NOTE]
705
+ > If results have been completely deleted, the command will report that the results folder was not found, which may indicate that results have been deleted or scheduled for deletion.
706
+
672
707
 
673
708
  #### Clone or Resume job
674
709
 
@@ -930,6 +965,42 @@ cloudos job workdir \
930
965
  --link --session-id your_session_id
931
966
  ```
932
967
 
968
+ **Check Working Directory Deletion Status**
969
+
970
+ You can check the deletion status of a job's working directory using the `--status` flag. This is useful for monitoring the deletion lifecycle of intermediate job files.
971
+
972
+ ```bash
973
+ cloudos job workdir --status --profile my_profile --job-id "12345678910"
974
+ ```
975
+
976
+ The command will display the current status of the working directory folder. Possible statuses include:
977
+ - **available**: Working directory is available and accessible
978
+ - **scheduled for deletion**: Working directory is scheduled to be deleted
979
+ - **deleting**: Working directory is currently being deleted
980
+ - **deleted**: Working directory has been deleted
981
+ - **failed to delete**: Deletion process failed
982
+
983
+ Example output for available working directory:
984
+ ```console
985
+ The working directory of job 6912036aa6ed001148c96018 is in status: available
986
+ ```
987
+
988
+ For working directories in any state other than available, the output includes additional information about when the status changed and who initiated the change:
989
+ ```console
990
+ The working directory of job 6912036aa6ed001148c96018 is in status: scheduled for deletion
991
+ Status changed at: 2025-11-11T14:43:44.416Z
992
+ User: Leila Mansouri (leila.mansouri@lifebit.ai)
993
+ ```
994
+
995
+ Use the `--verbose` flag to see detailed information including the working directory folder name, folder ID, creation and update timestamps:
996
+ ```bash
997
+ cloudos job workdir --status --profile my_profile --job-id "12345678910" --verbose
998
+ ```
999
+
1000
+ > [!NOTE]
1001
+ > This command only works for jobs that were run with resumable mode enabled (using the `--resumable` flag). Jobs without resumable mode will not have a working directory to check.
1002
+ > If the working directory has been completely deleted, the command will report that the working directory was not found.
1003
+
933
1004
  #### List Jobs
934
1005
 
935
1006
  You can get a summary of workspace jobs in two different formats:
@@ -634,6 +634,41 @@ You can also link all result directories to an interactive session using the `--
634
634
  cloudos job results --profile my_profile --job-id "12345678910" --link --session-id your_session_id
635
635
  ```
636
636
 
637
+ **Check Results Deletion Status**
638
+
639
+ You can check the deletion status of a job's results folder using the `--status` flag. This is useful for monitoring the deletion lifecycle of analysis results.
640
+
641
+ ```bash
642
+ cloudos job results --status --profile my_profile --job-id "12345678910"
643
+ ```
644
+
645
+ The command will display the current status of the results folder. Possible statuses include:
646
+ - **available**: Results are available and accessible
647
+ - **scheduled for deletion**: Results are scheduled to be deleted
648
+ - **deleting**: Results are currently being deleted
649
+ - **deleted**: Results have been deleted
650
+ - **failed to delete**: Deletion process failed
651
+
652
+ Example output for available results:
653
+ ```console
654
+ The results of job 1234567890 are in status: available
655
+ ```
656
+
657
+ For results in any state other than available, the output includes additional information about when the status changed and who initiated the change:
658
+ ```console
659
+ The results of job 6912036aa6ed001148c96018 are in status: scheduled for deletion
660
+ Status changed at: 2025-11-11T14:43:44.416Z
661
+ User: Leila Mansouri (leila.mansouri@lifebit.ai)
662
+ ```
663
+
664
+ Use the `--verbose` flag to see detailed information including the results folder name, folder ID, creation and update timestamps:
665
+ ```bash
666
+ cloudos job results --status --profile my_profile --job-id "12345678910" --verbose
667
+ ```
668
+
669
+ > [!NOTE]
670
+ > If results have been completely deleted, the command will report that the results folder was not found, which may indicate that results have been deleted or scheduled for deletion.
671
+
637
672
 
638
673
  #### Clone or Resume job
639
674
 
@@ -895,6 +930,42 @@ cloudos job workdir \
895
930
  --link --session-id your_session_id
896
931
  ```
897
932
 
933
+ **Check Working Directory Deletion Status**
934
+
935
+ You can check the deletion status of a job's working directory using the `--status` flag. This is useful for monitoring the deletion lifecycle of intermediate job files.
936
+
937
+ ```bash
938
+ cloudos job workdir --status --profile my_profile --job-id "12345678910"
939
+ ```
940
+
941
+ The command will display the current status of the working directory folder. Possible statuses include:
942
+ - **available**: Working directory is available and accessible
943
+ - **scheduled for deletion**: Working directory is scheduled to be deleted
944
+ - **deleting**: Working directory is currently being deleted
945
+ - **deleted**: Working directory has been deleted
946
+ - **failed to delete**: Deletion process failed
947
+
948
+ Example output for available working directory:
949
+ ```console
950
+ The working directory of job 6912036aa6ed001148c96018 is in status: available
951
+ ```
952
+
953
+ For working directories in any state other than available, the output includes additional information about when the status changed and who initiated the change:
954
+ ```console
955
+ The working directory of job 6912036aa6ed001148c96018 is in status: scheduled for deletion
956
+ Status changed at: 2025-11-11T14:43:44.416Z
957
+ User: Leila Mansouri (leila.mansouri@lifebit.ai)
958
+ ```
959
+
960
+ Use the `--verbose` flag to see detailed information including the working directory folder name, folder ID, creation and update timestamps:
961
+ ```bash
962
+ cloudos job workdir --status --profile my_profile --job-id "12345678910" --verbose
963
+ ```
964
+
965
+ > [!NOTE]
966
+ > This command only works for jobs that were run with resumable mode enabled (using the `--resumable` flag). Jobs without resumable mode will not have a working directory to check.
967
+ > If the working directory has been completely deleted, the command will report that the working directory was not found.
968
+
898
969
  #### List Jobs
899
970
 
900
971
  You can get a summary of workspace jobs in two different formats:
@@ -729,6 +729,9 @@ def job_status(ctx,
729
729
  @click.option('--session-id',
730
730
  help='The specific CloudOS interactive session id. Required when using --link flag.',
731
731
  required=False)
732
+ @click.option('--status',
733
+ help='Check the deletion status of the working directory.',
734
+ is_flag=True)
732
735
  @click.option('--verbose',
733
736
  help='Whether to print information messages or not.',
734
737
  is_flag=True)
@@ -748,20 +751,101 @@ def job_workdir(ctx,
748
751
  job_id,
749
752
  link,
750
753
  session_id,
754
+ status,
751
755
  verbose,
752
756
  disable_ssl_verification,
753
757
  ssl_cert,
754
758
  profile):
755
- """Get the path to the working directory of a specified job."""
759
+ """Get the path to the working directory of a specified job or check deletion status."""
756
760
  # apikey, cloudos_url, and workspace_id are now automatically resolved by the decorator
757
761
  # session_id is also resolved if provided in profile
758
762
 
763
+ verify_ssl = ssl_selector(disable_ssl_verification, ssl_cert)
764
+
765
+ # Handle --status flag
766
+ if status:
767
+ console = Console()
768
+
769
+ if verbose:
770
+ console.print('[bold cyan]Checking deletion status of job working directory...[/bold cyan]')
771
+ console.print('\t[dim]...Preparing objects[/dim]')
772
+ console.print('\t[bold]Using the following parameters:[/bold]')
773
+ console.print(f'\t\t[cyan]CloudOS url:[/cyan] {cloudos_url}')
774
+ console.print(f'\t\t[cyan]Workspace ID:[/cyan] {workspace_id}')
775
+ console.print(f'\t\t[cyan]Job ID:[/cyan] {job_id}')
776
+
777
+ # Use Cloudos object to access the deletion status method
778
+ cl = Cloudos(cloudos_url, apikey, None)
779
+
780
+ if verbose:
781
+ console.print('\t[dim]The following Cloudos object was created:[/dim]')
782
+ console.print('\t' + str(cl) + '\n')
783
+
784
+ try:
785
+ deletion_status = cl.get_workdir_deletion_status(
786
+ job_id=job_id,
787
+ workspace_id=workspace_id,
788
+ verify=verify_ssl
789
+ )
790
+
791
+ # Convert API status to user-friendly terminology with color
792
+ status_config = {
793
+ "ready": ("available", "green"),
794
+ "deleting": ("deleting", "yellow"),
795
+ "scheduledForDeletion": ("scheduled for deletion", "yellow"),
796
+ "deleted": ("deleted", "red"),
797
+ "failedToDelete": ("failed to delete", "red")
798
+ }
799
+
800
+ # Get the status of the workdir folder itself and convert it
801
+ api_status = deletion_status.get("status", "unknown")
802
+ folder_status, status_color = status_config.get(api_status, (api_status, "white"))
803
+ folder_info = deletion_status.get("items", {})
804
+
805
+ # Display results in a clear, styled format with human-readable sentence
806
+ console.print(f'The working directory of job [cyan]{deletion_status["job_id"]}[/cyan] is in status: [bold {status_color}]{folder_status}[/bold {status_color}]')
807
+
808
+ # For non-available statuses, always show update time and user info
809
+ if folder_status != "available":
810
+ if folder_info.get("updatedAt"):
811
+ console.print(f'[magenta]Status changed at:[/magenta] {folder_info.get("updatedAt")}')
812
+
813
+ # Show user information - prefer deletedBy over user field
814
+ user_info = folder_info.get("deletedBy") or folder_info.get("user", {})
815
+ if user_info:
816
+ user_name = f"{user_info.get('name', '')} {user_info.get('surname', '')}".strip()
817
+ user_email = user_info.get('email', '')
818
+ if user_name or user_email:
819
+ user_display = f'{user_name} ({user_email})' if user_name and user_email else (user_name or user_email)
820
+ console.print(f'[blue]User:[/blue] {user_display}')
821
+
822
+ # Display detailed information if verbose
823
+ if verbose:
824
+ console.print(f'\n[bold]Additional information:[/bold]')
825
+ console.print(f' [cyan]Job name:[/cyan] {deletion_status["job_name"]}')
826
+ console.print(f' [cyan]Working directory folder name:[/cyan] {deletion_status["workdir_folder_name"]}')
827
+ console.print(f' [cyan]Working directory folder ID:[/cyan] {deletion_status["workdir_folder_id"]}')
828
+
829
+ # Show folder metadata if available
830
+ if folder_info.get("createdAt"):
831
+ console.print(f' [cyan]Created at:[/cyan] {folder_info.get("createdAt")}')
832
+ if folder_info.get("updatedAt"):
833
+ console.print(f' [cyan]Updated at:[/cyan] {folder_info.get("updatedAt")}')
834
+ if folder_info.get("folderType"):
835
+ console.print(f' [cyan]Folder type:[/cyan] {folder_info.get("folderType")}')
836
+
837
+ except ValueError as e:
838
+ raise click.ClickException(str(e))
839
+ except Exception as e:
840
+ raise click.ClickException(f"Failed to retrieve deletion status: {str(e)}")
841
+
842
+ return
843
+
759
844
  # Validate link flag requirements AFTER loading profile
760
845
  if link and not session_id:
761
846
  raise click.ClickException("--session-id is required when using --link flag")
762
847
 
763
848
  print('Finding working directory path...')
764
- verify_ssl = ssl_selector(disable_ssl_verification, ssl_cert)
765
849
  if verbose:
766
850
  print('\t...Preparing objects')
767
851
  print('\tUsing the following parameters:')
@@ -932,6 +1016,9 @@ def job_logs(ctx,
932
1016
  @click.option('--session-id',
933
1017
  help='The specific CloudOS interactive session id. Required when using --link flag.',
934
1018
  required=False)
1019
+ @click.option('--status',
1020
+ help='Check the deletion status of the job results.',
1021
+ is_flag=True)
935
1022
  @click.option('--verbose',
936
1023
  help='Whether to print information messages or not.',
937
1024
  is_flag=True)
@@ -951,20 +1038,101 @@ def job_results(ctx,
951
1038
  job_id,
952
1039
  link,
953
1040
  session_id,
1041
+ status,
954
1042
  verbose,
955
1043
  disable_ssl_verification,
956
1044
  ssl_cert,
957
1045
  profile):
958
- """Get the path to the results of a specified job."""
1046
+ """Get the path to the results of a specified job or check deletion status."""
959
1047
  # apikey, cloudos_url, and workspace_id are now automatically resolved by the decorator
960
1048
  # session_id is also resolved if provided in profile
961
1049
 
1050
+ verify_ssl = ssl_selector(disable_ssl_verification, ssl_cert)
1051
+
1052
+ # Handle --status flag
1053
+ if status:
1054
+ console = Console()
1055
+
1056
+ if verbose:
1057
+ console.print('[bold cyan]Checking deletion status of job results...[/bold cyan]')
1058
+ console.print('\t[dim]...Preparing objects[/dim]')
1059
+ console.print('\t[bold]Using the following parameters:[/bold]')
1060
+ console.print(f'\t\t[cyan]CloudOS url:[/cyan] {cloudos_url}')
1061
+ console.print(f'\t\t[cyan]Workspace ID:[/cyan] {workspace_id}')
1062
+ console.print(f'\t\t[cyan]Job ID:[/cyan] {job_id}')
1063
+
1064
+ # Use Cloudos object to access the deletion status method
1065
+ cl = Cloudos(cloudos_url, apikey, None)
1066
+
1067
+ if verbose:
1068
+ console.print('\t[dim]The following Cloudos object was created:[/dim]')
1069
+ console.print('\t' + str(cl) + '\n')
1070
+
1071
+ try:
1072
+ deletion_status = cl.get_results_deletion_status(
1073
+ job_id=job_id,
1074
+ workspace_id=workspace_id,
1075
+ verify=verify_ssl
1076
+ )
1077
+
1078
+ # Convert API status to user-friendly terminology with color
1079
+ status_config = {
1080
+ "ready": ("available", "green"),
1081
+ "deleting": ("deleting", "yellow"),
1082
+ "scheduledForDeletion": ("scheduled for deletion", "yellow"),
1083
+ "deleted": ("deleted", "red"),
1084
+ "failedToDelete": ("failed to delete", "red")
1085
+ }
1086
+
1087
+ # Get the status of the results folder itself and convert it
1088
+ api_status = deletion_status.get("status", "unknown")
1089
+ folder_status, status_color = status_config.get(api_status, (api_status, "white"))
1090
+ folder_info = deletion_status.get("items", {})
1091
+
1092
+ # Display results in a clear, styled format with human-readable sentence
1093
+ console.print(f'The results of job [cyan]{deletion_status["job_id"]}[/cyan] are in status: [bold {status_color}]{folder_status}[/bold {status_color}]')
1094
+
1095
+ # For non-available statuses, always show update time and user info
1096
+ if folder_status != "available":
1097
+ if folder_info.get("updatedAt"):
1098
+ console.print(f'[magenta]Status changed at:[/magenta] {folder_info.get("updatedAt")}')
1099
+
1100
+ # Show user information - prefer deletedBy over user field
1101
+ user_info = folder_info.get("deletedBy") or folder_info.get("user", {})
1102
+ if user_info:
1103
+ user_name = f"{user_info.get('name', '')} {user_info.get('surname', '')}".strip()
1104
+ user_email = user_info.get('email', '')
1105
+ if user_name or user_email:
1106
+ user_display = f'{user_name} ({user_email})' if user_name and user_email else (user_name or user_email)
1107
+ console.print(f'[blue]User:[/blue] {user_display}')
1108
+
1109
+ # Display detailed information if verbose
1110
+ if verbose:
1111
+ console.print(f'\n[bold]Additional information:[/bold]')
1112
+ console.print(f' [cyan]Job name:[/cyan] {deletion_status["job_name"]}')
1113
+ console.print(f' [cyan]Results folder name:[/cyan] {deletion_status["results_folder_name"]}')
1114
+ console.print(f' [cyan]Results folder ID:[/cyan] {deletion_status["results_folder_id"]}')
1115
+
1116
+ # Show folder metadata if available
1117
+ if folder_info.get("createdAt"):
1118
+ console.print(f' [cyan]Created at:[/cyan] {folder_info.get("createdAt")}')
1119
+ if folder_info.get("updatedAt"):
1120
+ console.print(f' [cyan]Updated at:[/cyan] {folder_info.get("updatedAt")}')
1121
+ if folder_info.get("folderType"):
1122
+ console.print(f' [cyan]Folder type:[/cyan] {folder_info.get("folderType")}')
1123
+
1124
+ except ValueError as e:
1125
+ raise click.ClickException(str(e))
1126
+ except Exception as e:
1127
+ raise click.ClickException(f"Failed to retrieve deletion status: {str(e)}")
1128
+
1129
+ return
1130
+
962
1131
  # Validate link flag requirements AFTER loading profile
963
1132
  if link and not session_id:
964
1133
  raise click.ClickException("--session-id is required when using --link flag")
965
1134
 
966
1135
  print('Executing results...')
967
- verify_ssl = ssl_selector(disable_ssl_verification, ssl_cert)
968
1136
  if verbose:
969
1137
  print('\t...Preparing objects')
970
1138
  print('\tUsing the following parameters:')
@@ -0,0 +1 @@
1
+ __version__ = '2.69.1'
@@ -224,8 +224,56 @@ class Cloudos:
224
224
  if "resumeWorkDir" not in r_json:
225
225
  raise ValueError("Working directories are not available. This may be because the analysis was run without resumable mode enabled, or because intermediate results have since been removed.")
226
226
 
227
- # If resumeWorkDir exists, use the folders API to get the shared working directory
227
+ # Check if intermediate results have been deleted
228
+ # When intermediate results are deleted, resumeWorkDir becomes None but workDirectory still exists with folderId
228
229
  resume_workdir_id = r_json.get("resumeWorkDir")
230
+ if resume_workdir_id is None and "workDirectory" in r_json:
231
+ work_directory = r_json["workDirectory"]
232
+ # If workDirectory has a folderId, it means the job was resumeable but intermediate results were deleted
233
+ if work_directory.get("folderId") is not None:
234
+ # Get the actual deletion status from the folders API
235
+ api_status = None
236
+ try:
237
+ folder_id = work_directory["folderId"]
238
+ folder_response = self.get_folder_deletion_status(folder_id, workspace_id, verify)
239
+ folder_data = json.loads(folder_response.content)
240
+
241
+ # If the API returns the folder, get its status
242
+ if folder_data and len(folder_data) > 0:
243
+ api_status = folder_data[0].get("status")
244
+ else:
245
+ # If the folder is not returned, check if deletedBy exists in workDirectory
246
+ if "deletedBy" in work_directory:
247
+ api_status = "scheduledForDeletion" # Assume scheduled for deletion
248
+
249
+ except Exception:
250
+ # If we can't get the status, check if deletedBy exists
251
+ if "deletedBy" in work_directory:
252
+ api_status = "scheduledForDeletion" # Assume scheduled for deletion
253
+
254
+ # Build contextually appropriate error message based on status
255
+ # Only raise error for non-ready statuses (ready means it's available, so no error)
256
+ if api_status == "deleting":
257
+ error_msg = "Intermediate job results are currently being deleted. The working directory is not accessible."
258
+ raise ValueError(error_msg)
259
+ elif api_status == "scheduledForDeletion":
260
+ error_msg = "Intermediate job results have been scheduled for deletion. The working directory is no longer available."
261
+ raise ValueError(error_msg)
262
+ elif api_status == "deleted":
263
+ error_msg = "Intermediate job results have been deleted. The working directory is no longer available."
264
+ raise ValueError(error_msg)
265
+ elif api_status == "failedToDelete":
266
+ error_msg = "Intermediate job results were marked for deletion but failed to delete. The working directory may not be accessible."
267
+ raise ValueError(error_msg)
268
+ elif api_status != "ready" and api_status is not None:
269
+ # For any other unknown status (not ready), raise generic error
270
+ error_msg = "Intermediate job results have been removed. The working directory is no longer available."
271
+ raise ValueError(error_msg)
272
+ # If status is "ready", set resume_workdir_id so we can retrieve the workdir path
273
+ elif api_status == "ready":
274
+ resume_workdir_id = folder_id
275
+
276
+ # If resumeWorkDir exists, use the folders API to get the shared working directory
229
277
  if resume_workdir_id:
230
278
  try:
231
279
  # Use folders API to get the actual shared working directory
@@ -421,6 +469,53 @@ class Cloudos:
421
469
  job_workspace = req_obj["team"]
422
470
  if job_workspace != workspace_id:
423
471
  raise ValueError("Workspace provided or configured is different from workspace where the job was executed")
472
+
473
+ # Check if analysis results have been deleted or scheduled for deletion
474
+ # Similar to workdir check - if analysisResults exists with folderId, check its status
475
+ if "analysisResults" in req_obj and req_obj.get("analysisResults"):
476
+ analysis_results = req_obj["analysisResults"]
477
+ results_folder_id = analysis_results.get("folderId")
478
+
479
+ if results_folder_id:
480
+ # Get the actual deletion status from the folders API
481
+ api_status = None
482
+ try:
483
+ folder_response = self.get_folder_deletion_status(results_folder_id, workspace_id, verify)
484
+ folder_data = json.loads(folder_response.content)
485
+
486
+ # If the API returns the folder, get its status
487
+ if folder_data and len(folder_data) > 0:
488
+ api_status = folder_data[0].get("status")
489
+ else:
490
+ # If the folder is not returned, check if deletedBy exists in analysisResults
491
+ if "deletedBy" in analysis_results:
492
+ api_status = "scheduledForDeletion" # Assume scheduled for deletion
493
+
494
+ except Exception:
495
+ # If we can't get the status, check if deletedBy exists
496
+ if "deletedBy" in analysis_results:
497
+ api_status = "scheduledForDeletion" # Assume scheduled for deletion
498
+
499
+ # Build contextually appropriate error message based on status
500
+ # Only raise error for non-ready statuses (ready means it's available, so no error)
501
+ if api_status == "deleting":
502
+ error_msg = "Analysis results are currently being deleted. The results folder is not accessible."
503
+ raise ValueError(error_msg)
504
+ elif api_status == "scheduledForDeletion":
505
+ error_msg = "Analysis results have been scheduled for deletion. The results folder is no longer available."
506
+ raise ValueError(error_msg)
507
+ elif api_status == "deleted":
508
+ error_msg = "Analysis results have been deleted. The results folder is no longer available."
509
+ raise ValueError(error_msg)
510
+ elif api_status == "failedToDelete":
511
+ error_msg = "Analysis results were marked for deletion but failed to delete. The results folder may not be accessible."
512
+ raise ValueError(error_msg)
513
+ elif api_status != "ready" and api_status is not None:
514
+ # For any other unknown status (not ready), raise generic error
515
+ error_msg = "Analysis results have been removed. The results folder is no longer available."
516
+ raise ValueError(error_msg)
517
+ # If status is "ready" or None, don't raise error - let the code continue to retrieve the results path
518
+
424
519
  cloud_name, meta, cloud_storage = find_cloud(self.cloudos_url, self.apikey, workspace_id, req_obj["logs"])
425
520
  # cont_name
426
521
  results_obj = req_obj["results"]
@@ -439,6 +534,343 @@ class Cloudos:
439
534
  results[filename] = f"{scheme}://{storage_account_prefix}{results_container}/{item['path']}"
440
535
  return results
441
536
 
537
+ def get_folder_items_deletion_status(self, folder_id, workspace_id, verify=True):
538
+ """Get deletion status of items within a folder.
539
+
540
+ Simple API wrapper to query the datasets API for items in a folder
541
+ with their deletion status (ready/deleting).
542
+
543
+ Parameters
544
+ ----------
545
+ folder_id : str
546
+ The CloudOS folder ID.
547
+ workspace_id : str
548
+ The CloudOS workspace ID.
549
+ verify : [bool | str], optional
550
+ Whether to use SSL verification or not. Alternatively, if
551
+ a string is passed, it will be interpreted as the path to
552
+ the SSL certificate file. Default is True.
553
+
554
+ Returns
555
+ -------
556
+ Response
557
+ The API response containing folders and files with their status.
558
+
559
+ Raises
560
+ ------
561
+ BadRequestException
562
+ If the request fails with a status code indicating an error.
563
+ """
564
+ headers = {
565
+ "Content-type": "application/json",
566
+ "apikey": self.apikey
567
+ }
568
+
569
+ # Query all possible deletion statuses
570
+ params = {
571
+ "status": ["ready", "deleted", "deleting", "scheduledForDeletion", "failedToDelete"],
572
+ "teamId": workspace_id
573
+ }
574
+
575
+ url = f"{self.cloudos_url}/api/v1/datasets/{folder_id}/items"
576
+ response = retry_requests_get(url, params=params, headers=headers, verify=verify)
577
+
578
+ if response.status_code >= 400:
579
+ raise BadRequestException(response)
580
+
581
+ return response
582
+
583
+ def get_results_deletion_status(self, job_id, workspace_id, verify=True):
584
+ """Get the deletion status of a specific job's results folder.
585
+
586
+ This method orchestrates finding the job's results folder and retrieving
587
+ the deletion status of items within it.
588
+
589
+ Parameters
590
+ ----------
591
+ job_id : str
592
+ The CloudOS job ID.
593
+ workspace_id : str
594
+ The CloudOS workspace ID.
595
+ verify : [bool | str], optional
596
+ Whether to use SSL verification or not. Alternatively, if
597
+ a string is passed, it will be interpreted as the path to
598
+ the SSL certificate file. Default is True.
599
+
600
+ Returns
601
+ -------
602
+ dict
603
+ A dictionary containing the deletion status information with the following structure:
604
+ {
605
+ "job_id": str, # The job ID
606
+ "job_name": str, # The job name
607
+ "results_folder_id": str, # The ID of the job's results folder
608
+ "results_folder_name": str, # The name of the job's results folder
609
+ "items": dict # Dictionary with 'folders' and 'files' arrays containing items and their status
610
+ }
611
+
612
+ Raises
613
+ ------
614
+ BadRequestException
615
+ If the request fails with a status code indicating an error.
616
+ ValueError
617
+ If the job's results folder is not found.
618
+ """
619
+ # First, get job details to find the project and job name
620
+ job_status = self.get_job_status(job_id, workspace_id, verify)
621
+ job_data = json.loads(job_status.content)
622
+ job_name = job_data.get("name", job_id)
623
+ project_info = job_data.get("project")
624
+
625
+ # Extract deletedBy info from analysisResults if available
626
+ analysis_results_deleted_by = None
627
+ if "analysisResults" in job_data and job_data.get("analysisResults"):
628
+ analysis_results_deleted_by = job_data["analysisResults"].get("deletedBy")
629
+
630
+ if not project_info:
631
+ raise ValueError(f"Could not find project for job '{job_id}'")
632
+
633
+ # Extract project ID and name from the project info dict
634
+ project_id = project_info.get("_id")
635
+ project_name = project_info.get("name")
636
+
637
+ if not project_name or not project_id:
638
+ raise ValueError(f"Could not extract project information from job '{job_id}'")
639
+
640
+ from cloudos_cli.datasets.datasets import Datasets
641
+
642
+ # Create Datasets object to navigate to the Analysis Results folder
643
+ ds = Datasets(
644
+ cloudos_url=self.cloudos_url,
645
+ apikey=self.apikey,
646
+ cromwell_token=self.cromwell_token,
647
+ workspace_id=workspace_id,
648
+ project_name=project_name,
649
+ verify=verify
650
+ )
651
+
652
+ # Get project content to find Analysis Results folder
653
+ try:
654
+ project_content = ds.list_project_content()
655
+ except Exception as e:
656
+ raise ValueError(f"Failed to list project content for project '{project_name}': {str(e)}")
657
+
658
+ # Find the Analysis Results folder ID
659
+ analysis_results_id = None
660
+ for folder in project_content.get("folders", []):
661
+ if folder['name'] in ['Analyses Results', 'AnalysesResults']:
662
+ analysis_results_id = folder['_id']
663
+ break
664
+
665
+ if not analysis_results_id:
666
+ raise ValueError(f"Analyses Results folder not found in project '{project_name}'.")
667
+
668
+ # Get items in Analysis Results folder to find the job's specific results folder
669
+ # The Analysis Results folder contains folders for each job's results
670
+ try:
671
+ response = self.get_folder_items_deletion_status(analysis_results_id, workspace_id, verify)
672
+ content = json.loads(response.content)
673
+ except Exception as e:
674
+ raise ValueError(f"Failed to get items from Analyses Results folder: {str(e)}")
675
+
676
+ # The API response contains folders and files arrays
677
+ # Find the entry matching our job_id
678
+ job_status_info = None
679
+
680
+ # Check if it's a dict with folders/files arrays
681
+ if isinstance(content, dict):
682
+ # Check for 'folders' or 'files' keys (common dataset API response format)
683
+ items_to_search = []
684
+ if 'folders' in content:
685
+ items_to_search.extend(content['folders'])
686
+ if 'files' in content:
687
+ items_to_search.extend(content['files'])
688
+
689
+ # If no folders/files keys, treat dict values as items
690
+ if not items_to_search:
691
+ items_to_search = list(content.values())
692
+
693
+ for item in items_to_search:
694
+ if not isinstance(item, dict):
695
+ continue
696
+
697
+ item_name = item.get("name", "")
698
+
699
+ # Match by exact job ID in the item name (format: workflowname-jobid)
700
+ # The folder name should contain the exact job ID
701
+ if job_id in item_name:
702
+ job_status_info = item
703
+ break
704
+ elif isinstance(content, list):
705
+ for item in content:
706
+ if not isinstance(item, dict):
707
+ continue
708
+
709
+ item_name = item.get("name", "")
710
+
711
+ # Match by exact job ID in the item name
712
+ if job_id in item_name:
713
+ job_status_info = item
714
+ break
715
+
716
+ if not job_status_info:
717
+ raise ValueError(
718
+ f"Results folder for job '{job_name}' (ID: {job_id}) not found in Analyses Results.\n"
719
+ f"This may indicate that the results have been deleted or are scheduled for deletion."
720
+ )
721
+
722
+ # Merge the deletedBy info from job data with the folder info
723
+ # The deletedBy from analysisResults is more reliable than folder's user field
724
+ if analysis_results_deleted_by:
725
+ job_status_info["deletedBy"] = analysis_results_deleted_by
726
+
727
+ return {
728
+ "job_id": job_id,
729
+ "job_name": job_name,
730
+ "results_folder_id": job_status_info.get("_id"),
731
+ "results_folder_name": job_status_info.get("name"),
732
+ "status": job_status_info.get("status"),
733
+ "items": job_status_info
734
+ }
735
+
736
+ def get_folder_deletion_status(self, folder_id, workspace_id, verify=True):
737
+ """Get deletion status of a specific folder by ID.
738
+
739
+ Simple API wrapper to query the folders API for a specific folder
740
+ with its deletion status (ready/deleting/etc).
741
+
742
+ Parameters
743
+ ----------
744
+ folder_id : str
745
+ The CloudOS folder ID.
746
+ workspace_id : str
747
+ The CloudOS workspace ID.
748
+ verify : [bool | str], optional
749
+ Whether to use SSL verification or not. Alternatively, if
750
+ a string is passed, it will be interpreted as the path to
751
+ the SSL certificate file. Default is True.
752
+
753
+ Returns
754
+ -------
755
+ Response
756
+ The API response containing folder information with status.
757
+
758
+ Raises
759
+ ------
760
+ BadRequestException
761
+ If the request fails with a status code indicating an error.
762
+ """
763
+ headers = {
764
+ "Content-type": "application/json",
765
+ "apikey": self.apikey
766
+ }
767
+
768
+ # Query with all possible deletion statuses
769
+ params = {
770
+ "id": folder_id,
771
+ "status": ["ready", "deleted", "deleting", "scheduledForDeletion", "failedToDelete"],
772
+ "teamId": workspace_id
773
+ }
774
+
775
+ url = f"{self.cloudos_url}/api/v1/folders/"
776
+ response = retry_requests_get(url, params=params, headers=headers, verify=verify)
777
+
778
+ if response.status_code >= 400:
779
+ raise BadRequestException(response)
780
+
781
+ return response
782
+
783
+ def get_workdir_deletion_status(self, job_id, workspace_id, verify=True):
784
+ """Get the deletion status of a specific job's working directory.
785
+
786
+ This method retrieves the deletion status of the job's working directory
787
+ using the folders API endpoint.
788
+
789
+ Parameters
790
+ ----------
791
+ job_id : str
792
+ The CloudOS job ID.
793
+ workspace_id : str
794
+ The CloudOS workspace ID.
795
+ verify : [bool | str], optional
796
+ Whether to use SSL verification or not. Alternatively, if
797
+ a string is passed, it will be interpreted as the path to
798
+ the SSL certificate file. Default is True.
799
+
800
+ Returns
801
+ -------
802
+ dict
803
+ A dictionary containing the deletion status information with the following structure:
804
+ {
805
+ "job_id": str, # The job ID
806
+ "job_name": str, # The job name
807
+ "workdir_folder_id": str, # The ID of the job's working directory folder
808
+ "workdir_folder_name": str, # The name of the job's working directory folder
809
+ "status": str, # The deletion status
810
+ "items": dict # Full folder object with metadata
811
+ }
812
+
813
+ Raises
814
+ ------
815
+ BadRequestException
816
+ If the request fails with a status code indicating an error.
817
+ ValueError
818
+ If the job's working directory is not found or not accessible.
819
+ """
820
+ # First, get job details to find the working directory folder ID
821
+ job_status = self.get_job_status(job_id, workspace_id, verify)
822
+ job_data = json.loads(job_status.content)
823
+ job_name = job_data.get("name", job_id)
824
+
825
+ # Try to get the workdir folder ID from workDirectory.folderId first (new format)
826
+ # If not available, fall back to resumeWorkDir (old format)
827
+ workdir_folder_id = None
828
+ workdir_deleted_by = None
829
+
830
+ if "workDirectory" in job_data and job_data.get("workDirectory"):
831
+ workdir_folder_id = job_data["workDirectory"].get("folderId")
832
+ # Get deletedBy info if available (contains user who scheduled deletion)
833
+ workdir_deleted_by = job_data["workDirectory"].get("deletedBy")
834
+
835
+ if not workdir_folder_id and "resumeWorkDir" in job_data:
836
+ workdir_folder_id = job_data.get("resumeWorkDir")
837
+
838
+ if not workdir_folder_id:
839
+ raise ValueError(
840
+ "Working directory is not available for this job. "
841
+ "This may be because the analysis was run without resumable mode enabled, "
842
+ "or because intermediate results have been removed."
843
+ )
844
+
845
+ # Use the folders API to get the working directory status
846
+ response = self.get_folder_deletion_status(workdir_folder_id, workspace_id, verify)
847
+
848
+ # Parse the response
849
+ content = json.loads(response.content)
850
+
851
+ # The API returns an array with the folder info
852
+ if not content or len(content) == 0:
853
+ raise ValueError(
854
+ f"Working directory for job '{job_name}' (ID: {job_id}) not found.\n"
855
+ f"This may indicate that the working directory has been deleted or is scheduled for deletion."
856
+ )
857
+
858
+ workdir_info = content[0] # Get the first (and should be only) result
859
+
860
+ # Merge the deletedBy info from job data with the folder info
861
+ # The deletedBy from workDirectory is more reliable than folder's user field
862
+ if workdir_deleted_by:
863
+ workdir_info["deletedBy"] = workdir_deleted_by
864
+
865
+ return {
866
+ "job_id": job_id,
867
+ "job_name": job_name,
868
+ "workdir_folder_id": workdir_info.get("_id"),
869
+ "workdir_folder_name": workdir_info.get("name"),
870
+ "status": workdir_info.get("status"),
871
+ "items": workdir_info
872
+ }
873
+
442
874
  def _create_cromwell_header(self):
443
875
  """Generates cromwell header.
444
876
 
@@ -4,14 +4,13 @@ from cloudos_cli.utils.errors import BadRequestException, AccountNotLinkedExcept
4
4
  from cloudos_cli.utils.requests import retry_requests_post, retry_requests_get
5
5
  import json
6
6
  from requests.exceptions import RetryError
7
- import sys
8
7
 
9
8
 
10
9
  class WFImport(ABC):
11
10
  def __init__(self, cloudos_url, cloudos_apikey, workspace_id, platform,
12
11
  workflow_name, workflow_url, workflow_docs_link="", workflow_description="", cost_limit=30, main_file=None, verify=True):
13
12
  self.cloudos_url = cloudos_url
14
- self.workflow_url = workflow_url.rstrip('.git')
13
+ self.workflow_url = workflow_url.removesuffix('.git')
15
14
  self.workspace_id = workspace_id
16
15
  self.platform = platform
17
16
  self.parsed_url = urlsplit(self.workflow_url)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.68.0
3
+ Version: 2.69.1
4
4
  Summary: Python package for interacting with CloudOS
5
5
  Home-page: https://github.com/lifebit-ai/cloudos-cli
6
6
  Author: David Piñeyro
@@ -669,6 +669,41 @@ You can also link all result directories to an interactive session using the `--
669
669
  cloudos job results --profile my_profile --job-id "12345678910" --link --session-id your_session_id
670
670
  ```
671
671
 
672
+ **Check Results Deletion Status**
673
+
674
+ You can check the deletion status of a job's results folder using the `--status` flag. This is useful for monitoring the deletion lifecycle of analysis results.
675
+
676
+ ```bash
677
+ cloudos job results --status --profile my_profile --job-id "12345678910"
678
+ ```
679
+
680
+ The command will display the current status of the results folder. Possible statuses include:
681
+ - **available**: Results are available and accessible
682
+ - **scheduled for deletion**: Results are scheduled to be deleted
683
+ - **deleting**: Results are currently being deleted
684
+ - **deleted**: Results have been deleted
685
+ - **failed to delete**: Deletion process failed
686
+
687
+ Example output for available results:
688
+ ```console
689
+ The results of job 1234567890 are in status: available
690
+ ```
691
+
692
+ For results in any state other than available, the output includes additional information about when the status changed and who initiated the change:
693
+ ```console
694
+ The results of job 6912036aa6ed001148c96018 are in status: scheduled for deletion
695
+ Status changed at: 2025-11-11T14:43:44.416Z
696
+ User: Leila Mansouri (leila.mansouri@lifebit.ai)
697
+ ```
698
+
699
+ Use the `--verbose` flag to see detailed information including the results folder name, folder ID, creation and update timestamps:
700
+ ```bash
701
+ cloudos job results --status --profile my_profile --job-id "12345678910" --verbose
702
+ ```
703
+
704
+ > [!NOTE]
705
+ > If results have been completely deleted, the command will report that the results folder was not found, which may indicate that results have been deleted or scheduled for deletion.
706
+
672
707
 
673
708
  #### Clone or Resume job
674
709
 
@@ -930,6 +965,42 @@ cloudos job workdir \
930
965
  --link --session-id your_session_id
931
966
  ```
932
967
 
968
+ **Check Working Directory Deletion Status**
969
+
970
+ You can check the deletion status of a job's working directory using the `--status` flag. This is useful for monitoring the deletion lifecycle of intermediate job files.
971
+
972
+ ```bash
973
+ cloudos job workdir --status --profile my_profile --job-id "12345678910"
974
+ ```
975
+
976
+ The command will display the current status of the working directory folder. Possible statuses include:
977
+ - **available**: Working directory is available and accessible
978
+ - **scheduled for deletion**: Working directory is scheduled to be deleted
979
+ - **deleting**: Working directory is currently being deleted
980
+ - **deleted**: Working directory has been deleted
981
+ - **failed to delete**: Deletion process failed
982
+
983
+ Example output for available working directory:
984
+ ```console
985
+ The working directory of job 6912036aa6ed001148c96018 is in status: available
986
+ ```
987
+
988
+ For working directories in any state other than available, the output includes additional information about when the status changed and who initiated the change:
989
+ ```console
990
+ The working directory of job 6912036aa6ed001148c96018 is in status: scheduled for deletion
991
+ Status changed at: 2025-11-11T14:43:44.416Z
992
+ User: Leila Mansouri (leila.mansouri@lifebit.ai)
993
+ ```
994
+
995
+ Use the `--verbose` flag to see detailed information including the working directory folder name, folder ID, creation and update timestamps:
996
+ ```bash
997
+ cloudos job workdir --status --profile my_profile --job-id "12345678910" --verbose
998
+ ```
999
+
1000
+ > [!NOTE]
1001
+ > This command only works for jobs that were run with resumable mode enabled (using the `--resumable` flag). Jobs without resumable mode will not have a working directory to check.
1002
+ > If the working directory has been completely deleted, the command will report that the working directory was not found.
1003
+
933
1004
  #### List Jobs
934
1005
 
935
1006
  You can get a summary of workspace jobs in two different formats:
@@ -1 +0,0 @@
1
- __version__ = '2.68.0'
File without changes
File without changes
File without changes