fal 0.15.2__py3-none-any.whl → 1.0.0__py3-none-any.whl

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.

@@ -1,26 +1,37 @@
1
- fal/__init__.py,sha256=unz75H_W1OpXsdkjHqjJs_GrA_BzwHaXImRH-pekaL0,617
2
- fal/__main__.py,sha256=8hDtWlaFZK24KhfNq_ZKgtXqYHsDQDetukOCMlsbW0Q,59
1
+ fal/__init__.py,sha256=suif79hYcYwlZ8dAaVXCErKhuD2AYf8uU78rty8jow8,721
2
+ fal/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
3
+ fal/_fal_version.py,sha256=DGJ4pj32xs3_DRJhSzQwCiRNnAQrMgo09USYpyMZsKc,411
3
4
  fal/_serialization.py,sha256=Tx_c_mpJ8dYAVmPwdLkwgozSqfdvdFyWRYx3lH3-koQ,7595
4
- fal/api.py,sha256=Nqs8qhYHRpCit1DiXqfFCacSnpUnI3__bssqcx5eVPc,36095
5
- fal/app.py,sha256=bo8NbJTCjbIoeMVyW5YBxGljzDAKAqOjOJwP5erT2GM,13129
5
+ fal/_version.py,sha256=EBGqrknaf1WygENX-H4fBefLvHryvJBBGtVJetaB0NY,266
6
+ fal/api.py,sha256=g8ypixM_3AZMdDbGQTUkBE92kphGE3dpLJPsY2cJMIk,36287
7
+ fal/app.py,sha256=s9ba4t4D5KJrPFGKeRzL3XsdKH-W1Be6NmDQgYjPnCw,13826
6
8
  fal/apps.py,sha256=UhR6mq8jBiTAp-QvUnvbnMNcuJ5wHIKSqdlfyx8aBQ8,6829
7
- fal/cli.py,sha256=2sPCP4G_y3aUX0SKVSOTkTaLdYJpcm9B6FYDqIVhx-8,18431
8
- fal/flags.py,sha256=aWzOn3Ynl1s-eACerj1ZnRwlj3CvaGu0wIFcp7YXqX4,887
9
+ fal/flags.py,sha256=oWN_eidSUOcE9wdPK_77si3A1fpgOC0UEERPsvNLIMc,842
9
10
  fal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
11
  fal/rest_client.py,sha256=kGBGmuyHfX1lR910EoKCYPjsyU8MdXawT_cW2q8Sajc,568
11
- fal/sdk.py,sha256=4aQikS2xH90xZpphKljml6cG38aahK2gcVSLTBQQCBY,19925
12
+ fal/sdk.py,sha256=eHcg5TRouGL5d_9-p44x6lQUCYXVXdul6iEETCUov5Q,19976
12
13
  fal/sync.py,sha256=ZuIJA2-hTPNANG9B_NNJZUsO68EIdTH0dc9MzeVE2VU,4340
14
+ fal/utils.py,sha256=PfmRrW3EFJ4Y07XsRRfydS1NT4Fll5t7oChmFvusr3g,1793
13
15
  fal/workflows.py,sha256=4rjqL4xB6GHLJsqTplJmAvpd6uHZJ28sc8su33BFXEo,14682
14
16
  fal/auth/__init__.py,sha256=r8iA2-5ih7-Fik3gEC4HEWNFbGoxpYnXpZu1icPIoS0,3561
15
17
  fal/auth/auth0.py,sha256=rSG1mgH-QGyKfzd7XyAaj1AYsWt-ho8Y_LZ-FUVWzh4,5421
16
18
  fal/auth/local.py,sha256=sndkM6vKpeVny6NHTacVlTbiIFqaksOmw0Viqs_RN1U,1790
19
+ fal/cli/__init__.py,sha256=padK4o0BFqq61kxAA1qQ0jYr2SuhA2mf90B3AaRkmJA,37
20
+ fal/cli/apps.py,sha256=HKQDOiTlLUKrJ2oVABRdKhB2842l1zA0eB2p-4XczAo,8060
21
+ fal/cli/auth.py,sha256=--MhfHGwxmtHbRkGioyn1prKn_U-pBzbz0G_QeZou-U,1352
22
+ fal/cli/debug.py,sha256=1doDNwoaPDfLQginGNBxpC20dZYs5UxIojflDvV1Q04,1342
23
+ fal/cli/deploy.py,sha256=S_HIMLqDpGyzDdbiIxudRizwjGoAaHpN-sXcl2uCaQ4,4329
24
+ fal/cli/keys.py,sha256=-9N6ZY6rW-_IE9tpupgaBPDGjGdKB3HKqU2g9daM3Xc,3109
25
+ fal/cli/main.py,sha256=RSMXLUyzYmZhS0Wcq9phXJcMJM_UpQD3su7F7j8Wr3M,1933
26
+ fal/cli/parser.py,sha256=8W9VhxDBOSru6Vs_HsUm_RA7_YMnzXLTzUrXA0mGVRA,2109
27
+ fal/cli/run.py,sha256=NXwzkAWCKrRwgoMLsBOgW7RJPJW4IgSTrG85q2iePyk,894
28
+ fal/cli/secrets.py,sha256=mgHp3gBr8d2su7wBApeADKWHPkYu2ueB6gG3eNMETh8,2595
17
29
  fal/console/__init__.py,sha256=ernZ4bzvvliQh5SmrEqQ7lA5eVcbw6Ra2jalKtA7dxg,132
18
30
  fal/console/icons.py,sha256=De9MfFaSkO2Lqfne13n3PrYfTXJVIzYZVqYn5BWsdrA,108
19
31
  fal/console/ux.py,sha256=KMQs3UHQvVHDxDQQqlot-WskVKoMQXOE3jiVkkfmIMY,356
20
- fal/exceptions/__init__.py,sha256=yAPFUv-RZCFi8joRVOE1hV79bb8WFHnj1zslF_zhYxw,996
32
+ fal/exceptions/__init__.py,sha256=x3fp97qMr5zCQJghMq6k2ESXWSrkWumO1BZebh3pWsI,92
21
33
  fal/exceptions/_base.py,sha256=U3n_4OtUx5MvfT2eol_a-N0dV9_eYFMvdbrhP-b_NXg,160
22
34
  fal/exceptions/auth.py,sha256=gxRago5coI__vSIcdcsqhhq1lRPkvCnwPAueIaXTAdw,329
23
- fal/exceptions/handlers.py,sha256=nZCmmWU47k4P9NBISNqn0b6-L53KMoNuuGBW4G9Bweo,1674
24
35
  fal/logging/__init__.py,sha256=snqprf7-sKw6oAATS_Yxklf-a3XhLg0vIHICPwLp6TM,1583
25
36
  fal/logging/isolate.py,sha256=Gj_xylXc0ulGIyozLwTWisIclP7-du4tvhJWyPilrgo,1742
26
37
  fal/logging/style.py,sha256=ckIgHzvF4DShM5kQh8F133X53z_vF46snuDHVmo_h9g,386
@@ -81,8 +92,8 @@ openapi_fal_rest/models/workflow_node_type.py,sha256=-FzyeY2bxcNmizKbJI8joG7byRi
81
92
  openapi_fal_rest/models/workflow_schema.py,sha256=4K5gsv9u9pxx2ItkffoyHeNjBBYf6ur5bN4m_zePZNY,2019
82
93
  openapi_fal_rest/models/workflow_schema_input.py,sha256=2OkOXWHTNsCXHWS6EGDFzcJKkW5FIap-2gfO233EvZQ,1191
83
94
  openapi_fal_rest/models/workflow_schema_output.py,sha256=EblwSPAGfWfYVWw_WSSaBzQVju296is9o28rMBAd0mc,1196
84
- fal-0.15.2.dist-info/METADATA,sha256=SfYdtZgpSWTSE3GKU-rqZc5fAi7XGwSM1AB1ZJdqoHM,3738
85
- fal-0.15.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
86
- fal-0.15.2.dist-info/entry_points.txt,sha256=1GadEh1IgXO5Bb42Xo9lwNsJnm9Xjfo3qIdqwbfdV8Q,36
87
- fal-0.15.2.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
88
- fal-0.15.2.dist-info/RECORD,,
95
+ fal-1.0.0.dist-info/METADATA,sha256=1n_Jk_wpas1IT64tggvZhChSuT1QvP6TJ17T35mxpp4,3740
96
+ fal-1.0.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
97
+ fal-1.0.0.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
98
+ fal-1.0.0.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
99
+ fal-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ fal = fal.cli:main
fal/cli.py DELETED
@@ -1,622 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- from dataclasses import dataclass, field
5
- from http import HTTPStatus
6
- from sys import argv
7
- from typing import Any, Callable, Literal
8
- from uuid import uuid4
9
-
10
- import click
11
- import openapi_fal_rest.api.billing.get_user_details as get_user_details
12
- from rich.table import Table
13
- from rich_click import RichCommand, RichGroup
14
-
15
- import fal
16
- import fal.auth as auth
17
- from fal import _serialization, api, sdk
18
- from fal.console import console
19
- from fal.exceptions import ApplicationExceptionHandler
20
- from fal.logging import get_logger, set_debug_logging
21
- from fal.logging.trace import get_tracer
22
- from fal.rest_client import REST_CLIENT
23
- from fal.sdk import AliasInfo, KeyScope
24
-
25
- DEFAULT_HOST = "api.alpha.fal.ai"
26
- HOST_ENVVAR = "FAL_HOST"
27
-
28
- DEFAULT_PORT = "443"
29
- PORT_ENVVAR = "FAL_PORT"
30
-
31
- DEBUG_ENABLED = False
32
-
33
-
34
- logger = get_logger(__name__)
35
-
36
-
37
- @dataclass
38
- class State:
39
- debug: bool = False
40
- invocation_id: str = field(default_factory=lambda: str(uuid4()))
41
-
42
-
43
- def debug_option(*param_decls: str, **kwargs: Any) -> Callable[[click.FC], click.FC]:
44
- def callback(ctx: click.Context, param: click.Parameter, value: bool) -> None:
45
- state = ctx.ensure_object(State)
46
- state.debug = value
47
- set_debug_logging(value)
48
-
49
- if not param_decls:
50
- param_decls = ("--debug",)
51
-
52
- kwargs.setdefault("is_flag", True)
53
- kwargs.setdefault("expose_value", False)
54
- kwargs.setdefault("callback", callback)
55
- kwargs.setdefault("help", "Enable detailed errors and verbose logging.")
56
- return click.option(*param_decls, **kwargs)
57
-
58
-
59
- class MainGroup(RichGroup):
60
- """A custom implementation of the top-level group
61
- (i.e. called on all commands and subcommands).
62
-
63
- This implementation allows for centralized behavior, including
64
- exception handling.
65
- """
66
-
67
- _exception_handler = ApplicationExceptionHandler()
68
-
69
- _tracer = get_tracer(__name__)
70
-
71
- def invoke(self, ctx):
72
- from click.exceptions import Abort, ClickException, Exit
73
-
74
- state = ctx.ensure_object(State)
75
- qualified_name = " ".join([ctx.info_name] + argv[1:])
76
-
77
- with self._tracer.start_as_current_span(
78
- qualified_name, attributes={"invocation_id": state.invocation_id}
79
- ):
80
- try:
81
- logger.debug(
82
- f"Executing command: {qualified_name}",
83
- command=qualified_name,
84
- )
85
- return super().invoke(ctx)
86
- except (EOFError, KeyboardInterrupt, ClickException, Exit, Abort):
87
- # let click's main handle these
88
- raise
89
- except Exception as exception:
90
- logger.error(exception)
91
- if state.debug:
92
- # Here we supress detailed errors on click lines because they're
93
- # mostly decorator calls, irrelevant to the dev's error tracing
94
- console.print_exception(suppress=[click])
95
- console.print()
96
- console.print(
97
- "The [markdown.code]invocation_id[/] for this operation is: "
98
- f"[white]{state.invocation_id}[/]"
99
- )
100
- else:
101
- self._exception_handler.handle(exception)
102
-
103
- def add_command(
104
- self,
105
- cmd: RichCommand,
106
- name: str | None = None,
107
- aliases: list[str] | None = None,
108
- ) -> None:
109
- name = name or cmd.name
110
- assert name, "Command must have a name"
111
-
112
- if not aliases:
113
- aliases = []
114
-
115
- if aliases:
116
- # Add aliases to the help text
117
- cmd.help = (cmd.help or "") + "\n\nAlias: " + ", ".join([name, *aliases])
118
- cmd.short_help = (
119
- (cmd.short_help or "") + "(Alias: " + ", ".join(aliases) + ")"
120
- )
121
-
122
- super().add_command(cmd, name)
123
- alias_cmd = AliasCommand(cmd)
124
-
125
- for alias in aliases:
126
- self.add_command(alias_cmd, alias)
127
-
128
-
129
- class AliasCommand(RichCommand):
130
- def __init__(self, wrapped):
131
- self._wrapped = wrapped
132
-
133
- def __getattribute__(self, __name: str):
134
- if __name == "_wrapped":
135
- # To be able to call `self._wrapped` below
136
- return super().__getattribute__(__name)
137
-
138
- if __name == "hidden":
139
- return True
140
-
141
- return self._wrapped.__getattribute__(__name)
142
-
143
-
144
- @click.group(cls=MainGroup)
145
- @click.version_option()
146
- @debug_option()
147
- def cli():
148
- pass
149
-
150
-
151
- ###### Auth group ######
152
- @click.group(cls=RichGroup)
153
- @debug_option()
154
- def auth_cli():
155
- pass
156
-
157
-
158
- @auth_cli.command(name="login")
159
- @debug_option()
160
- def auth_login():
161
- auth.login()
162
-
163
-
164
- @auth_cli.command(name="logout")
165
- @debug_option()
166
- def auth_logout():
167
- auth.logout()
168
-
169
-
170
- @auth_cli.command(name="hello", hidden=True)
171
- @debug_option()
172
- def auth_test():
173
- """
174
- To test auth.
175
- """
176
- print(f"Hello, {auth.USER.info['name']} - '{auth.USER.info['sub']}'")
177
-
178
-
179
- ###### Key group ######
180
- @click.group(cls=RichGroup)
181
- @click.option("--host", default=DEFAULT_HOST, envvar=HOST_ENVVAR)
182
- @click.option("--port", default=DEFAULT_PORT, envvar=PORT_ENVVAR, hidden=True)
183
- @debug_option()
184
- @click.pass_context
185
- def key_cli(ctx, host: str, port: str):
186
- ctx.obj = sdk.FalServerlessClient(f"{host}:{port}")
187
-
188
-
189
- @key_cli.command(name="generate", no_args_is_help=True)
190
- @click.option(
191
- "--scope",
192
- default=None,
193
- required=True,
194
- type=click.Choice([KeyScope.ADMIN.value, KeyScope.API.value]),
195
- help="The privilage scope of the key.",
196
- )
197
- @click.option(
198
- "--alias",
199
- default=None,
200
- help="An alias for the key.",
201
- )
202
- @debug_option()
203
- @click.pass_obj
204
- def key_generate(client: sdk.FalServerlessClient, scope: str, alias: str | None):
205
- with client.connect() as connection:
206
- parsed_scope = KeyScope(scope)
207
- result = connection.create_user_key(parsed_scope, alias)
208
- print(
209
- f"Generated key id and key secret, with the scope `{scope}`.\n"
210
- "This is the only time the secret will be visible.\n"
211
- "You will need to generate a new key pair if you lose access to this "
212
- "secret."
213
- )
214
- print(f"FAL_KEY='{result[1]}:{result[0]}'")
215
-
216
-
217
- @key_cli.command(name="list")
218
- @debug_option()
219
- @click.pass_obj
220
- def key_list(client: sdk.FalServerlessClient):
221
- table = Table(title="Keys")
222
- table.add_column("Key ID")
223
- table.add_column("Created At")
224
- table.add_column("Scope")
225
- table.add_column("Alias")
226
-
227
- with client.connect() as connection:
228
- keys = connection.list_user_keys()
229
- for key in keys:
230
- table.add_row(
231
- key.key_id, str(key.created_at), str(key.scope.value), key.alias
232
- )
233
-
234
- console.print(table)
235
-
236
-
237
- @key_cli.command(name="revoke")
238
- @click.argument("key_id", required=True)
239
- @debug_option()
240
- @click.pass_obj
241
- def key_revoke(client: sdk.FalServerlessClient, key_id: str):
242
- with client.connect() as connection:
243
- connection.revoke_user_key(key_id)
244
-
245
-
246
- ##### Function group #####
247
- ALIAS_AUTH_OPTIONS = ["public", "private", "shared"]
248
- ALIAS_AUTH_TYPE = Literal["public", "private", "shared"]
249
-
250
-
251
- @click.group(cls=RichGroup)
252
- @click.option("--host", default=DEFAULT_HOST, envvar=HOST_ENVVAR)
253
- @click.option("--port", default=DEFAULT_PORT, envvar=PORT_ENVVAR, hidden=True)
254
- @debug_option()
255
- @click.pass_context
256
- def function_cli(ctx, host: str, port: str):
257
- ctx.obj = api.FalServerlessHost(f"{host}:{port}")
258
-
259
-
260
- def load_function_from(
261
- host: api.FalServerlessHost,
262
- file_path: str,
263
- function_name: str,
264
- ) -> api.IsolatedFunction:
265
- import runpy
266
-
267
- module = runpy.run_path(file_path)
268
- if function_name not in module:
269
- raise api.FalServerlessError(f"Function '{function_name}' not found in module")
270
-
271
- # The module for the function is set to <run_path> when runpy is used, in which
272
- # case we want to manually include the package it is defined in.
273
- _serialization.include_package_from_path(file_path)
274
-
275
- target = module[function_name]
276
- if isinstance(target, type) and issubclass(target, fal.App):
277
- target = fal.wrap_app(target, host=host)
278
-
279
- if not isinstance(target, api.IsolatedFunction):
280
- raise api.FalServerlessError(
281
- f"Function '{function_name}' is not a fal.function or a fal.App"
282
- )
283
- return target
284
-
285
-
286
- @function_cli.command("serve")
287
- @click.option("--alias", default=None)
288
- @click.option(
289
- "--auth",
290
- "auth_mode",
291
- type=click.Choice(ALIAS_AUTH_OPTIONS),
292
- default="private",
293
- )
294
- @click.argument("file_path", required=True)
295
- @click.argument("function_name", required=True)
296
- @debug_option()
297
- @click.pass_obj
298
- def register_application(
299
- host: api.FalServerlessHost,
300
- file_path: str,
301
- function_name: str,
302
- alias: str | None,
303
- auth_mode: ALIAS_AUTH_TYPE,
304
- ):
305
- user_id = _get_user_id()
306
-
307
- isolated_function = load_function_from(host, file_path, function_name)
308
- gateway_options = isolated_function.options.gateway
309
- if "serve" not in gateway_options and "exposed_port" not in gateway_options:
310
- raise api.FalServerlessError(
311
- "One of `serve` or `exposed_port` options needs to be specified "
312
- "in the isolated annotation to register a function"
313
- )
314
- elif (
315
- "exposed_port" in gateway_options
316
- and str(gateway_options.get("exposed_port")) != "8080"
317
- ):
318
- raise api.FalServerlessError(
319
- "Must expose port 8080 for now. This will be configurable in the future."
320
- )
321
-
322
- id = host.register(
323
- func=isolated_function.func,
324
- options=isolated_function.options,
325
- application_name=alias,
326
- application_auth_mode=auth_mode,
327
- metadata=isolated_function.options.host.get("metadata", {}),
328
- )
329
-
330
- if id:
331
- gateway_host = remove_http_and_port_from_url(host.url)
332
- gateway_host = (
333
- gateway_host.replace("api.", "").replace("alpha.", "").replace("ai", "run")
334
- )
335
-
336
- if alias:
337
- console.print(
338
- f"Registered a new revision for function '{alias}' (revision='{id}')."
339
- )
340
- console.print(f"URL: https://{gateway_host}/{user_id}/{alias}")
341
- else:
342
- console.print(f"Registered anonymous function '{id}'.")
343
- console.print(f"URL: https://{gateway_host}/{user_id}/{id}")
344
-
345
-
346
- @function_cli.command("run")
347
- @click.argument("file_path", required=True)
348
- @click.argument("function_name", required=True)
349
- @debug_option()
350
- @click.pass_obj
351
- def run(host: api.FalServerlessHost, file_path: str, function_name: str):
352
- isolated_function = load_function_from(host, file_path, function_name)
353
- # let our exc handlers handle UserFunctionException
354
- isolated_function.reraise = False
355
- isolated_function()
356
-
357
-
358
- @function_cli.command("logs")
359
- @click.option("--lines", default=100)
360
- @click.option("--url", default=None)
361
- @debug_option()
362
- @click.pass_obj
363
- def get_logs(
364
- host: api.FalServerlessHost, lines: int | None = 100, url: str | None = None
365
- ):
366
- console.print(
367
- "logs command is deprecated. To see logs, got to fal web page: https://www.fal.ai/dashboard/logs"
368
- )
369
-
370
-
371
- ##### Alias group #####
372
- @click.group(cls=RichGroup)
373
- @click.option("--host", default=DEFAULT_HOST, envvar=HOST_ENVVAR)
374
- @click.option("--port", default=DEFAULT_PORT, envvar=PORT_ENVVAR, hidden=True)
375
- @debug_option()
376
- @click.pass_context
377
- def alias_cli(ctx, host: str, port: str):
378
- ctx.obj = api.FalServerlessClient(f"{host}:{port}")
379
-
380
-
381
- def _alias_table(aliases: list[AliasInfo]):
382
- table = Table(title="Function Aliases")
383
- table.add_column("Alias")
384
- table.add_column("Revision")
385
- table.add_column("Auth")
386
- table.add_column("Min Concurrency")
387
- table.add_column("Max Concurrency")
388
- table.add_column("Max Multiplexing")
389
- table.add_column("Keep Alive")
390
- table.add_column("Active Workers")
391
-
392
- for app_alias in aliases:
393
- table.add_row(
394
- app_alias.alias,
395
- app_alias.revision,
396
- app_alias.auth_mode,
397
- str(app_alias.min_concurrency),
398
- str(app_alias.max_concurrency),
399
- str(app_alias.max_multiplexing),
400
- str(app_alias.keep_alive),
401
- str(app_alias.active_runners),
402
- )
403
-
404
- return table
405
-
406
-
407
- @alias_cli.command("set")
408
- @click.argument("alias", required=True)
409
- @click.argument("revision", required=True)
410
- @click.option(
411
- "--auth",
412
- "auth_mode",
413
- type=click.Choice(ALIAS_AUTH_OPTIONS),
414
- default="private",
415
- )
416
- @debug_option()
417
- @click.pass_obj
418
- def alias_set(
419
- client: api.FalServerlessClient,
420
- alias: str,
421
- revision: str,
422
- auth_mode: ALIAS_AUTH_TYPE,
423
- ):
424
- with client.connect() as connection:
425
- connection.create_alias(alias, revision, auth_mode)
426
-
427
-
428
- @alias_cli.command("delete")
429
- @click.argument("alias", required=True)
430
- @debug_option()
431
- @click.pass_obj
432
- def alias_delete(client: api.FalServerlessClient, alias: str):
433
- with client.connect() as connection:
434
- application_id = connection.delete_alias(alias)
435
-
436
- console.print(f"Deleted alias '{alias}' for application '{application_id}'.")
437
-
438
-
439
- @alias_cli.command("list")
440
- @debug_option()
441
- @click.pass_obj
442
- def alias_list(client: api.FalServerlessClient):
443
- with client.connect() as connection:
444
- aliases = connection.list_aliases()
445
- table = _alias_table(aliases)
446
-
447
- console.print(table)
448
-
449
-
450
- @alias_cli.command("update")
451
- @click.argument("alias", required=True)
452
- @click.option("--keep-alive", "-k", type=int)
453
- @click.option("--max-multiplexing", "-m", type=int)
454
- @click.option("--max-concurrency", "-c", type=int)
455
- @click.option("--min-concurrency", type=int)
456
- # TODO: add auth_mode
457
- # @click.option(
458
- # "--auth",
459
- # "auth_mode",
460
- # type=click.Choice(ALIAS_AUTH_OPTIONS),
461
- # )
462
- @debug_option()
463
- @click.pass_obj
464
- def alias_update(
465
- client: api.FalServerlessClient,
466
- alias: str,
467
- keep_alive: int | None,
468
- max_multiplexing: int | None,
469
- max_concurrency: int | None,
470
- min_concurrency: int | None,
471
- ):
472
- with client.connect() as connection:
473
- if (
474
- keep_alive is None
475
- and max_multiplexing is None
476
- and max_concurrency is None
477
- and min_concurrency is None
478
- ):
479
- console.log("No parameters for update were provided, ignoring.")
480
- return
481
-
482
- alias_info = connection.update_application(
483
- application_name=alias,
484
- keep_alive=keep_alive,
485
- max_multiplexing=max_multiplexing,
486
- max_concurrency=max_concurrency,
487
- min_concurrency=min_concurrency,
488
- )
489
- table = _alias_table([alias_info])
490
-
491
- console.print(table)
492
-
493
-
494
- @alias_cli.command("runners")
495
- @click.argument("alias", required=True)
496
- @debug_option()
497
- @click.pass_obj
498
- def alias_list_runners(
499
- client: api.FalServerlessClient,
500
- alias: str,
501
- ):
502
- with client.connect() as connection:
503
- runners = connection.list_alias_runners(alias=alias)
504
-
505
- table = Table(title="Application Runners")
506
- table.add_column("Runner ID")
507
- table.add_column("In Flight Requests")
508
- table.add_column("Expires in")
509
- table.add_column("Uptime")
510
-
511
- for runner in runners:
512
- table.add_row(
513
- runner.runner_id,
514
- str(runner.in_flight_requests),
515
- (
516
- "N/A (active)"
517
- if not runner.expiration_countdown
518
- else f"{runner.expiration_countdown}s"
519
- ),
520
- f"{runner.uptime} ({runner.uptime.total_seconds()}s)",
521
- )
522
-
523
- console.print(table)
524
-
525
-
526
- ##### Secrets group #####
527
- @click.group(cls=RichGroup)
528
- @click.option("--host", default=DEFAULT_HOST, envvar=HOST_ENVVAR)
529
- @click.option("--port", default=DEFAULT_PORT, envvar=PORT_ENVVAR, hidden=True)
530
- @debug_option()
531
- @click.pass_context
532
- def secrets_cli(ctx, host: str, port: str):
533
- ctx.obj = sdk.FalServerlessClient(f"{host}:{port}")
534
-
535
-
536
- @secrets_cli.command("list")
537
- @debug_option()
538
- @click.pass_obj
539
- def list_secrets(client: api.FalServerlessClient):
540
- table = Table(title="Secrets")
541
- table.add_column("Secret Name")
542
- table.add_column("Created At")
543
-
544
- with client.connect() as connection:
545
- for secret in connection.list_secrets():
546
- table.add_row(secret.name, str(secret.created_at))
547
-
548
- console.print(table)
549
-
550
-
551
- @secrets_cli.command("set")
552
- @click.argument("secret_name", required=True)
553
- @click.argument("secret_value", required=True)
554
- @debug_option()
555
- @click.pass_obj
556
- def set_secret(client: api.FalServerlessClient, secret_name: str, secret_value: str):
557
- with client.connect() as connection:
558
- connection.set_secret(secret_name, secret_value)
559
- console.print(f"Secret '{secret_name}' has set")
560
-
561
-
562
- @secrets_cli.command("delete")
563
- @click.argument("secret_name", required=True)
564
- @debug_option()
565
- @click.pass_obj
566
- def delete_secret(client: api.FalServerlessClient, secret_name: str):
567
- with client.connect() as connection:
568
- connection.delete_secret(secret_name)
569
- console.print(f"Secret '{secret_name}' has deleted")
570
-
571
-
572
- # Setup of groups
573
- cli.add_command(auth_cli, name="auth")
574
- cli.add_command(key_cli, name="key", aliases=["keys"])
575
- cli.add_command(function_cli, name="function", aliases=["fn"])
576
- cli.add_command(alias_cli, name="alias", aliases=["aliases"])
577
- cli.add_command(secrets_cli, name="secret", aliases=["secrets"])
578
-
579
-
580
- def remove_http_and_port_from_url(url):
581
- # Remove http://
582
- if "http://" in url:
583
- url = url.replace("http://", "")
584
-
585
- # Remove https://
586
- if "https://" in url:
587
- url = url.replace("https://", "")
588
-
589
- # Remove port information
590
- url_parts = url.split(":")
591
- if len(url_parts) > 1:
592
- url = url_parts[0]
593
-
594
- return url
595
-
596
-
597
- def _get_user_id() -> str:
598
- try:
599
- user_details_response = get_user_details.sync_detailed(
600
- client=REST_CLIENT,
601
- )
602
- except Exception as e:
603
- raise api.FalServerlessError(f"Error fetching user details: {str(e)}")
604
-
605
- if user_details_response.status_code != HTTPStatus.OK:
606
- try:
607
- content = json.loads(user_details_response.content.decode("utf8"))
608
- except Exception:
609
- raise api.FalServerlessError(
610
- f"Error fetching user details: {user_details_response}"
611
- )
612
- else:
613
- raise api.FalServerlessError(content["detail"])
614
- try:
615
- full_user_id = user_details_response.parsed.user_id
616
- _provider, _, user_id = full_user_id.partition("|")
617
- if not user_id:
618
- user_id = full_user_id
619
-
620
- return user_id
621
- except Exception as e:
622
- raise api.FalServerlessError(f"Could not parse the user data: {e}")