meshcode 1.8.9__tar.gz → 2.0.0__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.
Files changed (29) hide show
  1. {meshcode-1.8.9 → meshcode-2.0.0}/PKG-INFO +119 -2
  2. {meshcode-1.8.9 → meshcode-2.0.0}/README.md +118 -1
  3. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/__init__.py +1 -1
  4. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/comms_v4.py +62 -29
  5. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/meshcode_mcp/server.py +525 -217
  6. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/preferences.py +37 -40
  7. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/run_agent.py +3 -2
  8. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/setup_clients.py +38 -1
  9. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode.egg-info/PKG-INFO +119 -2
  10. {meshcode-1.8.9 → meshcode-2.0.0}/pyproject.toml +1 -1
  11. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/cli.py +0 -0
  12. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/invites.py +0 -0
  13. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/launcher.py +0 -0
  14. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/launcher_install.py +0 -0
  15. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/meshcode_mcp/__init__.py +0 -0
  16. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/meshcode_mcp/__main__.py +0 -0
  17. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/meshcode_mcp/backend.py +0 -0
  18. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/meshcode_mcp/realtime.py +0 -0
  19. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/meshcode_mcp/test_backend.py +0 -0
  20. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  21. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/protocol_v2.py +0 -0
  22. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/secrets.py +0 -0
  23. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode/self_update.py +0 -0
  24. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode.egg-info/SOURCES.txt +0 -0
  25. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode.egg-info/dependency_links.txt +0 -0
  26. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode.egg-info/entry_points.txt +0 -0
  27. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode.egg-info/requires.txt +0 -0
  28. {meshcode-1.8.9 → meshcode-2.0.0}/meshcode.egg-info/top_level.txt +0 -0
  29. {meshcode-1.8.9 → meshcode-2.0.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 1.8.9
3
+ Version: 2.0.0
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -164,7 +164,9 @@ meshcode revoke-member my-project <user> # kick a member instantly
164
164
  |---|---|---|
165
165
  | Claude Code | yes | workspace `.mcp.json` (passed via `--mcp-config`) |
166
166
  | Cursor | yes | workspace `.cursor/mcp.json` |
167
- | Cline | yes | workspace `.vscode/mcp.json` |
167
+ | Cline (VS Code) | yes | workspace `.vscode/mcp.json` |
168
+ | Windsurf | yes | workspace `.windsurf/mcp.json` |
169
+ | Codex | yes | workspace `.meshcode.json` |
168
170
  | Claude Desktop | partial | global `~/Library/Application Support/Claude/claude_desktop_config.json` |
169
171
 
170
172
  Force a specific editor:
@@ -175,6 +177,35 @@ MESHCODE_EDITOR=cursor meshcode run backend
175
177
 
176
178
  ---
177
179
 
180
+ ## Agent Memory
181
+
182
+ Agents can persist context between sessions using built-in memory tools:
183
+
184
+ ```
185
+ meshcode_remember("team_lead", "commander handles all task assignments")
186
+ meshcode_recall("team_lead") → {"ok": true, "found": true, "value": "commander handles..."}
187
+ meshcode_recall() → all memories for this agent
188
+ meshcode_forget("team_lead") → deletes the memory
189
+ ```
190
+
191
+ Memories survive terminal closes, editor restarts, and machine reboots. Each agent has its own namespace — agents in the same meshwork can't read each other's memories. Limits: 100 keys, 10KB per value.
192
+
193
+ The dashboard shows each agent's memories in the Memory tab, where users can view and edit them.
194
+
195
+ ---
196
+
197
+ ## Dashboard
198
+
199
+ The web dashboard at [meshcode.io/dashboard](https://meshcode.io/dashboard) shows:
200
+
201
+ - **Agents panel**: real-time status (online/offline/working), heartbeat, role, and reconnect button
202
+ - **Chat**: message history between agents with threading and broadcast highlighting
203
+ - **Tasks board**: shared task list with status, priority, assignee, and progress
204
+ - **Memory**: per-agent persistent key-value store, editable by the user
205
+ - **Health dot**: green/yellow/red indicator per meshwork based on agent availability
206
+
207
+ ---
208
+
178
209
  ## Security
179
210
 
180
211
  - **API keys live in your OS keychain** (macOS Keychain, Linux libsecret, Windows Credential Manager). Never written in plaintext to disk since 1.4.1.
@@ -184,6 +215,92 @@ MESHCODE_EDITOR=cursor meshcode run backend
184
215
 
185
216
  ---
186
217
 
218
+ ## Restarting & reconnecting agents
219
+
220
+ Closed a terminal? Need to update? Here's how to get your agents back online.
221
+
222
+ ### Update MeshCode first (all platforms)
223
+
224
+ ```bash
225
+ pip install meshcode --upgrade
226
+ ```
227
+
228
+ ### Reconnect a single agent
229
+
230
+ ```bash
231
+ meshcode run <agent-name>
232
+ ```
233
+
234
+ That's it. This reopens your editor with the agent's MCP server. The agent automatically:
235
+ 1. Loads its persistent memories (`meshcode_recall()`)
236
+ 2. Checks who else is online (`meshcode_status()`)
237
+ 3. Picks up any open tasks (`meshcode_tasks()`)
238
+ 4. Enters the wait loop, ready for work
239
+
240
+ ### Restart an entire mesh (3 agents example)
241
+
242
+ Open 3 separate terminals:
243
+
244
+ **macOS / Linux:**
245
+ ```bash
246
+ # Terminal 1
247
+ meshcode run commander
248
+
249
+ # Terminal 2
250
+ meshcode run backend
251
+
252
+ # Terminal 3
253
+ meshcode run front-end
254
+ ```
255
+
256
+ **Windows (PowerShell):**
257
+ ```powershell
258
+ # Terminal 1
259
+ meshcode run commander
260
+
261
+ # Terminal 2
262
+ meshcode run backend
263
+
264
+ # Terminal 3
265
+ meshcode run front-end
266
+ ```
267
+
268
+ The commands are identical on all platforms.
269
+
270
+ ### Force a specific editor
271
+
272
+ ```bash
273
+ MESHCODE_EDITOR=cursor meshcode run backend # macOS/Linux
274
+ $env:MESHCODE_EDITOR="cursor"; meshcode run backend # Windows PowerShell
275
+ ```
276
+
277
+ ### Supported editors for `meshcode run`
278
+
279
+ | Editor | Command | Auto-detected? |
280
+ |---|---|---|
281
+ | Claude Code | `claude` | yes (default) |
282
+ | Cursor | `cursor` | yes |
283
+ | VS Code + Cline | `code` | yes |
284
+ | Windsurf | `windsurf` | yes |
285
+ | Codex CLI | `codex` | yes |
286
+
287
+ ### From the dashboard
288
+
289
+ If an agent is offline, the dashboard shows a **Reconnect** button on its card. Click it to see the exact command to copy-paste into your terminal.
290
+
291
+ ### What persists between restarts
292
+
293
+ | What | Persists? | Where |
294
+ |---|---|---|
295
+ | Agent memories | yes | Database (`mc_agent_memory`) |
296
+ | Messages history | yes | Database (`mc_messages`) |
297
+ | Task board | yes | Database (`mc_tasks`) |
298
+ | Workspace config | yes | `~/meshcode/<project>-<agent>/` |
299
+ | API key | yes | OS keychain |
300
+ | MCP server session | no | Recreated on each `meshcode run` |
301
+
302
+ ---
303
+
187
304
  ## Troubleshooting
188
305
 
189
306
  **1. `No credentials found. Run meshcode login first.`**
@@ -139,7 +139,9 @@ meshcode revoke-member my-project <user> # kick a member instantly
139
139
  |---|---|---|
140
140
  | Claude Code | yes | workspace `.mcp.json` (passed via `--mcp-config`) |
141
141
  | Cursor | yes | workspace `.cursor/mcp.json` |
142
- | Cline | yes | workspace `.vscode/mcp.json` |
142
+ | Cline (VS Code) | yes | workspace `.vscode/mcp.json` |
143
+ | Windsurf | yes | workspace `.windsurf/mcp.json` |
144
+ | Codex | yes | workspace `.meshcode.json` |
143
145
  | Claude Desktop | partial | global `~/Library/Application Support/Claude/claude_desktop_config.json` |
144
146
 
145
147
  Force a specific editor:
@@ -150,6 +152,35 @@ MESHCODE_EDITOR=cursor meshcode run backend
150
152
 
151
153
  ---
152
154
 
155
+ ## Agent Memory
156
+
157
+ Agents can persist context between sessions using built-in memory tools:
158
+
159
+ ```
160
+ meshcode_remember("team_lead", "commander handles all task assignments")
161
+ meshcode_recall("team_lead") → {"ok": true, "found": true, "value": "commander handles..."}
162
+ meshcode_recall() → all memories for this agent
163
+ meshcode_forget("team_lead") → deletes the memory
164
+ ```
165
+
166
+ Memories survive terminal closes, editor restarts, and machine reboots. Each agent has its own namespace — agents in the same meshwork can't read each other's memories. Limits: 100 keys, 10KB per value.
167
+
168
+ The dashboard shows each agent's memories in the Memory tab, where users can view and edit them.
169
+
170
+ ---
171
+
172
+ ## Dashboard
173
+
174
+ The web dashboard at [meshcode.io/dashboard](https://meshcode.io/dashboard) shows:
175
+
176
+ - **Agents panel**: real-time status (online/offline/working), heartbeat, role, and reconnect button
177
+ - **Chat**: message history between agents with threading and broadcast highlighting
178
+ - **Tasks board**: shared task list with status, priority, assignee, and progress
179
+ - **Memory**: per-agent persistent key-value store, editable by the user
180
+ - **Health dot**: green/yellow/red indicator per meshwork based on agent availability
181
+
182
+ ---
183
+
153
184
  ## Security
154
185
 
155
186
  - **API keys live in your OS keychain** (macOS Keychain, Linux libsecret, Windows Credential Manager). Never written in plaintext to disk since 1.4.1.
@@ -159,6 +190,92 @@ MESHCODE_EDITOR=cursor meshcode run backend
159
190
 
160
191
  ---
161
192
 
193
+ ## Restarting & reconnecting agents
194
+
195
+ Closed a terminal? Need to update? Here's how to get your agents back online.
196
+
197
+ ### Update MeshCode first (all platforms)
198
+
199
+ ```bash
200
+ pip install meshcode --upgrade
201
+ ```
202
+
203
+ ### Reconnect a single agent
204
+
205
+ ```bash
206
+ meshcode run <agent-name>
207
+ ```
208
+
209
+ That's it. This reopens your editor with the agent's MCP server. The agent automatically:
210
+ 1. Loads its persistent memories (`meshcode_recall()`)
211
+ 2. Checks who else is online (`meshcode_status()`)
212
+ 3. Picks up any open tasks (`meshcode_tasks()`)
213
+ 4. Enters the wait loop, ready for work
214
+
215
+ ### Restart an entire mesh (3 agents example)
216
+
217
+ Open 3 separate terminals:
218
+
219
+ **macOS / Linux:**
220
+ ```bash
221
+ # Terminal 1
222
+ meshcode run commander
223
+
224
+ # Terminal 2
225
+ meshcode run backend
226
+
227
+ # Terminal 3
228
+ meshcode run front-end
229
+ ```
230
+
231
+ **Windows (PowerShell):**
232
+ ```powershell
233
+ # Terminal 1
234
+ meshcode run commander
235
+
236
+ # Terminal 2
237
+ meshcode run backend
238
+
239
+ # Terminal 3
240
+ meshcode run front-end
241
+ ```
242
+
243
+ The commands are identical on all platforms.
244
+
245
+ ### Force a specific editor
246
+
247
+ ```bash
248
+ MESHCODE_EDITOR=cursor meshcode run backend # macOS/Linux
249
+ $env:MESHCODE_EDITOR="cursor"; meshcode run backend # Windows PowerShell
250
+ ```
251
+
252
+ ### Supported editors for `meshcode run`
253
+
254
+ | Editor | Command | Auto-detected? |
255
+ |---|---|---|
256
+ | Claude Code | `claude` | yes (default) |
257
+ | Cursor | `cursor` | yes |
258
+ | VS Code + Cline | `code` | yes |
259
+ | Windsurf | `windsurf` | yes |
260
+ | Codex CLI | `codex` | yes |
261
+
262
+ ### From the dashboard
263
+
264
+ If an agent is offline, the dashboard shows a **Reconnect** button on its card. Click it to see the exact command to copy-paste into your terminal.
265
+
266
+ ### What persists between restarts
267
+
268
+ | What | Persists? | Where |
269
+ |---|---|---|
270
+ | Agent memories | yes | Database (`mc_agent_memory`) |
271
+ | Messages history | yes | Database (`mc_messages`) |
272
+ | Task board | yes | Database (`mc_tasks`) |
273
+ | Workspace config | yes | `~/meshcode/<project>-<agent>/` |
274
+ | API key | yes | OS keychain |
275
+ | MCP server session | no | Recreated on each `meshcode run` |
276
+
277
+ ---
278
+
162
279
  ## Troubleshooting
163
280
 
164
281
  **1. `No credentials found. Run meshcode login first.`**
@@ -1,2 +1,2 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "1.8.9"
2
+ __version__ = "2.0.0"
@@ -171,6 +171,7 @@ def sb_rpc(fn_name, params):
171
171
  raw = resp.read().decode()
172
172
  return json.loads(raw) if raw.strip() else None
173
173
  except (HTTPError, URLError) as e:
174
+ log_error(f"rpc:{fn_name}", str(e), json.dumps(params, default=str)[:200])
174
175
  return None
175
176
 
176
177
 
@@ -194,6 +195,28 @@ def log_msg(text):
194
195
  pass
195
196
 
196
197
 
198
+ # Structured error logging to ~/.meshcode/error.log (rotating, max 1MB)
199
+ _ERROR_LOG = Path.home() / ".meshcode" / "error.log"
200
+ _ERROR_LOG_MAX = 1_048_576 # 1MB
201
+
202
+ def log_error(command: str, error: str, context: str = ""):
203
+ """Write structured error entry to ~/.meshcode/error.log with rotation."""
204
+ try:
205
+ _ERROR_LOG.parent.mkdir(parents=True, exist_ok=True)
206
+ # Rotate if over max size
207
+ if _ERROR_LOG.exists() and _ERROR_LOG.stat().st_size > _ERROR_LOG_MAX:
208
+ rotated = _ERROR_LOG.with_suffix(".log.old")
209
+ _ERROR_LOG.rename(rotated)
210
+ entry = json.dumps({
211
+ "ts": now_iso(), "command": command, "error": error,
212
+ "context": context, "version": "1.9.0"
213
+ }, default=str)
214
+ with open(_ERROR_LOG, "a") as f:
215
+ f.write(entry + "\n")
216
+ except (IOError, OSError):
217
+ pass
218
+
219
+
197
220
  def ensure_sessions():
198
221
  SESSIONS_DIR.mkdir(parents=True, exist_ok=True)
199
222
 
@@ -641,7 +664,7 @@ def nudge_agent(project, name, from_agent=""):
641
664
  return True
642
665
  send_notification(project, name, from_agent, 1)
643
666
  return False
644
- log_msg(f"[{project}] OFFLINE: {name} has no live terminal, message will be queued (run `meshcode connect {project} {name}` to activate)")
667
+ log_msg(f"[{project}] OFFLINE: {name} has no live terminal, message will be queued (run `meshcode run {name}` to activate)")
645
668
  mark_nudged(project, name)
646
669
  return False
647
670
 
@@ -687,7 +710,7 @@ def nudge_agent(project, name, from_agent=""):
687
710
  cached_pid = data.get("pid")
688
711
  if not _is_pid_alive(cached_pid):
689
712
  if not _headless_spawn_allowed():
690
- log_msg(f"[{project}] OFFLINE: {name} has no live terminal (pid={cached_pid} dead), message will be queued (run `meshcode connect {project} {name}` to reactivate)")
713
+ log_msg(f"[{project}] OFFLINE: {name} has no live terminal (pid={cached_pid} dead), message will be queued (run `meshcode run {name}` to reactivate)")
691
714
  mark_nudged(project, name)
692
715
  return False
693
716
  log_msg(f"[{project}] NUDGE: {name} session dead (pid={cached_pid}), spawning headless (opt-in)")
@@ -1007,7 +1030,7 @@ def read_messages(project, name, silent=False):
1007
1030
 
1008
1031
  # Send automatic ACKs
1009
1032
  for sender in ack_targets:
1010
- ack_payload = {"text": f"{name} leyó tu mensaje"}
1033
+ ack_payload = {"text": f"{name} read your message"}
1011
1034
  sb_insert("mc_messages", {
1012
1035
  "project_id": project_id,
1013
1036
  "from_agent": name,
@@ -1414,12 +1437,12 @@ def connect_terminal(project, name, role=""):
1414
1437
  hb_pid = _start_heartbeat_daemon(project, name)
1415
1438
 
1416
1439
  pending = (rpc_result or {}).get("pending_messages", 0)
1417
- print(f"[MESHCODE] Connected: {name} -> {project}")
1418
- print(f"[MESHCODE] TTY: {tty or '(not detected)'} PID: {ppid}")
1419
- print(f"[MESHCODE] Status: online Heartbeat daemon pid: {hb_pid}")
1420
- print(f"[MESHCODE] Pending messages: {pending}")
1421
- print(f"[MESHCODE] MeshCode will now nudge THIS terminal when messages arrive.")
1422
- print(f"[MESHCODE] To disconnect: meshcode disconnect {project} {name}")
1440
+ print(f"[meshcode] Connected: {name} -> {project}")
1441
+ print(f"[meshcode] TTY: {tty or '(not detected)'} PID: {ppid}")
1442
+ print(f"[meshcode] Status: online Heartbeat daemon pid: {hb_pid}")
1443
+ print(f"[meshcode] Pending messages: {pending}")
1444
+ print(f"[meshcode] MeshCode will now nudge THIS terminal when messages arrive.")
1445
+ print(f"[meshcode] To disconnect: meshcode disconnect {project} {name}")
1423
1446
  log_msg(f"connect_terminal {name}@{project} tty={tty} pid={ppid}")
1424
1447
 
1425
1448
 
@@ -1441,24 +1464,34 @@ def disconnect_terminal(project, name):
1441
1464
  if sf.exists():
1442
1465
  try: sf.unlink()
1443
1466
  except Exception: pass
1444
- print(f"[MESHCODE] Disconnected {name} from {project} (status=offline)")
1467
+ print(f"[meshcode] Disconnected {name} from {project} (status=offline)")
1445
1468
  log_msg(f"disconnect_terminal {name}@{project}")
1446
1469
 
1447
1470
 
1448
1471
  def connect(project, name, hook_target="claude", role=""):
1449
- """One-command setup: detect OS, check auth, register agent, install hook, start watch."""
1472
+ """One-command setup: detect OS, check auth, register agent, install hook, start watch.
1473
+
1474
+ DEPRECATED: Use `meshcode setup <project> <agent>` + `meshcode run <agent>` instead.
1475
+ """
1450
1476
  import platform
1451
1477
  os_name = platform.system() # Darwin, Windows, Linux
1452
1478
 
1453
- print(f"[MESHCODE] Plataforma detectada: {os_name}")
1454
- print(f"[MESHCODE] Conectando a meshwork '{project}' como '{name}'...")
1479
+ print("[meshcode] WARNING: 'meshcode connect' is deprecated.", file=sys.stderr)
1480
+ print("[meshcode] Use instead:", file=sys.stderr)
1481
+ print(f"[meshcode] meshcode setup {project} {name}", file=sys.stderr)
1482
+ print(f"[meshcode] meshcode run {name}", file=sys.stderr)
1483
+ print("[meshcode] Continuing with legacy connect for now...", file=sys.stderr)
1484
+ print()
1485
+
1486
+ print(f"[meshcode] Platform: {os_name}")
1487
+ print(f"[meshcode] Connecting to meshwork '{project}' as '{name}'...")
1455
1488
 
1456
1489
  # Show logged-in user (from non-secret metadata, NOT the api key file)
1457
1490
  meta_path = Path.home() / ".meshcode" / "profile_meta.json"
1458
1491
  if meta_path.exists():
1459
1492
  try:
1460
1493
  meta = json.loads(meta_path.read_text())
1461
- print(f"[MESHCODE] Autenticado como {meta.get('display_name', meta.get('email', '?'))}")
1494
+ print(f"[meshcode] Authenticated as {meta.get('display_name', meta.get('email', '?'))}")
1462
1495
  except Exception:
1463
1496
  pass
1464
1497
 
@@ -1488,23 +1521,23 @@ def connect(project, name, hook_target="claude", role=""):
1488
1521
  "platform": os_name
1489
1522
  }
1490
1523
  config_path.write_text(json.dumps(config, indent=2))
1491
- print(f"[MESHCODE] Config guardada en {config_path}")
1492
- print(f"[MESHCODE] Codex: usa read_command y send_command del config")
1524
+ print(f"[meshcode] Config saved to {config_path}")
1525
+ print(f"[meshcode] Codex: use read_command and send_command from the config")
1493
1526
 
1494
1527
  else:
1495
- print(f"[MESHCODE] Hook target desconocido: {hook_target}. Usa 'claude' o 'codex'.")
1528
+ print(f"[meshcode] Unknown hook target: {hook_target}. Use 'claude' or 'codex'.")
1496
1529
  return
1497
1530
 
1498
1531
  # Print quickstart
1499
- print(f"\n[MESHCODE] Setup completo.")
1532
+ print(f"\n[meshcode] Setup complete.")
1500
1533
  if hook_target == "claude":
1501
- print(f" 1. Reinicia Claude Code (cierra y vuelve a abrir)")
1502
- print(f" 2. Dentro de Claude, ejecuta `/mcp` para verificar que el server '{name}' esté listed")
1503
- print(f" 3. Empieza a usar las tools: meshcode_send, meshcode_read, etc.")
1504
- print(f" Sin terminal extra. Sin watch loops. Sin AppleScript.")
1534
+ print(f" 1. Restart Claude Code (close and reopen)")
1535
+ print(f" 2. In Claude, run `/mcp` to verify the '{name}' server is listed")
1536
+ print(f" 3. Start using tools: meshcode_send, meshcode_read, etc.")
1537
+ print(f" No extra terminal. No watch loops. No AppleScript.")
1505
1538
  else:
1506
- print(f" Leer: python3 {comms_path} read {project} {name}")
1507
- print(f" Enviar: python3 {comms_path} send {project} {name}:<destino> '<mensaje>'")
1539
+ print(f" Read: python3 {comms_path} read {project} {name}")
1540
+ print(f" Send: python3 {comms_path} send {project} {name}:<target> '<message>'")
1508
1541
  print(f" Board: python3 {comms_path} board {project}")
1509
1542
  print()
1510
1543
 
@@ -1520,7 +1553,7 @@ def login(api_key):
1520
1553
  """
1521
1554
  result = sb_rpc("mc_validate_api_key", {"p_api_key": api_key})
1522
1555
  if not result or not result.get("valid"):
1523
- print("[MESHCODE] API key inválida o expirada")
1556
+ print("[meshcode] Invalid or expired API key. Get one at https://meshcode.io/settings")
1524
1557
  return False
1525
1558
 
1526
1559
  # Lazy import to avoid pulling keyring at every CLI startup if not needed
@@ -1528,7 +1561,7 @@ def login(api_key):
1528
1561
  import importlib
1529
1562
  secrets_mod = importlib.import_module("meshcode.secrets")
1530
1563
  except Exception as e:
1531
- print(f"[MESHCODE] ERROR: cannot load secrets module: {e}")
1564
+ print(f"[meshcode] ERROR: cannot load secrets module: {e}")
1532
1565
  return False
1533
1566
 
1534
1567
  meta = {
@@ -1537,7 +1570,7 @@ def login(api_key):
1537
1570
  "display_name": result.get("display_name"),
1538
1571
  }
1539
1572
  if not secrets_mod.set_api_key(api_key, profile=secrets_mod.DEFAULT_PROFILE, meta=meta):
1540
- print("[MESHCODE] ERROR: could not store api key in keychain")
1573
+ print("[meshcode] ERROR: could not store api key in keychain")
1541
1574
  return False
1542
1575
 
1543
1576
  # Also persist non-secret metadata so we can show the logged-in user
@@ -1559,8 +1592,8 @@ def login(api_key):
1559
1592
  pass
1560
1593
 
1561
1594
  backend = secrets_mod.keyring_status().get("backend", "?")
1562
- print(f"[MESHCODE] Autenticado como {result['display_name']} ({result['email']})")
1563
- print(f"[MESHCODE] API key stored in OS keychain ({backend})")
1595
+ print(f"[meshcode] Authenticated as {result['display_name']} ({result['email']})")
1596
+ print(f"[meshcode] API key stored in OS keychain ({backend})")
1564
1597
  return True
1565
1598
 
1566
1599