fal 1.26.3__tar.gz → 1.26.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.

Potentially problematic release.


This version of fal might be problematic. Click here for more details.

Files changed (185) hide show
  1. {fal-1.26.3/fal.egg-info → fal-1.26.4}/PKG-INFO +1 -1
  2. {fal-1.26.3 → fal-1.26.4/fal.egg-info}/PKG-INFO +1 -1
  3. {fal-1.26.3 → fal-1.26.4}/src/fal/_fal_version.py +2 -2
  4. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/profile.py +65 -8
  5. {fal-1.26.3 → fal-1.26.4}/src/fal/config.py +21 -17
  6. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/file/providers/fal.py +21 -32
  7. {fal-1.26.3 → fal-1.26.4}/.gitignore +0 -0
  8. {fal-1.26.3 → fal-1.26.4}/Makefile +0 -0
  9. {fal-1.26.3 → fal-1.26.4}/README.md +0 -0
  10. {fal-1.26.3 → fal-1.26.4}/docs/conf.py +0 -0
  11. {fal-1.26.3 → fal-1.26.4}/docs/index.rst +0 -0
  12. {fal-1.26.3 → fal-1.26.4}/fal.egg-info/SOURCES.txt +0 -0
  13. {fal-1.26.3 → fal-1.26.4}/fal.egg-info/dependency_links.txt +0 -0
  14. {fal-1.26.3 → fal-1.26.4}/fal.egg-info/entry_points.txt +0 -0
  15. {fal-1.26.3 → fal-1.26.4}/fal.egg-info/requires.txt +0 -0
  16. {fal-1.26.3 → fal-1.26.4}/fal.egg-info/top_level.txt +0 -0
  17. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/README.md +0 -0
  18. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  19. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  20. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  21. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  22. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  23. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  24. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  25. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  26. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  27. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  28. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  29. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  30. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  31. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  32. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  33. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  34. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  35. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  36. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  37. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  38. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  39. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  40. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  41. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  42. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  43. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  44. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  45. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  46. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  47. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  48. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  49. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  50. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  51. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  52. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  53. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  54. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  55. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  56. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  57. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  58. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  59. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  60. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  61. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  62. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  63. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  64. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  65. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  66. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  67. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  68. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  69. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  70. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  71. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  72. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  73. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  74. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  75. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  76. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  77. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  78. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  79. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  80. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  81. {fal-1.26.3 → fal-1.26.4}/openapi-fal-rest/pyproject.toml +0 -0
  82. {fal-1.26.3 → fal-1.26.4}/openapi_rest.config.yaml +0 -0
  83. {fal-1.26.3 → fal-1.26.4}/pyproject.toml +0 -0
  84. {fal-1.26.3 → fal-1.26.4}/setup.cfg +0 -0
  85. {fal-1.26.3 → fal-1.26.4}/src/fal/__init__.py +0 -0
  86. {fal-1.26.3 → fal-1.26.4}/src/fal/__main__.py +0 -0
  87. {fal-1.26.3 → fal-1.26.4}/src/fal/_serialization.py +0 -0
  88. {fal-1.26.3 → fal-1.26.4}/src/fal/_version.py +0 -0
  89. {fal-1.26.3 → fal-1.26.4}/src/fal/api.py +0 -0
  90. {fal-1.26.3 → fal-1.26.4}/src/fal/app.py +0 -0
  91. {fal-1.26.3 → fal-1.26.4}/src/fal/apps.py +0 -0
  92. {fal-1.26.3 → fal-1.26.4}/src/fal/auth/__init__.py +0 -0
  93. {fal-1.26.3 → fal-1.26.4}/src/fal/auth/auth0.py +0 -0
  94. {fal-1.26.3 → fal-1.26.4}/src/fal/auth/local.py +0 -0
  95. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/__init__.py +0 -0
  96. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/_utils.py +0 -0
  97. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/api.py +0 -0
  98. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/apps.py +0 -0
  99. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/auth.py +0 -0
  100. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/cli_nested_json.py +0 -0
  101. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/create.py +0 -0
  102. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/debug.py +0 -0
  103. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/deploy.py +0 -0
  104. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/doctor.py +0 -0
  105. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/files.py +0 -0
  106. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/keys.py +0 -0
  107. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/main.py +0 -0
  108. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/parser.py +0 -0
  109. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/run.py +0 -0
  110. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/runners.py +0 -0
  111. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/secrets.py +0 -0
  112. {fal-1.26.3 → fal-1.26.4}/src/fal/cli/teams.py +0 -0
  113. {fal-1.26.3 → fal-1.26.4}/src/fal/console/__init__.py +0 -0
  114. {fal-1.26.3 → fal-1.26.4}/src/fal/console/icons.py +0 -0
  115. {fal-1.26.3 → fal-1.26.4}/src/fal/console/ux.py +0 -0
  116. {fal-1.26.3 → fal-1.26.4}/src/fal/container.py +0 -0
  117. {fal-1.26.3 → fal-1.26.4}/src/fal/exceptions/__init__.py +0 -0
  118. {fal-1.26.3 → fal-1.26.4}/src/fal/exceptions/_base.py +0 -0
  119. {fal-1.26.3 → fal-1.26.4}/src/fal/exceptions/_cuda.py +0 -0
  120. {fal-1.26.3 → fal-1.26.4}/src/fal/exceptions/auth.py +0 -0
  121. {fal-1.26.3 → fal-1.26.4}/src/fal/files.py +0 -0
  122. {fal-1.26.3 → fal-1.26.4}/src/fal/flags.py +0 -0
  123. {fal-1.26.3 → fal-1.26.4}/src/fal/logging/__init__.py +0 -0
  124. {fal-1.26.3 → fal-1.26.4}/src/fal/logging/isolate.py +0 -0
  125. {fal-1.26.3 → fal-1.26.4}/src/fal/logging/style.py +0 -0
  126. {fal-1.26.3 → fal-1.26.4}/src/fal/logging/trace.py +0 -0
  127. {fal-1.26.3 → fal-1.26.4}/src/fal/logging/user.py +0 -0
  128. {fal-1.26.3 → fal-1.26.4}/src/fal/project.py +0 -0
  129. {fal-1.26.3 → fal-1.26.4}/src/fal/py.typed +0 -0
  130. {fal-1.26.3 → fal-1.26.4}/src/fal/rest_client.py +0 -0
  131. {fal-1.26.3 → fal-1.26.4}/src/fal/sdk.py +0 -0
  132. {fal-1.26.3 → fal-1.26.4}/src/fal/sync.py +0 -0
  133. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/__init__.py +0 -0
  134. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/audio/__init__.py +0 -0
  135. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/audio/audio.py +0 -0
  136. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/exceptions.py +0 -0
  137. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/file/__init__.py +0 -0
  138. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/file/file.py +0 -0
  139. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/file/providers/gcp.py +0 -0
  140. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/file/providers/r2.py +0 -0
  141. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/file/providers/s3.py +0 -0
  142. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/file/types.py +0 -0
  143. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/__init__.py +0 -0
  144. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/image.py +0 -0
  145. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  146. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  147. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  148. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  149. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  150. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/image/safety_checker.py +0 -0
  151. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/kv.py +0 -0
  152. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/optimize.py +0 -0
  153. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/types.py +0 -0
  154. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/utils/__init__.py +0 -0
  155. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/utils/download_utils.py +0 -0
  156. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/utils/endpoint.py +0 -0
  157. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/utils/retry.py +0 -0
  158. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/video/__init__.py +0 -0
  159. {fal-1.26.3 → fal-1.26.4}/src/fal/toolkit/video/video.py +0 -0
  160. {fal-1.26.3 → fal-1.26.4}/src/fal/utils.py +0 -0
  161. {fal-1.26.3 → fal-1.26.4}/src/fal/workflows.py +0 -0
  162. {fal-1.26.3 → fal-1.26.4}/tests/__init__.py +0 -0
  163. {fal-1.26.3 → fal-1.26.4}/tests/assets/cat.png +0 -0
  164. {fal-1.26.3 → fal-1.26.4}/tests/cli/__init__.py +0 -0
  165. {fal-1.26.3 → fal-1.26.4}/tests/cli/test_apps.py +0 -0
  166. {fal-1.26.3 → fal-1.26.4}/tests/cli/test_auth.py +0 -0
  167. {fal-1.26.3 → fal-1.26.4}/tests/cli/test_deploy.py +0 -0
  168. {fal-1.26.3 → fal-1.26.4}/tests/cli/test_keys.py +0 -0
  169. {fal-1.26.3 → fal-1.26.4}/tests/cli/test_run.py +0 -0
  170. {fal-1.26.3 → fal-1.26.4}/tests/cli/test_secrets.py +0 -0
  171. {fal-1.26.3 → fal-1.26.4}/tests/conftest.py +0 -0
  172. {fal-1.26.3 → fal-1.26.4}/tests/integration_test.py +0 -0
  173. {fal-1.26.3 → fal-1.26.4}/tests/mainify_package/__init__.py +0 -0
  174. {fal-1.26.3 → fal-1.26.4}/tests/mainify_package/impl.py +0 -0
  175. {fal-1.26.3 → fal-1.26.4}/tests/mainify_package/utils.py +0 -0
  176. {fal-1.26.3 → fal-1.26.4}/tests/mainify_target.py +0 -0
  177. {fal-1.26.3 → fal-1.26.4}/tests/test_apps.py +0 -0
  178. {fal-1.26.3 → fal-1.26.4}/tests/test_kv.py +0 -0
  179. {fal-1.26.3 → fal-1.26.4}/tests/test_stability.py +0 -0
  180. {fal-1.26.3 → fal-1.26.4}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
  181. {fal-1.26.3 → fal-1.26.4}/tests/toolkit/file_test.py +0 -0
  182. {fal-1.26.3 → fal-1.26.4}/tests/toolkit/image_test.py +0 -0
  183. {fal-1.26.3 → fal-1.26.4}/tests/toolkit/test_types.py +0 -0
  184. {fal-1.26.3 → fal-1.26.4}/tests/toolkit/utils/retry.py +0 -0
  185. {fal-1.26.3 → fal-1.26.4}/tools/demo_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.26.3
3
+ Version: 1.26.4
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.26.3
3
+ Version: 1.26.4
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.26.3'
21
- __version_tuple__ = version_tuple = (1, 26, 3)
20
+ __version__ = version = '1.26.4'
21
+ __version_tuple__ = version_tuple = (1, 26, 4)
@@ -23,14 +23,38 @@ def _list(args):
23
23
 
24
24
 
25
25
  def _set(args):
26
- with Config().edit() as config:
27
- config.set_internal("profile", args.PROFILE)
28
- args.console.print(f"Default profile set to [cyan]{args.PROFILE}[/].")
29
- config.profile = args.PROFILE
30
- if not config.get("key"):
26
+ config = Config()
27
+
28
+ # Check if the profile exists
29
+ if args.PROFILE not in config._config:
30
+ # Profile doesn't exist, offer to create it
31
+ args.console.print(f"Profile [cyan]{args.PROFILE}[/] does not exist.")
32
+ create_profile = input("Would you like to create it? (y/N): ").strip().lower()
33
+
34
+ if create_profile in ["y", "yes"]:
35
+ # Create the profile by setting it
36
+ with config.edit() as config:
37
+ config.set_internal("profile", args.PROFILE)
38
+ config._config[args.PROFILE] = {}
39
+ config._profile = args.PROFILE
31
40
  args.console.print(
32
- "No key set for profile. Use [bold]fal profile key[/] to set a key."
41
+ f"Profile [cyan]{args.PROFILE}[/] created and set as default."
33
42
  )
43
+ else:
44
+ args.console.print("Profile creation cancelled.")
45
+ return
46
+ else:
47
+ # Profile exists, just set it as default
48
+ with config.edit() as config:
49
+ config.set_internal("profile", args.PROFILE)
50
+ config._profile = args.PROFILE
51
+ args.console.print(f"Default profile set to [cyan]{args.PROFILE}[/].")
52
+
53
+ # Check if key is set for the profile
54
+ if not config.get("key"):
55
+ args.console.print(
56
+ "No key set for profile. Use [bold]fal profile key[/] to set a key."
57
+ )
34
58
 
35
59
 
36
60
  def _unset(args, config: Config | None = None):
@@ -42,6 +66,8 @@ def _unset(args, config: Config | None = None):
42
66
 
43
67
 
44
68
  def _key_set(args):
69
+ config = Config(validate_profile=True)
70
+
45
71
  while True:
46
72
  key = input("Enter the key: ")
47
73
  if ":" in key:
@@ -50,7 +76,7 @@ def _key_set(args):
50
76
  "[red]Invalid key. The key must be in the format [bold]key:value[/].[/]"
51
77
  )
52
78
 
53
- with Config().edit() as config:
79
+ with config.edit():
54
80
  config.set("key", key)
55
81
  args.console.print(f"Key set for profile [cyan]{config.profile}[/].")
56
82
 
@@ -61,6 +87,24 @@ def _host_set(args):
61
87
  args.console.print(f"Fal host set to [cyan]{args.HOST}[/].")
62
88
 
63
89
 
90
+ def _create(args):
91
+ config = Config()
92
+
93
+ # Check if the profile already exists
94
+ if args.PROFILE in config._config:
95
+ args.console.print(f"Profile [cyan]{args.PROFILE}[/] already exists.")
96
+ return
97
+
98
+ # Create the profile
99
+ with config.edit() as config:
100
+ config._config[args.PROFILE] = {}
101
+
102
+ args.console.print(f"Profile [cyan]{args.PROFILE}[/] created.")
103
+ args.console.print(
104
+ f"Use [bold]fal profile set {args.PROFILE}[/] to set it as default."
105
+ )
106
+
107
+
64
108
  def _delete(args):
65
109
  with Config().edit() as config:
66
110
  if config.profile == args.PROFILE:
@@ -96,7 +140,7 @@ def add_parser(main_subparsers, parents):
96
140
  )
97
141
  list_parser.set_defaults(func=_list)
98
142
 
99
- set_help = "Set default profile."
143
+ set_help = "Set default profile. If the profile doesn't exist, you'll be prompted to create it." # noqa: E501
100
144
  set_parser = subparsers.add_parser(
101
145
  "set",
102
146
  description=set_help,
@@ -140,6 +184,19 @@ def add_parser(main_subparsers, parents):
140
184
  )
141
185
  host_set_parser.set_defaults(func=_host_set)
142
186
 
187
+ create_help = "Create a new profile. Use 'fal profile set <name>' to set it as default after creation." # noqa: E501
188
+ create_parser = subparsers.add_parser(
189
+ "create",
190
+ description=create_help,
191
+ help=create_help,
192
+ parents=parents,
193
+ )
194
+ create_parser.add_argument(
195
+ "PROFILE",
196
+ help="Profile name.",
197
+ )
198
+ create_parser.set_defaults(func=_create)
199
+
143
200
  delete_help = "Delete profile."
144
201
  delete_parser = subparsers.add_parser(
145
202
  "delete",
@@ -4,8 +4,9 @@ import os
4
4
  from contextlib import contextmanager
5
5
  from typing import Dict, Iterator, List, Optional
6
6
 
7
- SETTINGS_SECTION = "__internal__" # legacy
8
- DEFAULT_PROFILE = "default"
7
+ SETTINGS_SECTION = "__internal__"
8
+
9
+ NO_PROFILE_ERROR = ValueError("No profile set.")
9
10
 
10
11
 
11
12
  class Config:
@@ -15,7 +16,7 @@ class Config:
15
16
 
16
17
  DEFAULT_CONFIG_PATH = "~/.fal/config.toml"
17
18
 
18
- def __init__(self):
19
+ def __init__(self, *, validate_profile: bool = False):
19
20
  import tomli
20
21
 
21
22
  self.config_path = os.path.expanduser(
@@ -28,11 +29,17 @@ class Config:
28
29
  except FileNotFoundError:
29
30
  self._config = {}
30
31
 
31
- profile = (
32
- os.getenv("FAL_PROFILE") or self.get_internal("profile") or DEFAULT_PROFILE
33
- )
32
+ profile = os.getenv("FAL_PROFILE") or self.get_internal("profile")
33
+
34
+ # Try to set the profile, but don't fail if it doesn't exist
35
+ try:
36
+ self.profile = profile
37
+ except ValueError:
38
+ # Profile doesn't exist, set to None
39
+ self._profile = None
34
40
 
35
- self.profile = profile
41
+ if validate_profile and not self.profile:
42
+ raise NO_PROFILE_ERROR
36
43
 
37
44
  @property
38
45
  def profile(self) -> Optional[str]:
@@ -41,9 +48,10 @@ class Config:
41
48
  @profile.setter
42
49
  def profile(self, value: Optional[str]) -> None:
43
50
  if value and value not in self._config:
44
- # Make sure the section exists
45
- self._config[value] = {}
46
- self.set_internal("profile", value)
51
+ # Don't automatically create profiles - they should be created explicitly
52
+ raise ValueError(
53
+ f"Profile '{value}' does not exist. Create it first or use the profile set command." # noqa: E501
54
+ )
47
55
  elif not value:
48
56
  self.unset_internal("profile")
49
57
 
@@ -53,11 +61,7 @@ class Config:
53
61
  keys: List[str] = []
54
62
  for key in self._config:
55
63
  if key != SETTINGS_SECTION:
56
- if key == DEFAULT_PROFILE:
57
- # Add it at the beginning
58
- keys.insert(0, key)
59
- else:
60
- keys.append(key)
64
+ keys.append(key)
61
65
 
62
66
  return keys
63
67
 
@@ -75,13 +79,13 @@ class Config:
75
79
 
76
80
  def set(self, key: str, value: str) -> None:
77
81
  if not self.profile:
78
- raise ValueError("No profile set.")
82
+ raise NO_PROFILE_ERROR
79
83
 
80
84
  self._config[self.profile][key] = value
81
85
 
82
86
  def unset(self, key: str) -> None:
83
87
  if not self.profile:
84
- raise ValueError("No profile set.")
88
+ raise NO_PROFILE_ERROR
85
89
 
86
90
  self._config.get(self.profile, {}).pop(key, None)
87
91
 
@@ -9,7 +9,7 @@ from contextlib import contextmanager
9
9
  from dataclasses import dataclass
10
10
  from datetime import datetime, timezone
11
11
  from pathlib import Path
12
- from typing import Any, Generator, Generic, TypeVar
12
+ from typing import Any, Dict, Generator, Generic, TypeVar
13
13
  from urllib.error import HTTPError
14
14
  from urllib.parse import urlparse, urlunparse
15
15
  from urllib.request import Request, urlopen
@@ -69,6 +69,19 @@ def _maybe_retry_request(
69
69
  yield response
70
70
 
71
71
 
72
+ def _object_lifecycle_headers(
73
+ headers: dict[str, str],
74
+ object_lifecycle_preference: dict[str, str] | None,
75
+ ):
76
+ if object_lifecycle_preference:
77
+ # Used by V3 CDN
78
+ headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
79
+ # Used by V1 CDN
80
+ headers["X-Fal-Object-Lifecycle-Preference"] = json.dumps(
81
+ object_lifecycle_preference
82
+ )
83
+
84
+
72
85
  @dataclass
73
86
  class FalV2Token:
74
87
  token: str
@@ -451,14 +464,6 @@ class MultipartUploadGCS:
451
464
 
452
465
  @dataclass
453
466
  class FalFileRepository(FalFileRepositoryBase):
454
- def _object_lifecycle_headers(
455
- self,
456
- headers: dict[str, str],
457
- object_lifecycle_preference: dict[str, str] | None,
458
- ):
459
- if object_lifecycle_preference:
460
- headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
461
-
462
467
  def save(
463
468
  self,
464
469
  file: FileData,
@@ -479,9 +484,8 @@ class FalFileRepository(FalFileRepositoryBase):
479
484
  max_concurrency=multipart_max_concurrency,
480
485
  )
481
486
 
482
- headers = {}
483
- if object_lifecycle_preference:
484
- headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
487
+ headers: Dict[str, str] = {}
488
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
485
489
 
486
490
  return self._save(file, "gcs", headers=headers)
487
491
 
@@ -1124,6 +1128,8 @@ class FalFileRepositoryV2(FalFileRepositoryBase):
1124
1128
  "Content-Type": file.content_type,
1125
1129
  }
1126
1130
 
1131
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1132
+
1127
1133
  storage_url = f"{token.base_upload_url}/upload"
1128
1134
 
1129
1135
  try:
@@ -1195,16 +1201,6 @@ class InMemoryRepository(FileRepository):
1195
1201
 
1196
1202
  @dataclass
1197
1203
  class FalCDNFileRepository(FileRepository):
1198
- def _object_lifecycle_headers(
1199
- self,
1200
- headers: dict[str, str],
1201
- object_lifecycle_preference: dict[str, str] | None,
1202
- ):
1203
- if object_lifecycle_preference:
1204
- headers["X-Fal-Object-Lifecycle-Preference"] = json.dumps(
1205
- object_lifecycle_preference
1206
- )
1207
-
1208
1204
  def save(
1209
1205
  self,
1210
1206
  file: FileData,
@@ -1221,7 +1217,7 @@ class FalCDNFileRepository(FileRepository):
1221
1217
  "X-Fal-File-Name": file.file_name,
1222
1218
  }
1223
1219
 
1224
- self._object_lifecycle_headers(headers, object_lifecycle_preference)
1220
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1225
1221
 
1226
1222
  url = os.getenv("FAL_CDN_HOST", _FAL_CDN) + "/files/upload"
1227
1223
  request = Request(url, headers=headers, method="POST", data=file.data)
@@ -1288,6 +1284,7 @@ class FalFileRepositoryV3(FileRepository):
1288
1284
  "Accept": "application/json",
1289
1285
  "Content-Type": "application/json",
1290
1286
  }
1287
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1291
1288
 
1292
1289
  grpc_host = os.environ.get("FAL_HOST", "api.alpha.fal.ai")
1293
1290
  rest_host = grpc_host.replace("api", "rest", 1)
@@ -1376,14 +1373,6 @@ class InternalFalFileRepositoryV3(FileRepository):
1376
1373
  That way it can avoid the need to refresh the token for every upload.
1377
1374
  """
1378
1375
 
1379
- def _object_lifecycle_headers(
1380
- self,
1381
- headers: dict[str, str],
1382
- object_lifecycle_preference: dict[str, str] | None,
1383
- ):
1384
- if object_lifecycle_preference:
1385
- headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
1386
-
1387
1376
  def save(
1388
1377
  self,
1389
1378
  file: FileData,
@@ -1413,7 +1402,7 @@ class InternalFalFileRepositoryV3(FileRepository):
1413
1402
  "X-Fal-File-Name": file.file_name,
1414
1403
  }
1415
1404
 
1416
- self._object_lifecycle_headers(headers, object_lifecycle_preference)
1405
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1417
1406
 
1418
1407
  url = os.getenv("FAL_CDN_V3_HOST", _FAL_CDN_V3) + "/files/upload"
1419
1408
  request = Request(url, headers=headers, method="POST", data=file.data)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes