vibego 0.2.54__py3-none-any.whl → 0.2.56__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.
bot.py CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- import asyncio, os, sys, time, uuid, shlex, subprocess, socket, re, json, shutil, hashlib, html
10
+ import asyncio, os, sys, time, uuid, shlex, subprocess, socket, re, json, shutil, hashlib, html, mimetypes
11
11
  from datetime import datetime, UTC
12
12
  from pathlib import Path
13
13
  from typing import Any, Dict, Optional, Sequence, Tuple, List, Callable, Awaitable, Literal
@@ -108,6 +108,19 @@ def _env_int(name: str, default: int) -> int:
108
108
  worker_log.warning("环境变量 %s=%r 解析为整数失败,已使用默认值 %s", name, raw, default)
109
109
  return default
110
110
 
111
+
112
+ def _env_float(name: str, default: float) -> float:
113
+ """读取浮点型环境变量,解析失败时回退默认值。"""
114
+
115
+ raw = os.environ.get(name)
116
+ if raw is None or not raw.strip():
117
+ return default
118
+ try:
119
+ return float(raw.strip())
120
+ except ValueError:
121
+ worker_log.warning("环境变量 %s=%r 解析为浮点数失败,已使用默认值 %s", name, raw, default)
122
+ return default
123
+
111
124
  _PARSE_MODE_CANDIDATES: Dict[str, Optional[ParseMode]] = {
112
125
  "": None,
113
126
  "none": None,
@@ -1290,6 +1303,450 @@ PROJECT_SLUG = (PROJECT_NAME or "default").replace("/", "-") or "default"
1290
1303
  TASK_DB_PATH = DATA_ROOT / f"{PROJECT_SLUG}.db"
1291
1304
  TASK_SERVICE = TaskService(TASK_DB_PATH, PROJECT_SLUG)
1292
1305
 
1306
+ ATTACHMENT_STORAGE_ROOT = (DATA_ROOT / "telegram").expanduser()
1307
+ ATTACHMENT_STORAGE_ROOT.mkdir(parents=True, exist_ok=True)
1308
+ _ATTACHMENT_TOTAL_MB = max(_env_int("TELEGRAM_ATTACHMENT_MAX_TOTAL_MB", 512), 16)
1309
+ ATTACHMENT_TOTAL_LIMIT_BYTES = _ATTACHMENT_TOTAL_MB * 1024 * 1024
1310
+ MEDIA_GROUP_AGGREGATION_DELAY = max(_env_float("TELEGRAM_MEDIA_GROUP_DELAY", 0.8), 0.1)
1311
+
1312
+
1313
+ @dataclass
1314
+ class TelegramSavedAttachment:
1315
+ """记录单个附件的落地信息,便于提示模型读取。"""
1316
+
1317
+ kind: str
1318
+ display_name: str
1319
+ mime_type: str
1320
+ absolute_path: Path
1321
+ relative_path: str
1322
+
1323
+
1324
+ @dataclass
1325
+ class PendingMediaGroupState:
1326
+ """聚合 Telegram 媒体组的临时缓存。"""
1327
+
1328
+ chat_id: int
1329
+ origin_message: Message
1330
+ attachment_dir: Path
1331
+ attachments: list[TelegramSavedAttachment]
1332
+ captions: list[str]
1333
+ finalize_task: Optional[asyncio.Task] = None
1334
+
1335
+
1336
+ MEDIA_GROUP_STATE: dict[str, PendingMediaGroupState] = {}
1337
+ MEDIA_GROUP_LOCK = asyncio.Lock()
1338
+
1339
+ ATTACHMENT_USAGE_HINT = (
1340
+ "请按需读取附件:图片可使用 Codex 的 view_image 功能或 Claude Code 的文件引用能力;"
1341
+ "文本/日志可直接通过 @<路径> 打开;若需其他处理请说明。"
1342
+ )
1343
+
1344
+ _FS_SAFE_PATTERN = re.compile(r"[^A-Za-z0-9._-]")
1345
+
1346
+
1347
+ def _sanitize_fs_component(value: str, fallback: str) -> str:
1348
+ """清理路径片段中的特殊字符,避免越权访问。"""
1349
+
1350
+ stripped = (value or "").strip()
1351
+ cleaned = _FS_SAFE_PATTERN.sub("_", stripped)
1352
+ cleaned = cleaned.strip("._")
1353
+ return cleaned or fallback
1354
+
1355
+
1356
+ def _format_relative_path(path: Path) -> str:
1357
+ """将绝对路径转换为模型更易识别的相对路径。"""
1358
+
1359
+ try:
1360
+ rel = path.relative_to(ROOT_DIR_PATH)
1361
+ rel_str = rel.as_posix()
1362
+ if not rel_str.startswith("."):
1363
+ return f"./{rel_str}"
1364
+ return rel_str
1365
+ except ValueError:
1366
+ return path.resolve().as_posix()
1367
+
1368
+
1369
+ def _directory_size(path: Path) -> int:
1370
+ """计算目录占用的总字节数。"""
1371
+
1372
+ total = 0
1373
+ if not path.exists():
1374
+ return 0
1375
+ for entry in path.rglob("*"):
1376
+ try:
1377
+ if entry.is_file():
1378
+ total += entry.stat().st_size
1379
+ except FileNotFoundError:
1380
+ continue
1381
+ return total
1382
+
1383
+
1384
+ def _cleanup_attachment_storage() -> None:
1385
+ """控制附件目录容量,避免磁盘被占满。"""
1386
+
1387
+ if ATTACHMENT_TOTAL_LIMIT_BYTES <= 0:
1388
+ return
1389
+ total = _directory_size(ATTACHMENT_STORAGE_ROOT)
1390
+ if total <= ATTACHMENT_TOTAL_LIMIT_BYTES:
1391
+ return
1392
+ candidates = sorted(
1393
+ (p for p in ATTACHMENT_STORAGE_ROOT.iterdir() if p.is_dir()),
1394
+ key=lambda item: item.stat().st_mtime,
1395
+ )
1396
+ for folder in candidates:
1397
+ try:
1398
+ shutil.rmtree(folder, ignore_errors=True)
1399
+ except Exception as exc: # noqa: BLE001
1400
+ worker_log.warning(
1401
+ "清理旧附件目录失败:%s",
1402
+ exc,
1403
+ extra=_session_extra(path=folder),
1404
+ )
1405
+ if _directory_size(ATTACHMENT_STORAGE_ROOT) <= ATTACHMENT_TOTAL_LIMIT_BYTES:
1406
+ break
1407
+
1408
+
1409
+ def _guess_extension(mime_type: Optional[str], fallback: str = ".bin") -> str:
1410
+ """根据 MIME 类型推断扩展名。"""
1411
+
1412
+ if mime_type:
1413
+ guessed = mimetypes.guess_extension(mime_type, strict=False)
1414
+ if guessed:
1415
+ return guessed
1416
+ return fallback
1417
+
1418
+
1419
+ def _attachment_dir_for_message(message: Message, media_group_id: Optional[str] = None) -> Path:
1420
+ """为当前消息生成附件目录,按项目标识 + 日期归档,便于模型定位。"""
1421
+
1422
+ # media_group_id 参数保留用于兼容旧调用,目前统一归档至日期目录。
1423
+ _ = media_group_id
1424
+
1425
+ # 优先使用项目 slug,回退到 bot 名称或通用前缀。
1426
+ project_identifier = PROJECT_SLUG or ""
1427
+ sanitized_project = _sanitize_fs_component(project_identifier, "project")
1428
+ if sanitized_project == "project":
1429
+ bot_username = getattr(message.bot, "username", None)
1430
+ sanitized_project = _sanitize_fs_component(bot_username or "bot", "bot")
1431
+
1432
+ # 使用消息时间(UTC)格式化日期,确保相同日期的附件集中存放。
1433
+ event_time = message.date or datetime.now(UTC)
1434
+ try:
1435
+ event_time = event_time.astimezone(UTC)
1436
+ except Exception:
1437
+ event_time = datetime.now(UTC)
1438
+ date_component = event_time.strftime("%Y-%m-%d")
1439
+
1440
+ target = ATTACHMENT_STORAGE_ROOT / sanitized_project / date_component
1441
+ target.mkdir(parents=True, exist_ok=True)
1442
+ return target
1443
+
1444
+
1445
+ async def _download_telegram_file(
1446
+ message: Message,
1447
+ *,
1448
+ file_id: str,
1449
+ file_name_hint: str,
1450
+ mime_type: Optional[str],
1451
+ target_dir: Path,
1452
+ ) -> Path:
1453
+ """从 Telegram 下载文件并返回本地路径。"""
1454
+
1455
+ bot = message.bot or current_bot()
1456
+ telegram_file = await bot.get_file(file_id)
1457
+ stem = _sanitize_fs_component(Path(file_name_hint).stem, "file")
1458
+ extension = Path(file_name_hint).suffix or _guess_extension(mime_type, ".bin")
1459
+ if not extension.startswith("."):
1460
+ extension = f".{extension}"
1461
+ filename = f"{stem}{extension}"
1462
+ destination = target_dir / filename
1463
+ counter = 1
1464
+ while destination.exists():
1465
+ destination = target_dir / f"{stem}_{counter}{extension}"
1466
+ counter += 1
1467
+ await bot.download_file(telegram_file.file_path, destination=destination)
1468
+ return destination
1469
+
1470
+
1471
+ async def _collect_saved_attachments(message: Message, target_dir: Path) -> list[TelegramSavedAttachment]:
1472
+ """下载消息中的所有附件,并返回保存记录。"""
1473
+
1474
+ saved: list[TelegramSavedAttachment] = []
1475
+
1476
+ if message.photo:
1477
+ photo = message.photo[-1]
1478
+ path = await _download_telegram_file(
1479
+ message,
1480
+ file_id=photo.file_id,
1481
+ file_name_hint=f"photo_{photo.file_unique_id}.jpg",
1482
+ mime_type="image/jpeg",
1483
+ target_dir=target_dir,
1484
+ )
1485
+ saved.append(
1486
+ TelegramSavedAttachment(
1487
+ kind="photo",
1488
+ display_name=path.name,
1489
+ mime_type="image/jpeg",
1490
+ absolute_path=path,
1491
+ relative_path=_format_relative_path(path),
1492
+ )
1493
+ )
1494
+
1495
+ document = message.document
1496
+ if document:
1497
+ file_name = document.file_name or f"document_{document.file_unique_id}"
1498
+ path = await _download_telegram_file(
1499
+ message,
1500
+ file_id=document.file_id,
1501
+ file_name_hint=file_name,
1502
+ mime_type=document.mime_type or "application/octet-stream",
1503
+ target_dir=target_dir,
1504
+ )
1505
+ saved.append(
1506
+ TelegramSavedAttachment(
1507
+ kind="document",
1508
+ display_name=file_name,
1509
+ mime_type=document.mime_type or "application/octet-stream",
1510
+ absolute_path=path,
1511
+ relative_path=_format_relative_path(path),
1512
+ )
1513
+ )
1514
+
1515
+ video = message.video
1516
+ if video:
1517
+ file_name = video.file_name or f"video_{video.file_unique_id}"
1518
+ path = await _download_telegram_file(
1519
+ message,
1520
+ file_id=video.file_id,
1521
+ file_name_hint=file_name,
1522
+ mime_type=video.mime_type or "video/mp4",
1523
+ target_dir=target_dir,
1524
+ )
1525
+ saved.append(
1526
+ TelegramSavedAttachment(
1527
+ kind="video",
1528
+ display_name=file_name,
1529
+ mime_type=video.mime_type or "video/mp4",
1530
+ absolute_path=path,
1531
+ relative_path=_format_relative_path(path),
1532
+ )
1533
+ )
1534
+
1535
+ audio = message.audio
1536
+ if audio:
1537
+ file_name = audio.file_name or f"audio_{audio.file_unique_id}"
1538
+ path = await _download_telegram_file(
1539
+ message,
1540
+ file_id=audio.file_id,
1541
+ file_name_hint=file_name,
1542
+ mime_type=audio.mime_type or "audio/mpeg",
1543
+ target_dir=target_dir,
1544
+ )
1545
+ saved.append(
1546
+ TelegramSavedAttachment(
1547
+ kind="audio",
1548
+ display_name=file_name,
1549
+ mime_type=audio.mime_type or "audio/mpeg",
1550
+ absolute_path=path,
1551
+ relative_path=_format_relative_path(path),
1552
+ )
1553
+ )
1554
+
1555
+ voice = message.voice
1556
+ if voice:
1557
+ file_name = f"voice_{voice.file_unique_id}.ogg"
1558
+ path = await _download_telegram_file(
1559
+ message,
1560
+ file_id=voice.file_id,
1561
+ file_name_hint=file_name,
1562
+ mime_type=voice.mime_type or "audio/ogg",
1563
+ target_dir=target_dir,
1564
+ )
1565
+ saved.append(
1566
+ TelegramSavedAttachment(
1567
+ kind="voice",
1568
+ display_name=file_name,
1569
+ mime_type=voice.mime_type or "audio/ogg",
1570
+ absolute_path=path,
1571
+ relative_path=_format_relative_path(path),
1572
+ )
1573
+ )
1574
+
1575
+ animation = message.animation
1576
+ if animation:
1577
+ file_name = animation.file_name or f"animation_{animation.file_unique_id}"
1578
+ path = await _download_telegram_file(
1579
+ message,
1580
+ file_id=animation.file_id,
1581
+ file_name_hint=file_name,
1582
+ mime_type=animation.mime_type or "video/mp4",
1583
+ target_dir=target_dir,
1584
+ )
1585
+ saved.append(
1586
+ TelegramSavedAttachment(
1587
+ kind="animation",
1588
+ display_name=file_name,
1589
+ mime_type=animation.mime_type or "video/mp4",
1590
+ absolute_path=path,
1591
+ relative_path=_format_relative_path(path),
1592
+ )
1593
+ )
1594
+
1595
+ video_note = message.video_note
1596
+ if video_note:
1597
+ file_name = f"video_note_{video_note.file_unique_id}.mp4"
1598
+ path = await _download_telegram_file(
1599
+ message,
1600
+ file_id=video_note.file_id,
1601
+ file_name_hint=file_name,
1602
+ mime_type=video_note.mime_type or "video/mp4",
1603
+ target_dir=target_dir,
1604
+ )
1605
+ saved.append(
1606
+ TelegramSavedAttachment(
1607
+ kind="video_note",
1608
+ display_name=file_name,
1609
+ mime_type=video_note.mime_type or "video/mp4",
1610
+ absolute_path=path,
1611
+ relative_path=_format_relative_path(path),
1612
+ )
1613
+ )
1614
+
1615
+ if saved:
1616
+ _cleanup_attachment_storage()
1617
+ return saved
1618
+
1619
+
1620
+ def _build_prompt_with_attachments(
1621
+ text_part: Optional[str],
1622
+ attachments: Sequence[TelegramSavedAttachment],
1623
+ ) -> str:
1624
+ """将文字与附件描述拼接成模型可读的提示。"""
1625
+
1626
+ sections: list[str] = []
1627
+ base_text = (text_part or "").strip()
1628
+ if base_text:
1629
+ sections.append(base_text)
1630
+ if attachments:
1631
+ lines = ["附件列表(文件位于项目工作目录,可直接读取):"]
1632
+ for idx, item in enumerate(attachments, 1):
1633
+ lines.append(
1634
+ f"{idx}. {item.display_name}({item.mime_type})→ {item.relative_path}"
1635
+ )
1636
+ lines.append("")
1637
+ lines.append(ATTACHMENT_USAGE_HINT)
1638
+ sections.append("\n".join(lines))
1639
+ if not sections:
1640
+ fallback = [
1641
+ "收到一条仅包含附件的消息,没有额外文字说明。",
1642
+ "请直接阅读列出的附件并给出观察结果或结论。",
1643
+ ]
1644
+ sections.append("\n".join(fallback))
1645
+ return "\n\n".join(sections).strip()
1646
+
1647
+
1648
+ async def _finalize_media_group_after_delay(media_group_id: str) -> None:
1649
+ """在短暂延迟后合并媒体组消息,确保 Telegram 全部照片到齐。"""
1650
+
1651
+ try:
1652
+ await asyncio.sleep(MEDIA_GROUP_AGGREGATION_DELAY)
1653
+ except asyncio.CancelledError:
1654
+ return
1655
+
1656
+ async with MEDIA_GROUP_LOCK:
1657
+ state = MEDIA_GROUP_STATE.pop(media_group_id, None)
1658
+
1659
+ if state is None:
1660
+ return
1661
+
1662
+ text_block = "\n".join(state.captions).strip()
1663
+ prompt = _build_prompt_with_attachments(text_block, state.attachments)
1664
+ try:
1665
+ await _handle_prompt_dispatch(state.origin_message, prompt)
1666
+ except Exception as exc: # noqa: BLE001
1667
+ worker_log.exception(
1668
+ "媒体组消息推送模型失败:%s",
1669
+ exc,
1670
+ extra=_session_extra(media_group=media_group_id),
1671
+ )
1672
+
1673
+
1674
+ async def _enqueue_media_group_message(message: Message, text_part: Optional[str]) -> None:
1675
+ """收集媒体组中的每一条消息,统一延迟推送。"""
1676
+
1677
+ media_group_id = message.media_group_id
1678
+ if not media_group_id:
1679
+ return
1680
+
1681
+ async with MEDIA_GROUP_LOCK:
1682
+ state = MEDIA_GROUP_STATE.get(media_group_id)
1683
+ if state is None:
1684
+ attachment_dir = _attachment_dir_for_message(message, media_group_id=media_group_id)
1685
+ state = PendingMediaGroupState(
1686
+ chat_id=message.chat.id,
1687
+ origin_message=message,
1688
+ attachment_dir=attachment_dir,
1689
+ attachments=[],
1690
+ captions=[],
1691
+ )
1692
+ MEDIA_GROUP_STATE[media_group_id] = state
1693
+ else:
1694
+ attachment_dir = state.attachment_dir
1695
+
1696
+ attachments = await _collect_saved_attachments(message, attachment_dir)
1697
+ caption = (text_part or "").strip()
1698
+
1699
+ async with MEDIA_GROUP_LOCK:
1700
+ state = MEDIA_GROUP_STATE.get(media_group_id)
1701
+ if state is None:
1702
+ # 若期间被清理,重新创建并继续积累,避免丢失后续内容。
1703
+ state = PendingMediaGroupState(
1704
+ chat_id=message.chat.id,
1705
+ origin_message=message,
1706
+ attachment_dir=attachment_dir,
1707
+ attachments=[],
1708
+ captions=[],
1709
+ )
1710
+ MEDIA_GROUP_STATE[media_group_id] = state
1711
+ state.attachments.extend(attachments)
1712
+ if caption:
1713
+ state.captions.append(caption)
1714
+ # 使用首条消息作为引用对象,便于 Telegram 回复。
1715
+ if state.origin_message.message_id > message.message_id:
1716
+ state.origin_message = message
1717
+ if state.finalize_task and not state.finalize_task.done():
1718
+ state.finalize_task.cancel()
1719
+ state.finalize_task = asyncio.create_task(_finalize_media_group_after_delay(media_group_id))
1720
+
1721
+
1722
+ async def _handle_prompt_dispatch(message: Message, prompt: str) -> None:
1723
+ """统一封装向模型推送提示词的流程。"""
1724
+
1725
+ if ENV_ISSUES:
1726
+ message_text = _format_env_issue_message()
1727
+ worker_log.warning(
1728
+ "拒绝处理消息,环境异常: %s",
1729
+ message_text,
1730
+ extra={**_session_extra(), "chat": message.chat.id},
1731
+ )
1732
+ await message.answer(message_text)
1733
+ return
1734
+
1735
+ bot = current_bot()
1736
+ await bot.send_chat_action(message.chat.id, "typing")
1737
+
1738
+ if MODE == "A":
1739
+ if not AGENT_CMD:
1740
+ await message.answer("AGENT_CMD 未配置(.env)")
1741
+ return
1742
+ rc, out = run_subprocess_capture(AGENT_CMD, input_text=prompt)
1743
+ out = out or ""
1744
+ out = out + ("" if rc == 0 else f"\n(exit={rc})")
1745
+ await reply_large_text(message.chat.id, out)
1746
+ return
1747
+
1748
+ await _dispatch_prompt_to_model(message.chat.id, prompt, reply_to=message)
1749
+
1293
1750
  BOT_COMMANDS: list[tuple[str, str]] = [
1294
1751
  ("help", "查看全部命令"),
1295
1752
  ("tasks", "任务命令清单"),
@@ -7390,6 +7847,28 @@ async def on_edit_new_value(message: Message, state: FSMContext) -> None:
7390
7847
  await _answer_with_markdown(message, f"任务已更新:\n{detail_text}", reply_markup=markup)
7391
7848
 
7392
7849
 
7850
+ @router.message(
7851
+ F.photo | F.document | F.video | F.audio | F.voice | F.animation | F.video_note
7852
+ )
7853
+ async def on_media_message(message: Message) -> None:
7854
+ """处理带附件的普通消息,将附件下载并拼接提示词。"""
7855
+
7856
+ _auto_record_chat_id(message.chat.id)
7857
+ text_part = (message.caption or message.text or "").strip()
7858
+
7859
+ if message.media_group_id:
7860
+ await _enqueue_media_group_message(message, text_part)
7861
+ return
7862
+
7863
+ attachment_dir = _attachment_dir_for_message(message)
7864
+ attachments = await _collect_saved_attachments(message, attachment_dir)
7865
+ if not attachments and not text_part:
7866
+ await message.answer("未检测到可处理的附件或文字内容。")
7867
+ return
7868
+ prompt = _build_prompt_with_attachments(text_part, attachments)
7869
+ await _handle_prompt_dispatch(message, prompt)
7870
+
7871
+
7393
7872
  @router.message(CommandStart())
7394
7873
  async def on_start(m: Message):
7395
7874
  # 首次收到消息时自动记录 chat_id 到 state 文件
@@ -7422,30 +7901,7 @@ async def on_text(m: Message):
7422
7901
  return
7423
7902
  if prompt.startswith("/"):
7424
7903
  return
7425
-
7426
- if ENV_ISSUES:
7427
- message = _format_env_issue_message()
7428
- worker_log.warning(
7429
- "拒绝处理消息,环境异常: %s",
7430
- message,
7431
- extra={**_session_extra(), "chat": m.chat.id},
7432
- )
7433
- await m.answer(message)
7434
- return
7435
-
7436
- bot = current_bot()
7437
- await bot.send_chat_action(m.chat.id, "typing") # “正在输入”提示
7438
-
7439
- if MODE == "A":
7440
- if not AGENT_CMD:
7441
- return await m.answer("AGENT_CMD 未配置(.env)")
7442
- rc, out = run_subprocess_capture(AGENT_CMD, input_text=prompt)
7443
- out = out or ""
7444
- out = out + ("" if rc == 0 else f"\n(exit={rc})")
7445
- await reply_large_text(m.chat.id, out)
7446
-
7447
- else:
7448
- await _dispatch_prompt_to_model(m.chat.id, prompt, reply_to=m)
7904
+ await _handle_prompt_dispatch(m, prompt)
7449
7905
 
7450
7906
 
7451
7907
  async def ensure_telegram_connectivity(bot: Bot, timeout: float = 30.0):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibego
3
- Version: 0.2.54
3
+ Version: 0.2.56
4
4
  Summary: vibego CLI:用于初始化与管理 Telegram Master Bot 的工具
5
5
  Author: Hypha
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,4 +1,4 @@
1
- bot.py,sha256=EEU9Z-X1QcsIN70nKNDA19zYGoJLt7BKWyQpDNTPtEU,274675
1
+ bot.py,sha256=61C8rX5QSAEW2k-uo07-1fsAkxzPHQU_pIK8sNv3Yno,290330
2
2
  logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
3
3
  master.py,sha256=Jwxf6I94jOADzb9Xio1wb-tWy5wgQ9PlmdpKW4mhQMg,117114
4
4
  project_repository.py,sha256=UcthtSGOJK0cTE5bQCneo3xkomRG-kyc1N1QVqxeHIs,17577
@@ -426,14 +426,14 @@ tasks/constants.py,sha256=tS1kZxBIUm3JJUMHm25XI-KHNUZl5NhbbuzjzL_rF-c,299
426
426
  tasks/fsm.py,sha256=rKXXLEieQQU4r2z_CZUvn1_70FXiZXBBugF40gpe_tQ,1476
427
427
  tasks/models.py,sha256=N_qqRBo9xMSV0vbn4k6bLBXT8C_dp_oTFUxvdx16ZQM,2459
428
428
  tasks/service.py,sha256=w_S_aWiVqRXzXEpimLDsuCCCX2lB5uDkff9aKThBw9c,41916
429
- vibego_cli/__init__.py,sha256=X2jkHBM3C13k76pNE_JPkacyuxp6z5r0XZwb58Ik-YY,311
429
+ vibego_cli/__init__.py,sha256=QFEe29DY2UJ5zPJ4ZaUjupZTUDyDW8bEYxjvErxtMr0,311
430
430
  vibego_cli/__main__.py,sha256=qqTrYmRRLe4361fMzbI3-CqpZ7AhTofIHmfp4ykrrBY,158
431
431
  vibego_cli/config.py,sha256=VxkPJMq01tA3h3cOkH-z_tiP7pMgfSGGicRvUnCWkhI,3054
432
432
  vibego_cli/deps.py,sha256=1nRXI7Dd-S1hYE8DligzK5fIluQWETRUj4_OKL0DikQ,1419
433
433
  vibego_cli/main.py,sha256=X__NXwZnIDIFbdKSTbNyZgZHKcPlN0DQz9sqTI1aQ9E,12158
434
434
  vibego_cli/data/worker_requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
435
- vibego-0.2.54.dist-info/METADATA,sha256=9wx4NUWLXvv6D4nSbVqwvmzyY0oKyILEknrZ8aY8egI,10519
436
- vibego-0.2.54.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
437
- vibego-0.2.54.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
438
- vibego-0.2.54.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
439
- vibego-0.2.54.dist-info/RECORD,,
435
+ vibego-0.2.56.dist-info/METADATA,sha256=2jRuQcO_K3wbOi441F0MHDxuJKJ0GB3t4UM9BzpzDck,10519
436
+ vibego-0.2.56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
437
+ vibego-0.2.56.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
438
+ vibego-0.2.56.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
439
+ vibego-0.2.56.dist-info/RECORD,,
vibego_cli/__init__.py CHANGED
@@ -7,6 +7,6 @@ from __future__ import annotations
7
7
 
8
8
  __all__ = ["main", "__version__"]
9
9
 
10
- __version__ = "0.2.54"
10
+ __version__ = "0.2.56"
11
11
 
12
12
  from .main import main # noqa: E402