meshagent-cli 0.5.8b4__tar.gz → 0.5.8b5__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 meshagent-cli might be problematic. Click here for more details.

Files changed (41) hide show
  1. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/PKG-INFO +13 -11
  2. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/auth_async.py +1 -1
  3. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/chatbot.py +15 -15
  4. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/cli.py +4 -0
  5. meshagent_cli-0.5.8b5/meshagent/cli/containers.py +849 -0
  6. meshagent_cli-0.5.8b5/meshagent/cli/oauth2.py +75 -0
  7. meshagent_cli-0.5.8b5/meshagent/cli/version.py +1 -0
  8. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent_cli.egg-info/PKG-INFO +13 -11
  9. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent_cli.egg-info/SOURCES.txt +2 -0
  10. meshagent_cli-0.5.8b5/meshagent_cli.egg-info/requires.txt +23 -0
  11. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/pyproject.toml +12 -10
  12. meshagent_cli-0.5.8b4/meshagent/cli/version.py +0 -1
  13. meshagent_cli-0.5.8b4/meshagent_cli.egg-info/requires.txt +0 -21
  14. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/README.md +0 -0
  15. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/__init__.py +0 -0
  16. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/agent.py +0 -0
  17. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/api_keys.py +0 -0
  18. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/async_typer.py +0 -0
  19. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/auth.py +0 -0
  20. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/call.py +0 -0
  21. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/cli_mcp.py +0 -0
  22. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/cli_secrets.py +0 -0
  23. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/common_options.py +0 -0
  24. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/developer.py +0 -0
  25. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/exec.py +0 -0
  26. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/helper.py +0 -0
  27. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/mailbot.py +0 -0
  28. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/messaging.py +0 -0
  29. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/otel.py +0 -0
  30. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/participant_token.py +0 -0
  31. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/projects.py +0 -0
  32. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/queue.py +0 -0
  33. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/services.py +0 -0
  34. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/sessions.py +0 -0
  35. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/storage.py +0 -0
  36. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/voicebot.py +0 -0
  37. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent/cli/webhook.py +0 -0
  38. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent_cli.egg-info/dependency_links.txt +0 -0
  39. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent_cli.egg-info/entry_points.txt +0 -0
  40. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/meshagent_cli.egg-info/top_level.txt +0 -0
  41. {meshagent_cli-0.5.8b4 → meshagent_cli-0.5.8b5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshagent-cli
3
- Version: 0.5.8b4
3
+ Version: 0.5.8b5
4
4
  Summary: CLI for Meshagent
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Documentation, https://docs.meshagent.com
@@ -16,17 +16,19 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.33
16
16
  Requires-Dist: art~=6.5
17
17
  Requires-Dist: pydantic-yaml~=1.5
18
18
  Provides-Extra: all
19
- Requires-Dist: meshagent-agents[all]~=0.5.8b4; extra == "all"
20
- Requires-Dist: meshagent-api[all]~=0.5.8b4; extra == "all"
21
- Requires-Dist: meshagent-computers~=0.5.8b4; extra == "all"
22
- Requires-Dist: meshagent-openai~=0.5.8b4; extra == "all"
23
- Requires-Dist: meshagent-mcp~=0.5.8b4; extra == "all"
24
- Requires-Dist: meshagent-tools~=0.5.8b4; extra == "all"
19
+ Requires-Dist: meshagent-agents[all]~=0.5.8b5; extra == "all"
20
+ Requires-Dist: meshagent-api[all]~=0.5.8b5; extra == "all"
21
+ Requires-Dist: meshagent-computers~=0.5.8b5; extra == "all"
22
+ Requires-Dist: meshagent-openai~=0.5.8b5; extra == "all"
23
+ Requires-Dist: meshagent-mcp~=0.5.8b5; extra == "all"
24
+ Requires-Dist: meshagent-tools~=0.5.8b5; extra == "all"
25
+ Requires-Dist: supabase-auth~=2.12.3; extra == "all"
25
26
  Provides-Extra: mcp-service
26
- Requires-Dist: meshagent-agents[all]~=0.5.8b4; extra == "mcp-service"
27
- Requires-Dist: meshagent-api~=0.5.8b4; extra == "mcp-service"
28
- Requires-Dist: meshagent-mcp~=0.5.8b4; extra == "mcp-service"
29
- Requires-Dist: meshagent-tools~=0.5.8b4; extra == "mcp-service"
27
+ Requires-Dist: meshagent-agents[all]~=0.5.8b5; extra == "mcp-service"
28
+ Requires-Dist: meshagent-api~=0.5.8b5; extra == "mcp-service"
29
+ Requires-Dist: meshagent-mcp~=0.5.8b5; extra == "mcp-service"
30
+ Requires-Dist: meshagent-tools~=0.5.8b5; extra == "mcp-service"
31
+ Requires-Dist: supabase-auth~=2.12.3; extra == "mcp-service"
30
32
 
31
33
  # [Meshagent](https://www.meshagent.com)
32
34
 
@@ -9,7 +9,7 @@ from supabase._async.client import (
9
9
  create_client,
10
10
  ) # async flavour :contentReference[oaicite:1]{index=1}
11
11
  from supabase.lib.client_options import ClientOptions
12
- from gotrue import AsyncMemoryStorage
12
+ from supabase_auth import AsyncMemoryStorage
13
13
 
14
14
  AUTH_URL = os.getenv("MESHAGENT_AUTH_URL", "https://infra.meshagent.com")
15
15
  AUTH_ANON_KEY = os.getenv(
@@ -70,22 +70,22 @@ def build_chatbot(
70
70
  except FileNotFoundError:
71
71
  print(f"[yellow]rules file not found at {rules_file}[/yellow]")
72
72
 
73
- BaseClass = ChatBot
74
- if computer_use:
75
- if ComputerAgent is None:
76
- raise RuntimeError(
77
- "Computer use is enabled, but meshagent.computers is not installed."
78
- )
79
- BaseClass = ComputerAgent
80
- llm_adapter = OpenAIResponsesAdapter(
81
- model=model,
82
- response_options={
83
- "reasoning": {"generate_summary": "concise"},
84
- "truncation": "auto",
85
- },
73
+ BaseClass = ChatBot
74
+ if computer_use:
75
+ if ComputerAgent is None:
76
+ raise RuntimeError(
77
+ "Computer use is enabled, but meshagent.computers is not installed."
86
78
  )
87
- else:
88
- llm_adapter = OpenAIResponsesAdapter(model=model)
79
+ BaseClass = ComputerAgent
80
+ llm_adapter = OpenAIResponsesAdapter(
81
+ model=model,
82
+ response_options={
83
+ "reasoning": {"generate_summary": "concise"},
84
+ "truncation": "auto",
85
+ },
86
+ )
87
+ else:
88
+ llm_adapter = OpenAIResponsesAdapter(model=model)
89
89
 
90
90
  class CustomChatbot(BaseClass):
91
91
  def __init__(self):
@@ -22,6 +22,8 @@ from meshagent.cli import cli_mcp
22
22
  from meshagent.cli import chatbot
23
23
  from meshagent.cli import voicebot
24
24
  from meshagent.cli import mailbot
25
+ from meshagent.cli import containers
26
+ from meshagent.cli import oauth2
25
27
  from meshagent.cli.exec import register as register_exec
26
28
  from meshagent.cli.version import __version__
27
29
 
@@ -62,6 +64,8 @@ app.add_typer(cli_mcp.app, name="mcp")
62
64
  app.add_typer(chatbot.app, name="chatbot")
63
65
  app.add_typer(voicebot.app, name="voicebot")
64
66
  app.add_typer(mailbot.app, name="mailbot")
67
+ app.add_typer(containers.app, name="container")
68
+ app.add_typer(oauth2.app, name="oauth2")
65
69
 
66
70
  register_exec(app)
67
71
 
@@ -0,0 +1,849 @@
1
+ # meshagent/cli/containers.py
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import io
6
+ import os
7
+ import sys
8
+ import tarfile
9
+ import time
10
+ from pathlib import Path
11
+
12
+ import typer
13
+ from rich import print
14
+ from typing import Annotated, Optional, List, Dict
15
+
16
+ from meshagent.cli import async_typer
17
+ from meshagent.cli.common_options import ProjectIdOption, ApiKeyIdOption, RoomOption
18
+ from meshagent.cli.helper import (
19
+ get_client,
20
+ resolve_project_id,
21
+ resolve_api_key,
22
+ resolve_room,
23
+ )
24
+ from meshagent.api import (
25
+ RoomClient,
26
+ ParticipantToken,
27
+ WebSocketClientProtocol,
28
+ ApiScope,
29
+ )
30
+ from meshagent.api.helpers import meshagent_base_url, websocket_room_url
31
+ from meshagent.api.room_server_client import (
32
+ BuildSource,
33
+ BuildSourceGit,
34
+ BuildSourceContext,
35
+ BuildSourceRoom,
36
+ DockerSecret,
37
+ )
38
+
39
+ app = async_typer.AsyncTyper(help="Manage containers and images inside a room")
40
+
41
+ # -------------------------
42
+ # Helpers
43
+ # -------------------------
44
+
45
+
46
+ def _parse_keyvals(items: List[str]) -> Dict[str, str]:
47
+ """
48
+ Parse ["KEY=VAL", "FOO=BAR"] -> {"KEY":"VAL", "FOO":"BAR"}
49
+ """
50
+ out: Dict[str, str] = {}
51
+ for s in items or []:
52
+ if "=" not in s:
53
+ raise typer.BadParameter(f"Expected KEY=VALUE, got: {s}")
54
+ k, v = s.split("=", 1)
55
+ out[k] = v
56
+ return out
57
+
58
+
59
+ def _parse_ports(items: List[str]) -> Dict[int, int]:
60
+ """
61
+ Parse ["8080:3000", "9999:9999"] as CONTAINER:HOST -> {8080:3000, 9999:9999}
62
+ (Matches server's expectation: container_port -> host_port.)
63
+ """
64
+ out: Dict[int, int] = {}
65
+ for s in items or []:
66
+ if ":" not in s:
67
+ raise typer.BadParameter(f"Expected CONTAINER:HOST, got: {s}")
68
+ c, h = s.split(":", 1)
69
+ try:
70
+ cp, hp = int(c), int(h)
71
+ except ValueError:
72
+ raise typer.BadParameter(f"Ports must be integers, got: {s}")
73
+ out[cp] = hp
74
+ return out
75
+
76
+
77
+ def _parse_creds(items: List[str]) -> List[DockerSecret]:
78
+ """
79
+ Parse creds given as:
80
+ --cred username,password
81
+ --cred registry,username,password
82
+ """
83
+ creds: List[DockerSecret] = []
84
+ for s in items or []:
85
+ parts = [p.strip() for p in s.split(",")]
86
+ if len(parts) == 2:
87
+ u, p = parts
88
+ creds.append(DockerSecret(username=u, password=p))
89
+ elif len(parts) == 3:
90
+ r, u, p = parts
91
+ creds.append(DockerSecret(registry=r, username=u, password=p))
92
+ else:
93
+ raise typer.BadParameter(
94
+ f"Invalid --cred format: {s}. Use username,password or registry,username,password"
95
+ )
96
+ return creds
97
+
98
+
99
+ def _tarfilter_strip_macos(ti: tarfile.TarInfo) -> tarfile.TarInfo | None:
100
+ """
101
+ Filter to make Linux-friendly tarballs:
102
+ - Drop AppleDouble files (._*)
103
+ - Reset uid/gid/uname/gname
104
+ - Clear pax headers
105
+ """
106
+ base = os.path.basename(ti.name)
107
+ if base.startswith("._"):
108
+ return None
109
+ ti.uid = 0
110
+ ti.gid = 0
111
+ ti.uname = ""
112
+ ti.gname = ""
113
+ ti.pax_headers = {}
114
+ # Preserve mode & type; set a stable-ish mtime
115
+ if ti.mtime is None:
116
+ ti.mtime = int(time.time())
117
+ return ti
118
+
119
+
120
+ def _make_targz_from_dir(path: Path) -> bytes:
121
+ buf = io.BytesIO()
122
+ with tarfile.open(fileobj=buf, mode="w:gz") as tar:
123
+ tar.add(path, arcname=".", filter=_tarfilter_strip_macos)
124
+ return buf.getvalue()
125
+
126
+
127
+ def _make_targz_with_dockerfile_text(text: str) -> bytes:
128
+ b = text.encode("utf-8")
129
+ buf = io.BytesIO()
130
+ with tarfile.open(fileobj=buf, mode="w:gz") as tar:
131
+ ti = tarfile.TarInfo("Dockerfile")
132
+ ti.size = len(b)
133
+ ti.mtime = int(time.time())
134
+ ti.mode = 0o644
135
+ tar.addfile(ti, io.BytesIO(b))
136
+ return buf.getvalue()
137
+
138
+
139
+ async def _drain_stream_plain(stream, *, show_progress: bool = True):
140
+ async def _logs():
141
+ async for line in stream.logs():
142
+ # Server emits plain lines; print as-is
143
+ if line is not None:
144
+ print(line)
145
+
146
+ async def _prog():
147
+ if not show_progress:
148
+ async for _ in stream.progress():
149
+ pass
150
+ return
151
+ async for p in stream.progress():
152
+ if p is None:
153
+ return
154
+ msg = p.message or ""
155
+ # Show progress if we have numbers, else just the message.
156
+ if p.current is not None and p.total:
157
+ print(f"[cyan]{msg} ({p.current}/{p.total})[/cyan]")
158
+ elif msg:
159
+ print(f"[cyan]{msg}[/cyan]")
160
+
161
+ t1 = asyncio.create_task(_logs())
162
+ t2 = asyncio.create_task(_prog())
163
+ try:
164
+ return await stream
165
+ finally:
166
+ await asyncio.gather(t1, t2, return_exceptions=True)
167
+
168
+
169
+ async def _drain_stream_pretty(stream):
170
+ import asyncio
171
+ import math
172
+ from rich.table import Column
173
+ from rich.live import Live
174
+ from rich.panel import Panel
175
+ from rich.console import Group
176
+ from rich.text import Text
177
+ from rich.progress import (
178
+ Progress,
179
+ TextColumn,
180
+ BarColumn,
181
+ TimeElapsedColumn,
182
+ ProgressColumn,
183
+ SpinnerColumn,
184
+ )
185
+
186
+ class MaybeMofN(ProgressColumn):
187
+ def render(self, task):
188
+ import math
189
+ from rich.text import Text
190
+
191
+ def _fmt_bytes(n):
192
+ if n is None:
193
+ return ""
194
+ n = float(n)
195
+ units = ["B", "KiB", "MiB", "GiB", "TiB"]
196
+ i = 0
197
+ while n >= 1024 and i < len(units) - 1:
198
+ n /= 1024
199
+ i += 1
200
+ return f"{n:.1f} {units[i]}"
201
+
202
+ if task.total == 0 or math.isinf(task.total):
203
+ return Text("")
204
+ return Text(f"{_fmt_bytes(task.completed)} / {_fmt_bytes(task.total)}")
205
+
206
+ class MaybeBarColumn(BarColumn):
207
+ def __init__(
208
+ self,
209
+ *,
210
+ bar_width: int | None = 28,
211
+ hide_when_unknown: bool = False,
212
+ column_width: int | None = None,
213
+ **kwargs,
214
+ ):
215
+ # bar_width controls the drawn bar size; None = flex
216
+ super().__init__(bar_width=bar_width, **kwargs)
217
+ self.hide_when_unknown = hide_when_unknown
218
+ self.column_width = column_width # fix the table column if set
219
+
220
+ def get_table_column(self) -> Column:
221
+ if self.column_width is None:
222
+ # default behavior (may flex depending on layout)
223
+ return Column(no_wrap=True)
224
+ return Column(
225
+ width=self.column_width,
226
+ min_width=self.column_width,
227
+ max_width=self.column_width,
228
+ no_wrap=True,
229
+ overflow="crop",
230
+ justify="left",
231
+ )
232
+
233
+ def render(self, task):
234
+ if task.total is None or task.total == 0 or math.isinf(task.total):
235
+ return Text("") # hide bar for indeterminate tasks
236
+ return super().render(task)
237
+
238
+ class MaybeETA(ProgressColumn):
239
+ """Show ETA only if total is known."""
240
+
241
+ _elapsed = TimeElapsedColumn()
242
+
243
+ def render(self, task):
244
+ # You can swap this to a TimeRemainingColumn() if you prefer,
245
+ # but hide when total is unknown.
246
+ if task.total == 0 or math.isinf(task.total):
247
+ return Text("")
248
+ return self._elapsed.render(task) # or TimeRemainingColumn().render(task)
249
+
250
+ progress = Progress(
251
+ SpinnerColumn(),
252
+ TextColumn(
253
+ "[bold]{task.description}",
254
+ table_column=Column(ratio=8, no_wrap=True, overflow="ellipsis"),
255
+ ),
256
+ MaybeMofN(table_column=Column(ratio=2, no_wrap=True, overflow="ellipsis")),
257
+ MaybeETA(table_column=Column(ratio=1, no_wrap=True, overflow="ellipsis")),
258
+ MaybeBarColumn(pulse_style="cyan", bar_width=20, hide_when_unknown=True),
259
+ # pulses automatically if total=None
260
+ transient=False, # we’re inside Live; we’ll hide tasks ourselves
261
+ expand=True,
262
+ )
263
+
264
+ logs_tail: list[str] = []
265
+ tasks: dict[str, int] = {} # layer -> task_id
266
+
267
+ def render():
268
+ tail = "\n".join(logs_tail[-12:]) or "waiting…"
269
+ return Group(
270
+ progress,
271
+ Panel(tail, title="logs", border_style="cyan", height=12),
272
+ )
273
+
274
+ async def _logs():
275
+ async for line in stream.logs():
276
+ if line:
277
+ logs_tail.append(line.strip())
278
+
279
+ async def _prog():
280
+ async for p in stream.progress():
281
+ layer = p.layer or "overall"
282
+ if layer not in tasks:
283
+ tasks[layer] = progress.add_task(
284
+ p.message or layer, total=p.total if p.total is not None else 0
285
+ )
286
+ task_id = tasks[layer]
287
+
288
+ updates = {}
289
+ # Keep total=None for pulsing; only set if we get a real number.
290
+ if p.total is not None and not math.isinf(p.total):
291
+ updates["total"] = p.total
292
+ if p.current is not None:
293
+ updates["completed"] = p.current
294
+ if p.message:
295
+ updates["description"] = p.message
296
+ if updates:
297
+ progress.update(task_id, **updates)
298
+
299
+ with Live(render(), refresh_per_second=10) as live:
300
+
301
+ async def _refresh():
302
+ while True:
303
+ live.update(render())
304
+ await asyncio.sleep(0.1)
305
+
306
+ t_logs = asyncio.create_task(_logs())
307
+ t_prog = asyncio.create_task(_prog())
308
+ t_ui = asyncio.create_task(_refresh())
309
+ try:
310
+ result = await stream
311
+ return result
312
+ finally:
313
+ # Hide any still-visible tasks (e.g., indeterminate ones with total=None)
314
+ for tid in list(tasks.values()):
315
+ progress.update(tid, visible=False)
316
+ live.update(render())
317
+
318
+ for t in (t_logs, t_prog):
319
+ await t
320
+
321
+ t_ui.cancel()
322
+
323
+
324
+ async def _with_client(
325
+ *,
326
+ project_id: ProjectIdOption,
327
+ room: RoomOption,
328
+ api_key_id: ApiKeyIdOption,
329
+ name: str,
330
+ role: str,
331
+ ):
332
+ account_client = await get_client()
333
+ try:
334
+ project_id = await resolve_project_id(project_id=project_id)
335
+ api_key_id = await resolve_api_key(project_id, api_key_id)
336
+ room = resolve_room(room)
337
+
338
+ key = (
339
+ await account_client.decrypt_project_api_key(
340
+ project_id=project_id, id=api_key_id
341
+ )
342
+ )["token"]
343
+
344
+ token = ParticipantToken(
345
+ name=name, project_id=project_id, api_key_id=api_key_id
346
+ )
347
+ token.add_api_grant(ApiScope.agent_default())
348
+ token.add_role_grant(role=role)
349
+ token.add_room_grant(room)
350
+
351
+ print("[bold green]Connecting to room...[/bold green]", flush=True)
352
+ proto = WebSocketClientProtocol(
353
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
354
+ token=token.to_jwt(token=key),
355
+ )
356
+ client_cm = RoomClient(protocol=proto)
357
+ await client_cm.__aenter__()
358
+ return account_client, client_cm
359
+ except Exception:
360
+ await account_client.close()
361
+ raise
362
+
363
+
364
+ # -------------------------
365
+ # Top-level: ps / stop / logs / run
366
+ # -------------------------
367
+
368
+
369
+ @app.async_command("ps")
370
+ async def list_containers(
371
+ *,
372
+ project_id: ProjectIdOption = None,
373
+ room: RoomOption = None,
374
+ api_key_id: ApiKeyIdOption = None,
375
+ name: Annotated[str, typer.Option(...)] = "cli",
376
+ role: Annotated[str, typer.Option(...)] = "user",
377
+ output: Annotated[Optional[str], typer.Option(help="json | table")] = "json",
378
+ ):
379
+ account_client, client = await _with_client(
380
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
381
+ )
382
+ try:
383
+ containers = await client.containers.list()
384
+ if output == "table":
385
+ from rich.table import Table
386
+ from rich.console import Console
387
+
388
+ table = Table(title="Containers")
389
+ table.add_column("ID", style="cyan")
390
+ table.add_column("Image")
391
+ table.add_column("Status")
392
+ for c in containers:
393
+ table.add_row(c.id, c.image or "", c.status or "")
394
+ Console().print(table)
395
+ else:
396
+ # default json-ish
397
+ print([c.model_dump() for c in containers])
398
+ finally:
399
+ await client.__aexit__(None, None, None)
400
+ await account_client.close()
401
+
402
+
403
+ @app.async_command("stop")
404
+ async def stop_container(
405
+ *,
406
+ project_id: ProjectIdOption = None,
407
+ room: RoomOption = None,
408
+ api_key_id: ApiKeyIdOption = None,
409
+ id: Annotated[str, typer.Option(..., help="Container ID")],
410
+ name: Annotated[str, typer.Option(...)] = "cli",
411
+ role: Annotated[str, typer.Option(...)] = "user",
412
+ ):
413
+ account_client, client = await _with_client(
414
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
415
+ )
416
+ try:
417
+ await client.containers.stop(container_id=id)
418
+ print("[green]Stopped[/green]")
419
+ finally:
420
+ await client.__aexit__(None, None, None)
421
+ await account_client.close()
422
+
423
+
424
+ @app.async_command("logs")
425
+ async def container_logs(
426
+ *,
427
+ project_id: ProjectIdOption = None,
428
+ room: RoomOption = None,
429
+ api_key_id: ApiKeyIdOption = None,
430
+ id: Annotated[str, typer.Option(..., help="Container ID")],
431
+ follow: Annotated[bool, typer.Option("--follow/--no-follow")] = False,
432
+ name: Annotated[str, typer.Option(...)] = "cli",
433
+ role: Annotated[str, typer.Option(...)] = "user",
434
+ ):
435
+ account_client, client = await _with_client(
436
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
437
+ )
438
+ try:
439
+ stream = client.containers.logs(container_id=id, follow=follow)
440
+ await _drain_stream_plain(stream)
441
+ finally:
442
+ await client.__aexit__(None, None, None)
443
+ await account_client.close()
444
+
445
+
446
+ # -------------------------
447
+ # Run (detached) and run-attached
448
+ # -------------------------
449
+
450
+
451
+ @app.async_command("run")
452
+ async def run_container(
453
+ *,
454
+ project_id: ProjectIdOption = None,
455
+ room: RoomOption = None,
456
+ api_key_id: ApiKeyIdOption = None,
457
+ image: Annotated[str, typer.Option(..., help="Image to run")],
458
+ command: Annotated[Optional[str], typer.Option(...)] = None,
459
+ env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
460
+ port: Annotated[
461
+ List[str], typer.Option("--port", "-p", help="CONTAINER:HOST")
462
+ ] = [],
463
+ var: Annotated[
464
+ List[str],
465
+ typer.Option("--var", help="Template variable KEY=VALUE (optional)"),
466
+ ] = [],
467
+ cred: Annotated[
468
+ List[str],
469
+ typer.Option(
470
+ "--cred",
471
+ help="Docker creds (username,password) or (registry,username,password)",
472
+ ),
473
+ ] = [],
474
+ mount_path: Annotated[Optional[str], typer.Option()] = None,
475
+ mount_subpath: Annotated[Optional[str], typer.Option()] = None,
476
+ participant_name: Annotated[Optional[str], typer.Option()] = None,
477
+ role: Annotated[str, typer.Option(...)] = "user",
478
+ name: Annotated[str, typer.Option(...)] = "cli",
479
+ ):
480
+ account_client, client = await _with_client(
481
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
482
+ )
483
+ try:
484
+ creds = _parse_creds(cred)
485
+ env_map = _parse_keyvals(env)
486
+ ports_map = _parse_ports(port)
487
+ vars_map = _parse_keyvals(var)
488
+
489
+ stream = client.containers.run(
490
+ image=image,
491
+ command=command,
492
+ env=env_map,
493
+ mount_path=mount_path,
494
+ mount_subpath=mount_subpath,
495
+ role=role,
496
+ participant_name=participant_name,
497
+ ports=ports_map,
498
+ credentials=creds,
499
+ variables=vars_map or None,
500
+ )
501
+ result = await _drain_stream_plain(stream)
502
+ print(result.model_dump() if hasattr(result, "model_dump") else result)
503
+ finally:
504
+ await client.__aexit__(None, None, None)
505
+ await account_client.close()
506
+
507
+
508
+ @app.async_command("run-attached")
509
+ async def run_attached(
510
+ *,
511
+ project_id: ProjectIdOption = None,
512
+ room: RoomOption = None,
513
+ api_key_id: ApiKeyIdOption = None,
514
+ image: Annotated[str, typer.Option(..., help="Image to run")],
515
+ command: Annotated[Optional[str], typer.Option(...)] = None,
516
+ tty: Annotated[bool, typer.Option("--tty/--no-tty")] = False,
517
+ env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
518
+ port: Annotated[
519
+ List[str], typer.Option("--port", "-p", help="CONTAINER:HOST")
520
+ ] = [],
521
+ send: Annotated[
522
+ List[str],
523
+ typer.Option(
524
+ "--send",
525
+ help="Optional lines to send to container stdin (each becomes a line)",
526
+ ),
527
+ ] = [],
528
+ name: Annotated[str, typer.Option(...)] = "cli",
529
+ role: Annotated[str, typer.Option(...)] = "user",
530
+ ):
531
+ account_client, client = await _with_client(
532
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
533
+ )
534
+ try:
535
+ env_map = _parse_keyvals(env)
536
+ ports_map = _parse_ports(port)
537
+
538
+ tty_obj = client.containers.run_attached(
539
+ image=image,
540
+ command=command,
541
+ env=env_map,
542
+ ports=ports_map,
543
+ tty=tty,
544
+ role=role,
545
+ participant_name=name,
546
+ )
547
+
548
+ # Output reader
549
+ async def _read():
550
+ async for b in tty_obj.output():
551
+ if not b:
552
+ continue
553
+ try:
554
+ sys.stdout.buffer.write(b)
555
+ sys.stdout.flush()
556
+ except Exception:
557
+ # fallback printing
558
+ print(b.decode(errors="ignore"), end="")
559
+
560
+ # Optional sender (from --send args)
561
+ async def _preload():
562
+ for line in send:
563
+ await tty_obj.write(line.encode("utf-8") + b"\n")
564
+
565
+ readers = asyncio.gather(_read(), _preload())
566
+ status = await tty_obj.result
567
+ await readers
568
+ if status is not None:
569
+ print(f"\n[green]Exit status:[/green] {status}")
570
+ finally:
571
+ await client.__aexit__(None, None, None)
572
+ await account_client.close()
573
+
574
+
575
+ # -------------------------
576
+ # Images sub-commands
577
+ # -------------------------
578
+
579
+ images_app = async_typer.AsyncTyper(help="Image operations")
580
+ app.add_typer(images_app, name="images")
581
+
582
+
583
+ @images_app.async_command("list")
584
+ async def images_list(
585
+ *,
586
+ project_id: ProjectIdOption = None,
587
+ room: RoomOption = None,
588
+ api_key_id: ApiKeyIdOption = None,
589
+ name: Annotated[str, typer.Option(...)] = "cli",
590
+ role: Annotated[str, typer.Option(...)] = "user",
591
+ ):
592
+ account_client, client = await _with_client(
593
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
594
+ )
595
+ try:
596
+ imgs = await client.containers.list_images()
597
+ print([i.model_dump() for i in imgs])
598
+ finally:
599
+ await client.__aexit__(None, None, None)
600
+ await account_client.close()
601
+
602
+
603
+ @images_app.async_command("delete")
604
+ async def images_delete(
605
+ *,
606
+ project_id: ProjectIdOption = None,
607
+ room: RoomOption = None,
608
+ api_key_id: ApiKeyIdOption = None,
609
+ image: Annotated[str, typer.Option(..., help="Image ref/tag to delete")],
610
+ name: Annotated[str, typer.Option(...)] = "cli",
611
+ role: Annotated[str, typer.Option(...)] = "user",
612
+ ):
613
+ account_client, client = await _with_client(
614
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
615
+ )
616
+ try:
617
+ await client.containers.delete_image(image=image)
618
+ print("[green]Deleted[/green]")
619
+ finally:
620
+ await client.__aexit__(None, None, None)
621
+ await account_client.close()
622
+
623
+
624
+ @images_app.async_command("pull")
625
+ async def images_pull(
626
+ *,
627
+ project_id: ProjectIdOption = None,
628
+ room: RoomOption = None,
629
+ api_key_id: ApiKeyIdOption = None,
630
+ tag: Annotated[str, typer.Option(..., help="Image tag/ref to pull")],
631
+ cred: Annotated[
632
+ List[str],
633
+ typer.Option(
634
+ "--cred",
635
+ help="Docker creds (username,password) or (registry,username,password)",
636
+ ),
637
+ ] = [],
638
+ name: Annotated[str, typer.Option(...)] = "cli",
639
+ role: Annotated[str, typer.Option(...)] = "user",
640
+ ):
641
+ account_client, client = await _with_client(
642
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
643
+ )
644
+ try:
645
+ stream = client.containers.pull_image(tag=tag, credentials=_parse_creds(cred))
646
+ result = await _drain_stream_plain(stream)
647
+ print(result.model_dump() if hasattr(result, "model_dump") else result)
648
+ finally:
649
+ await client.__aexit__(None, None, None)
650
+ await account_client.close()
651
+
652
+
653
+ # -------------------------
654
+ # Build sub-commands
655
+ # -------------------------
656
+
657
+ build_app = async_typer.AsyncTyper(help="Build images")
658
+ app.add_typer(build_app, name="build")
659
+
660
+
661
+ @build_app.async_command("git")
662
+ async def build_git(
663
+ *,
664
+ project_id: ProjectIdOption = None,
665
+ room: RoomOption = None,
666
+ api_key_id: ApiKeyIdOption = None,
667
+ tag: Annotated[str, typer.Option(..., help="Resulting image tag")],
668
+ url: Annotated[str, typer.Option(..., help="Git URL")],
669
+ ref: Annotated[str, typer.Option(..., help="Git ref/branch/tag")],
670
+ cred: Annotated[
671
+ List[str],
672
+ typer.Option(
673
+ "--cred",
674
+ help="Docker creds (username,password) or (registry,username,password)",
675
+ ),
676
+ ] = [],
677
+ name: Annotated[str, typer.Option(...)] = "cli",
678
+ role: Annotated[str, typer.Option(...)] = "user",
679
+ pretty: Annotated[bool, typer.Option(...)] = True,
680
+ ):
681
+ account_client, client = await _with_client(
682
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
683
+ )
684
+ try:
685
+ source = BuildSource(git=BuildSourceGit(url=url, ref=ref))
686
+ stream = client.containers.build(
687
+ tag=tag, source=source, credentials=_parse_creds(cred)
688
+ )
689
+ await _drain_stream_pretty(stream) if pretty else await _drain_stream_plain(
690
+ stream
691
+ )
692
+ finally:
693
+ await client.__aexit__(None, None, None)
694
+ await account_client.close()
695
+
696
+
697
+ @build_app.async_command("context")
698
+ async def build_context(
699
+ *,
700
+ project_id: ProjectIdOption = None,
701
+ room: RoomOption = None,
702
+ api_key_id: ApiKeyIdOption = None,
703
+ tag: Annotated[str, typer.Option(..., help="Resulting image tag")],
704
+ from_dir: Annotated[
705
+ Optional[str],
706
+ typer.Option(help="Directory to tar.gz as build context"),
707
+ ] = None,
708
+ dockerfile: Annotated[
709
+ Optional[str],
710
+ typer.Option(help="Path to a Dockerfile; sends just this file as context"),
711
+ ] = None,
712
+ dockerfile_inline: Annotated[
713
+ Optional[str],
714
+ typer.Option(help="Inline Dockerfile text; sends only this as context"),
715
+ ] = None,
716
+ tgz: Annotated[
717
+ Optional[str],
718
+ typer.Option(help="Use an existing .tar.gz file as the context"),
719
+ ] = None,
720
+ cred: Annotated[
721
+ List[str],
722
+ typer.Option(
723
+ "--cred",
724
+ help="Docker creds (username,password) or (registry,username,password)",
725
+ ),
726
+ ] = [],
727
+ name: Annotated[str, typer.Option(...)] = "cli",
728
+ role: Annotated[str, typer.Option(...)] = "user",
729
+ pretty: Annotated[bool, typer.Option(...)] = True,
730
+ ):
731
+ # Validate mutually exclusive inputs
732
+ specified = [x for x in [from_dir, dockerfile, dockerfile_inline, tgz] if x]
733
+ if len(specified) != 1:
734
+ raise typer.BadParameter(
735
+ "Specify exactly one of --from-dir, --dockerfile, --dockerfile-inline, or --tgz"
736
+ )
737
+
738
+ # Prepare context bytes
739
+ if from_dir:
740
+ ctx_bytes = _make_targz_from_dir(Path(from_dir).resolve())
741
+ elif dockerfile_inline:
742
+ ctx_bytes = _make_targz_with_dockerfile_text(dockerfile_inline)
743
+ elif dockerfile:
744
+ text = Path(dockerfile).read_text(encoding="utf-8")
745
+ ctx_bytes = _make_targz_with_dockerfile_text(text)
746
+ else:
747
+ ctx_bytes = Path(tgz).read_bytes()
748
+
749
+ account_client, client = await _with_client(
750
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
751
+ )
752
+ try:
753
+ source = BuildSource(context=BuildSourceContext(encoding="gzip"))
754
+ stream = client.containers.build(
755
+ tag=tag,
756
+ source=source,
757
+ context_bytes=ctx_bytes,
758
+ credentials=_parse_creds(cred),
759
+ )
760
+ await _drain_stream_pretty(stream) if pretty else await _drain_stream_plain(
761
+ stream
762
+ )
763
+ finally:
764
+ await client.__aexit__(None, None, None)
765
+ await account_client.close()
766
+
767
+
768
+ @build_app.async_command("room")
769
+ async def build_room(
770
+ *,
771
+ project_id: ProjectIdOption = None,
772
+ room: RoomOption = None,
773
+ api_key_id: ApiKeyIdOption = None,
774
+ tag: Annotated[str, typer.Option(..., help="Resulting image tag")],
775
+ path: Annotated[str, typer.Option(..., help="Room path to a .tar.gz context")],
776
+ cred: Annotated[
777
+ List[str],
778
+ typer.Option(
779
+ "--cred",
780
+ help="Docker creds (username,password) or (registry,username,password)",
781
+ ),
782
+ ] = [],
783
+ name: Annotated[str, typer.Option(...)] = "cli",
784
+ role: Annotated[str, typer.Option(...)] = "user",
785
+ pretty: Annotated[bool, typer.Option(...)] = True,
786
+ ):
787
+ account_client, client = await _with_client(
788
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
789
+ )
790
+ try:
791
+ source = BuildSource(room=BuildSourceRoom(path=path))
792
+ stream = client.containers.build(
793
+ tag=tag, source=source, credentials=_parse_creds(cred)
794
+ )
795
+ await _drain_stream_pretty(stream) if pretty else await _drain_stream_plain(
796
+ stream
797
+ )
798
+ finally:
799
+ await client.__aexit__(None, None, None)
800
+ await account_client.close()
801
+
802
+
803
+ # -------------------------
804
+ # Build admin: list/stop
805
+ # -------------------------
806
+
807
+ builds_app = async_typer.AsyncTyper(help="Inspect or manage running builds")
808
+ app.add_typer(builds_app, name="builds")
809
+
810
+
811
+ @builds_app.async_command("list")
812
+ async def list_builds(
813
+ *,
814
+ project_id: ProjectIdOption = None,
815
+ room: RoomOption = None,
816
+ api_key_id: ApiKeyIdOption = None,
817
+ name: Annotated[str, typer.Option(...)] = "cli",
818
+ role: Annotated[str, typer.Option(...)] = "user",
819
+ ):
820
+ account_client, client = await _with_client(
821
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
822
+ )
823
+ try:
824
+ builds = await client.containers.list_builds()
825
+ print([b.model_dump() for b in builds])
826
+ finally:
827
+ await client.__aexit__(None, None, None)
828
+ await account_client.close()
829
+
830
+
831
+ @builds_app.async_command("stop")
832
+ async def stop_build(
833
+ *,
834
+ project_id: ProjectIdOption = None,
835
+ room: RoomOption = None,
836
+ api_key_id: ApiKeyIdOption = None,
837
+ request_id: Annotated[str, typer.Option(..., help="Build request_id to stop")],
838
+ name: Annotated[str, typer.Option(...)] = "cli",
839
+ role: Annotated[str, typer.Option(...)] = "user",
840
+ ):
841
+ account_client, client = await _with_client(
842
+ project_id=project_id, room=room, api_key_id=api_key_id, name=name, role=role
843
+ )
844
+ try:
845
+ await client.containers.stop_build(request_id=request_id)
846
+ print("[green]Stopped[/green]")
847
+ finally:
848
+ await client.__aexit__(None, None, None)
849
+ await account_client.close()
@@ -0,0 +1,75 @@
1
+ from meshagent.cli import async_typer
2
+ from meshagent.cli.common_options import ProjectIdOption, ApiKeyIdOption, RoomOption
3
+ from meshagent.cli.helper import (
4
+ get_client,
5
+ resolve_project_id,
6
+ resolve_api_key,
7
+ resolve_token_jwt,
8
+ )
9
+ from meshagent.api import RoomClient, WebSocketClientProtocol
10
+ from meshagent.api.helpers import meshagent_base_url, websocket_room_url
11
+ from rich import print
12
+ from typing import Annotated, Optional
13
+ import typer
14
+
15
+ app = async_typer.AsyncTyper(help="OAuth2 test commands")
16
+
17
+
18
+ @app.async_command("request")
19
+ async def oauth2(
20
+ *,
21
+ project_id: ProjectIdOption = None,
22
+ room: RoomOption,
23
+ token_path: Annotated[Optional[str], typer.Option()] = None,
24
+ api_key_id: ApiKeyIdOption = None,
25
+ name: Annotated[str, typer.Option()] = "cli",
26
+ from_participant_id: Annotated[str, typer.Option()],
27
+ client_id: Annotated[str, typer.Option()],
28
+ authorization_endpoint: Annotated[str, typer.Option()],
29
+ token_endpoint: Annotated[str, typer.Option()],
30
+ role: str = "user",
31
+ scopes: Annotated[Optional[str], typer.Option()] = None,
32
+ client_secret: Annotated[Optional[str], typer.Option()],
33
+ redirect_uri: Annotated[Optional[str], typer.Option()],
34
+ ):
35
+ """
36
+ Run an OAuth2 request test between two participants in the same room.
37
+ One will act as the consumer, the other as the provider.
38
+ """
39
+
40
+ account_client = await get_client()
41
+ try:
42
+ project_id = await resolve_project_id(project_id=project_id)
43
+ api_key_id = await resolve_api_key(project_id, api_key_id)
44
+
45
+ jwt_consumer = await resolve_token_jwt(
46
+ project_id=project_id,
47
+ api_key_id=api_key_id,
48
+ token_path=token_path,
49
+ name=f"{name}-consumer",
50
+ role=role,
51
+ room=room,
52
+ )
53
+
54
+ async with RoomClient(
55
+ protocol=WebSocketClientProtocol(
56
+ url=websocket_room_url(room_name=room, base_url=meshagent_base_url()),
57
+ token=jwt_consumer,
58
+ )
59
+ ) as consumer:
60
+ print("[green]Requesting OAuth token from consumer side...[/green]")
61
+ token = await consumer.secrets.request_oauth_token(
62
+ client_id=client_id,
63
+ authorization_endpoint=authorization_endpoint,
64
+ token_endpoint=token_endpoint,
65
+ scopes=scopes.split(",") if scopes is not None else scopes,
66
+ from_participant_id=from_participant_id,
67
+ timeout=10,
68
+ client_secret=client_secret,
69
+ redirect_uri=redirect_uri,
70
+ )
71
+
72
+ print(f"[bold cyan]Got access token:[/bold cyan] {token}")
73
+
74
+ finally:
75
+ await account_client.close()
@@ -0,0 +1 @@
1
+ __version__ = "0.5.8b5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshagent-cli
3
- Version: 0.5.8b4
3
+ Version: 0.5.8b5
4
4
  Summary: CLI for Meshagent
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Documentation, https://docs.meshagent.com
@@ -16,17 +16,19 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.33
16
16
  Requires-Dist: art~=6.5
17
17
  Requires-Dist: pydantic-yaml~=1.5
18
18
  Provides-Extra: all
19
- Requires-Dist: meshagent-agents[all]~=0.5.8b4; extra == "all"
20
- Requires-Dist: meshagent-api[all]~=0.5.8b4; extra == "all"
21
- Requires-Dist: meshagent-computers~=0.5.8b4; extra == "all"
22
- Requires-Dist: meshagent-openai~=0.5.8b4; extra == "all"
23
- Requires-Dist: meshagent-mcp~=0.5.8b4; extra == "all"
24
- Requires-Dist: meshagent-tools~=0.5.8b4; extra == "all"
19
+ Requires-Dist: meshagent-agents[all]~=0.5.8b5; extra == "all"
20
+ Requires-Dist: meshagent-api[all]~=0.5.8b5; extra == "all"
21
+ Requires-Dist: meshagent-computers~=0.5.8b5; extra == "all"
22
+ Requires-Dist: meshagent-openai~=0.5.8b5; extra == "all"
23
+ Requires-Dist: meshagent-mcp~=0.5.8b5; extra == "all"
24
+ Requires-Dist: meshagent-tools~=0.5.8b5; extra == "all"
25
+ Requires-Dist: supabase-auth~=2.12.3; extra == "all"
25
26
  Provides-Extra: mcp-service
26
- Requires-Dist: meshagent-agents[all]~=0.5.8b4; extra == "mcp-service"
27
- Requires-Dist: meshagent-api~=0.5.8b4; extra == "mcp-service"
28
- Requires-Dist: meshagent-mcp~=0.5.8b4; extra == "mcp-service"
29
- Requires-Dist: meshagent-tools~=0.5.8b4; extra == "mcp-service"
27
+ Requires-Dist: meshagent-agents[all]~=0.5.8b5; extra == "mcp-service"
28
+ Requires-Dist: meshagent-api~=0.5.8b5; extra == "mcp-service"
29
+ Requires-Dist: meshagent-mcp~=0.5.8b5; extra == "mcp-service"
30
+ Requires-Dist: meshagent-tools~=0.5.8b5; extra == "mcp-service"
31
+ Requires-Dist: supabase-auth~=2.12.3; extra == "mcp-service"
30
32
 
31
33
  # [Meshagent](https://www.meshagent.com)
32
34
 
@@ -12,11 +12,13 @@ meshagent/cli/cli.py
12
12
  meshagent/cli/cli_mcp.py
13
13
  meshagent/cli/cli_secrets.py
14
14
  meshagent/cli/common_options.py
15
+ meshagent/cli/containers.py
15
16
  meshagent/cli/developer.py
16
17
  meshagent/cli/exec.py
17
18
  meshagent/cli/helper.py
18
19
  meshagent/cli/mailbot.py
19
20
  meshagent/cli/messaging.py
21
+ meshagent/cli/oauth2.py
20
22
  meshagent/cli/otel.py
21
23
  meshagent/cli/participant_token.py
22
24
  meshagent/cli/projects.py
@@ -0,0 +1,23 @@
1
+ typer~=0.15
2
+ supabase~=2.15
3
+ fastmcp~=2.8
4
+ opentelemetry-distro~=0.54b1
5
+ opentelemetry-exporter-otlp-proto-http~=1.33
6
+ art~=6.5
7
+ pydantic-yaml~=1.5
8
+
9
+ [all]
10
+ meshagent-agents[all]~=0.5.8b5
11
+ meshagent-api[all]~=0.5.8b5
12
+ meshagent-computers~=0.5.8b5
13
+ meshagent-openai~=0.5.8b5
14
+ meshagent-mcp~=0.5.8b5
15
+ meshagent-tools~=0.5.8b5
16
+ supabase-auth~=2.12.3
17
+
18
+ [mcp-service]
19
+ meshagent-agents[all]~=0.5.8b5
20
+ meshagent-api~=0.5.8b5
21
+ meshagent-mcp~=0.5.8b5
22
+ meshagent-tools~=0.5.8b5
23
+ supabase-auth~=2.12.3
@@ -23,19 +23,21 @@ dynamic = ["version", "readme"]
23
23
  [project.optional-dependencies]
24
24
 
25
25
  all = [
26
- "meshagent-agents[all]~=0.5.8b4",
27
- "meshagent-api[all]~=0.5.8b4",
28
- "meshagent-computers~=0.5.8b4",
29
- "meshagent-openai~=0.5.8b4",
30
- "meshagent-mcp~=0.5.8b4",
31
- "meshagent-tools~=0.5.8b4"
26
+ "meshagent-agents[all]~=0.5.8b5",
27
+ "meshagent-api[all]~=0.5.8b5",
28
+ "meshagent-computers~=0.5.8b5",
29
+ "meshagent-openai~=0.5.8b5",
30
+ "meshagent-mcp~=0.5.8b5",
31
+ "meshagent-tools~=0.5.8b5",
32
+ "supabase-auth~=2.12.3",
32
33
  ]
33
34
 
34
35
  mcp-service = [
35
- "meshagent-agents[all]~=0.5.8b4",
36
- "meshagent-api~=0.5.8b4",
37
- "meshagent-mcp~=0.5.8b4",
38
- "meshagent-tools~=0.5.8b4"
36
+ "meshagent-agents[all]~=0.5.8b5",
37
+ "meshagent-api~=0.5.8b5",
38
+ "meshagent-mcp~=0.5.8b5",
39
+ "meshagent-tools~=0.5.8b5",
40
+ "supabase-auth~=2.12.3",
39
41
  ]
40
42
 
41
43
  [project.scripts]
@@ -1 +0,0 @@
1
- __version__ = "0.5.8b4"
@@ -1,21 +0,0 @@
1
- typer~=0.15
2
- supabase~=2.15
3
- fastmcp~=2.8
4
- opentelemetry-distro~=0.54b1
5
- opentelemetry-exporter-otlp-proto-http~=1.33
6
- art~=6.5
7
- pydantic-yaml~=1.5
8
-
9
- [all]
10
- meshagent-agents[all]~=0.5.8b4
11
- meshagent-api[all]~=0.5.8b4
12
- meshagent-computers~=0.5.8b4
13
- meshagent-openai~=0.5.8b4
14
- meshagent-mcp~=0.5.8b4
15
- meshagent-tools~=0.5.8b4
16
-
17
- [mcp-service]
18
- meshagent-agents[all]~=0.5.8b4
19
- meshagent-api~=0.5.8b4
20
- meshagent-mcp~=0.5.8b4
21
- meshagent-tools~=0.5.8b4