meshagent-cli 0.0.37__py3-none-any.whl → 0.0.39__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 meshagent-cli might be problematic. Click here for more details.

meshagent/cli/helper.py CHANGED
@@ -5,7 +5,7 @@ from pydantic import BaseModel
5
5
  from pathlib import Path
6
6
  from typing import Optional
7
7
 
8
- from meshagent.cli import auth_async
8
+ from meshagent.cli import auth_async
9
9
  from meshagent.cli import async_typer
10
10
  from meshagent.api.helpers import meshagent_base_url
11
11
  from meshagent.api.accounts_client import AccountsClient
@@ -15,27 +15,32 @@ import os
15
15
 
16
16
  SETTINGS_FILE = Path.home() / ".meshagent" / "project.json"
17
17
 
18
+
18
19
  def _ensure_cache_dir():
19
20
  SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True)
20
21
 
22
+
21
23
  class Settings(BaseModel):
22
24
  active_project: Optional[str] = None
23
25
  active_api_keys: Optional[dict] = {}
24
26
 
27
+
25
28
  def _save_settings(s: Settings):
26
29
  _ensure_cache_dir()
27
30
  SETTINGS_FILE.write_text(s.model_dump_json())
28
-
31
+
32
+
29
33
  def _load_settings():
30
34
  _ensure_cache_dir()
31
35
  if SETTINGS_FILE.exists():
32
36
  return Settings.model_validate_json(SETTINGS_FILE.read_text())
33
-
37
+
34
38
  return Settings()
35
39
 
40
+
36
41
  async def get_active_project():
37
42
  settings = _load_settings()
38
- if settings == None:
43
+ if settings is None:
39
44
  return None
40
45
  return settings.active_project
41
46
 
@@ -45,9 +50,10 @@ async def set_active_project(project_id: str | None):
45
50
  settings.active_project = project_id
46
51
  _save_settings(settings)
47
52
 
53
+
48
54
  async def get_active_api_key(project_id: str):
49
55
  settings = _load_settings()
50
- if settings == None:
56
+ if settings is None:
51
57
  return None
52
58
  return settings.active_api_keys.get(project_id, None)
53
59
 
@@ -60,12 +66,13 @@ async def set_active_api_key(project_id: str, api_key_id: str | None):
60
66
 
61
67
  app = async_typer.AsyncTyper()
62
68
 
69
+
63
70
  async def get_client():
64
71
  access_token = await auth_async.get_access_token()
65
72
  return AccountsClient(base_url=meshagent_base_url(), token=access_token)
66
73
 
74
+
67
75
  def print_json_table(records: list, *cols):
68
-
69
76
  if not records:
70
77
  raise SystemExit("No rows to print")
71
78
 
@@ -75,7 +82,7 @@ def print_json_table(records: list, *cols):
75
82
  if len(cols) > 0:
76
83
  # use the keys of the first object as column order
77
84
  for col in cols:
78
- table.add_column(col.title()) # "id" → "Id"
85
+ table.add_column(col.title()) # "id" → "Id"
79
86
 
80
87
  for row in records:
81
88
  table.add_row(*(str(row.get(col, "")) for col in cols))
@@ -83,7 +90,7 @@ def print_json_table(records: list, *cols):
83
90
  else:
84
91
  # use the keys of the first object as column order
85
92
  for col in records[0]:
86
- table.add_column(col.title()) # "id" → "Id"
93
+ table.add_column(col.title()) # "id" → "Id"
87
94
 
88
95
  for row in records:
89
96
  table.add_row(*(str(row.get(col, "")) for col in records[0]))
@@ -93,60 +100,74 @@ def print_json_table(records: list, *cols):
93
100
 
94
101
 
95
102
  def resolve_room(room_name: Optional[str] = None):
96
- if room_name == None:
103
+ if room_name is None:
97
104
  room_name = os.getenv("MESHAGENT_ROOM")
98
-
105
+
99
106
  return room_name
100
107
 
108
+
101
109
  async def resolve_project_id(project_id: Optional[str] = None):
102
- if project_id == None:
110
+ if project_id is None:
103
111
  project_id = await get_active_project()
104
-
105
- if project_id == None:
106
- print("[red]Project ID not specified, activate a project or pass a project on the command line[/red]")
112
+
113
+ if project_id is None:
114
+ print(
115
+ "[red]Project ID not specified, activate a project or pass a project on the command line[/red]"
116
+ )
107
117
  raise typer.Exit(code=1)
108
-
118
+
109
119
  return project_id
110
-
120
+
121
+
111
122
  async def resolve_api_key(project_id: str, api_key_id: Optional[str] = None):
112
- if api_key_id == None:
123
+ if api_key_id is None:
113
124
  api_key_id = await get_active_api_key(project_id=project_id)
114
-
115
- if api_key_id == None:
116
- print("[red]API Key ID not specified, activate an api key or pass an api key id on the command line[/red]")
125
+
126
+ if api_key_id is None:
127
+ print(
128
+ "[red]API Key ID not specified, activate an api key or pass an api key id on the command line[/red]"
129
+ )
117
130
  raise typer.Exit(code=1)
118
-
119
- return api_key_id
120
-
121
131
 
122
- async def resolve_token_jwt(*, project_id: str, api_key_id: Optional[str] = None, token_path: Optional[str] = None, name: Optional[str] = None, role: Optional[str] = None, room: Optional[str]= None) -> str:
123
- jwt = None
132
+ return api_key_id
124
133
 
125
- if api_key_id == None:
126
-
127
- if token_path != None:
128
134
 
129
- if token_path == None:
135
+ async def resolve_token_jwt(
136
+ *,
137
+ project_id: str,
138
+ api_key_id: Optional[str] = None,
139
+ token_path: Optional[str] = None,
140
+ name: Optional[str] = None,
141
+ role: Optional[str] = None,
142
+ room: Optional[str] = None,
143
+ ) -> str:
144
+ jwt = None
130
145
 
131
- token_path = os.getenv("MESHAGENT_TOKEN_PATH", (Path.home() / ".meshagent" / "token").as_posix())
146
+ if api_key_id is None:
147
+ if token_path is not None:
148
+ if token_path is None:
149
+ token_path = os.getenv(
150
+ "MESHAGENT_TOKEN_PATH",
151
+ (Path.home() / ".meshagent" / "token").as_posix(),
152
+ )
132
153
 
133
154
  p = Path(token_path)
134
155
  jwt = p.read_text().strip()
135
-
136
- else:
137
156
 
157
+ else:
138
158
  jwt = os.getenv("MESHAGENT_TOKEN", None)
139
159
 
140
- if jwt == None:
141
-
160
+ if jwt is None:
142
161
  account_client = await get_client()
143
162
  try:
144
- key = (await account_client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
163
+ key = (
164
+ await account_client.decrypt_project_api_key(
165
+ project_id=project_id, id=api_key_id
166
+ )
167
+ )["token"]
145
168
 
146
169
  token = ParticipantToken(
147
- name=name,
148
- project_id=project_id,
149
- api_key_id=api_key_id
170
+ name=name, project_id=project_id, api_key_id=api_key_id
150
171
  )
151
172
 
152
173
  token.add_role_grant(role=role)
@@ -154,7 +175,6 @@ async def resolve_token_jwt(*, project_id: str, api_key_id: Optional[str] = None
154
175
 
155
176
  jwt = token.to_jwt(token=key)
156
177
  finally:
157
-
158
178
  await account_client.close()
159
179
 
160
180
  return jwt
@@ -3,10 +3,16 @@ from rich import print
3
3
  from typing import Annotated, Optional
4
4
  import json
5
5
 
6
- from meshagent.api import RoomClient, ParticipantToken, WebSocketClientProtocol
6
+ from meshagent.api import RoomClient, WebSocketClientProtocol
7
7
  from meshagent.api.helpers import meshagent_base_url, websocket_room_url
8
8
  from meshagent.cli import async_typer
9
- from meshagent.cli.helper import get_client, resolve_project_id, resolve_api_key, resolve_token_jwt, resolve_room
9
+ from meshagent.cli.helper import (
10
+ get_client,
11
+ resolve_project_id,
12
+ resolve_api_key,
13
+ resolve_token_jwt,
14
+ resolve_room,
15
+ )
10
16
 
11
17
  app = async_typer.AsyncTyper()
12
18
 
@@ -16,10 +22,10 @@ async def messaging_list_participants_command(
16
22
  *,
17
23
  project_id: str = None,
18
24
  room: Annotated[Optional[str], typer.Option()] = None,
19
- token_path: Annotated[Optional[str], typer.Option()] = None,
25
+ token_path: Annotated[Optional[str], typer.Option()] = None,
20
26
  api_key_id: Annotated[Optional[str], typer.Option()] = None,
21
27
  name: Annotated[str, typer.Option()] = "cli",
22
- role: str = "user"
28
+ role: str = "user",
23
29
  ):
24
30
  """
25
31
  List all messaging-enabled participants in the room.
@@ -29,19 +35,25 @@ async def messaging_list_participants_command(
29
35
  # Resolve project_id if not provided
30
36
  project_id = await resolve_project_id(project_id=project_id)
31
37
  api_key_id = await resolve_api_key(project_id, api_key_id)
32
-
38
+
33
39
  room = resolve_room(room)
34
-
35
- jwt = await resolve_token_jwt(project_id=project_id, api_key_id=api_key_id, token_path=token_path, name=name, role=role, room=room)
40
+
41
+ jwt = await resolve_token_jwt(
42
+ project_id=project_id,
43
+ api_key_id=api_key_id,
44
+ token_path=token_path,
45
+ name=name,
46
+ role=role,
47
+ room=room,
48
+ )
36
49
 
37
50
  print("[bold green]Connecting to room...[/bold green]")
38
51
  async with RoomClient(
39
52
  protocol=WebSocketClientProtocol(
40
53
  url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
41
- token=jwt
54
+ token=jwt,
42
55
  )
43
56
  ) as client:
44
-
45
57
  # Must enable before we can see who else is enabled
46
58
  await client.messaging.enable()
47
59
  await client.messaging.start()
@@ -49,11 +61,7 @@ async def messaging_list_participants_command(
49
61
  participants = client.messaging.get_participants()
50
62
  output = []
51
63
  for p in participants:
52
- output.append({
53
- "id": p.id,
54
- "role": p.role,
55
- "attributes": p._attributes
56
- })
64
+ output.append({"id": p.id, "role": p.role, "attributes": p._attributes})
57
65
 
58
66
  print(json.dumps(output, indent=2))
59
67
 
@@ -68,11 +76,13 @@ async def messaging_send_command(
68
76
  *,
69
77
  project_id: str = None,
70
78
  room: Annotated[Optional[str], typer.Option()] = None,
71
- token_path: Annotated[Optional[str], typer.Option()] = None,
79
+ token_path: Annotated[Optional[str], typer.Option()] = None,
72
80
  api_key_id: Annotated[Optional[str], typer.Option()] = None,
73
81
  name: Annotated[str, typer.Option()] = "cli",
74
82
  role: str = "user",
75
- to_participant_id: Annotated[str, typer.Option(..., help="Participant ID to send a message to")],
83
+ to_participant_id: Annotated[
84
+ str, typer.Option(..., help="Participant ID to send a message to")
85
+ ],
76
86
  data: Annotated[str, typer.Option(..., help="JSON message to send")],
77
87
  ):
78
88
  """
@@ -83,16 +93,23 @@ async def messaging_send_command(
83
93
  # Resolve project_id if not provided
84
94
  project_id = await resolve_project_id(project_id=project_id)
85
95
  api_key_id = await resolve_api_key(project_id, api_key_id)
86
-
96
+
87
97
  room = resolve_room(room)
88
-
89
- jwt = await resolve_token_jwt(project_id=project_id, api_key_id=api_key_id, token_path=token_path, name=name, role=role, room=room)
98
+
99
+ jwt = await resolve_token_jwt(
100
+ project_id=project_id,
101
+ api_key_id=api_key_id,
102
+ token_path=token_path,
103
+ name=name,
104
+ role=role,
105
+ room=room,
106
+ )
90
107
 
91
108
  print("[bold green]Connecting to room...[/bold green]")
92
109
  async with RoomClient(
93
110
  protocol=WebSocketClientProtocol(
94
111
  url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
95
- token=jwt
112
+ token=jwt,
96
113
  )
97
114
  ) as client:
98
115
  # Create and enable messaging
@@ -105,16 +122,18 @@ async def messaging_send_command(
105
122
  if p.id == to_participant_id:
106
123
  participant = p
107
124
  break
108
-
125
+
109
126
  if participant is None:
110
- print(f"[bold red]Participant with ID {to_participant_id} not found or not messaging-enabled.[/bold red]")
127
+ print(
128
+ f"[bold red]Participant with ID {to_participant_id} not found or not messaging-enabled.[/bold red]"
129
+ )
111
130
  else:
112
131
  # Send the message
113
132
  await client.messaging.send_message(
114
133
  to=participant,
115
134
  type="chat.message",
116
135
  message=json.loads(data),
117
- attachment=None
136
+ attachment=None,
118
137
  )
119
138
  print("[bold cyan]Message sent successfully.[/bold cyan]")
120
139
 
@@ -123,17 +142,16 @@ async def messaging_send_command(
123
142
  await account_client.close()
124
143
 
125
144
 
126
-
127
145
  @app.async_command("broadcast")
128
146
  async def messaging_broadcast_command(
129
147
  *,
130
148
  project_id: str = None,
131
149
  room: Annotated[Optional[str], typer.Option()] = None,
132
- token_path: Annotated[Optional[str], typer.Option()] = None,
150
+ token_path: Annotated[Optional[str], typer.Option()] = None,
133
151
  api_key_id: Annotated[Optional[str], typer.Option()] = None,
134
152
  name: Annotated[str, typer.Option()] = "cli",
135
153
  role: str = "user",
136
- data: Annotated[str, typer.Option(..., help="JSON message to broadcast")]
154
+ data: Annotated[str, typer.Option(..., help="JSON message to broadcast")],
137
155
  ):
138
156
  """
139
157
  Broadcast a message to all messaging-enabled participants in the room.
@@ -145,13 +163,20 @@ async def messaging_broadcast_command(
145
163
  api_key_id = await resolve_api_key(project_id, api_key_id)
146
164
 
147
165
  room = resolve_room(room)
148
- jwt = await resolve_token_jwt(project_id=project_id, api_key_id=api_key_id, token_path=token_path, name=name, role=role, room=room)
166
+ jwt = await resolve_token_jwt(
167
+ project_id=project_id,
168
+ api_key_id=api_key_id,
169
+ token_path=token_path,
170
+ name=name,
171
+ role=role,
172
+ room=room,
173
+ )
149
174
 
150
175
  print("[bold green]Connecting to room...[/bold green]")
151
176
  async with RoomClient(
152
177
  protocol=WebSocketClientProtocol(
153
178
  url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
154
- token=jwt
179
+ token=jwt,
155
180
  )
156
181
  ) as client:
157
182
  # Create and enable messaging
@@ -160,9 +185,7 @@ async def messaging_broadcast_command(
160
185
 
161
186
  # Broadcast the message
162
187
  await client.messaging.broadcast_message(
163
- type="chat.broadcast",
164
- message=json.loads(data),
165
- attachment=None
188
+ type="chat.broadcast", message=json.loads(data), attachment=None
166
189
  )
167
190
  print("[bold cyan]Broadcast message sent successfully.[/bold cyan]")
168
191
 
meshagent/cli/otel.py CHANGED
@@ -8,8 +8,11 @@ from opentelemetry import metrics
8
8
  from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
9
9
  from opentelemetry.sdk._logs.export import ConsoleLogExporter
10
10
  from opentelemetry.sdk.metrics import MeterProvider
11
- from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter
12
- from opentelemetry import _logs
11
+ from opentelemetry.sdk.metrics.export import (
12
+ PeriodicExportingMetricReader,
13
+ ConsoleMetricExporter,
14
+ )
15
+ from opentelemetry import _logs
13
16
  from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
14
17
  from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
15
18
  from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
@@ -19,6 +22,7 @@ import logging
19
22
 
20
23
  import os
21
24
 
25
+
22
26
  def _call_once(fn):
23
27
  called = False
24
28
  result = None
@@ -29,6 +33,7 @@ def _call_once(fn):
29
33
  result = fn(*args, **kwargs)
30
34
  called = True
31
35
  return result
36
+
32
37
  return wrapper
33
38
 
34
39
 
@@ -36,13 +41,13 @@ attributes = {
36
41
  SERVICE_NAME: "room-server",
37
42
  }
38
43
 
39
- if os.getenv("MESHAGENT_PROJECT_ID") != None:
44
+ if os.getenv("MESHAGENT_PROJECT_ID") is not None:
40
45
  attributes["project"] = os.getenv("MESHAGENT_PROJECT_ID")
41
46
 
42
- if os.getenv("MESHAGENT_SESSION_ID") != None:
47
+ if os.getenv("MESHAGENT_SESSION_ID") is not None:
43
48
  attributes["session"] = os.getenv("MESHAGENT_SESSION_ID")
44
49
 
45
- if os.getenv("MESHAGENT_ROOM") != None:
50
+ if os.getenv("MESHAGENT_ROOM") is not None:
46
51
  attributes["room"] = os.getenv("MESHAGENT_ROOM")
47
52
 
48
53
  resource = Resource.create(attributes=attributes)
@@ -53,66 +58,65 @@ meter_provider = None
53
58
 
54
59
  add_console_exporters = False
55
60
 
56
- otel_endpoint = os.getenv("OTEL_ENDPOINT")
57
-
58
- if otel_endpoint != None:
61
+ otel_endpoint = os.getenv("OTEL_ENDPOINT")
59
62
 
63
+ if otel_endpoint is not None:
60
64
  otel_logs_endpoint = otel_endpoint + "/v1/logs"
61
65
  otel_traces_endpoint = otel_endpoint + "/v1/traces"
62
66
  otel_metrics_endpoint = otel_endpoint + "/v1/metrics"
63
-
64
- if otel_logs_endpoint != None:
67
+
68
+ if otel_logs_endpoint is not None:
65
69
  logs_exporter = OTLPLogExporter(
66
70
  endpoint=otel_logs_endpoint,
67
71
  )
68
72
  logger_provider = LoggerProvider(resource=resource)
69
- _logs.set_logger_provider(logger_provider)
70
-
71
- logger_provider.add_log_record_processor(
72
- BatchLogRecordProcessor(logs_exporter)
73
- )
73
+ _logs.set_logger_provider(logger_provider)
74
74
 
75
- if add_console_exporters:
76
- logger_provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogExporter()))
75
+ logger_provider.add_log_record_processor(BatchLogRecordProcessor(logs_exporter))
77
76
 
78
-
79
- if otel_traces_endpoint != None:
77
+ if add_console_exporters:
78
+ logger_provider.add_log_record_processor(
79
+ BatchLogRecordProcessor(ConsoleLogExporter())
80
+ )
81
+
82
+ if otel_traces_endpoint is not None:
80
83
  tracer_provider = TracerProvider(resource=resource)
81
84
  processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=otel_traces_endpoint))
82
85
  tracer_provider.add_span_processor(processor)
83
- if add_console_exporters:
84
- tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
86
+ if add_console_exporters:
87
+ tracer_provider.add_span_processor(
88
+ BatchSpanProcessor(ConsoleSpanExporter())
89
+ )
85
90
  trace.set_tracer_provider(tracer_provider)
86
91
 
87
- if otel_metrics_endpoint != None:
92
+ if otel_metrics_endpoint is not None:
88
93
  reader = PeriodicExportingMetricReader(
89
94
  exporter=OTLPMetricExporter(
90
95
  endpoint=otel_metrics_endpoint,
91
- preferred_temporality={
96
+ preferred_temporality={
92
97
  Counter: AggregationTemporality.DELTA,
93
98
  Histogram: AggregationTemporality.DELTA,
94
99
  },
95
100
  ),
96
- export_interval_millis=1000
101
+ export_interval_millis=1000,
97
102
  )
98
-
103
+
99
104
  readers = [
100
105
  reader,
101
106
  ]
102
107
  if add_console_exporters:
103
- readers.append(PeriodicExportingMetricReader(
104
- ConsoleMetricExporter()
105
- ))
106
-
108
+ readers.append(PeriodicExportingMetricReader(ConsoleMetricExporter()))
109
+
107
110
  meter_provider = MeterProvider(resource=resource, metric_readers=readers)
108
111
  metrics.set_meter_provider(meter_provider)
109
112
 
113
+
110
114
  @_call_once
111
115
  def init(level):
112
- if logger_provider != None:
116
+ if logger_provider is not None:
113
117
  logging_handler = LoggingHandler(level=level, logger_provider=logger_provider)
114
- root = logging.getLogger()
115
- root.setLevel(level)
118
+ root = logging.getLogger()
119
+ root.setLevel(level)
116
120
  root.addHandler(logging_handler)
117
121
  else:
118
122
  logging.basicConfig(level=level)
@@ -1,40 +1,49 @@
1
1
  import typer
2
2
  from rich import print
3
3
  from typing import Annotated, Optional
4
- from meshagent.api import RoomClient, ParticipantToken
5
- from meshagent.cli.helper import set_active_project, get_active_project, resolve_project_id, resolve_api_key
4
+ from meshagent.api import ParticipantToken
5
+ from meshagent.cli.helper import resolve_project_id, resolve_api_key
6
6
  from meshagent.cli import async_typer
7
- from meshagent.cli.helper import get_client, print_json_table, set_active_project, resolve_project_id
7
+ from meshagent.cli.helper import get_client
8
8
  import pathlib
9
9
 
10
10
  app = async_typer.AsyncTyper()
11
11
 
12
+
12
13
  @app.async_command("generate")
13
- async def generate(*, project_id: str = None, token_path: Annotated[str, typer.Option()], room: Annotated[str, typer.Option()], api_key_id: Annotated[Optional[str], typer.Option()] = None, name: Annotated[str, typer.Option()], role: str = "agent"):
14
+ async def generate(
15
+ *,
16
+ project_id: str = None,
17
+ token_path: Annotated[str, typer.Option()],
18
+ room: Annotated[str, typer.Option()],
19
+ api_key_id: Annotated[Optional[str], typer.Option()] = None,
20
+ name: Annotated[str, typer.Option()],
21
+ role: str = "agent",
22
+ ):
14
23
  client = await get_client()
15
24
  try:
16
25
  project_id = await resolve_project_id(project_id=project_id)
17
26
  api_key_id = await resolve_api_key(project_id=project_id, api_key_id=api_key_id)
18
27
 
19
- key = (await client.decrypt_project_api_key(project_id=project_id, id=api_key_id))["token"]
28
+ key = (
29
+ await client.decrypt_project_api_key(project_id=project_id, id=api_key_id)
30
+ )["token"]
20
31
 
21
32
  token = ParticipantToken(
22
- name=name,
23
- project_id=project_id,
24
- api_key_id=api_key_id
33
+ name=name, project_id=project_id, api_key_id=api_key_id
25
34
  )
26
35
 
27
36
  token.add_role_grant(role=role)
28
37
 
29
38
  token.add_room_grant(room)
30
39
 
31
- if token_path == None:
32
-
40
+ if token_path is None:
33
41
  print(token.to_jwt(token=key))
34
42
 
35
43
  else:
44
+ pathlib.Path(token_path).expanduser().resolve().write_text(
45
+ token.to_jwt(token=key)
46
+ )
36
47
 
37
- pathlib.Path(token_path).expanduser().resolve().write_text(token.to_jwt(token=key))
38
-
39
48
  finally:
40
49
  await client.close()
meshagent/cli/projects.py CHANGED
@@ -16,10 +16,11 @@ async def create(name: str):
16
16
  client = await get_client()
17
17
  try:
18
18
  result = await client.create_project(name)
19
- print(f"[green]Project created:[/] {result["id"]}")
19
+ print(f"[green]Project created:[/] {result['id']}")
20
20
  finally:
21
21
  await client.close()
22
22
 
23
+
23
24
  @app.async_command("list")
24
25
  async def list():
25
26
  client = await get_client()
@@ -32,6 +33,7 @@ async def list():
32
33
  print_json_table(projects["projects"], "id", "name")
33
34
  await client.close()
34
35
 
36
+
35
37
  @app.async_command("activate")
36
38
  async def activate(
37
39
  project_id: str | None = typer.Argument(None),
@@ -95,4 +97,3 @@ async def activate(
95
97
  raise typer.Exit(code=1)
96
98
  finally:
97
99
  await client.close()
98
-