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

meshagent/cli/chatbot.py CHANGED
@@ -1,36 +1,38 @@
1
1
  import typer
2
2
  from rich import print
3
3
  from typing import Annotated, Optional
4
+ from meshagent.tools.storage import StorageToolkitBuilder
4
5
  from meshagent.cli.common_options import (
5
6
  ProjectIdOption,
6
- ApiKeyIdOption,
7
7
  RoomOption,
8
8
  )
9
- from meshagent.tools import Toolkit
10
- from meshagent.api import RoomClient, WebSocketClientProtocol
9
+ from meshagent.api import (
10
+ RoomClient,
11
+ WebSocketClientProtocol,
12
+ ParticipantToken,
13
+ ApiScope,
14
+ )
11
15
  from meshagent.api.helpers import meshagent_base_url, websocket_room_url
12
16
  from meshagent.cli import async_typer
13
17
  from meshagent.cli.helper import (
14
18
  get_client,
15
19
  resolve_project_id,
16
- resolve_api_key,
17
- resolve_token_jwt,
18
20
  resolve_room,
21
+ resolve_key,
19
22
  )
20
- from meshagent.agents.chat import ChatBot
23
+
21
24
  from meshagent.openai import OpenAIResponsesAdapter
22
- from meshagent.api.services import ServiceHost
23
- from meshagent.computers.agent import ComputerAgent
24
- from meshagent.agents.chat import (
25
- ChatBotThreadOpenAIImageGenerationTool,
26
- ChatBotThreadLocalShellTool,
27
- )
25
+
28
26
 
29
27
  from typing import List
30
28
  from pathlib import Path
31
29
 
32
- from meshagent.openai.tools.responses_adapter import WebSearchTool
30
+ from meshagent.openai.tools.responses_adapter import (
31
+ WebSearchToolkitBuilder,
32
+ MCPToolkitBuilder,
33
+ )
33
34
  from meshagent.api import RequiredToolkit, RequiredSchema
35
+ from meshagent.api.services import ServiceHost
34
36
 
35
37
  app = async_typer.AsyncTyper(help="Join a chatbot to a room")
36
38
 
@@ -46,8 +48,17 @@ def build_chatbot(
46
48
  local_shell: bool,
47
49
  computer_use: bool,
48
50
  web_search: bool,
51
+ mcp: bool,
52
+ storage: bool,
49
53
  rules_file: Optional[str] = None,
50
54
  ):
55
+ from meshagent.agents.chat import ChatBot
56
+
57
+ from meshagent.agents.chat import (
58
+ ChatBotThreadOpenAIImageGenerationToolkitBuilder,
59
+ ChatBotThreadLocalShellToolkitBuilder,
60
+ )
61
+
51
62
  requirements = []
52
63
 
53
64
  toolkits = []
@@ -67,8 +78,13 @@ def build_chatbot(
67
78
 
68
79
  BaseClass = ChatBot
69
80
  if computer_use:
70
- BaseClass = ComputerAgent
81
+ from meshagent.computers.agent import ComputerAgent
71
82
 
83
+ if ComputerAgent is None:
84
+ raise RuntimeError(
85
+ "Computer use is enabled, but meshagent.computers is not installed."
86
+ )
87
+ BaseClass = ComputerAgent
72
88
  llm_adapter = OpenAIResponsesAdapter(
73
89
  model=model,
74
90
  response_options={
@@ -77,7 +93,9 @@ def build_chatbot(
77
93
  },
78
94
  )
79
95
  else:
80
- llm_adapter = OpenAIResponsesAdapter(model=model)
96
+ llm_adapter = OpenAIResponsesAdapter(
97
+ model=model,
98
+ )
81
99
 
82
100
  class CustomChatbot(BaseClass):
83
101
  def __init__(self):
@@ -89,33 +107,31 @@ def build_chatbot(
89
107
  rules=rule if len(rule) > 0 else None,
90
108
  )
91
109
 
92
- async def get_thread_toolkits(self, *, thread_context, participant):
93
- toolkits = await super().get_thread_toolkits(
94
- thread_context=thread_context, participant=participant
95
- )
110
+ async def get_thread_toolkit_builders(self, *, thread_context, participant):
111
+ providers = []
96
112
 
97
- thread_toolkit = Toolkit(name="thread_toolkit", tools=[])
113
+ if image_generation:
114
+ providers.append(
115
+ ChatBotThreadOpenAIImageGenerationToolkitBuilder(
116
+ thread_context=thread_context
117
+ )
118
+ )
98
119
 
99
120
  if local_shell:
100
- thread_toolkit.tools.append(
101
- ChatBotThreadLocalShellTool(thread_context=thread_context)
121
+ providers.append(
122
+ ChatBotThreadLocalShellToolkitBuilder(thread_context=thread_context)
102
123
  )
103
124
 
104
- if image_generation is not None:
105
- print("adding openai image gen to thread", flush=True)
106
- thread_toolkit.tools.append(
107
- ChatBotThreadOpenAIImageGenerationTool(
108
- model=image_generation,
109
- thread_context=thread_context,
110
- partial_images=3,
111
- )
112
- )
125
+ if mcp:
126
+ providers.append(MCPToolkitBuilder())
113
127
 
114
128
  if web_search:
115
- thread_toolkit.tools.append(WebSearchTool())
129
+ providers.append(WebSearchToolkitBuilder())
130
+
131
+ if storage:
132
+ providers.append(StorageToolkitBuilder())
116
133
 
117
- toolkits.append(thread_toolkit)
118
- return toolkits
134
+ return providers
119
135
 
120
136
  return CustomChatbot
121
137
 
@@ -124,11 +140,9 @@ def build_chatbot(
124
140
  async def make_call(
125
141
  *,
126
142
  project_id: ProjectIdOption = None,
127
- room: RoomOption = None,
128
- api_key_id: ApiKeyIdOption = None,
143
+ room: RoomOption,
129
144
  role: str = "agent",
130
145
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
131
- token_path: Annotated[Optional[str], typer.Option()] = None,
132
146
  rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
133
147
  rules_file: Optional[str] = None,
134
148
  toolkit: Annotated[
@@ -141,7 +155,7 @@ async def make_call(
141
155
  ] = [],
142
156
  model: Annotated[
143
157
  str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
144
- ] = "gpt-4o",
158
+ ] = "gpt-5",
145
159
  image_generation: Annotated[
146
160
  Optional[str], typer.Option(..., help="Name of an image gen model")
147
161
  ] = None,
@@ -157,21 +171,34 @@ async def make_call(
157
171
  web_search: Annotated[
158
172
  Optional[bool], typer.Option(..., help="Enable web search tool calling")
159
173
  ] = False,
174
+ mcp: Annotated[
175
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
176
+ ] = False,
177
+ storage: Annotated[
178
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
179
+ ] = False,
180
+ key: Annotated[
181
+ str,
182
+ typer.Option("--key", help="an api key to sign the token with"),
183
+ ] = None,
160
184
  ):
185
+ key = await resolve_key(project_id=project_id, key=key)
161
186
  account_client = await get_client()
162
187
  try:
163
188
  project_id = await resolve_project_id(project_id=project_id)
164
- api_key_id = await resolve_api_key(project_id, api_key_id)
165
189
  room = resolve_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,
190
+
191
+ token = ParticipantToken(
170
192
  name=agent_name,
171
- role=role,
172
- room=room,
173
193
  )
174
194
 
195
+ token.add_api_grant(ApiScope.agent_default())
196
+
197
+ token.add_role_grant(role=role)
198
+ token.add_room_grant(room)
199
+
200
+ jwt = token.to_jwt(api_key=key)
201
+
175
202
  print("[bold green]Connecting to room...[/bold green]", flush=True)
176
203
  async with RoomClient(
177
204
  protocol=WebSocketClientProtocol(
@@ -198,6 +225,8 @@ async def make_call(
198
225
  image_generation=image_generation,
199
226
  web_search=web_search,
200
227
  rules_file=rules_file,
228
+ mcp=mcp,
229
+ storage=storage,
201
230
  )
202
231
 
203
232
  bot = CustomChatbot()
@@ -219,7 +248,6 @@ async def make_call(
219
248
  @app.async_command("service")
220
249
  async def service(
221
250
  *,
222
- room: RoomOption = None,
223
251
  agent_name: Annotated[str, typer.Option(..., help="Name of the agent to call")],
224
252
  rule: Annotated[List[str], typer.Option("--rule", "-r", help="a system rule")] = [],
225
253
  rules_file: Optional[str] = None,
@@ -233,7 +261,7 @@ async def service(
233
261
  ] = [],
234
262
  model: Annotated[
235
263
  str, typer.Option(..., help="Name of the LLM model to use for the chatbot")
236
- ] = "gpt-4o",
264
+ ] = "gpt-5",
237
265
  image_generation: Annotated[
238
266
  Optional[str], typer.Option(..., help="Name of an image gen model")
239
267
  ] = None,
@@ -249,12 +277,16 @@ async def service(
249
277
  web_search: Annotated[
250
278
  Optional[bool], typer.Option(..., help="Enable web search tool calling")
251
279
  ] = False,
280
+ mcp: Annotated[
281
+ Optional[bool], typer.Option(..., help="Enable mcp tool calling")
282
+ ] = False,
283
+ storage: Annotated[
284
+ Optional[bool], typer.Option(..., help="Enable storage toolkit")
285
+ ] = False,
252
286
  host: Annotated[Optional[str], typer.Option()] = None,
253
287
  port: Annotated[Optional[int], typer.Option()] = None,
254
288
  path: Annotated[str, typer.Option()] = "/agent",
255
289
  ):
256
- room = resolve_room(room)
257
-
258
290
  print("[bold green]Connecting to room...[/bold green]", flush=True)
259
291
 
260
292
  service = ServiceHost(host=host, port=port)
@@ -271,6 +303,8 @@ async def service(
271
303
  web_search=web_search,
272
304
  image_generation=image_generation,
273
305
  rules_file=rules_file,
306
+ mcp=mcp,
307
+ storage=storage,
274
308
  ),
275
309
  )
276
310
 
meshagent/cli/cli.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import typer
2
2
  import asyncio
3
- from typing import Optional
4
3
 
5
4
  from meshagent.cli import async_typer
6
5
 
@@ -22,10 +21,15 @@ from meshagent.cli import cli_mcp
22
21
  from meshagent.cli import chatbot
23
22
  from meshagent.cli import voicebot
24
23
  from meshagent.cli import mailbot
24
+ from meshagent.cli import containers
25
+ from meshagent.cli import oauth2
26
+ from meshagent.cli import helpers
27
+ from meshagent.cli import meeting_transcriber
25
28
  from meshagent.cli.exec import register as register_exec
26
29
  from meshagent.cli.version import __version__
30
+ from meshagent.cli.helper import get_active_api_key
31
+ from meshagent.otel import otel_config
27
32
 
28
- from meshagent.cli import otel
29
33
 
30
34
  from art import tprint
31
35
 
@@ -34,16 +38,15 @@ import logging
34
38
  import os
35
39
  import sys
36
40
  from pathlib import Path
37
- from meshagent.cli.helper import get_client, resolve_project_id, resolve_api_key
38
41
 
42
+ otel_config(service_name="meshagent-cli")
39
43
 
40
- otel.init(level=logging.INFO)
41
44
 
42
45
  # Turn down OpenAI logs, they are a bit noisy
43
46
  logging.getLogger("openai").setLevel(logging.ERROR)
44
47
  logging.getLogger("httpx").setLevel(logging.ERROR)
45
48
 
46
- app = async_typer.AsyncTyper()
49
+ app = async_typer.AsyncTyper(no_args_is_help=True)
47
50
  app.add_typer(call.app, name="call")
48
51
  app.add_typer(auth.app, name="auth")
49
52
  app.add_typer(projects.app, name="project")
@@ -62,6 +65,10 @@ app.add_typer(cli_mcp.app, name="mcp")
62
65
  app.add_typer(chatbot.app, name="chatbot")
63
66
  app.add_typer(voicebot.app, name="voicebot")
64
67
  app.add_typer(mailbot.app, name="mailbot")
68
+ app.add_typer(containers.app, name="container")
69
+ app.add_typer(oauth2.app, name="oauth2")
70
+ app.add_typer(helpers.app, name="helpers")
71
+ app.add_typer(meeting_transcriber.app, name="meeting-transcriber")
65
72
 
66
73
  register_exec(app)
67
74
 
@@ -142,66 +149,6 @@ def version():
142
149
  print(__version__)
143
150
 
144
151
 
145
- @app.command(
146
- "env",
147
- help="Generate shell commands to set meshagent environment variables.",
148
- )
149
- def env(
150
- shell: Optional[str] = typer.Option(
151
- None,
152
- "--shell",
153
- case_sensitive=False,
154
- help="bash | zsh | fish | powershell | cmd",
155
- ),
156
- unset: bool = typer.Option(
157
- False, "--unset", help="Output commands to unset the variables."
158
- ),
159
- ):
160
- """Print shell-specific exports/unsets for Docker environment variables."""
161
-
162
- async def command():
163
- nonlocal shell, unset
164
- shell = (shell or detect_shell()).lower()
165
- if shell not in SHELL_RENDERERS:
166
- typer.echo(f"Unsupported shell '{shell}'.", err=True)
167
- raise typer.Exit(code=1)
168
-
169
- client = await get_client()
170
- try:
171
- project_id = await resolve_project_id(project_id=None)
172
- api_key_id = await resolve_api_key(project_id=project_id, api_key_id=None)
173
-
174
- token = (
175
- await client.decrypt_project_api_key(
176
- project_id=project_id, id=api_key_id
177
- )
178
- )["token"]
179
- finally:
180
- await client.close()
181
-
182
- vars = {
183
- "MESHAGENT_PROJECT_ID": project_id,
184
- "MESHAGENT_KEY_ID": api_key_id,
185
- "MESHAGENT_SECRET": token,
186
- }
187
- if shell not in SHELL_RENDERERS:
188
- typer.echo(f"Unsupported shell '{shell}'.", err=True)
189
- raise typer.Exit(code=1)
190
-
191
- render = SHELL_RENDERERS[shell]
192
-
193
- for name, value in vars.items():
194
- typer.echo(render(name, value, unset))
195
-
196
- if not unset and shell in ("bash", "zsh"):
197
- typer.echo(
198
- "\n# Run this command to configure your current shell:\n"
199
- '# eval "$(meshagent env)"'
200
- )
201
-
202
- _run_async(command())
203
-
204
-
205
152
  @app.command("setup")
206
153
  def setup_command():
207
154
  """Perform initial login and project/api key activation."""
@@ -215,11 +162,20 @@ def setup_command():
215
162
  project_id = await projects.activate(None, interactive=True)
216
163
  if project_id is None:
217
164
  print("You have choosen to not activate a project. Exiting.")
218
- if project_id is not None:
219
- print("Activate an api-key...")
220
- api_key_id = await api_keys.activate(None, interactive=True)
221
- if api_key_id is None:
222
- print("You have choosen to not activate an api-key. Exiting.")
165
+ if (
166
+ project_id is not None
167
+ and await get_active_api_key(project_id=project_id) is None
168
+ ):
169
+ if typer.confirm(
170
+ "You do not have an active api key for this project. Would you like to create and activate a new api key?",
171
+ default=True,
172
+ ):
173
+ name = typer.prompt(
174
+ "Enter a name for your API Key (must be a unique name):"
175
+ )
176
+ await api_keys.create(
177
+ project_id=None, activate=True, silent=True, name=name
178
+ )
223
179
 
224
180
  _run_async(runner())
225
181
 
meshagent/cli/cli_mcp.py CHANGED
@@ -1,11 +1,7 @@
1
- from mcp.client.session import ClientSession
2
- from mcp.client.sse import sse_client
3
- from mcp.client.stdio import stdio_client, StdioServerParameters
4
-
5
1
  import typer
6
2
  from rich import print
7
3
  from typing import Annotated, Optional, List
8
- from meshagent.cli.common_options import ProjectIdOption, ApiKeyIdOption, RoomOption
4
+ from meshagent.cli.common_options import ProjectIdOption, RoomOption
9
5
 
10
6
  from meshagent.api.helpers import meshagent_base_url, websocket_room_url
11
7
  from meshagent.api import RoomClient, WebSocketClientProtocol, RoomException
@@ -13,21 +9,32 @@ from meshagent.cli import async_typer
13
9
  from meshagent.cli.helper import (
14
10
  get_client,
15
11
  resolve_project_id,
16
- resolve_api_key,
17
- resolve_token_jwt,
18
12
  resolve_room,
13
+ resolve_key,
19
14
  )
20
15
 
21
16
  from meshagent.tools.hosting import RemoteToolkit
22
17
 
23
- from meshagent.mcp import MCPToolkit
24
18
 
25
19
  from meshagent.api.services import ServiceHost
26
20
  import os
27
21
 
28
- from meshagent.cli.services import _kv_to_dict
29
22
  import shlex
30
23
 
24
+ from meshagent.api import ParticipantToken, ApiScope
25
+
26
+
27
+ def _kv_to_dict(pairs: List[str]) -> dict[str, str]:
28
+ """Convert ["A=1","B=2"] → {"A":"1","B":"2"}."""
29
+ out: dict[str, str] = {}
30
+ for p in pairs:
31
+ if "=" not in p:
32
+ raise typer.BadParameter(f"'{p}' must be KEY=VALUE")
33
+ k, v = p.split("=", 1)
34
+ out[k] = v
35
+ return out
36
+
37
+
31
38
  app = async_typer.AsyncTyper()
32
39
 
33
40
 
@@ -36,30 +43,41 @@ async def sse(
36
43
  *,
37
44
  project_id: ProjectIdOption = None,
38
45
  room: RoomOption,
39
- token_path: Annotated[Optional[str], typer.Option()] = None,
40
- api_key_id: ApiKeyIdOption = None,
41
46
  name: Annotated[str, typer.Option(..., help="Participant name")] = "cli",
42
47
  role: str = "tool",
43
48
  url: Annotated[str, typer.Option()],
44
49
  toolkit_name: Annotated[Optional[str], typer.Option()] = None,
50
+ key: Annotated[
51
+ str,
52
+ typer.Option("--key", help="an api key to sign the token with"),
53
+ ] = None,
45
54
  ):
55
+ from mcp.client.session import ClientSession
56
+ from mcp.client.sse import sse_client
57
+
58
+ from meshagent.mcp import MCPToolkit
59
+
60
+ key = await resolve_key(project_id=project_id, key=key)
61
+
46
62
  if toolkit_name is None:
47
63
  toolkit_name = "mcp"
48
64
 
49
65
  account_client = await get_client()
50
66
  try:
51
67
  project_id = await resolve_project_id(project_id=project_id)
52
- api_key_id = await resolve_api_key(project_id, api_key_id)
53
68
  room = resolve_room(room)
54
- jwt = await resolve_token_jwt(
55
- project_id=project_id,
56
- api_key_id=api_key_id,
57
- token_path=token_path,
69
+
70
+ token = ParticipantToken(
58
71
  name=name,
59
- role=role,
60
- room=room,
61
72
  )
62
73
 
74
+ token.add_api_grant(ApiScope.agent_default())
75
+
76
+ token.add_role_grant(role=role)
77
+ token.add_room_grant(room)
78
+
79
+ jwt = token.to_jwt(api_key=key)
80
+
63
81
  print("[bold green]Connecting to room...[/bold green]")
64
82
  async with RoomClient(
65
83
  protocol=WebSocketClientProtocol(
@@ -103,31 +121,42 @@ async def stdio(
103
121
  *,
104
122
  project_id: ProjectIdOption = None,
105
123
  room: RoomOption,
106
- token_path: Annotated[Optional[str], typer.Option()] = None,
107
- api_key_id: ApiKeyIdOption = None,
108
124
  name: Annotated[str, typer.Option(..., help="Participant name")] = "cli",
109
125
  role: str = "tool",
110
126
  command: Annotated[str, typer.Option()],
111
127
  toolkit_name: Annotated[Optional[str], typer.Option()] = None,
112
128
  env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
129
+ key: Annotated[
130
+ str,
131
+ typer.Option("--key", help="an api key to sign the token with"),
132
+ ] = None,
113
133
  ):
134
+ from mcp.client.session import ClientSession
135
+ from mcp.client.stdio import stdio_client, StdioServerParameters
136
+
137
+ from meshagent.mcp import MCPToolkit
138
+
139
+ key = await resolve_key(project_id=project_id, key=key)
140
+
114
141
  if toolkit_name is None:
115
142
  toolkit_name = "mcp"
116
143
 
117
144
  account_client = await get_client()
118
145
  try:
119
146
  project_id = await resolve_project_id(project_id=project_id)
120
- api_key_id = await resolve_api_key(project_id, api_key_id)
121
147
  room = resolve_room(room)
122
- jwt = await resolve_token_jwt(
123
- project_id=project_id,
124
- api_key_id=api_key_id,
125
- token_path=token_path,
148
+
149
+ token = ParticipantToken(
126
150
  name=name,
127
- role=role,
128
- room=room,
129
151
  )
130
152
 
153
+ token.add_api_grant(ApiScope.agent_default())
154
+
155
+ token.add_role_grant(role=role)
156
+ token.add_room_grant(room)
157
+
158
+ jwt = token.to_jwt(api_key=key)
159
+
131
160
  print("[bold green]Connecting to room...[/bold green]")
132
161
  async with RoomClient(
133
162
  protocol=WebSocketClientProtocol(
@@ -253,6 +282,11 @@ async def stdio_service(
253
282
  toolkit_name: Annotated[Optional[str], typer.Option()] = None,
254
283
  env: Annotated[List[str], typer.Option("--env", "-e", help="KEY=VALUE")] = [],
255
284
  ):
285
+ from mcp.client.session import ClientSession
286
+ from mcp.client.stdio import stdio_client, StdioServerParameters
287
+
288
+ from meshagent.mcp import MCPToolkit
289
+
256
290
  try:
257
291
  parsed_command = shlex.split(command)
258
292
 
@@ -8,7 +8,7 @@ from meshagent.cli.common_options import ProjectIdOption
8
8
 
9
9
  from meshagent.cli import async_typer
10
10
  from meshagent.cli.helper import get_client, print_json_table, resolve_project_id
11
- from meshagent.api.accounts_client import (
11
+ from meshagent.api.client import (
12
12
  PullSecret,
13
13
  KeysSecret,
14
14
  SecretLike,
@@ -14,18 +14,10 @@ ProjectIdOption = Annotated[
14
14
  ),
15
15
  ]
16
16
 
17
- ApiKeyIdOption = Annotated[
18
- Optional[str],
19
- typer.Option(
20
- "--api-key-id",
21
- help="A MeshAgent project API key id. If empty, the activated api key will be used.",
22
- ),
23
- ]
24
-
25
17
  RoomOption = Annotated[
26
- Optional[str],
18
+ str,
27
19
  typer.Option(
28
20
  "--room",
29
- help="Room name. If empty, the MESHAGENT_ROOM environment variable will be used.",
21
+ help="Room name",
30
22
  ),
31
23
  ]