معماری
‏aipager یک برنامهٔ asyncio تکپروسهای است که در پسزمینه اجرا میشود. سه دنیا را به هم وصل میکند: چت تلگرام شما، پروسههای Claude Code که زیر dtach اجرا میکنید، و سیستم هوکهای Claude Code. همهچیز از یک event loop میگذرد: بدون worker pool، بدون پایگاه داده، بدون صف بین پروسهها.
aipager یک برنامهٔ asyncio تکپروسهای است که در پسزمینه اجرا میشود. سه دنیا را به هم وصل میکند: چت تلگرام شما، پروسههای Claude Code که زیر dtach اجرا میکنید، و سیستم هوکهای Claude Code. همهچیز از یک event loop میگذرد: بدون worker pool، بدون پایگاه داده، بدون صف بین پروسهها.
نمودار اجزا#
هر چه در نمودار هست، جز تلگرام، روی همین کامپیوتر اجرا میشود. aipager با چند جای دیگر هم حرف میزند: تونل Cloudflare برای Mini App (و یک بار GitHub، برای cloudflared)، telegram.org برای اسکریپت Mini App، چک کردن نسخهها وقتی آپدیت میخواهید، دانلود مدل تبدیل صدا به متن وقتی اولین بار ویس میفرستید، و مقصد گزارش مشکل وقتی یکی میفرستید. همه در مدل امنیتی ← ارتباطات شبکه آمدهاند.
ساختار پروسه#
یک فراخوانی asyncio.run در aipager/cli/daemon.py همهچیز را میگرداند. داخل آن:
-
TelegramBot(aipager/bot/): یکApplicationازpython-telegram-botرا در اختیار دارد. آپدیتها را از تلگرام میگیرد، آنها را به گردانندههای پیام، دکمه و ویس میدهد، و کارتهای در حال کار، نوار وضعیت سنجاقشده و جوابها را آپدیت نگه میدارد. -
HookReceiver(aipager/dtach/hook_receiver.py): یک سوکت datagram یونیکس در $XDG_RUNTIME_DIR/aipager.sockباز میکند و JSONی را کهaipager-hookوaipager-statuslineمیفرستند میخواند، و بر اساسhook_event_nameآن (یاtypeبرای پیامهای خود aipager؛ رویدادهای هوک را ببینید) کار را پخش میکند. -
SessionMonitor(aipager/session_monitor.py): هر ۲ ثانیه بیدار میشود تا /tmp/claude-dtach-*.sockرا بگردد، سشنهایی را که پیدا میکند با فهرست سشنها هماهنگ کند و نگهبانها را اجرا کند: کارت در حال کاری که دیگر جلو نمیرود، پیامی که Claude Code هیچوقت تحویل نگرفت، درخواست اجازهای که ۵ دقیقه هیچ خبری از آن نیست و گفتگوی ذخیرهشده نشان میدهد تمام شده (AIPAGER_INTERACTIVE_TIMEOUT)، کاری که پایانش دیده نشد. -
SessionRegistry(aipager/state.py): شیءهایTrackedSessionکه با اسم سشن پیدا میشوند. هنگام خاموش شدن، و هر وقت چیزی که باید بعد از ریاستارت بماند عوض شود، (بهشکل اتمی) در ~/.claude/aipager-sessions.jsonذخیره میشود. - سرور Mini App (
aipager/miniapp/server.py): یک اپ aiohttp روی127.0.0.1و پورتMINIAPP_PORT(پیشفرض 8765)، که تا وقتی روشن است (پیشفرض) از طریق یک تونل Cloudflare مدیریتشده به تلگرام میرسد. داشبورد /appرا سرو میکند.
همهٔ اینها بهشکل task روی همان loop اجرا میشوند. کارهایی که loop را معطل میکنند در threadهای جدا اجرا میشوند: تبدیل ویس به متن، ساختن و فرستادن گزارش مشکل، چک کردن آپدیت و برنامهریزی ریاستارت، دانلود اسکریپت Mini App و cloudflared، و چک کردن لاگین در شروع.
ترتیب شروع#
از aipager/cli/daemon.py:
- تنظیمات قدیمی (
config.env،team.yaml) را یک بار بهaipager.yamlمنتقل میکند و فایلهای قدیمی را کنار میگذارد. - اگر aipager دیگری روی سوکت جواب بدهد، شروع نمیشود؛ بعد قفل aipager را میگیرد (
~/.local/share/aipager/daemon.lock). - توکن ربات را با تلگرام چک میکند (
getMe). - فایلهای خود Claude Code را درست میکند: هر هوک aipager یا خط وضعیتی را که در
~/.claude/settings.jsonنیست اضافه میکند، و به سؤالهای اولین اجرای Claude Code که کاربر تلگرام نمیتواند جوابشان را بدهد، جواب میدهد (مدل امنیتی را ببینید). -
SessionRegistry.load(): ~/.claude/aipager-sessions.jsonرا میخواند. اگر فایل نباشد یا خوانده نشود، با فهرست خالی شروع میکند. - آدرس Mini App را پیدا میکند (آدرس خودتان یا Tailscale)، بعد
bot.start(): گرفتن آپدیتها از تلگرام شروع میشود. -
HookReceiver.start(): فایل سوکت جامانده را پاک میکند، سوکت را باز میکند و گوش میدهد. -
bot.recover_sessions(): کارتها و درخواستهای سشنهایی را که موقع خاموش شدن aipager در حال کار یا منتظر بودند برمیگرداند (فرمانها ← جوابها را ببینید). -
SessionMonitor.start(): تیک ۲ ثانیهای را شروع میکند. - سرور Mini App شروع میشود، بعد تونل (وقتی آدرس خودتان را ندادهاید)، و دکمههای
📱 Appمنتشر میشوند. - aipager منتظر سیگنالها میماند: SIGINT یا SIGTERM متوقفش میکند و SIGUSR1 چتها و قانونها را دوباره میخواند (استفادهٔ تیمی ← خواندن دوبارهٔ تنظیمات بدون ریاستارت).
ترتیب خاموش شدن#
- SIGINT یا SIGTERM رویداد
stopرا روشن میکند. - یک
/updateدر حال اجرا کمی فرصت میگیرد و بعد نصبکنندهاش متوقف میشود (فرمانها ← آپدیت را ببینید). registry.save().- تونل متوقف میشود، بعد سرور Mini App.
-
session_monitor.stop()،hook_receiver.stop() (سوکت را میبندد و فایلش را پاک میکند). -
bot.stop(): انیمیشنها را لغو میکند وApplicationرا متوقف میکند. - دوباره
registry.save()، برای کارتی که ارسالش هنوز در جریان بود.
ریاستارتی که از تلگرام انجام میشود به این وابسته است که کل این مسیر قبل از اینکه جانشین سوکت را باز کند تا آخر اجرا شود. رفتاری که شما میبینید در فرمانهای ربات ← ریاستارت آمده است.
جای فایلها و سوکتها#
| مسیر | کاربرد | صاحب |
|---|---|---|
$XDG_RUNTIME_DIR/aipager.sock | سوکت datagram یونیکس برای رویدادهای هوک (بدون $XDG_RUNTIME_DIR در /tmp/aipager.sock؛ $AIPAGER_SOCKET_PATH بر هر دو مقدم است) | aipager (bind میکند) |
aipager-reply-<id>.sock (کنار سوکت کنترل) | یک سوکت جواب کوتاهعمر برای هر درخواست اجازهای که هوک منتظرش است | aipager-hook |
aipager-flood-mute.json، aipager-flood-backoff.json (کنار سوکت کنترل) | وضعیت فعلی محدودیت سرعت تلگرام، برای aipager status و doctor | aipager |
aipager-modelswitch-<name>.json (کنار سوکت کنترل) | علامت یک تعویض مدل که خود aipager تایپ کرده، برای ۳۰ ثانیه (هوکها) | aipager (مینویسد)، aipager-hook (برمیدارد) |
/tmp/claude-dtach-<name>.sock | سوکت کنترل dtach برای هر سشن | dtach |
/tmp/claude-status-<name>.json | دادهٔ خط وضعیت هر سشن | aipager-statusline |
/tmp/claude-notes-<name>/ | یک یادداشت اجازه برای هر پیام تلگرامی که هنوز برداشته نشده، و علامت turn-open که هوک تا وقتی یک کار در جریان است نگه میدارد (پیامی که به آن کار اضافه شود فقط میتواند قانونهایش را سختتر کند) | aipager (یادداشتها را مینویسد)، aipager-hook (یادداشتها را مصرف میکند؛ turn-open را مینویسد و هر دو پاکش میکنند) |
/tmp/claude-policy-<name>.json | عکس فوری اصلی از اجازههای کار در جریان | aipager-hook (موقع برداشتن پیام مینویسد)، بررسی PreToolUse میخواندش |
/tmp/claude-policy-.floor-<uid>.json | کف ایمنی، یعنی فهرست داخلی بهعلاوهٔ بخش safety: شما، برای حالتهای جایگزین هوک | aipager |
/tmp/claude-reply-<name>.txt | پیامی که به آن جواب دادید، وقتی به یک پیام قدیمیتر جواب میدهید | aipager |
/tmp/aipager-files/<chat>/ | فایلها و ویسهایی که از تلگرام فرستاده شده، در یک پوشهٔ خصوصی برای هر چت (0700، فایلها 0600) | aipager |
/tmp/aipager.log | خروجی ریاستارت خودکار وقتی سرویسی در کار نیست | aipager |
~/.claude/aipager-sessions.json | وضعیت ماندگار فهرست سشنها | aipager |
~/.claude/aipager-audit.jsonl | لاگ اجازه دادن، رد کردن و جوابها | aipager (فقط اضافه میکند) |
~/.claude/aipager-flood-state.json | تاریخچهٔ محدودیت سرعت تلگرام برای هر چت، که بعد از ریاستارت هم میماند (رفع مشکل) | aipager |
~/.claude/aipager-tunnel.json، aipager-cloudflared.log | تونل Mini App که aipager نگهش میدارد: پروسهاش، پورتهای محلیاش و هش آدرسش (هیچوقت خود آدرس)، تا aipager بعدی بعد از یک ریاستارت برنامهریزیشده آن را تحویل بگیرد (حالت دسترسی 600)؛ و هشدارهای cloudflared | aipager؛ این رکورد را نگهبانِ تونلِ نگهداشتهشده و aipager uninstall هم پاک میکنند |
~/.claude/aipager-pending-users.json | کسانی که در گروهی که ربات در آن کار نمیکند ربات را صدا زدهاند، یا عضو گروهی که ربات در آن کار میکند نیستند، تا aipager config بتواند اضافهشان کند (حالت دسترسی 0600، هر بار کامل عوض میشود، ۲۰۰ نفر آخری که در ۳۰ روز گذشته دیده شدهاند) | aipager (مینویسد)، aipager config (میخواند) |
~/aipager-users/<label>/ | پوشهٔ root پیشفرض اعضا (پوشههای root)، با حالت دسترسی 0700، جایی که اعضای user و read_only سشنهایشان را شروع میکنند؛ aipager uninstall نگهشان میدارد | aipager (در اولین /new یا «سشن جدید» یک عضو در Mini App میسازدش) |
~/.claude/settings.json | تنظیم هوکهای Claude Code | aipager config / aipager setup؛ در هر شروع، aipager هر هوک یا خط وضعیتی را که کم باشد اضافه میکند و skipDangerousModePermissionPrompt را تنظیم میکند (مدل امنیتی) |
~/.claude/settings.json.bak.* | نسخهٔ پشتیبان قبل از هر بازنویسی | aipager config، aipager setup، aipager uninstall |
~/.claude.json | فایل خود Claude Code: aipager پوشهای را که سشن در آن شروع میشود درست پیش از شروع، مورد اعتماد علامت میزند (پوشهٔ خانه هم، ولی هیچوقت / یا پوشهای بالاتر از پوشهٔ خانه)، تا سؤال اعتماد Claude Code جلوی سشنی را که از تلگرام شروع میشود نگیرد. موقع شروع aipager چیزی علامت نمیخورد. اگر نشود کامل خواندش یا یک Claude در حال اجرا در همین فاصله تغییرش داده باشد، هیچوقت بازنویسی نمیشود | aipager |
~/.claude/.credentials.json ← .credentials.json.stale | فایل لاگین Claude Code؛ وقتی سشنی با CLAUDE_CODE_OAUTH_TOKEN شروع شود و این فایل فقط یک لاگین منقضی یا خالی داشته باشد کنار گذاشته میشود (وگرنه بر توکن مقدم میشد)؛ برای برگرداندن، دوباره سر جایش بگذاریدش | aipager |
~/.config/aipager/aipager.yaml | توکن ربات، چتها، اعضا و نقشها، تنظیمات Mini App | aipager config (حالت دسترسی 600)؛ وقتی تلگرام یک گروه را به سوپرگروه ارتقا میدهد aipager بخش آن گروه را جابهجا میکند، و وقتی تنظیمات قدیمی Mini App را به آن منتقل میکند یک کپی aipager.yaml.bak.<time> نگه میدارد |
~/.config/aipager/policy.yaml | قانونهای هر نقش و قانونهای اضافهٔ ایمنی | خودتان (با aipager policy validate چک میشود)؛ وقتی یک نصب قدیمی منتقل میشود، یک بار یک نمونهٔ شروع با توضیح نوشته میشود |
~/.config/aipager/preferences.json | انتخابهای /settings هر چت و هر سشن | aipager |
~/.config/aipager/daemon.env | اطلاعات ورود Claude برای سشنهایی که شروع میشوند | خودتان / aipager service install / aipager doctor --fix (حالت دسترسی 600) |
~/.config/aipager/config.env، team.yaml ← *.bak.<time>، *.retired.<time> | تنظیمات یک نصب قدیمی: کپی میشود و بعد از اینکه aipager.yaml آن را گرفت کنار گذاشته میشود | aipager، aipager config، aipager setup |
~/.config/aipager/keyboard.json | تغییرات اختیاری کیبورد | خودتان |
~/.local/share/aipager/sessions/ | یک پوشه برای هر سشن با SESSION.md آن (چه کسی میتواند با آن حرف بزند، و قانونها)، که در هر شروع از نو ساخته میشود | aipager |
~/.local/share/aipager/webapp-sdk/، cloudflared/ | اسکریپت Mini App تلگرام و فایل اجرایی cloudflared که چک شده | aipager |
~/.local/share/aipager/daemon.lock، update.lock، update-restart.json | هر بار فقط یک aipager؛ هر بار فقط یک آپدیت؛ آپدیتی که بعد از ریاستارت باید اعلام شود | aipager |
~/.local/share/aipager/install.json، running.json | aipager اولین بار کی اینجا شروع شد؛ و اینکه الان در حال اجراست یا نه (تا کرش از توقف عادی جدا شود) | aipager |
~/.local/state/aipager/reports.json | گزارش مشکل: fingerprint خطاهای ثبتشده، تعدادها و وضعیت پیشنهادها (حالت دسترسی 600)؛ گزارش مشکل را ببینید | aipager |
~/.local/state/aipager/report-sends.json | گزارشهایی که امروز فرستاده شده (سقف روزانه) | aipager، aipager report |
~/.local/state/aipager/languages.json | وضعیت زبان (حالت دسترسی 600): چه کسانی قبل از اضافه شدن زبانها از aipager استفاده میکردند، آخرین زبان تلگرامی که از هر نفر دیده شد، اینکه پیشنهاد یکبارهٔ زبان و خط اولین آشنایی فرستاده شده یا نه، و هش هر توضیح رباتی که aipager تنظیم کرده؛ زبانها را ببینید | aipager |
~/.config/systemd/user/aipager.service (Linux)، ~/Library/LaunchAgents/com.aipager.daemon.plist (macOS)، و یک کپی .bak.<time> از فایلی که نصب جایگزینش میکند (روی Linux فقط وقتی فرق داشته باشد) | سرویس پسزمینه | aipager service install |
~/Library/Logs/aipager.log (macOS) | لاگ سرویس launchd، که aipager logs میخواندش | launchd، برای سرویس |
جدا از اینها، aipager وقتی بخواهید یک پوشهٔ جدید برای سشن میسازد («پوشهٔ جدید» در /new یا Mini App)، و نصبکنندهها و مدل ویس وقتی آپدیت میکنید یا از ویس استفاده میکنید فایلهای خودشان را مینویسند. همهٔ اینها زیر پوشهٔ خانهٔ شما، زیر /tmp یا کنار سوکت کنترل است. aipager هیچوقت دسترسی بالاتر نمیگیرد؛ مدل امنیتی را ببینید.
یک نسخهٔ جدا (برای تست)#
AIPAGER_INSTANCE_DIR را برابر یک پوشهٔ مطلق که مال خودتان است بگذارید تا همهٔ فایلهای مشترک زمان اجرا در جدول بالا به آن منتقل شوند: سوکت کنترل (و سوکتهای جواب، فایلهای علامت محدودیت سرعت و علامت تعویض مدل که کنارش هستند)، سوکتهای claude-dtach-*، فایلهای claude-policy-*، claude-notes-*، claude-status-* و claude-reply-*، فایل کف ایمنی، پوشهٔ دانلود aipager-files و لاگ ریاستارت خودکار. سشنهایی که این نسخه شروع میکند این تنظیم را به ارث میبرند، پس هوکهایشان فقط با همین نسخه حرف میزنند، و قانونهای ایمنی از فایلهای منتقلشده هم همانطور محافظت میکنند. این نسخه .env یک checkout را نمیخواند.
فایلهای زیر پوشهٔ خانه جابهجا نمیشوند، پس HOME را هم به یک پوشهٔ جدا ببرید. aipager start اجرا نمیشود اگر پوشهٔ نسخه تنظیم شده باشد و HOME پوشهٔ خانهٔ واقعی شما باشد، اگر آن پوشه یک مسیر مطلق به پوشهای که مال شماست نباشد، یا اگر برای مسیر یک سوکت زیادی طولانی باشد.
/update و /restart جدا نمیشوند: روی پکیج نصبشدهٔ aipager و سرویس aipager.service اثر میگذارند، پس هیچوقت آنها را به یک نسخهٔ تست نفرستید.
AIPAGER_TELEGRAM_API_BASE (مثلاً http://127.0.0.1:41234) همهٔ درخواستهای Bot API و دانلود فایلها را به جای https://api.telegram.org به آن آدرس میفرستد. این آدرس باید همین کامپیوتر باشد (localhost یا یک آدرس loopback)، مگر اینکه AIPAGER_TELEGRAM_API_ALLOW_REMOTE=1 باشد، تا یک اشتباه تایپی نتواند توکن ربات را جای دیگری بفرستد.
هر دو تنظیم برای این هستند که تستهای end-to-end بتوانند یک aipager دوم را کنار aipager واقعی شما، با یک تلگرام قلابی محلی، اجرا کنند. اگر هیچکدام تنظیم نشده باشد، همهٔ مسیرها و آدرسها همانهایی است که در جدول بالا آمده.
چرا dtach#
dtach به هر سشن Claude Code یک PTY واقعی میدهد، بدون اینکه آن را به ترمینالی ببندد که باید باز بماند. aipager سشن را با dtach -n شروع میکند و با dtach -p در آن تایپ میکند (پیامها، Escape، کلیدهایی که به یک پنجرهٔ سؤال جواب میدهند)؛ هیچوقت صفحه را نمیخواند، چون هوکها و گفتگوی ذخیرهشده میگویند چه اتفاقی میافتد. میتوانید از هر shell با aipager session <name> بهطور مستقیم به آن وصل شوید. این اتصال سشن را در ریاستارتی که از تلگرام انجام میشود (/mode به مود دسترسی کامل یا از آن، /restart، دکمهٔ ریاستارت) دنبال میکند: وقتی سوکت سشن از بین میرود، تا ۱۵ ثانیه منتظر میماند که همان سوکت برگردد و خودش دوباره وصل میشود. سشنی که واقعاً بسته شده، بعد از این انتظار به shell برمیگردد (Ctrl-C زودتر تمامش میکند). این اتصال با کلید جدا شدن dtach خاموش اجرا میشود، پس Ctrl-\ به Claude میرسد؛ برای بیرون آمدن، ترمینال یا پنل tmux را ببندید و سشن به کارش ادامه میدهد.
نتیجه: Claude Code طوری اجرا میشود که انگار در یک ترمینال تایپ کردهاید، ولی آن ترمینال میتواند بیاید و برود بدون اینکه سشن در حال اجرا به هم بریزد. aipager سشنها را با گشتن /tmp/claude-dtach-*.sock در هر تیک ۲ ثانیهای پیدا میکند. سشنی که پیدا میشود مال چتی است که اسمش برای آن ساخته شده، و سشنی که در ترمینال شروع شده، در نصبی که هم چت خصوصی دارد و هم گروه، مال چت خصوصی مالک است.
اینها را هم ببینید#
- رویدادهای هوک: چه چیزهایی از سوکت کنترل میرسد.
- فرمانهای ربات: چه چیزهایی از تلگرام میرسد.
- مدل امنیتی: مرز دسترسی، رازها، لاگ ممیزی.
- رفع مشکل: مرجع
aipager doctor.