pal-found-cli 0.1.2__tar.gz → 0.1.4__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 (146) hide show
  1. pal_found_cli-0.1.4/LICENSE +21 -0
  2. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/PKG-INFO +44 -1
  3. pal_found_cli-0.1.2/src/pal_found_cli.egg-info/PKG-INFO → pal_found_cli-0.1.4/README.md +32 -17
  4. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/pyproject.toml +16 -0
  5. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/_version.py +3 -3
  6. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/datasets/scripts/pal_found_datasets_cli.py +204 -52
  7. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/functions/scripts/pal_found_functions_cli.py +5 -0
  8. pal_found_cli-0.1.4/src/pal_found_cli.egg-info/PKG-INFO +117 -0
  9. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli.egg-info/SOURCES.txt +1 -0
  10. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli.egg-info/scm_file_list.json +1 -0
  11. pal_found_cli-0.1.4/src/pal_found_cli.egg-info/scm_version.json +8 -0
  12. pal_found_cli-0.1.4/tests/test_datasets_console_wrapper.py +340 -0
  13. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_functions_console_wrapper.py +44 -0
  14. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_functions_cli.py +2 -2
  15. pal_found_cli-0.1.2/README.md +0 -57
  16. pal_found_cli-0.1.2/src/pal_found_cli.egg-info/scm_version.json +0 -8
  17. pal_found_cli-0.1.2/tests/test_datasets_console_wrapper.py +0 -30
  18. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/.env.example +0 -0
  19. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/.github/workflows/ci.yml +0 -0
  20. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/.github/workflows/publish.yml +0 -0
  21. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/.gitignore +0 -0
  22. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/conda.recipe/meta.yaml +0 -0
  23. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/setup.cfg +0 -0
  24. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/__init__.py +0 -0
  25. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/admin/__init__.py +0 -0
  26. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/admin/scripts/__init__.py +0 -0
  27. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/admin/scripts/pal_found_admin_cli.py +0 -0
  28. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/aip_agents/__init__.py +0 -0
  29. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/aip_agents/metadata-allow-list.md +0 -0
  30. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/aip_agents/scripts/__init__.py +0 -0
  31. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/aip_agents/scripts/pal_found_aip_agents_cli.py +0 -0
  32. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/audit/__init__.py +0 -0
  33. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/audit/metadata-allow-list.md +0 -0
  34. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/audit/scripts/__init__.py +0 -0
  35. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/audit/scripts/pal_found_audit_cli.py +0 -0
  36. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/checkpoints/__init__.py +0 -0
  37. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/checkpoints/metadata-allow-list.md +0 -0
  38. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/checkpoints/scripts/__init__.py +0 -0
  39. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/checkpoints/scripts/pal_found_checkpoints_cli.py +0 -0
  40. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/__init__.py +0 -0
  41. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/access_control_guard.py +0 -0
  42. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/async_client_factory.py +0 -0
  43. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/auth_provider.py +0 -0
  44. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/binary_download_handler.py +0 -0
  45. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/config_loader.py +0 -0
  46. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/error_serializer.py +0 -0
  47. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/log_setup.py +0 -0
  48. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/metadata-allow-list.md +0 -0
  49. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/output_formatter.py +0 -0
  50. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/pagination_helper.py +0 -0
  51. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/retry.py +0 -0
  52. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/sdk_error_utils.py +0 -0
  53. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/session_manager.py +0 -0
  54. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/common/tracing_provider.py +0 -0
  55. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/connectivity/__init__.py +0 -0
  56. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/connectivity/metadata-allow-list.md +0 -0
  57. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/connectivity/scripts/__init__.py +0 -0
  58. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/connectivity/scripts/pal_found_connectivity_cli.py +0 -0
  59. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/data_health/__init__.py +0 -0
  60. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/data_health/metadata-allow-list.md +0 -0
  61. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/data_health/scripts/__init__.py +0 -0
  62. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/data_health/scripts/pal_found_data_health_cli.py +0 -0
  63. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/datasets/__init__.py +0 -0
  64. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/datasets/scripts/__init__.py +0 -0
  65. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/filesystem/__init__.py +0 -0
  66. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/filesystem/scripts/__init__.py +0 -0
  67. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/filesystem/scripts/pal_found_filesystem_cli.py +0 -0
  68. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/functions/__init__.py +0 -0
  69. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/functions/scripts/__init__.py +0 -0
  70. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/language_models/__init__.py +0 -0
  71. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/language_models/metadata-allow-list.md +0 -0
  72. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/language_models/scripts/__init__.py +0 -0
  73. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/language_models/scripts/pal_found_language_models_cli.py +0 -0
  74. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/media_sets/__init__.py +0 -0
  75. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/media_sets/metadata-allow-list.md +0 -0
  76. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/media_sets/scripts/__init__.py +0 -0
  77. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/media_sets/scripts/pal_found_media_sets_cli.py +0 -0
  78. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/models/__init__.py +0 -0
  79. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/models/metadata-allow-list.md +0 -0
  80. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/models/scripts/__init__.py +0 -0
  81. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/models/scripts/pal_found_models_cli.py +0 -0
  82. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/ontologies/__init__.py +0 -0
  83. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/ontologies/scripts/__init__.py +0 -0
  84. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/ontologies/scripts/pal_found_ontologies_cli.py +0 -0
  85. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/orchestration/__init__.py +0 -0
  86. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/orchestration/metadata-allow-list.md +0 -0
  87. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/orchestration/scripts/__init__.py +0 -0
  88. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/orchestration/scripts/pal_found_orchestration_cli.py +0 -0
  89. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/sql_queries/__init__.py +0 -0
  90. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/sql_queries/metadata-allow-list.md +0 -0
  91. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/sql_queries/scripts/__init__.py +0 -0
  92. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/sql_queries/scripts/pal_found_sql_queries_cli.py +0 -0
  93. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/streams/__init__.py +0 -0
  94. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/streams/metadata-allow-list.md +0 -0
  95. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/streams/scripts/__init__.py +0 -0
  96. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/streams/scripts/pal_found_streams_cli.py +0 -0
  97. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/third_party_applications/__init__.py +0 -0
  98. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/third_party_applications/metadata-allow-list.md +0 -0
  99. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/third_party_applications/scripts/__init__.py +0 -0
  100. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/third_party_applications/scripts/pal_found_third_party_applications_cli.py +0 -0
  101. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/widgets/__init__.py +0 -0
  102. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/widgets/metadata-allow-list.md +0 -0
  103. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/widgets/scripts/__init__.py +0 -0
  104. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli/widgets/scripts/pal_found_widgets_cli.py +0 -0
  105. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli.egg-info/dependency_links.txt +0 -0
  106. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli.egg-info/entry_points.txt +0 -0
  107. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli.egg-info/requires.txt +0 -0
  108. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/src/pal_found_cli.egg-info/top_level.txt +0 -0
  109. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/__init__.py +0 -0
  110. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_access_control_guard.py +0 -0
  111. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_admin_console_wrapper.py +0 -0
  112. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_aip_agents_console_wrapper.py +0 -0
  113. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_audit_console_wrapper.py +0 -0
  114. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_binary_download.py +0 -0
  115. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_checkpoints_console_wrapper.py +0 -0
  116. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_data_health_console_wrapper.py +0 -0
  117. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_exec_common_components.py +0 -0
  118. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_exec_retry_error_output_log.py +0 -0
  119. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_filesystem_console_wrapper.py +0 -0
  120. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_language_models_console_wrapper.py +0 -0
  121. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_ontologies_console_wrapper.py +0 -0
  122. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pagination_helper.py +0 -0
  123. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_admin_cli.py +0 -0
  124. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_aip_agents_cli.py +0 -0
  125. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_audit_cli.py +0 -0
  126. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_checkpoints_cli.py +0 -0
  127. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_connectivity_cli.py +0 -0
  128. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_data_health_cli.py +0 -0
  129. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_filesystem_cli.py +0 -0
  130. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_language_models_cli.py +0 -0
  131. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_media_sets_cli.py +0 -0
  132. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_models_cli.py +0 -0
  133. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_orchestration_cli.py +0 -0
  134. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_sql_queries_cli.py +0 -0
  135. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_streams_cli.py +0 -0
  136. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_third_party_applications_cli.py +0 -0
  137. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_pal_found_widgets_cli.py +0 -0
  138. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_release_artifacts.py +0 -0
  139. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_rename_migration.py +0 -0
  140. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_repository_hygiene.py +0 -0
  141. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_session_manager.py +0 -0
  142. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_third_party_applications_console_wrapper.py +0 -0
  143. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_tracing_provider.py +0 -0
  144. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/test_widgets_console_wrapper.py +0 -0
  145. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/unit_test_common_components.py +0 -0
  146. {pal_found_cli-0.1.2 → pal_found_cli-0.1.4}/tests/unit_test_retry_error_output_log.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 t-jet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,9 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pal_found_cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Foundry CLI - 33 operations across 5 resource clients
5
+ License-Expression: MIT
6
+ Project-URL: repository, https://github.com/t-jet/pal_found_cli_tool
7
+ Project-URL: homepage, https://github.com/t-jet/pal_found_cli_tool
8
+ Project-URL: documentation, https://github.com/t-jet/pal_found_cli#readme
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Software Development :: Libraries
5
14
  Requires-Python: >=3.11
6
15
  Description-Content-Type: text/markdown
16
+ License-File: LICENSE
7
17
  Requires-Dist: foundry-platform-sdk>=1.0.0
8
18
  Requires-Dist: python-dotenv>=1.0.0
9
19
  Requires-Dist: requests>=2.31.0
@@ -14,6 +24,7 @@ Requires-Dist: pytest-cov>=5.0; extra == "dev"
14
24
  Requires-Dist: mypy>=1.0; extra == "dev"
15
25
  Requires-Dist: ruff>=0.4.0; extra == "dev"
16
26
  Requires-Dist: bandit>=1.7.0; extra == "dev"
27
+ Dynamic: license-file
17
28
 
18
29
  # Palantir Foundry CLI
19
30
 
@@ -43,6 +54,38 @@ clean environment, and smoke-checked before PyPI publication.
43
54
  The conda recipe uses the same tag-derived version. After a channel release,
44
55
  install with `conda install -c t-jet pal_found_cli`.
45
56
 
57
+ ## Authentication and configuration
58
+
59
+ The CLI authenticates to Foundry with an API token and a target hostname, read
60
+ from a `.env` file or the environment. Before running any `pal-found-*` command,
61
+ configure credentials:
62
+
63
+ ```bash
64
+ # Copy the template and fill in your values
65
+ cp .env.example .env
66
+ ```
67
+
68
+ Set at minimum:
69
+
70
+ ```dotenv
71
+ FOUNDRY_TOKEN=your_foundry_api_token_here
72
+ FOUNDRY_HOSTNAME=https://foundry.example.com
73
+ ```
74
+
75
+ `FOUNDRY_TOKEN` is your Foundry API token (personal or service account) and
76
+ `FOUNDRY_HOSTNAME` is the URL of the Foundry instance you target. The `.env`
77
+ file is searched per ADR-006 (explicit path override via
78
+ `FOUNDRY_AGENTIC_CLI_ENV_FILE`, otherwise the repository root). Do not commit
79
+ `.env` to version control. Optional settings cover request timeout, output
80
+ format, access guards, retry, pagination, and tracing; see `.env.example` for
81
+ the full list.
82
+
83
+ Then verify the setup:
84
+
85
+ ```bash
86
+ pal-found-datasets --help
87
+ ```
88
+
46
89
  This repository contains the installable CLI. The project is split into three
47
90
  independently versioned repositories:
48
91
 
@@ -1,20 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: pal_found_cli
3
- Version: 0.1.2
4
- Summary: Foundry CLI - 33 operations across 5 resource clients
5
- Requires-Python: >=3.11
6
- Description-Content-Type: text/markdown
7
- Requires-Dist: foundry-platform-sdk>=1.0.0
8
- Requires-Dist: python-dotenv>=1.0.0
9
- Requires-Dist: requests>=2.31.0
10
- Provides-Extra: dev
11
- Requires-Dist: pytest>=8.0; extra == "dev"
12
- Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
13
- Requires-Dist: pytest-cov>=5.0; extra == "dev"
14
- Requires-Dist: mypy>=1.0; extra == "dev"
15
- Requires-Dist: ruff>=0.4.0; extra == "dev"
16
- Requires-Dist: bandit>=1.7.0; extra == "dev"
17
-
18
1
  # Palantir Foundry CLI
19
2
 
20
3
  Palantir Foundry CLI provides command-line access to Foundry resource operations with shared configuration, authentication, retry handling, access-control guards, and structured output helpers.
@@ -43,6 +26,38 @@ clean environment, and smoke-checked before PyPI publication.
43
26
  The conda recipe uses the same tag-derived version. After a channel release,
44
27
  install with `conda install -c t-jet pal_found_cli`.
45
28
 
29
+ ## Authentication and configuration
30
+
31
+ The CLI authenticates to Foundry with an API token and a target hostname, read
32
+ from a `.env` file or the environment. Before running any `pal-found-*` command,
33
+ configure credentials:
34
+
35
+ ```bash
36
+ # Copy the template and fill in your values
37
+ cp .env.example .env
38
+ ```
39
+
40
+ Set at minimum:
41
+
42
+ ```dotenv
43
+ FOUNDRY_TOKEN=your_foundry_api_token_here
44
+ FOUNDRY_HOSTNAME=https://foundry.example.com
45
+ ```
46
+
47
+ `FOUNDRY_TOKEN` is your Foundry API token (personal or service account) and
48
+ `FOUNDRY_HOSTNAME` is the URL of the Foundry instance you target. The `.env`
49
+ file is searched per ADR-006 (explicit path override via
50
+ `FOUNDRY_AGENTIC_CLI_ENV_FILE`, otherwise the repository root). Do not commit
51
+ `.env` to version control. Optional settings cover request timeout, output
52
+ format, access guards, retry, pagination, and tracing; see `.env.example` for
53
+ the full list.
54
+
55
+ Then verify the setup:
56
+
57
+ ```bash
58
+ pal-found-datasets --help
59
+ ```
60
+
46
61
  This repository contains the installable CLI. The project is split into three
47
62
  independently versioned repositories:
48
63
 
@@ -8,12 +8,28 @@ dynamic = ["version"]
8
8
  description = "Foundry CLI - 33 operations across 5 resource clients"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ # Classifiers must be consistent with the `license = "MIT"` expression above and
14
+ # the `requires-python` floor (3.11). Twine check enforces this consistency.
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Operating System :: OS Independent",
20
+ "Topic :: Software Development :: Libraries",
21
+ ]
11
22
  dependencies = [
12
23
  "foundry-platform-sdk>=1.0.0",
13
24
  "python-dotenv>=1.0.0",
14
25
  "requests>=2.31.0",
15
26
  ]
16
27
 
28
+ [project.urls]
29
+ repository = "https://github.com/t-jet/pal_found_cli_tool"
30
+ homepage = "https://github.com/t-jet/pal_found_cli_tool"
31
+ documentation = "https://github.com/t-jet/pal_found_cli#readme"
32
+
17
33
  [project.optional-dependencies]
18
34
  dev = [
19
35
  "pytest>=8.0",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.2'
22
- __version_tuple__ = version_tuple = (0, 1, 2)
21
+ __version__ = version = '0.1.4'
22
+ __version_tuple__ = version_tuple = (0, 1, 4)
23
23
 
24
- __commit_id__ = commit_id = 'gdb9ae68e3'
24
+ __commit_id__ = commit_id = 'g86b060271'
@@ -29,6 +29,10 @@ from pal_found_cli.common.access_control_guard import (
29
29
  AccessControlGuard,
30
30
  )
31
31
  from pal_found_cli.common.async_client_factory import AsyncClientFactory
32
+ from pal_found_cli.common.binary_download_handler import (
33
+ BinaryDownloadHandler,
34
+ DownloadError,
35
+ )
32
36
  from pal_found_cli.common.config_loader import ConfigLoader
33
37
  from pal_found_cli.common.error_serializer import (
34
38
  EXIT_ACCESS_CONTROL,
@@ -177,14 +181,32 @@ async def _invoke(
177
181
  dataset_rid=dr, branch_name=bn, request_timeout=timeout
178
182
  )
179
183
  if operation == "get_schema_batch":
180
- rids_arg = getattr(args, "dataset_rids", None)
181
- rids = json.loads(rids_arg) if isinstance(rids_arg, str) else rids_arg
182
- return await client.get_schema_batch(
183
- dataset_rids=rids, request_timeout=timeout
184
- )
184
+ body = json.loads(args.body_json)
185
+ if (
186
+ not isinstance(body, list)
187
+ or not 1 <= len(body) <= 1000
188
+ or not all(
189
+ isinstance(item, dict) and isinstance(item.get("datasetRid"), str)
190
+ for item in body
191
+ )
192
+ ):
193
+ raise ValueError(
194
+ "--body-json must contain 1-1000 objects with datasetRid"
195
+ )
196
+ return await client.get_schema_batch(body, request_timeout=timeout)
185
197
  if operation == "jobs":
198
+ order_by = json.loads(args.order_by_json)
199
+ if not isinstance(order_by, list) or not all(
200
+ isinstance(item, dict) for item in order_by
201
+ ):
202
+ raise ValueError("--order-by-json must be a JSON array of sort objects")
186
203
  return await client.jobs(
187
- dataset_rid=dr, page_size=ps, page_token=pt, request_timeout=timeout
204
+ dataset_rid=dr,
205
+ order_by=order_by,
206
+ branch_name=bn,
207
+ page_size=ps,
208
+ page_token=pt,
209
+ request_timeout=timeout,
188
210
  )
189
211
  if operation == "put_schema":
190
212
  schema_arg = getattr(args, "schema", None)
@@ -194,14 +216,6 @@ async def _invoke(
194
216
  return await client.put_schema(
195
217
  dataset_rid=dr, schema=schema, branch_name=bn, request_timeout=timeout
196
218
  )
197
- if operation == "read_table":
198
- return await client.read_table(
199
- dataset_rid=dr,
200
- branch_name=bn,
201
- page_size=ps,
202
- page_token=pt,
203
- request_timeout=timeout,
204
- )
205
219
  if operation == "transactions":
206
220
  return await client.transactions(
207
221
  dataset_rid=dr, page_size=ps, page_token=pt, request_timeout=timeout
@@ -232,15 +246,6 @@ async def _invoke(
232
246
  request_timeout=timeout,
233
247
  )
234
248
  elif resource == "file":
235
- if operation == "content":
236
- return await client.content(
237
- dataset_rid=dr,
238
- file_path=fp,
239
- branch_name=bn,
240
- end_transaction_rid=getattr(args, "end_transaction_rid", None),
241
- start_transaction_rid=getattr(args, "start_transaction_rid", None),
242
- request_timeout=timeout,
243
- )
244
249
  if operation == "delete":
245
250
  return await client.delete(
246
251
  dataset_rid=dr,
@@ -252,15 +257,20 @@ async def _invoke(
252
257
  return await client.get(
253
258
  dataset_rid=dr,
254
259
  file_path=fp,
255
- transaction_rid=tr,
260
+ branch_name=bn,
261
+ end_transaction_rid=getattr(args, "end_transaction_rid", None),
262
+ start_transaction_rid=getattr(args, "start_transaction_rid", None),
256
263
  request_timeout=timeout,
257
264
  )
258
265
  if operation == "list":
259
266
  return await client.list(
260
267
  dataset_rid=dr,
261
- transaction_rid=tr,
268
+ branch_name=bn,
269
+ end_transaction_rid=getattr(args, "end_transaction_rid", None),
262
270
  page_size=ps,
263
271
  page_token=pt,
272
+ path_prefix=getattr(args, "path_prefix", None),
273
+ start_transaction_rid=getattr(args, "start_transaction_rid", None),
264
274
  request_timeout=timeout,
265
275
  )
266
276
  if operation == "upload":
@@ -271,7 +281,7 @@ async def _invoke(
271
281
  return await client.upload(
272
282
  dataset_rid=dr,
273
283
  file_path=fp,
274
- content=data,
284
+ body=data,
275
285
  transaction_rid=tr,
276
286
  request_timeout=timeout,
277
287
  )
@@ -290,7 +300,10 @@ async def _invoke(
290
300
  )
291
301
  if operation == "create":
292
302
  return await client.create(
293
- dataset_rid=dr, branch_name=bn, request_timeout=timeout
303
+ dataset_rid=dr,
304
+ transaction_type=args.transaction_type,
305
+ branch_name=bn,
306
+ request_timeout=timeout,
294
307
  )
295
308
  if operation == "get":
296
309
  return await client.get(
@@ -318,9 +331,11 @@ async def _invoke(
318
331
  )
319
332
  if operation == "create":
320
333
  return await client.create(
321
- name=nm,
334
+ view_name=args.view_name,
322
335
  parent_folder_rid=pfr,
323
336
  backing_datasets=bd,
337
+ branch=br,
338
+ primary_key=pk,
324
339
  request_timeout=timeout,
325
340
  )
326
341
  if operation == "get":
@@ -344,6 +359,70 @@ async def _invoke(
344
359
  raise ValueError(f"Unknown operation: {resource}.{operation}")
345
360
 
346
361
 
362
+ async def _download_file_content(
363
+ client: Any,
364
+ args: argparse.Namespace,
365
+ timeout: int | None,
366
+ cfg: ConfigLoader,
367
+ ) -> dict[str, Any]:
368
+ """Stream Dataset file content into a bounded, atomic download."""
369
+ response_context = client.with_streaming_response.content(
370
+ dataset_rid=args.dataset_rid,
371
+ file_path=args.file_path,
372
+ branch_name=args.branch_name,
373
+ end_transaction_rid=args.end_transaction_rid,
374
+ start_transaction_rid=args.start_transaction_rid,
375
+ request_timeout=timeout,
376
+ )
377
+ async with response_context as response:
378
+ result = await BinaryDownloadHandler(config=cfg).save(
379
+ response.aiter_bytes(),
380
+ original_filename=args.output,
381
+ namespace="datasets",
382
+ operation="file.content",
383
+ )
384
+ return result.to_dict()
385
+
386
+
387
+ async def _download_read_table(
388
+ client: Any,
389
+ args: argparse.Namespace,
390
+ timeout: int | None,
391
+ cfg: ConfigLoader,
392
+ ) -> dict[str, Any]:
393
+ """Stream a Dataset table export into a bounded, atomic download."""
394
+ columns = None
395
+ if args.columns_json is not None:
396
+ try:
397
+ columns = json.loads(args.columns_json)
398
+ except json.JSONDecodeError as exc:
399
+ raise ValueError("--columns-json must be a JSON array of strings") from exc
400
+ if not isinstance(columns, list) or not all(
401
+ isinstance(column, str) for column in columns
402
+ ):
403
+ raise ValueError("--columns-json must be a JSON array of strings")
404
+
405
+ response_context = client.with_streaming_response.read_table(
406
+ dataset_rid=args.dataset_rid,
407
+ format=args.table_format,
408
+ branch_name=args.branch_name,
409
+ columns=columns,
410
+ end_transaction_rid=args.end_transaction_rid,
411
+ row_limit=args.row_limit,
412
+ start_transaction_rid=args.start_transaction_rid,
413
+ request_timeout=timeout,
414
+ )
415
+ async with response_context as response:
416
+ result = await BinaryDownloadHandler(config=cfg).save(
417
+ response.aiter_bytes(),
418
+ original_filename=args.output,
419
+ namespace="datasets",
420
+ operation="dataset.read_table",
421
+ mime_type="text/csv" if args.table_format == "CSV" else None,
422
+ )
423
+ return result.to_dict()
424
+
425
+
347
426
  # Operations that return a paginated response and accept page_size/page_token
348
427
  # (FR-PAG-3). For these, the CLI wraps the SDK call with PaginationHelper to
349
428
  # honour --page-size / --page-token / --batch-pages and emit metadata to stderr.
@@ -351,12 +430,10 @@ PAGINATED_OPS = frozenset(
351
430
  {
352
431
  ("dataset", "get_schedules"),
353
432
  ("dataset", "jobs"),
354
- ("dataset", "read_table"),
355
433
  ("dataset", "transactions"),
356
434
  ("branch", "list"),
357
435
  ("branch", "transactions"),
358
436
  ("file", "list"),
359
- ("transaction", "transactions"),
360
437
  }
361
438
  )
362
439
 
@@ -374,17 +451,15 @@ async def _invoke_paginated(
374
451
  timeout: int | None,
375
452
  helper: PaginationHelper,
376
453
  ) -> Any:
377
- """Invoke a paginated SDK operation through PaginationHelper.
454
+ """Fetch actual SDK response pages and preserve their continuation tokens.
378
455
 
379
- Builds a single-page callable that delegates to ``_invoke`` (which already
380
- forwards page_size/page_token) and feeds it to ``helper.paginate`` so the
381
- batch aggregation, max-cap enforcement, and token propagation behave per
382
- FR-PAG-1/2/4/5. The helper's metadata is emitted to stderr by the caller.
383
-
384
- The base pagination kwargs are extracted from the args namespace so they
385
- are NOT doubly passed (the SDK call receives them inside ``_invoke``).
456
+ Ordinary SDK list methods return ``AsyncResourceIterator``, which cannot be
457
+ awaited and exposes no public page cursor. Raw response methods return one
458
+ decodable page, so the shared helper can honor CLI paging options.
386
459
  """
387
460
 
461
+ raw_client = client.with_raw_response
462
+
388
463
  async def _single_page(**page_kwargs: Any) -> Any:
389
464
  # Materialize a fresh namespace so page tokens from the helper override
390
465
  # the CLI-supplied values without mutating the original args.
@@ -393,7 +468,14 @@ async def _invoke_paginated(
393
468
  "page_size", getattr(args, "page_size", None)
394
469
  )
395
470
  paged_args.page_token = page_kwargs.get("page_token", None)
396
- return await _invoke(resource, operation, client, paged_args, timeout)
471
+ raw_response = await _invoke(
472
+ resource, operation, raw_client, paged_args, timeout
473
+ )
474
+ page = raw_response.decode()
475
+ return {
476
+ "items": list(getattr(page, "data", None) or []),
477
+ "next_page_token": getattr(page, "next_page_token", None),
478
+ }
397
479
 
398
480
  return await helper.paginate(_single_page)
399
481
 
@@ -423,7 +505,7 @@ def _resolve(resource: str, op: str) -> str:
423
505
  return mapping.get(op, op.replace("-", "_"))
424
506
 
425
507
 
426
- def _common_parser() -> argparse.ArgumentParser:
508
+ def _common_parser(*, paginated: bool = True) -> argparse.ArgumentParser:
427
509
  """Build the shared argparse parent with global options.
428
510
 
429
511
  Every leaf operation subparser inherits these arguments via ``parents``,
@@ -433,9 +515,10 @@ def _common_parser() -> argparse.ArgumentParser:
433
515
  common.add_argument("--timeout", type=int, default=None)
434
516
  common.add_argument("--format", choices=["json", "toon", "auto"], default="auto")
435
517
  common.add_argument("--pretty", action="store_true")
436
- common.add_argument("--page-size", type=int, default=None, dest="page_size")
437
- common.add_argument("--page-token", type=str, default=None, dest="page_token")
438
- common.add_argument("--batch-pages", type=int, default=None, dest="batch_pages")
518
+ if paginated:
519
+ common.add_argument("--page-size", type=int, default=None, dest="page_size")
520
+ common.add_argument("--page-token", type=str, default=None, dest="page_token")
521
+ common.add_argument("--batch-pages", type=int, default=None, dest="batch_pages")
439
522
  return common
440
523
 
441
524
 
@@ -443,8 +526,11 @@ def _add_operation(
443
526
  subparsers: Any,
444
527
  name: str,
445
528
  *arguments: tuple[tuple[str, ...], dict[str, Any]],
529
+ paginated: bool = True,
446
530
  ) -> argparse.ArgumentParser:
447
- operation_parser = subparsers.add_parser(name, parents=[_common_parser()])
531
+ operation_parser = subparsers.add_parser(
532
+ name, parents=[_common_parser(paginated=paginated)]
533
+ )
448
534
  for flags, kwargs in arguments:
449
535
  operation_parser.add_argument(*flags, **kwargs)
450
536
  return cast(argparse.ArgumentParser, operation_parser)
@@ -472,7 +558,6 @@ def build_parser() -> argparse.ArgumentParser:
472
558
  "get-health-checks",
473
559
  "get-schedules",
474
560
  "get-schema",
475
- "read-table",
476
561
  ):
477
562
  _add_operation(
478
563
  ds_sub,
@@ -480,12 +565,43 @@ def build_parser() -> argparse.ArgumentParser:
480
565
  (("dataset_rid",), {}),
481
566
  (("--branch-name",), {"default": None, "dest": "branch_name"}),
482
567
  )
568
+ _add_operation(
569
+ ds_sub,
570
+ "read-table",
571
+ (("dataset_rid",), {}),
572
+ (
573
+ ("--table-format",),
574
+ {"required": True, "choices": ("ARROW", "CSV"), "dest": "table_format"},
575
+ ),
576
+ (("--output",), {"default": None}),
577
+ (("--branch-name",), {"default": None, "dest": "branch_name"}),
578
+ (("--columns-json",), {"default": None, "dest": "columns_json"}),
579
+ (("--end-transaction-rid",), {"default": None, "dest": "end_transaction_rid"}),
580
+ (("--row-limit",), {"type": int, "default": None, "dest": "row_limit"}),
581
+ (
582
+ ("--start-transaction-rid",),
583
+ {"default": None, "dest": "start_transaction_rid"},
584
+ ),
585
+ paginated=False,
586
+ )
483
587
  _add_operation(
484
588
  ds_sub,
485
589
  "get-schema-batch",
486
- (("--dataset-r",), {"required": True, "dest": "dataset_rids"}),
590
+ (("--body-json",), {"required": True, "dest": "body_json"}),
591
+ )
592
+ _add_operation(
593
+ ds_sub,
594
+ "jobs",
595
+ (("dataset_rid",), {}),
596
+ (
597
+ ("--order-by-json",),
598
+ {
599
+ "default": '[{"sortType":"BY_STARTED_TIME","sortDirection":"DESCENDING"}]',
600
+ "dest": "order_by_json",
601
+ },
602
+ ),
603
+ (("--branch-name",), {"default": None, "dest": "branch_name"}),
487
604
  )
488
- _add_operation(ds_sub, "jobs", (("dataset_rid",), {}))
489
605
  _add_operation(
490
606
  ds_sub,
491
607
  "put-schema",
@@ -526,6 +642,7 @@ def build_parser() -> argparse.ArgumentParser:
526
642
  "content",
527
643
  (("dataset_rid",), {}),
528
644
  (("--file-path",), {"required": True, "dest": "file_path"}),
645
+ (("--output",), {"default": None}),
529
646
  (("--branch-name",), {"default": None, "dest": "branch_name"}),
530
647
  (("--end-transaction-rid",), {"default": None, "dest": "end_transaction_rid"}),
531
648
  (
@@ -533,7 +650,7 @@ def build_parser() -> argparse.ArgumentParser:
533
650
  {"default": None, "dest": "start_transaction_rid"},
534
651
  ),
535
652
  )
536
- for operation_name in ("delete", "get", "upload"):
653
+ for operation_name in ("delete", "upload"):
537
654
  _add_operation(
538
655
  fl_sub,
539
656
  operation_name,
@@ -541,11 +658,29 @@ def build_parser() -> argparse.ArgumentParser:
541
658
  (("--file-path",), {"required": True, "dest": "file_path"}),
542
659
  (("--transaction-rid",), {"default": None, "dest": "transaction_rid"}),
543
660
  )
661
+ _add_operation(
662
+ fl_sub,
663
+ "get",
664
+ (("dataset_rid",), {}),
665
+ (("--file-path",), {"required": True, "dest": "file_path"}),
666
+ (("--branch-name",), {"default": None, "dest": "branch_name"}),
667
+ (("--end-transaction-rid",), {"default": None, "dest": "end_transaction_rid"}),
668
+ (
669
+ ("--start-transaction-rid",),
670
+ {"default": None, "dest": "start_transaction_rid"},
671
+ ),
672
+ )
544
673
  _add_operation(
545
674
  fl_sub,
546
675
  "list",
547
676
  (("dataset_rid",), {}),
548
- (("--transaction-rid",), {"default": None, "dest": "transaction_rid"}),
677
+ (("--branch-name",), {"default": None, "dest": "branch_name"}),
678
+ (("--end-transaction-rid",), {"default": None, "dest": "end_transaction_rid"}),
679
+ (("--path-prefix",), {"default": None, "dest": "path_prefix"}),
680
+ (
681
+ ("--start-transaction-rid",),
682
+ {"default": None, "dest": "start_transaction_rid"},
683
+ ),
549
684
  )
550
685
 
551
686
  tx = subparsers.add_parser("transaction")
@@ -561,6 +696,7 @@ def build_parser() -> argparse.ArgumentParser:
561
696
  tx_sub,
562
697
  "create",
563
698
  (("dataset_rid",), {}),
699
+ (("--transaction-type",), {"required": True, "dest": "transaction_type"}),
564
700
  (("--branch-name",), {"default": None, "dest": "branch_name"}),
565
701
  )
566
702
 
@@ -588,9 +724,11 @@ def build_parser() -> argparse.ArgumentParser:
588
724
  _add_operation(
589
725
  vw_sub,
590
726
  "create",
591
- (("--name",), {"required": True}),
727
+ (("--view-name",), {"required": True, "dest": "view_name"}),
592
728
  (("--parent-folder-rid",), {"required": True, "dest": "parent_folder_rid"}),
593
- (("--backing-datasets",), {"default": None, "dest": "backing_datasets"}),
729
+ (("--backing-datasets",), {"required": True, "dest": "backing_datasets"}),
730
+ (("--branch",), {"default": None}),
731
+ (("--primary-key",), {"default": None, "dest": "primary_key"}),
594
732
  )
595
733
  _add_operation(
596
734
  vw_sub,
@@ -668,7 +806,15 @@ async def async_main() -> int:
668
806
 
669
807
  # Invoke operation
670
808
  try:
671
- if helper is not None:
809
+ if resource == "file" and operation == "content":
810
+ result = await retry_handler.execute(
811
+ _download_file_content, client, args, timeout, cfg
812
+ )
813
+ elif resource == "dataset" and operation == "read_table":
814
+ result = await retry_handler.execute(
815
+ _download_read_table, client, args, timeout, cfg
816
+ )
817
+ elif helper is not None:
672
818
  result = await retry_handler.execute(
673
819
  _invoke_paginated,
674
820
  resource,
@@ -705,6 +851,12 @@ async def async_main() -> int:
705
851
  serializer = ErrorSerializer()
706
852
  serializer.serialize(exc)
707
853
  return EXIT_NOT_FOUND
854
+ except DownloadError as exc:
855
+ serializer = ErrorSerializer()
856
+ return serializer.serialize(exc)
857
+ except ValueError as exc:
858
+ serializer = ErrorSerializer()
859
+ return serializer.serialize(exc)
708
860
  except TimeoutError as exc:
709
861
  serializer = ErrorSerializer()
710
862
  serializer.serialize(exc)
@@ -350,6 +350,11 @@ async def main() -> int:
350
350
  args,
351
351
  timeout,
352
352
  )
353
+ if (resource, operation) == ("query", "streaming_execute"):
354
+ # The SDK returns UTF-8 NDJSON. Preserve data and error lines as
355
+ # returned; JSON/TOON formatting would turn the bytes into a count.
356
+ sys.stdout.write(result.decode("utf-8"))
357
+ return EXIT_SUCCESS
353
358
  formatter = OutputFormatter(
354
359
  format_setting=getattr(args, "format", "auto"),
355
360
  pretty=getattr(args, "pretty", False),