fal 1.11.0__tar.gz → 1.11.2__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.
- {fal-1.11.0/fal.egg-info → fal-1.11.2}/PKG-INFO +1 -1
- {fal-1.11.0 → fal-1.11.2/fal.egg-info}/PKG-INFO +1 -1
- {fal-1.11.0 → fal-1.11.2}/fal.egg-info/SOURCES.txt +1 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/_fal_version.py +2 -2
- {fal-1.11.0 → fal-1.11.2}/src/fal/auth/__init__.py +36 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/_utils.py +14 -4
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/apps.py +8 -21
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/auth.py +27 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/keys.py +4 -9
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/main.py +2 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/parser.py +4 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/profile.py +22 -27
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/runners.py +2 -3
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/secrets.py +4 -9
- fal-1.11.2/src/fal/cli/teams.py +89 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/config.py +17 -4
- {fal-1.11.0 → fal-1.11.2}/src/fal/sdk.py +21 -4
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/utils/download_utils.py +38 -6
- {fal-1.11.0 → fal-1.11.2}/tests/cli/test_deploy.py +21 -0
- {fal-1.11.0 → fal-1.11.2}/tests/test_apps.py +17 -5
- {fal-1.11.0 → fal-1.11.2}/.gitignore +0 -0
- {fal-1.11.0 → fal-1.11.2}/Makefile +0 -0
- {fal-1.11.0 → fal-1.11.2}/README.md +0 -0
- {fal-1.11.0 → fal-1.11.2}/docs/conf.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/docs/index.rst +0 -0
- {fal-1.11.0 → fal-1.11.2}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.11.0 → fal-1.11.2}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.11.0 → fal-1.11.2}/fal.egg-info/requires.txt +0 -0
- {fal-1.11.0 → fal-1.11.2}/fal.egg-info/top_level.txt +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/README.md +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.11.0 → fal-1.11.2}/openapi_rest.config.yaml +0 -0
- {fal-1.11.0 → fal-1.11.2}/pyproject.toml +0 -0
- {fal-1.11.0 → fal-1.11.2}/setup.cfg +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/__main__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/_serialization.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/_version.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/api.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/app.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/apps.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/auth/auth0.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/auth/local.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/api.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/cli_nested_json.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/create.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/debug.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/deploy.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/doctor.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/cli/run.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/console/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/console/icons.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/console/ux.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/container.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/exceptions/_base.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/exceptions/auth.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/files.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/flags.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/logging/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/logging/isolate.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/logging/style.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/logging/trace.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/logging/user.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/py.typed +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/rest_client.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/sync.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/file/file.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/file/providers/fal.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/file/providers/s3.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/types.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/utils.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/src/fal/workflows.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/assets/cat.png +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/cli/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/cli/test_apps.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/cli/test_auth.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/cli/test_keys.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/cli/test_run.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/cli/test_secrets.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/conftest.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/integration_test.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/mainify_package/__init__.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/mainify_package/impl.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/mainify_package/utils.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/mainify_target.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/test_stability.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/toolkit/file_test.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/toolkit/image_test.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/toolkit/test_types.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tests/toolkit/utils/retry.py +0 -0
- {fal-1.11.0 → fal-1.11.2}/tools/demo_script.py +0 -0
|
@@ -128,16 +128,40 @@ def _fetch_access_token() -> str:
|
|
|
128
128
|
return token_data["access_token"]
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
def _fetch_teams(bearer_token: str) -> list[dict]:
|
|
132
|
+
import json
|
|
133
|
+
from urllib.error import HTTPError
|
|
134
|
+
from urllib.request import Request, urlopen
|
|
135
|
+
|
|
136
|
+
from fal.exceptions import FalServerlessException
|
|
137
|
+
from fal.flags import REST_URL
|
|
138
|
+
|
|
139
|
+
request = Request(
|
|
140
|
+
method="GET",
|
|
141
|
+
url=f"{REST_URL}/users/teams",
|
|
142
|
+
headers={"Authorization": bearer_token},
|
|
143
|
+
)
|
|
144
|
+
try:
|
|
145
|
+
with urlopen(request) as response:
|
|
146
|
+
teams = json.load(response)
|
|
147
|
+
except HTTPError as exc:
|
|
148
|
+
raise FalServerlessException("Failed to fetch teams") from exc
|
|
149
|
+
|
|
150
|
+
return [team for team in teams if not team["is_personal"]]
|
|
151
|
+
|
|
152
|
+
|
|
131
153
|
@dataclass
|
|
132
154
|
class UserAccess:
|
|
133
155
|
_access_token: str | None = field(repr=False, default=None)
|
|
134
156
|
_user_info: dict | None = field(repr=False, default=None)
|
|
135
157
|
_exc: Exception | None = field(repr=False, default=None)
|
|
158
|
+
_teams: list[dict] | None = field(repr=False, default=None)
|
|
136
159
|
|
|
137
160
|
def invalidate(self) -> None:
|
|
138
161
|
self._access_token = None
|
|
139
162
|
self._user_info = None
|
|
140
163
|
self._exc = None
|
|
164
|
+
self._teams = None
|
|
141
165
|
|
|
142
166
|
@property
|
|
143
167
|
def info(self) -> dict:
|
|
@@ -167,5 +191,17 @@ class UserAccess:
|
|
|
167
191
|
def bearer_token(self) -> str:
|
|
168
192
|
return "Bearer " + self.access_token
|
|
169
193
|
|
|
194
|
+
@property
|
|
195
|
+
def teams(self) -> list[dict]:
|
|
196
|
+
if self._teams is None:
|
|
197
|
+
self._teams = _fetch_teams(self.bearer_token)
|
|
198
|
+
return self._teams
|
|
199
|
+
|
|
200
|
+
def get_team(self, team: str) -> dict:
|
|
201
|
+
for t in self.teams:
|
|
202
|
+
if t["nickname"].lower() == team.lower():
|
|
203
|
+
return t
|
|
204
|
+
raise ValueError(f"Team {team} not found")
|
|
205
|
+
|
|
170
206
|
|
|
171
207
|
USER = UserAccess()
|
|
@@ -3,6 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
from fal.files import find_project_root, find_pyproject_toml, parse_pyproject_toml
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
def get_client(host: str, team: str | None = None):
|
|
7
|
+
from fal.sdk import FalServerlessClient, get_default_credentials
|
|
8
|
+
|
|
9
|
+
credentials = get_default_credentials(team=team)
|
|
10
|
+
return FalServerlessClient(host, credentials)
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
def is_app_name(app_ref: tuple[str, str | None]) -> bool:
|
|
7
14
|
is_single_file = app_ref[1] is None
|
|
8
15
|
is_python_file = app_ref[0].endswith(".py")
|
|
@@ -25,7 +32,7 @@ def get_app_data_from_toml(app_name):
|
|
|
25
32
|
raise ValueError(f"App {app_name} not found in pyproject.toml")
|
|
26
33
|
|
|
27
34
|
try:
|
|
28
|
-
app_ref = app_data
|
|
35
|
+
app_ref = app_data.pop("ref")
|
|
29
36
|
except KeyError:
|
|
30
37
|
raise ValueError(f"App {app_name} does not have a ref key in pyproject.toml")
|
|
31
38
|
|
|
@@ -33,8 +40,11 @@ def get_app_data_from_toml(app_name):
|
|
|
33
40
|
project_root, _ = find_project_root(None)
|
|
34
41
|
app_ref = str(project_root / app_ref)
|
|
35
42
|
|
|
36
|
-
app_auth = app_data.
|
|
37
|
-
app_deployment_strategy = app_data.
|
|
38
|
-
app_no_scale = app_data.
|
|
43
|
+
app_auth = app_data.pop("auth", "private")
|
|
44
|
+
app_deployment_strategy = app_data.pop("deployment_strategy", "recreate")
|
|
45
|
+
app_no_scale = app_data.pop("no_scale", False)
|
|
46
|
+
|
|
47
|
+
if len(app_data) > 0:
|
|
48
|
+
raise ValueError(f"Found unexpected keys in pyproject.toml: {app_data}")
|
|
39
49
|
|
|
40
50
|
return app_ref, app_auth, app_deployment_strategy, app_no_scale
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
+
from ._utils import get_client
|
|
5
6
|
from .parser import FalClientParser
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
@@ -47,9 +48,7 @@ def _apps_table(apps: list[AliasInfo]):
|
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
def _list(args):
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
client = FalServerlessClient(args.host)
|
|
51
|
+
client = get_client(args.host, args.team)
|
|
53
52
|
with client.connect() as connection:
|
|
54
53
|
apps = connection.list_aliases()
|
|
55
54
|
|
|
@@ -120,9 +119,7 @@ def _app_rev_table(revs: list[ApplicationInfo]):
|
|
|
120
119
|
|
|
121
120
|
|
|
122
121
|
def _list_rev(args):
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
client = FalServerlessClient(args.host)
|
|
122
|
+
client = get_client(args.host, args.team)
|
|
126
123
|
with client.connect() as connection:
|
|
127
124
|
revs = connection.list_applications()
|
|
128
125
|
table = _app_rev_table(revs)
|
|
@@ -142,9 +139,7 @@ def _add_list_rev_parser(subparsers, parents):
|
|
|
142
139
|
|
|
143
140
|
|
|
144
141
|
def _scale(args):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
client = FalServerlessClient(args.host)
|
|
142
|
+
client = get_client(args.host, args.team)
|
|
148
143
|
with client.connect() as connection:
|
|
149
144
|
if (
|
|
150
145
|
args.keep_alive is None
|
|
@@ -240,9 +235,7 @@ def _add_scale_parser(subparsers, parents):
|
|
|
240
235
|
|
|
241
236
|
|
|
242
237
|
def _set_rev(args):
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
client = FalServerlessClient(args.host)
|
|
238
|
+
client = get_client(args.host, args.team)
|
|
246
239
|
with client.connect() as connection:
|
|
247
240
|
connection.create_alias(args.app_name, args.app_rev, args.auth)
|
|
248
241
|
|
|
@@ -277,9 +270,7 @@ def _add_set_rev_parser(subparsers, parents):
|
|
|
277
270
|
def _runners(args):
|
|
278
271
|
from rich.table import Table
|
|
279
272
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
client = FalServerlessClient(args.host)
|
|
273
|
+
client = get_client(args.host, args.team)
|
|
283
274
|
with client.connect() as connection:
|
|
284
275
|
runners = connection.list_alias_runners(alias=args.app_name)
|
|
285
276
|
|
|
@@ -350,9 +341,7 @@ def _add_runners_parser(subparsers, parents):
|
|
|
350
341
|
|
|
351
342
|
|
|
352
343
|
def _delete(args):
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
client = FalServerlessClient(args.host)
|
|
344
|
+
client = get_client(args.host, args.team)
|
|
356
345
|
with client.connect() as connection:
|
|
357
346
|
connection.delete_alias(args.app_name)
|
|
358
347
|
|
|
@@ -373,9 +362,7 @@ def _add_delete_parser(subparsers, parents):
|
|
|
373
362
|
|
|
374
363
|
|
|
375
364
|
def _delete_rev(args):
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
client = FalServerlessClient(args.host)
|
|
365
|
+
client = get_client(args.host, args.team)
|
|
379
366
|
with client.connect() as connection:
|
|
380
367
|
connection.delete_application(args.app_rev)
|
|
381
368
|
|
|
@@ -2,11 +2,38 @@ from fal.auth import USER, login, logout
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def _login(args):
|
|
5
|
+
from rich.prompt import Prompt
|
|
6
|
+
|
|
7
|
+
from fal.config import Config
|
|
8
|
+
|
|
5
9
|
login()
|
|
10
|
+
teams = [team["nickname"].lower() for team in USER.teams]
|
|
11
|
+
if not teams:
|
|
12
|
+
return
|
|
13
|
+
|
|
14
|
+
team = Prompt.ask(
|
|
15
|
+
"\nPlease choose a team account to use or leave blank to "
|
|
16
|
+
"use your personal account:",
|
|
17
|
+
choices=teams,
|
|
18
|
+
default=None,
|
|
19
|
+
)
|
|
20
|
+
with Config().edit() as config:
|
|
21
|
+
if team:
|
|
22
|
+
args.console.print(
|
|
23
|
+
f"Setting team to [cyan]{team}[/]. "
|
|
24
|
+
"You can change this later with [bold]fal team set[/]."
|
|
25
|
+
)
|
|
26
|
+
config.set("team", team)
|
|
27
|
+
else:
|
|
28
|
+
config.unset("team")
|
|
6
29
|
|
|
7
30
|
|
|
8
31
|
def _logout(args):
|
|
32
|
+
from fal.config import Config
|
|
33
|
+
|
|
9
34
|
logout()
|
|
35
|
+
with Config().edit() as config:
|
|
36
|
+
config.unset("team")
|
|
10
37
|
|
|
11
38
|
|
|
12
39
|
def _whoami(args):
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from fal.sdk import KeyScope
|
|
2
2
|
|
|
3
|
+
from ._utils import get_client
|
|
3
4
|
from .parser import FalClientParser
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def _create(args):
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
client = FalServerlessClient(args.host)
|
|
8
|
+
client = get_client(args.host, args.team)
|
|
10
9
|
with client.connect() as connection:
|
|
11
10
|
parsed_scope = KeyScope(args.scope)
|
|
12
11
|
result = connection.create_user_key(parsed_scope, args.desc)
|
|
@@ -43,15 +42,13 @@ def _add_create_parser(subparsers, parents):
|
|
|
43
42
|
def _list(args):
|
|
44
43
|
from rich.table import Table
|
|
45
44
|
|
|
46
|
-
from fal.sdk import FalServerlessClient
|
|
47
|
-
|
|
48
|
-
client = FalServerlessClient(args.host)
|
|
49
45
|
table = Table()
|
|
50
46
|
table.add_column("Key ID")
|
|
51
47
|
table.add_column("Created At")
|
|
52
48
|
table.add_column("Scope")
|
|
53
49
|
table.add_column("Description")
|
|
54
50
|
|
|
51
|
+
client = get_client(args.host, args.team)
|
|
55
52
|
with client.connect() as connection:
|
|
56
53
|
keys = connection.list_user_keys()
|
|
57
54
|
for key in keys:
|
|
@@ -77,9 +74,7 @@ def _add_list_parser(subparsers, parents):
|
|
|
77
74
|
|
|
78
75
|
|
|
79
76
|
def _revoke(args):
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
client = FalServerlessClient(args.host)
|
|
77
|
+
client = get_client(args.host, args.team)
|
|
83
78
|
with client.connect() as connection:
|
|
84
79
|
connection.revoke_user_key(args.key_id)
|
|
85
80
|
|
|
@@ -18,6 +18,7 @@ from . import (
|
|
|
18
18
|
run,
|
|
19
19
|
runners,
|
|
20
20
|
secrets,
|
|
21
|
+
teams,
|
|
21
22
|
)
|
|
22
23
|
from .debug import debugtools, get_debug_parser
|
|
23
24
|
from .parser import FalParser, FalParserExit
|
|
@@ -55,6 +56,7 @@ def _get_main_parser() -> argparse.ArgumentParser:
|
|
|
55
56
|
doctor,
|
|
56
57
|
create,
|
|
57
58
|
runners,
|
|
59
|
+
teams,
|
|
58
60
|
]:
|
|
59
61
|
cmd.add_parser(subparsers, parents)
|
|
60
62
|
|
|
@@ -4,13 +4,12 @@ from fal.config import Config
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def _list(args):
|
|
7
|
-
config = Config()
|
|
8
|
-
|
|
9
7
|
table = Table()
|
|
10
8
|
table.add_column("Default")
|
|
11
9
|
table.add_column("Profile")
|
|
12
10
|
table.add_column("Settings")
|
|
13
11
|
|
|
12
|
+
config = Config()
|
|
14
13
|
for profile in config.profiles():
|
|
15
14
|
table.add_row(
|
|
16
15
|
"*" if profile == config._profile else "",
|
|
@@ -22,28 +21,24 @@ def _list(args):
|
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
def _set(args):
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
config.save()
|
|
24
|
+
with Config().edit() as config:
|
|
25
|
+
config.set_internal("profile", args.PROFILE)
|
|
26
|
+
args.console.print(f"Default profile set to [cyan]{args.PROFILE}[/].")
|
|
27
|
+
config.profile = args.PROFILE
|
|
28
|
+
if not config.get("key"):
|
|
29
|
+
args.console.print(
|
|
30
|
+
"No key set for profile. Use [bold]fal profile key[/] to set a key."
|
|
31
|
+
)
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
def _unset(args):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
config.save()
|
|
35
|
+
with Config().edit() as config:
|
|
36
|
+
config.set_internal("profile", None)
|
|
37
|
+
args.console.print("Default profile unset.")
|
|
38
|
+
config.profile = None
|
|
42
39
|
|
|
43
40
|
|
|
44
41
|
def _key_set(args):
|
|
45
|
-
config = Config()
|
|
46
|
-
|
|
47
42
|
while True:
|
|
48
43
|
key = input("Enter the key: ")
|
|
49
44
|
if ":" in key:
|
|
@@ -52,25 +47,25 @@ def _key_set(args):
|
|
|
52
47
|
"[red]Invalid key. The key must be in the format [bold]key:value[/].[/]"
|
|
53
48
|
)
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
with Config().edit() as config:
|
|
51
|
+
config.set("key", key)
|
|
52
|
+
args.console.print(f"Key set for profile [cyan]{config.profile}[/].")
|
|
58
53
|
|
|
59
54
|
|
|
60
55
|
def _delete(args):
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
with Config().edit() as config:
|
|
57
|
+
if config.profile == args.PROFILE:
|
|
58
|
+
config.set_internal("profile", None)
|
|
64
59
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
config.save()
|
|
60
|
+
config.delete(args.PROFILE)
|
|
61
|
+
args.console.print(f"Profile [cyan]{args.PROFILE}[/] deleted.")
|
|
68
62
|
|
|
69
63
|
|
|
70
64
|
def add_parser(main_subparsers, parents):
|
|
71
65
|
auth_help = "Profile management."
|
|
72
66
|
parser = main_subparsers.add_parser(
|
|
73
67
|
"profile",
|
|
68
|
+
aliases=["profiles"],
|
|
74
69
|
description=auth_help,
|
|
75
70
|
help=auth_help,
|
|
76
71
|
parents=parents,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
from ._utils import get_client
|
|
1
2
|
from .parser import FalClientParser
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def _kill(args):
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
client = FalServerlessClient(args.host)
|
|
6
|
+
client = get_client(args.host, args.team)
|
|
8
7
|
with client.connect() as connection:
|
|
9
8
|
connection.kill_runner(args.id)
|
|
10
9
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
from ._utils import get_client
|
|
1
2
|
from .parser import DictAction, FalClientParser
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def _set(args):
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
client = FalServerlessClient(args.host)
|
|
6
|
+
client = get_client(args.host, args.team)
|
|
8
7
|
with client.connect() as connection:
|
|
9
8
|
for name, value in args.secrets.items():
|
|
10
9
|
connection.set_secret(name, value)
|
|
@@ -34,13 +33,11 @@ def _add_set_parser(subparsers, parents):
|
|
|
34
33
|
def _list(args):
|
|
35
34
|
from rich.table import Table
|
|
36
35
|
|
|
37
|
-
from fal.sdk import FalServerlessClient
|
|
38
|
-
|
|
39
36
|
table = Table()
|
|
40
37
|
table.add_column("Secret Name")
|
|
41
38
|
table.add_column("Created At")
|
|
42
39
|
|
|
43
|
-
client =
|
|
40
|
+
client = get_client(args.host, args.team)
|
|
44
41
|
with client.connect() as connection:
|
|
45
42
|
for secret in connection.list_secrets():
|
|
46
43
|
table.add_row(secret.name, str(secret.created_at))
|
|
@@ -60,9 +57,7 @@ def _add_list_parser(subparsers, parents):
|
|
|
60
57
|
|
|
61
58
|
|
|
62
59
|
def _unset(args):
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
client = FalServerlessClient(args.host)
|
|
60
|
+
client = get_client(args.host, args.team)
|
|
66
61
|
with client.connect() as connection:
|
|
67
62
|
connection.delete_secret(args.secret)
|
|
68
63
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
def _list(args):
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
|
|
4
|
+
from fal.auth import USER
|
|
5
|
+
from fal.config import Config
|
|
6
|
+
|
|
7
|
+
table = Table()
|
|
8
|
+
table.add_column("Default")
|
|
9
|
+
table.add_column("Team")
|
|
10
|
+
table.add_column("Full Name")
|
|
11
|
+
table.add_column("ID")
|
|
12
|
+
|
|
13
|
+
default_team = Config().get("team")
|
|
14
|
+
|
|
15
|
+
for team in USER.teams:
|
|
16
|
+
default = default_team and default_team.lower() == team["nickname"].lower()
|
|
17
|
+
table.add_row(
|
|
18
|
+
"*" if default else "", team["nickname"], team["full_name"], team["user_id"]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
args.console.print(table)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _set(args):
|
|
25
|
+
from fal.config import Config
|
|
26
|
+
from fal.sdk import USER
|
|
27
|
+
|
|
28
|
+
team = args.team.lower()
|
|
29
|
+
for team_info in USER.teams:
|
|
30
|
+
if team_info["nickname"].lower() == team:
|
|
31
|
+
break
|
|
32
|
+
else:
|
|
33
|
+
raise ValueError(f"Team {args.team} not found")
|
|
34
|
+
|
|
35
|
+
with Config().edit() as config:
|
|
36
|
+
config.set("team", team)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _unset(args):
|
|
40
|
+
from fal.config import Config
|
|
41
|
+
|
|
42
|
+
with Config().edit() as config:
|
|
43
|
+
config.unset("team")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def add_parser(main_subparsers, parents):
|
|
47
|
+
teams_help = "Manage teams."
|
|
48
|
+
parser = main_subparsers.add_parser(
|
|
49
|
+
"teams",
|
|
50
|
+
aliases=["team"],
|
|
51
|
+
description=teams_help,
|
|
52
|
+
help=teams_help,
|
|
53
|
+
parents=parents,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
subparsers = parser.add_subparsers(
|
|
57
|
+
title="Commands",
|
|
58
|
+
metavar="command",
|
|
59
|
+
dest="cmd",
|
|
60
|
+
required=True,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
list_help = "List teams."
|
|
64
|
+
list_parser = subparsers.add_parser(
|
|
65
|
+
"list",
|
|
66
|
+
description=list_help,
|
|
67
|
+
help=list_help,
|
|
68
|
+
parents=parents,
|
|
69
|
+
)
|
|
70
|
+
list_parser.set_defaults(func=_list)
|
|
71
|
+
|
|
72
|
+
set_help = "Set the current team."
|
|
73
|
+
set_parser = subparsers.add_parser(
|
|
74
|
+
"set",
|
|
75
|
+
description=set_help,
|
|
76
|
+
help=set_help,
|
|
77
|
+
parents=parents,
|
|
78
|
+
)
|
|
79
|
+
set_parser.add_argument("team", help="The team to set.")
|
|
80
|
+
set_parser.set_defaults(func=_set)
|
|
81
|
+
|
|
82
|
+
unset_help = "Unset the current team."
|
|
83
|
+
unset_parser = subparsers.add_parser(
|
|
84
|
+
"unset",
|
|
85
|
+
description=unset_help,
|
|
86
|
+
help=unset_help,
|
|
87
|
+
parents=parents,
|
|
88
|
+
)
|
|
89
|
+
unset_parser.set_defaults(func=_unset)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from contextlib import contextmanager
|
|
2
3
|
from typing import Dict, List, Optional
|
|
3
4
|
|
|
4
|
-
SETTINGS_SECTION = "__internal__"
|
|
5
|
+
SETTINGS_SECTION = "__internal__" # legacy
|
|
6
|
+
DEFAULT_PROFILE = "default"
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class Config:
|
|
@@ -23,9 +25,9 @@ class Config:
|
|
|
23
25
|
except FileNotFoundError:
|
|
24
26
|
self._config = {}
|
|
25
27
|
|
|
26
|
-
profile =
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
profile = (
|
|
29
|
+
os.getenv("FAL_PROFILE") or self.get_internal("profile") or DEFAULT_PROFILE
|
|
30
|
+
)
|
|
29
31
|
|
|
30
32
|
self.profile = profile
|
|
31
33
|
|
|
@@ -66,6 +68,12 @@ class Config:
|
|
|
66
68
|
|
|
67
69
|
self._config[self.profile][key] = value
|
|
68
70
|
|
|
71
|
+
def unset(self, key: str) -> None:
|
|
72
|
+
if not self.profile:
|
|
73
|
+
raise ValueError("No profile set.")
|
|
74
|
+
|
|
75
|
+
del self._config[self.profile][key]
|
|
76
|
+
|
|
69
77
|
def get_internal(self, key: str) -> Optional[str]:
|
|
70
78
|
if SETTINGS_SECTION not in self._config:
|
|
71
79
|
self._config[SETTINGS_SECTION] = {}
|
|
@@ -83,3 +91,8 @@ class Config:
|
|
|
83
91
|
|
|
84
92
|
def delete(self, profile: str) -> None:
|
|
85
93
|
del self._config[profile]
|
|
94
|
+
|
|
95
|
+
@contextmanager
|
|
96
|
+
def edit(self):
|
|
97
|
+
yield self
|
|
98
|
+
self.save()
|
|
@@ -129,12 +129,22 @@ class FalServerlessKeyCredentials(Credentials):
|
|
|
129
129
|
@dataclass
|
|
130
130
|
class AuthenticatedCredentials(Credentials):
|
|
131
131
|
user = USER
|
|
132
|
+
team_id: str | None = None
|
|
132
133
|
|
|
133
134
|
def to_grpc(self) -> grpc.ChannelCredentials:
|
|
134
|
-
|
|
135
|
+
creds = [
|
|
135
136
|
self.server_credentials.to_grpc(),
|
|
136
137
|
grpc.access_token_call_credentials(USER.access_token),
|
|
137
|
-
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
if self.team_id:
|
|
141
|
+
creds.append(
|
|
142
|
+
grpc.metadata_call_credentials(
|
|
143
|
+
_GRPCMetadata("fal-user-id", self.team_id)
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return grpc.composite_channel_credentials(*creds)
|
|
138
148
|
|
|
139
149
|
def to_headers(self) -> dict[str, str]:
|
|
140
150
|
token = USER.bearer_token
|
|
@@ -158,16 +168,23 @@ def get_agent_credentials(original_credentials: Credentials) -> Credentials:
|
|
|
158
168
|
return original_credentials
|
|
159
169
|
|
|
160
170
|
|
|
161
|
-
def get_default_credentials() -> Credentials:
|
|
171
|
+
def get_default_credentials(team: str | None = None) -> Credentials:
|
|
172
|
+
from fal.config import Config
|
|
173
|
+
|
|
162
174
|
if flags.AUTH_DISABLED:
|
|
163
175
|
return Credentials()
|
|
164
176
|
|
|
165
177
|
key_creds = key_credentials()
|
|
166
178
|
if key_creds:
|
|
167
179
|
logger.debug("Using key credentials")
|
|
180
|
+
if team:
|
|
181
|
+
raise ValueError("Using explicit team with key credentials is not allowed")
|
|
168
182
|
return FalServerlessKeyCredentials(key_creds[0], key_creds[1])
|
|
169
183
|
else:
|
|
170
|
-
|
|
184
|
+
config = Config()
|
|
185
|
+
team = team or config.get("team")
|
|
186
|
+
team_id = USER.get_team(team)["user_id"] if team else None
|
|
187
|
+
return AuthenticatedCredentials(team_id=team_id)
|
|
171
188
|
|
|
172
189
|
|
|
173
190
|
@dataclass
|