meshcode 2.10.66__tar.gz → 2.10.70__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.
- {meshcode-2.10.66 → meshcode-2.10.70}/PKG-INFO +1 -1
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/__init__.py +1 -1
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/ascii_art.py +135 -2
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/server.py +120 -1
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/run_agent.py +16 -11
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.66 → meshcode-2.10.70}/pyproject.toml +1 -1
- {meshcode-2.10.66 → meshcode-2.10.70}/README.md +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/cli.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/compat.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/error_hints.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/exceptions.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/invites.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/launcher.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/preferences.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/quickstart.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/secrets.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/self_update.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/supervisor.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode/upload.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/setup.cfg +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_core.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_exceptions.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_sentinel.py +0 -0
- {meshcode-2.10.66 → meshcode-2.10.70}/tests/test_status_enum_coverage.py +0 -0
|
@@ -148,6 +148,135 @@ def generate_art(agent_name: str, meshwork_name: str = "", size: int = 7) -> str
|
|
|
148
148
|
return "\n".join(lines)
|
|
149
149
|
|
|
150
150
|
|
|
151
|
+
# ── Pixel Mascot renderer (Unicode block art) ──────────────────────
|
|
152
|
+
# Renders a small pixel character using Unicode half-block chars.
|
|
153
|
+
# Deterministic from agent name hash, like the identicon but cuter.
|
|
154
|
+
|
|
155
|
+
_MASCOT_BODY_TEMPLATES = {
|
|
156
|
+
"round": [
|
|
157
|
+
" ████ ",
|
|
158
|
+
" ██████ ",
|
|
159
|
+
"████████",
|
|
160
|
+
"████████",
|
|
161
|
+
" ██████ ",
|
|
162
|
+
" ████ ",
|
|
163
|
+
],
|
|
164
|
+
"square": [
|
|
165
|
+
"████████",
|
|
166
|
+
"██ ██",
|
|
167
|
+
"██ ██",
|
|
168
|
+
"██ ██",
|
|
169
|
+
"██ ██",
|
|
170
|
+
"████████",
|
|
171
|
+
],
|
|
172
|
+
"tall": [
|
|
173
|
+
" ████ ",
|
|
174
|
+
" ██████ ",
|
|
175
|
+
" ██████ ",
|
|
176
|
+
" ██████ ",
|
|
177
|
+
" ██████ ",
|
|
178
|
+
" ████ ",
|
|
179
|
+
" ████ ",
|
|
180
|
+
" ████ ",
|
|
181
|
+
],
|
|
182
|
+
"wide": [
|
|
183
|
+
"██████████",
|
|
184
|
+
"██ ██",
|
|
185
|
+
"██████████",
|
|
186
|
+
" ████████ ",
|
|
187
|
+
],
|
|
188
|
+
"triangle": [
|
|
189
|
+
" ██ ",
|
|
190
|
+
" ████ ",
|
|
191
|
+
" ██████ ",
|
|
192
|
+
"████████",
|
|
193
|
+
"████████",
|
|
194
|
+
" ██ ██ ",
|
|
195
|
+
],
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
_MASCOT_EYES = {
|
|
199
|
+
"dots": ("●", "●"),
|
|
200
|
+
"circles": ("◉", "◉"),
|
|
201
|
+
"angry": ("▼", "▼"),
|
|
202
|
+
"happy": ("◠", "◠"),
|
|
203
|
+
"sleepy": ("—", "—"),
|
|
204
|
+
"star": ("★", "★"),
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
_MASCOT_HATS = {
|
|
208
|
+
"none": [],
|
|
209
|
+
"hat": [" ▄▄ ", " ████ "],
|
|
210
|
+
"glasses": [], # applied as eye overlay
|
|
211
|
+
"antenna": [" | ", " (◉) "],
|
|
212
|
+
"headphones": [" ╔══╗ "],
|
|
213
|
+
"crown": [" ▲ ▲ ▲ ", " ████ "],
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def render_pixel_mascot(agent_name: str, mascot_config: dict = None) -> str:
|
|
218
|
+
"""Render a pixel mascot as Unicode art for terminal display.
|
|
219
|
+
|
|
220
|
+
If mascot_config is None, generates deterministic defaults from agent name.
|
|
221
|
+
"""
|
|
222
|
+
h = _hash_bytes(agent_name)
|
|
223
|
+
hi = _hash_int(agent_name)
|
|
224
|
+
|
|
225
|
+
if mascot_config is None or mascot_config.get("generated"):
|
|
226
|
+
body_types = list(_MASCOT_BODY_TEMPLATES.keys())
|
|
227
|
+
eye_styles = list(_MASCOT_EYES.keys())
|
|
228
|
+
body_type = body_types[hi % len(body_types)]
|
|
229
|
+
eye_style = eye_styles[(hi // 10) % len(eye_styles)]
|
|
230
|
+
accessory = "none"
|
|
231
|
+
equipped = []
|
|
232
|
+
else:
|
|
233
|
+
body_type = mascot_config.get("body_type", "round")
|
|
234
|
+
eye_style = mascot_config.get("eye_style", "dots")
|
|
235
|
+
accessory = mascot_config.get("accessory", "none")
|
|
236
|
+
equipped = mascot_config.get("equipped", [])
|
|
237
|
+
|
|
238
|
+
# Get body template
|
|
239
|
+
body = list(_MASCOT_BODY_TEMPLATES.get(body_type, _MASCOT_BODY_TEMPLATES["round"]))
|
|
240
|
+
|
|
241
|
+
# Add eyes (on the 2nd or 3rd row depending on body)
|
|
242
|
+
eye_row = min(1, len(body) - 1)
|
|
243
|
+
if len(body) > 3:
|
|
244
|
+
eye_row = 2
|
|
245
|
+
eyes = _MASCOT_EYES.get(eye_style, _MASCOT_EYES["dots"])
|
|
246
|
+
if eye_row < len(body):
|
|
247
|
+
row = list(body[eye_row])
|
|
248
|
+
# Place eyes at 1/3 and 2/3 positions
|
|
249
|
+
w = len(row)
|
|
250
|
+
left_eye = w // 3
|
|
251
|
+
right_eye = 2 * w // 3
|
|
252
|
+
if left_eye < w:
|
|
253
|
+
row[left_eye] = eyes[0]
|
|
254
|
+
if right_eye < w:
|
|
255
|
+
row[right_eye] = eyes[1]
|
|
256
|
+
body[eye_row] = "".join(row)
|
|
257
|
+
|
|
258
|
+
# Add hat/accessory on top
|
|
259
|
+
hat_lines = _MASCOT_HATS.get(accessory, [])
|
|
260
|
+
# Check equipped accessories for hat/crown
|
|
261
|
+
for item in equipped:
|
|
262
|
+
if "crown" in item:
|
|
263
|
+
hat_lines = _MASCOT_HATS.get("crown", [])
|
|
264
|
+
break
|
|
265
|
+
elif "hat" in item:
|
|
266
|
+
hat_lines = _MASCOT_HATS.get("hat", [])
|
|
267
|
+
break
|
|
268
|
+
elif "antenna" in item:
|
|
269
|
+
hat_lines = _MASCOT_HATS.get("antenna", [])
|
|
270
|
+
break
|
|
271
|
+
|
|
272
|
+
lines = hat_lines + body
|
|
273
|
+
|
|
274
|
+
# Add feet
|
|
275
|
+
lines.append(" ▀ ▀ ")
|
|
276
|
+
|
|
277
|
+
return "\n".join(f" {line}" for line in lines)
|
|
278
|
+
|
|
279
|
+
|
|
151
280
|
# ── ANSI colors ─────────────────────────────────────────────────────
|
|
152
281
|
COLORS = [
|
|
153
282
|
"\033[36m", "\033[35m", "\033[32m", "\033[33m", "\033[34m",
|
|
@@ -441,7 +570,8 @@ def get_tip(agent_name: str) -> str:
|
|
|
441
570
|
def render_welcome(agent_name: str, meshwork_name: str, ascii_art: str,
|
|
442
571
|
version: str = "", is_commander: bool = False,
|
|
443
572
|
role: str = "", stats: dict = None,
|
|
444
|
-
profile_color: str = None
|
|
573
|
+
profile_color: str = None,
|
|
574
|
+
mascot_config: dict = None) -> str:
|
|
445
575
|
h = _hash_bytes(agent_name)
|
|
446
576
|
# Use dashboard profile color if available, otherwise MD5 hash fallback
|
|
447
577
|
color = None
|
|
@@ -465,7 +595,10 @@ def render_welcome(agent_name: str, meshwork_name: str, ascii_art: str,
|
|
|
465
595
|
lines.append(f"{color}{BOLD} ╚══════════════════════════════════════════╝{RESET}")
|
|
466
596
|
lines.append("")
|
|
467
597
|
|
|
468
|
-
|
|
598
|
+
# Always use pixel mascot — generates deterministic defaults from agent name if no config
|
|
599
|
+
art_to_render = render_pixel_mascot(agent_name, mascot_config)
|
|
600
|
+
|
|
601
|
+
for line in art_to_render.split("\n"):
|
|
469
602
|
lines.append(f" {color}{line}{RESET}")
|
|
470
603
|
|
|
471
604
|
lines.append("")
|
|
@@ -1678,8 +1678,17 @@ def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
|
|
|
1678
1678
|
else:
|
|
1679
1679
|
payload = {"text": str(message)}
|
|
1680
1680
|
|
|
1681
|
-
#
|
|
1681
|
+
# Universal DM render guarantee: dashboard chat renderer keys on
|
|
1682
|
+
# payload.text. Structured-dict DMs without a text field render blank.
|
|
1682
1683
|
import json as _json
|
|
1684
|
+
if "text" not in payload:
|
|
1685
|
+
_synth = _json.dumps(
|
|
1686
|
+
{k: v for k, v in payload.items() if not k.startswith("_")},
|
|
1687
|
+
default=str, ensure_ascii=False,
|
|
1688
|
+
)
|
|
1689
|
+
payload["text"] = _synth if len(_synth) <= 280 else _synth[:279] + "…"
|
|
1690
|
+
|
|
1691
|
+
# Enforce message size limit — long content belongs in task descriptions
|
|
1683
1692
|
_payload_len = len(_json.dumps(payload, default=str))
|
|
1684
1693
|
if _payload_len > 2000:
|
|
1685
1694
|
return {"error": f"message too large ({_payload_len} chars). Use meshcode_task_create for long content. Messages must be structured JSON <2000 chars."}
|
|
@@ -1899,6 +1908,116 @@ def meshcode_read_message(msg_id: str) -> Dict[str, Any]:
|
|
|
1899
1908
|
}
|
|
1900
1909
|
|
|
1901
1910
|
|
|
1911
|
+
@mcp.tool()
|
|
1912
|
+
@with_working_status
|
|
1913
|
+
def meshcode_download_file(file_id: str) -> Dict[str, Any]:
|
|
1914
|
+
"""Download a file attachment from a mesh message.
|
|
1915
|
+
|
|
1916
|
+
Returns file content for text/JSON files, or saves to temp file and
|
|
1917
|
+
returns the path for images (Claude can view image files directly).
|
|
1918
|
+
|
|
1919
|
+
Args:
|
|
1920
|
+
file_id: UUID of the file (from message payload's file.file_id).
|
|
1921
|
+
"""
|
|
1922
|
+
import tempfile
|
|
1923
|
+
import urllib.request as _req
|
|
1924
|
+
import urllib.error as _uerr
|
|
1925
|
+
|
|
1926
|
+
api_key = _get_api_key()
|
|
1927
|
+
if not api_key:
|
|
1928
|
+
return {"error": "no api key", "error_code": "auth_failed"}
|
|
1929
|
+
|
|
1930
|
+
# Step 1: Get file metadata via RPC
|
|
1931
|
+
file_info = be.sb_rpc("mc_get_file_download", {
|
|
1932
|
+
"p_api_key": api_key,
|
|
1933
|
+
"p_file_id": file_id,
|
|
1934
|
+
})
|
|
1935
|
+
if not file_info or not file_info.get("ok"):
|
|
1936
|
+
return {"error": file_info.get("error", "file not found"), "error_code": "not_found"}
|
|
1937
|
+
|
|
1938
|
+
storage_path = file_info["storage_path"]
|
|
1939
|
+
bucket = file_info.get("bucket", "meshcode-files")
|
|
1940
|
+
mime_type = file_info.get("mime_type", "application/octet-stream")
|
|
1941
|
+
file_name = file_info.get("file_name", "download")
|
|
1942
|
+
|
|
1943
|
+
# Step 2: Download from Supabase Storage (using service-level anon key)
|
|
1944
|
+
sb_url = os.environ.get("SUPABASE_URL", be._sb_url if hasattr(be, '_sb_url') else "")
|
|
1945
|
+
sb_key = os.environ.get("SUPABASE_KEY", be._sb_key if hasattr(be, '_sb_key') else "")
|
|
1946
|
+
|
|
1947
|
+
if not sb_url or not sb_key:
|
|
1948
|
+
return {"error": "storage not configured", "error_code": "config_error"}
|
|
1949
|
+
|
|
1950
|
+
# URL-encode each path segment so filenames with spaces/unicode (the common
|
|
1951
|
+
# 400 cause when dashboard composer attaches files like "Screenshot 1.png")
|
|
1952
|
+
# don't produce a malformed Storage URL.
|
|
1953
|
+
from urllib.parse import quote as _quote
|
|
1954
|
+
_enc_path = "/".join(_quote(seg, safe="") for seg in storage_path.split("/") if seg)
|
|
1955
|
+
_enc_bucket = _quote(bucket, safe="")
|
|
1956
|
+
|
|
1957
|
+
def _try_download(url: str):
|
|
1958
|
+
req = _req.Request(
|
|
1959
|
+
url,
|
|
1960
|
+
headers={
|
|
1961
|
+
"apikey": sb_key,
|
|
1962
|
+
"Authorization": f"Bearer {sb_key}",
|
|
1963
|
+
},
|
|
1964
|
+
)
|
|
1965
|
+
with _req.urlopen(req, timeout=30) as resp:
|
|
1966
|
+
return resp.read()
|
|
1967
|
+
|
|
1968
|
+
# Try authenticated object endpoint first; fall back to public endpoint
|
|
1969
|
+
# for buckets that have public read enabled. Many 400/403 cases on private
|
|
1970
|
+
# buckets resolve when the bucket is configured as public-readable.
|
|
1971
|
+
auth_url = f"{sb_url}/storage/v1/object/{_enc_bucket}/{_enc_path}"
|
|
1972
|
+
public_url = f"{sb_url}/storage/v1/object/public/{_enc_bucket}/{_enc_path}"
|
|
1973
|
+
last_err = None
|
|
1974
|
+
content = None
|
|
1975
|
+
for _url in (auth_url, public_url):
|
|
1976
|
+
try:
|
|
1977
|
+
content = _try_download(_url)
|
|
1978
|
+
break
|
|
1979
|
+
except _uerr.HTTPError as e:
|
|
1980
|
+
last_err = f"HTTP {e.code} on {_url}: {e.reason}"
|
|
1981
|
+
continue
|
|
1982
|
+
except Exception as e:
|
|
1983
|
+
last_err = f"{type(e).__name__} on {_url}: {e}"
|
|
1984
|
+
continue
|
|
1985
|
+
if content is None:
|
|
1986
|
+
return {
|
|
1987
|
+
"error": f"download failed: {last_err}",
|
|
1988
|
+
"error_code": "download_error",
|
|
1989
|
+
"tried_urls": [auth_url, public_url],
|
|
1990
|
+
"storage_path": storage_path,
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
# Step 3: Return content based on type
|
|
1994
|
+
if mime_type.startswith("text/") or mime_type == "application/json":
|
|
1995
|
+
try:
|
|
1996
|
+
text = content.decode("utf-8")
|
|
1997
|
+
if mime_type == "application/json":
|
|
1998
|
+
return {"ok": True, "file_name": file_name, "mime_type": mime_type,
|
|
1999
|
+
"content": json.loads(text)}
|
|
2000
|
+
return {"ok": True, "file_name": file_name, "mime_type": mime_type,
|
|
2001
|
+
"content": text[:50000]} # cap at 50k chars
|
|
2002
|
+
except Exception:
|
|
2003
|
+
pass
|
|
2004
|
+
|
|
2005
|
+
# For images and binary: save to temp file, return path
|
|
2006
|
+
suffix = "." + file_name.rsplit(".", 1)[-1] if "." in file_name else ""
|
|
2007
|
+
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, prefix="meshcode_")
|
|
2008
|
+
tmp.write(content)
|
|
2009
|
+
tmp.close()
|
|
2010
|
+
|
|
2011
|
+
return {
|
|
2012
|
+
"ok": True,
|
|
2013
|
+
"file_name": file_name,
|
|
2014
|
+
"mime_type": mime_type,
|
|
2015
|
+
"size_bytes": len(content),
|
|
2016
|
+
"local_path": tmp.name,
|
|
2017
|
+
"note": "File saved to local_path. For images, use Read tool to view.",
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
|
|
1902
2021
|
def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
|
1903
2022
|
"""Return {reason, from} if the list contains a global_done signal, else None."""
|
|
1904
2023
|
for m in messages:
|
|
@@ -33,23 +33,24 @@ REGISTRY_PATH = WORKSPACES_ROOT / ".registry.json"
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
def _fetch_or_generate_art(agent: str, project: str) -> tuple:
|
|
36
|
-
"""Fetch ASCII art + role + profile color from server.
|
|
37
|
-
Returns (ascii_art, role_description, profile_color)."""
|
|
36
|
+
"""Fetch ASCII art + role + profile color + mascot config from server.
|
|
37
|
+
Returns (ascii_art, role_description, profile_color, mascot_config)."""
|
|
38
38
|
from .ascii_art import generate_art
|
|
39
39
|
try:
|
|
40
40
|
from .setup_clients import _load_supabase_env
|
|
41
41
|
import importlib
|
|
42
42
|
secrets_mod = importlib.import_module("meshcode.secrets")
|
|
43
43
|
except Exception:
|
|
44
|
-
return generate_art(agent), agent, None
|
|
44
|
+
return generate_art(agent), agent, None, None
|
|
45
45
|
|
|
46
46
|
profile = os.environ.get("MESHCODE_KEYCHAIN_PROFILE") or "default"
|
|
47
47
|
api_key = secrets_mod.get_api_key(profile=profile)
|
|
48
48
|
if not api_key:
|
|
49
|
-
return generate_art(agent), agent, None
|
|
49
|
+
return generate_art(agent), agent, None, None
|
|
50
50
|
|
|
51
51
|
sb = _load_supabase_env()
|
|
52
52
|
profile_color = None
|
|
53
|
+
mascot_config = None
|
|
53
54
|
try:
|
|
54
55
|
from urllib.request import Request, urlopen
|
|
55
56
|
import urllib.parse
|
|
@@ -69,7 +70,7 @@ def _fetch_or_generate_art(agent: str, project: str) -> tuple:
|
|
|
69
70
|
with urlopen(proj_req, timeout=5) as resp:
|
|
70
71
|
proj_data = json.loads(resp.read().decode())
|
|
71
72
|
if not proj_data or not proj_data.get("project_id"):
|
|
72
|
-
return generate_art(agent), agent, None
|
|
73
|
+
return generate_art(agent), agent, None, None
|
|
73
74
|
project_id = proj_data["project_id"]
|
|
74
75
|
# Step 2: fetch existing art + role via mc_get_agents RPC
|
|
75
76
|
data = None
|
|
@@ -120,7 +121,7 @@ def _fetch_or_generate_art(agent: str, project: str) -> tuple:
|
|
|
120
121
|
if agent_id:
|
|
121
122
|
try:
|
|
122
123
|
prof_req = Request(
|
|
123
|
-
f"{sb['SUPABASE_URL']}/rest/v1/mc_agent_profiles?select=color&agent_id=eq.{agent_id}",
|
|
124
|
+
f"{sb['SUPABASE_URL']}/rest/v1/mc_agent_profiles?select=color,mascot_config&agent_id=eq.{agent_id}",
|
|
124
125
|
headers={
|
|
125
126
|
"apikey": sb["SUPABASE_KEY"],
|
|
126
127
|
"Authorization": f"Bearer {sb['SUPABASE_KEY']}",
|
|
@@ -129,12 +130,15 @@ def _fetch_or_generate_art(agent: str, project: str) -> tuple:
|
|
|
129
130
|
)
|
|
130
131
|
with urlopen(prof_req, timeout=5) as resp:
|
|
131
132
|
prof_data = json.loads(resp.read().decode())
|
|
132
|
-
if prof_data
|
|
133
|
-
|
|
133
|
+
if prof_data:
|
|
134
|
+
if prof_data[0].get("color"):
|
|
135
|
+
profile_color = prof_data[0]["color"]
|
|
136
|
+
if prof_data[0].get("mascot_config"):
|
|
137
|
+
mascot_config = prof_data[0]["mascot_config"]
|
|
134
138
|
except Exception:
|
|
135
139
|
pass
|
|
136
140
|
if data[0].get("ascii_art"):
|
|
137
|
-
return data[0]["ascii_art"], data[0].get("role") or agent, profile_color
|
|
141
|
+
return data[0]["ascii_art"], data[0].get("role") or agent, profile_color, mascot_config
|
|
138
142
|
except Exception:
|
|
139
143
|
pass
|
|
140
144
|
|
|
@@ -156,7 +160,7 @@ def _fetch_or_generate_art(agent: str, project: str) -> tuple:
|
|
|
156
160
|
urlopen(req, timeout=5)
|
|
157
161
|
except Exception:
|
|
158
162
|
pass
|
|
159
|
-
return art, agent, profile_color
|
|
163
|
+
return art, agent, profile_color, mascot_config
|
|
160
164
|
|
|
161
165
|
|
|
162
166
|
def _fetch_agent_stats(agent: str, project: str) -> dict:
|
|
@@ -784,7 +788,7 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
|
|
|
784
788
|
break
|
|
785
789
|
except Exception:
|
|
786
790
|
pass
|
|
787
|
-
ascii_art, agent_role, profile_color = _fetch_or_generate_art(agent, resolved_project)
|
|
791
|
+
ascii_art, agent_role, profile_color, mascot_cfg = _fetch_or_generate_art(agent, resolved_project)
|
|
788
792
|
_leader_haystack = (agent + ' ' + agent_role).lower()
|
|
789
793
|
_LEADER_KW = ('commander', 'lead', 'orchestrat', 'coordinator', 'coordinat',
|
|
790
794
|
'coordinad', 'jefe', 'líder', 'lider', 'director', 'manager',
|
|
@@ -795,6 +799,7 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
|
|
|
795
799
|
agent, resolved_project, ascii_art, cli_version,
|
|
796
800
|
is_commander=is_cmd, role=agent_role, stats=agent_stats,
|
|
797
801
|
profile_color=profile_color,
|
|
802
|
+
mascot_config=mascot_cfg,
|
|
798
803
|
), file=sys.stderr, flush=True)
|
|
799
804
|
except Exception as _banner_err:
|
|
800
805
|
import traceback as _tb
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|