استضافة OpenTag ذاتياً لإشارات @agent
شغّل OpenTag على VPS لتصل إشارات Slack وGitHub إلى وكيلك البرمجي، مع إعداد TLS، والتحقق من توقيع webhook، ونطاقات الرموز وإعدادات آمنة.
ما الذي يفعله OpenTag عند الإشارة إلى وكيل
يحوّل OpenTag إشارة @ في سلسلة محادثة على Slack أو في issue على GitHub إلى تشغيل لوكيل برمجي على جهاز تملكه. يعلّق أحدهم @opentag investigate this في issue. يستقبل مستمع الحدث القادم من المنصة، ويتحقق من توقيعه، ويطابق الإشارة مع مشروع مرتبط، ثم يشغّل وكيلاً برمجياً على نسخة checkout محلية، وينشر النتيجة في السلسلة نفسها.
المشروع مرخّص بموجب MIT وموجود في amplifthq/opentag. اعتباراً من August 2026، أحدث إصدار موسوم هو v0.9.0، وقد نُشر في 28 July 2026، ويُوزَّع كحزمة npm. لا توجد صورة حاوية رسمية، لذلك فإن الإصدار الذي تثبّته هو إصدار npm. كل أمر أدناه يثبّت الإصدار.
يصبح هذا مشروع VPS بدلاً من مشروع على جهاز محمول بسبب جانب GitHub. يرسل GitHub أحداث المستودع عبر إجراء طلب HTTP إلى عنوان URL تسجّله مرة واحدة، لذلك يجب أن يستجيب ذلك العنوان من الموقع نفسه غداً أيضاً.
الأجزاء الأربعة المتحركة
المستمع يستقبل أحداث المنصة، ولكل منصة مستمع خاص بها. مستمع GitHub هو نقطة نهاية HTTP على المنفذ 3050 ضمن المسار /github/webhooks. أما مستمع Slack Events API فيعمل على المنفذ 3040 ضمن /slack/events. ويمكن لـSlack أيضاً العمل في Socket Mode، حيث يفتح التطبيق اتصال WebSocket صادراً ولا يحتاج إلى أي منفذ وارد.
الموزّع هو المنسّق. يستمع افتراضياً على المنفذ 3030، ويحفظ حالة التشغيل في ملف قاعدة بيانات محلي يحدده OPENTAG_DATABASE_PATH، ويسجّل مسار تدقيق لكل تشغيل. يجب ألا يصل أي شيء من خارج الخادم إلى هذا المنفذ.
المشغّل هو البرنامج الخدمي المحلي. يستطلع الأعمال المتاحة، ويحجز عملية تشغيل، ويحتفظ بمدة حجز لها، ويرسل إشارة حياة كل 15 ثانية افتراضياً ما دامت عملية التشغيل مستمرة. ويرفض أي عملية تشغيل محجوزة يكون هدف المشروع الخاص بها مفقوداً أو خارج قائمة السماح في إعداداته الخاصة. هذا هو الفحص الذي يمنع حدثاً من GitHub من توجيه وكيلك إلى مستودع لم تربطه مطلقاً.
المنفّذ هو وكيل البرمجة نفسه. يشغّله OpenTag عبر ACP (agent client protocol)، وهو بروتوكول JSON-RPC يتبادل البيانات عبر الإدخال والإخراج القياسيين، لذلك يعمل الوكيل كعملية فرعية داخل دليل العمل الذي يمرّره إليه OpenTag. تتضمن الأسماء المضمّنة echo وcodex وclaude-code وcursor وopencode وhermes وopenclaw. ابدأ باستخدام echo، وهو المنفّذ المضمّن في إعداد المثال، لأنه يثبت عمل المسار الكامل قبل أن يتعامل نموذج مع شفرتك. إذا كانت الحلقة التي تقرأ مطالبة، وتستدعي الأدوات، وتحدد وقت الانتهاء لا تزال غير واضحة لك، فإن كتابة حلقة وكيل صغيرة بنفسك أولاً تجعل حالات الفشل في مسار التنفيذ هذا أسهل بكثير في القراءة.
لا يتغير الترتيب مطلقاً: حدث المنصة، والتحقق من التوقيع، وسجل التشغيل، والحجز، والوكيل، ثم الرد في سلسلة المحادثة.
لماذا لا يكفي حاسوب محمول ونفق
يطلب منك دليل إعداد GitHub تشغيل ngrok http 3050 ولصق مضيف النفق في webhook الخاص بالمستودع. ينجح ذلك خلال الدقائق العشر الأولى. يتغير مضيف النفق المجاني في كل مرة تُعاد فيها العملية، ويتوقف عن الوجود عندما يدخل الحاسوب المحمول في وضع السكون. يحتفظ GitHub بعنوان URL القديم للحمولة ويواصل محاولة استخدامه، لذلك تمتلئ علامة التبويب Recent Deliveries في إعدادات webhook بحالات الفشل بينما تظل المحادثة صامتة. لا يلاحظ أحد ذلك لمدة أسبوع، لأن webhook الذي لا يفعل شيئاً يبدو تماماً كروبوت لم يذكره أحد.
يعالج VPS السببين اللذين يؤديان إلى الفشل. لا يتغير اسم DNS، لذلك يظل عنوان URL للحمولة الذي تلصقه مرة واحدة صحيحاً. ولا يدخل الجهاز في وضع السكون، لذلك يحصل تعليق الساعة 02:00 على إجابة. أعد إعداد الجهاز بطريقة صحيحة أولاً: يشرح الدقائق العشر الأولى على VPS جديد مستخدم تسجيل الدخول وجدار الحماية اللذين يفترضهما هذا الدليل.
يمثل Slack استثناءً. في Socket Mode، ينشئ اتصالاً صادراً ولا يحتاج إلى عنوان URL عاماً، لذلك يمكن أن يبقى النشر الخاص بـSlack فقط مغلقاً. لا يملك GitHub ما يعادل ذلك. إن webhook الخاص بمستودع GitHub هو HTTP وارد، وهذا يعني ضرورة توفير نقطة نهاية عامة، وبالتالي استخدام TLS (أمان طبقة النقل) والتحقق من التوقيع.
استضافة OpenTag ذاتياً على Ubuntu باستخدام إصدار محدد
يتطلب OpenTag v0.9.0 إصدار Node.js 22 أو أحدث. يأتي Ubuntu 24.04 مع Node 18 في مستودعه الخاص، لذلك ثبّت Node.js من NodeSource.
curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs
node -vيجب أن يطبع node -v القيمة v22 أو قيمة أعلى. في Node 20، يطبع التثبيت تحذيراً من نوع EBADENGINE، وقد تفشل واجهة CLI بعد بدء تشغيلها.
أنشئ حساباً مخصصاً للخدمة. يعمل الوكيل بصلاحيات هذا المستخدم، لذلك يجب ألا يكون حساب تسجيل دخولك، ويجب ألا يكون root. يشرح المستخدمون ذوو الصلاحيات المحدودة على VPS سبب أهمية هذا الفصل رغم الخطوة الإضافية.
sudo adduser --disabled-password --gecos "" opentag
sudo loginctl enable-linger opentag
sudo npm install -g @opentag/cli@0.9.0
command -v opentagيجب أن يطبع command -v opentag مساراً مثل /usr/bin/opentag. يهم إعداد linger في Linux: يثبّت OpenTag خدمته الخلفية عبر systemd، وتتوقف خدمة المستخدم التي لا تستخدم lingering فور إغلاق جلسة SSH.
نفّذ الإعداد بهذا المستخدم.
sudo -iu opentag opentag setupيسألك الإعداد عن ستة أمور: لغة CLI، وعنوان الاستماع المحلي، ووكيل البرمجة، والمشروع المحلي الذي سيعمل عليه، وبيانات اعتماد المنصة التي سيحفظها، وطريقة التشغيل. أبقِ عنوان الاستماع على 127.0.0.1، لأن nginx ينهي TLS ويوجّه الطلبات إليه، ولذلك لا تحتاج نقاط الاستماع إلى أن تكون قابلة للوصول من خارج الخادم. بالنسبة إلى GitHub، سيطلب أيضاً المستودع بصيغة owner/repo، وما إذا كان يمكنه فتح pull requests، ومنفذ webhook (3050 افتراضياً)، والرمز المميز. اختر وضع الخدمة الخلفية في النهاية. إذا كان لديك إعداد موجود وتريد تثبيت الخدمة دون مطالبات، فإن opentag setup --service ينفّذ ذلك.
يُحفظ الإعداد في /home/opentag/.config/opentag/config.json، وتحفظ حالة التشغيل في /home/opentag/.local/state/opentag. تستحق هذه المفاتيح المراجعة يدوياً بعد أن يكتب الإعداد الملف.
{
"runnerId": "runner_local",
"dispatcherUrl": "http://localhost:3030",
"runnerToken": "...",
"approvalMode": "ask",
"repositories": []
}استخدم runnerToken، وهو bearer token مخصص للـrunner، بدلاً من pairingToken المشترك الأقدم. يحتفظ ملف الإعداد ببيانات الاعتماد كنص عادي ما لم تستبدلها بمرجع إلى سر، يقرأ القيمة من البيئة أو من ملف على القرص عند بدء التشغيل. في كلتا الحالتين، هذا الملف هو الأكثر حساسية على الخادم: اجعل mode له 600، واجعله مملوكاً لـopentag، ولا تضعه داخل مستودع git مطلقاً. يرد شرح أوسع في إبقاء الأسرار خارج وكلاء الذكاء الاصطناعي.
تحقق من التثبيت قبل إتاحة أي شيء خارجياً.
sudo -iu opentag opentag doctor
sudo -iu opentag opentag statusيتحقق opentag doctor من dispatcher، وbindings، وcheckouts، وexecutors. يطبع opentag status الإعداد وحالة التشغيل، ويمكن تقييده بتشغيل واحد بعد وجود تشغيلات. أصلح كل ما يبلّغ عنه doctor قبل توجيه منصة إلى هذا الخادم.
ضع TLS في الواجهة وافتح مسارين فقط
ينهي nginx جلسة TLS ويمرر مسارين محددين فقط. تُرجع جميع الطلبات الأخرى الحالة 404، لذلك لا يعرف الماسح الذي يعثر على المضيف شيئاً عن الخدمات التي تعمل خلفه.
اكتب كتلة خادم عادية على المنفذ 80 في /etc/nginx/sites-available/opentag، مع موقعي locations أدناه، ثم دع Certbot يضيف جزء TLS.
sudo apt install -y nginx certbot python3-certbot-nginx
sudo ln -s /etc/nginx/sites-available/opentag /etc/nginx/sites-enabled/opentag
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d opentag.example.comيطبع nginx -t القيمة syntax is ok وtest is successful، وهو الأداة الوحيدة التي تفصل بين خطأ مطبعي وإعادة تحميل تؤدي إلى توقف الموقع. يشرح Certbot على Ubuntu 24.04 مع nginx التجديد والطرق التي يفشل بها تحدي ACME (بيئة إدارة الشهادات تلقائياً). تبدو الكتلة النهائية كما يلي.
server {
listen 443 ssl;
server_name opentag.example.com;
ssl_certificate /etc/letsencrypt/live/opentag.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/opentag.example.com/privkey.pem;
client_max_body_size 2m;
location = /github/webhooks {
proxy_pass http://127.0.0.1:3050;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location = /slack/events {
proxy_pass http://127.0.0.1:3040;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location / {
return 404;
}
}إنّ = في location = /github/webhooks تطابق تام، بينما يمرر proxy_pass، من دون أي شيء بعد المنفذ، URI الأصلي من دون تغيير. إذا حذفت =، فستُمرَّر جميع المسارات الموجودة تحت /github/webhooks/ أيضاً، وهذا يوسّع سطح التعرض أكثر مما يحتاج إليه المستمع.
تبقى قواعد الجدار الناري محدودة.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw statusلا تُفتح المنافذ 3030 و3040 و3050 مطلقاً. تأكد من أنها مرتبطة بواجهة loopback، لا بجميع الواجهات.
sudo ss -tlnpيجب أن يقرأ كل سطر OpenTag 127.0.0.1:3030 أو ما يشابهه. ويعني السطر الذي يقرأ 0.0.0.0:3050 أن المستمع يقدّم نفسه للإنترنت بالكامل، وأن ufw وحده هو الذي يمنع الوصول إليه. يكفي خطأ واحد في إعداد الجدار الناري لفتح مُشغّل الوكيل. يشرح أساسيات جدار ufw الناري ما الذي يفعله المنع الافتراضي فعلياً.
يثبت فحصان صحة الواجهة الأمامية. يعيد curl -I https://opentag.example.com/ القيمة 404 من nginx، ما يثبت أن الشهادة صالحة وأن المسار الشامل مغلق. ويجب ألا يعيد طلب إلى /slack/events أو /github/webhooks، من دون توقيع، الحالة 200 مطلقاً.
تحقّق من كل توقيع، لأن عنوان URL عام
يمكن لأي شخص العثور على عنوان URL للحمولة. فهو موجود في إعدادات مستودعك، وفي سجل المتصفح، وفي لقطة شاشة أُلصقت في تذكرة. والتوقيع هو الشيء الوحيد الذي يميّز تسليماً حقيقياً من GitHub عن طلب كتبه شخص يدوياً.
يوقّع GitHub كل تسليم باستخدام سر webhook، ويرسل النتيجة في الرأس x-hub-signature-256. ويتحقق OpenTag من هذا الرأس مقابل platforms.github.webhookSecret. وتنص ملاحظات تحصين المشروع مباشرة على القاعدة التالية: لا تقبل أحداث المصدر غير الموقّعة على /github/webhooks. يوقّع Slack كل طلب باستخدام SLACK_SIGNING_SECRET ويضمّن طابعاً زمنياً، لذلك لا يمكن إعادة تشغيل جسم مُلتقط بعد ساعات.
تجاهل ذلك ليس خطراً بسيطاً. تقبل نقطة النهاية غير المتحقَّق منها حمولة issue_comment مكتوبة يدوياً وتحتوي على @opentag، ثم يشغّل OpenTag عميلاً برمجياً باستخدام رمزك المميّز، داخل checkout الخاص بك، وبناءً على تعليمات من شخص مجهول. ويُرسل الرد إلى أي سلسلة محادثة يحددها payload المزيف.
يضيف OpenTag طبقتين إضافيتين. تُتتبَّع عمليات تسليم المصدر بواسطة معرّف التسليم، لذلك لا تؤدي إعادة تسليم الحدث نفسه إلى بدء تشغيل ثانٍ. وتقبل استدعاءات Runner مفاتيح idempotency، لذلك تؤدي إعادة تشغيل أحدها إلى إرجاع النجاح من دون إلحاق حدث تدقيق آخر.
يمكن ضبط حدود المعدل، ويجب تفعيلها. يحدّد OPENTAG_RATE_LIMIT_WINDOW_MS وOPENTAG_RATE_LIMIT_MAX_REQUESTS معدل الطلبات، ويحدّد OPENTAG_MAX_REQUEST_BODY_BYTES حجم الجسم، وتُرفض الحمولة التي تتجاوز الحجم المسموح بالرمز 413 request_body_too_large. يوجد OPENTAG_RATE_LIMIT_DISABLED=true لأغراض التطوير المحلي، ولا مكان له على خادم عام. وتنص ملاحظة أخرى في الملاحظات نفسها على ما يلي: يجب أن يستخدم عنوان URL للترحيل العام HTTPS، ولا تسمح CLI باستخدام HTTP العادي إلا مع localhost.
ما نطاقات الرموز التي يحتاج إليها الروبوت فعلياً؟
في GitHub، يستخدم OpenTag رمز وصول شخصياً دقيق الصلاحيات بدلاً من GitHub App. توضح الوثائق أن مسار App مخطط له، وليس الإعداد الافتراضي الحالي لـCLI. ويترتب على ذلك أمر يغفل عنه البعض: يضيف الروبوت التعليقات باسم الشخص الذي أنشأ الرمز. أنشئ الرمز ضمن حساب تقبل ظهور اسمه في كل رد من ردود الفرز.
اضبط النطاقات بأضيق شكل توضحه تعليمات الإعداد. اختر Only select repositories وحدد مستودعاً واحداً. امنح Issues: Read and write وPull requests: Read and write. يكفي ذلك لقراءة الإشارة والرد في سلسلة المناقشة.
لاحظ ما هو غير موجود: صلاحية الكتابة على التعليمات البرمجية. لا يدفع OpenTag الفروع إلا إذا ضُبطت preparePullRequestBranch على true، ويوجد githubApplyToken منفصل حتى لا يكون الرمز الذي يكتب التعليمات البرمجية هو الرمز الذي يكتب التعليقات. أبقِ الرمزين منفصلين، ولا تفعّل رمز الكتابة حتى يعمل مسار القراءة والتعليق لبضعة أسابيع.
الإعداد الذي يجب تجنبه هو رمز يملك Contents: Read and write عبر All repositories. يستطيع كل من يمكنه التعليق في أي من هذه المستودعات توجيه وكيل يملك صلاحية إجراء commits، ويُظهر سجل التدقيق أن مالك الرمز هو من نفّذ ذلك. وسّع النطاق لمستودع واحد في كل مرة، بعد أن يثبت الوكيل استحقاقه.
في Slack، نطاقات الروبوت هي app_mentions:read وchat:write وreactions:write وchannels:history. تحتاج القنوات الخاصة أيضاً إلى groups:history، إضافة إلى الاشتراك في حدث message.groups. يتطلب Socket Mode رمزاً على مستوى التطبيق يتضمن connections:write، وهو الرمز الذي يبدأ بـxapp-. يقرأ channels:history سجل الرسائل في القنوات العامة التي أُضيف إليها الروبوت، لذا أضف الروبوت إلى القنوات المطلوبة بدلاً من إضافته إلى جميع القنوات.
تتبّع مشكلة واحدة من البداية إلى النهاية
يأتي webhook أولاً. في المستودع، افتح Settings، ثم Webhooks، ثم Add webhook. عنوان URL للـpayload هو https://opentag.example.com/github/webhooks، ونوع المحتوى هو application/json، أما السر فهو السر الذي أنشأه الإعداد. اشترك في Issue comments وPull request review comments فقط، دون أي أحداث أخرى.
يرسل GitHub عملية ping delivery فور الحفظ. افتح Recent Deliveries وتحقق من وصول الطلب إلى الخادم من الأساس. تعني استجابة 502 أن nginx لم يتمكن من الوصول إلى listener، وهذه مشكلة محلية وليست مشكلة في GitHub.
استخدمه الآن. افتح issue يصف خطأً، وأضف التعليق التالي:
@opentag triage this. Reproduce the report against the current main branch, then reply with the file and function most likely responsible, plus the test you would write first.يجب أن يحدث ما يلي بالترتيب. يسجل Recent Deliveries عملية issue_comment delivery باستجابة 2xx. يسجل dispatcher عملية تشغيل. يستحوذ runner عليها ويبدأ إرسال heartbeats. يفتح executor checkout ويبدأ العمل. تصل الإجابة في صورة تعليق ضمن سلسلة issue نفسها. يعرض sudo -iu opentag opentag status عملية التشغيل أثناء تنفيذها، لذا يمكنك مراقبتها بدلاً من التخمين.
عيّن approvalMode إلى ask قبل أول عملية تشغيل فعلية. في وضع ask تتوقف العملية مؤقتاً وتنتظر موافقة شخص قبل تنفيذ أي إجراء يغيّر الحالة. يتوفر الوضعان auto وautonomous، وهما مناسبان لاحقاً في مستودع قرأت فيه نصوص المحادثات لمدة شهر.
على جانب Slack، تبدأ العملية نفسها باستخدام /bind owner/repo في القناة، ثم بإشارة mention. يجيب bot أيضاً عن /help و/status و/doctor و/stop و/unbind confirm. قيّد من يمكنه تغيير الارتباطات باستخدام OPENTAG_SLACK_BINDING_ADMIN_USER_IDS، وهي قائمة مفصولة بفواصل تتضمن Slack user IDs، لأن الارتباط هو التعيين بين قناة عامة وcheckout على خادمك.
يُعد Triage مساراً أولياً جيداً لأنه يقرأ ولا يكتب، كما أن تقييم الإجابة سهل. وتأتي Review كخطوة تالية، حيث يعلّق agent على diff بدلاً من issue: agent لمراجعة pull request مستضاف ذاتياً هو البنية نفسها الموجّهة إلى pull requests. إذا أردت أن يصل agent إلى أنظمتك الخاصة أثناء عمله، فهذه مهمة خوادم MCP على VPS. أما البحث على الويب فهو القدرة الأخرى التي يطلبها Triage باستمرار، ويؤدي ربط agent بمثيل SearXNG الخاص بك إلى إبقاء عمليات البحث على أجهزة تديرها أنت، مقابل إضافة قناة أخرى يمكن أن يصل عبرها نص صادر عن شخص غريب إلى agent.
ماذا يحدث عندما يخطئ الوكيل أمام الجميع؟
سيخطئ الوكيل. السؤال هو تكلفة ذلك.
الرد الخاطئ في issue عامة هو تعليق يظهر تحت اسم يعرفه فريقك، ويرسل GitHub رسالة بريد إلكتروني إلى كل من اشترك فيها فور نشره. حذف التعليق لا يسحب رسالة البريد الإلكتروني. وينطبق الأمر نفسه على إشعار Slack. خطط على أساس أن الرد قد يكون خاطئاً علناً، لا على أساس أنه سيكون صحيحاً في الخاص.
تحد أربعة خيارات حجم الضرر، وهي أهم من أي prompt تكتبه.
- شغّل الوكيل في وضع
ask، بحيث يقترح الوكيل، ويوافق شخص، ولا تكلّف الخطة الخاطئة سوى نقرة واحدة. - اترك
preparePullRequestBranchعلى قيمته الافتراضية false، بحيث تكون أسوأ نتيجة للتشغيل السيئ تعليقاً خاطئاً لا فرعاً خاطئاً. - ابدأ بربط repository واحدة وقناة واحدة. يرفض runner أي تشغيل يكون هدف المشروع فيه خارج allowlist المحلية، لذلك لا يمكن لـrepository غير مرتبطة استدعاء الوكيل إلى داخلها.
- أبقِ token التعليق منفصلاً عن أي token للتطبيق، بحيث لا يؤدي سحب صلاحية الكتابة إلى إيقاف triage أيضاً.
يحتوي Slack على الأمر /stop لإيقاف تشغيل يسير في الاتجاه الخاطئ. يترك كل تشغيل أيضاً سجلاً للتدقيق يتضمن mention الذي بدأه وما فعله الوكيل. تقرأ هذا السجل لاحقاً لتحديد موضع الخطأ.
الجانب الاجتماعي مهم بقدر أهمية الإعدادات. ضع bot في قناة واحدة يتوقع الناس فيها وجود آلة ويعرفون أنها قد تخطئ. فالرد الواثق والخاطئ في قناة تضم أربعين شخصاً يفترضون أن إنساناً راجعه يسبب ضرراً أكبر من الفائدة التي حققها triage. اكتب في وصف القناة من المسؤول عن bot ومن يراجع مخرجاته.
النسخ الاحتياطية والترقيات وتثبيت الإصدار
يحتوي المساران /home/opentag/.config/opentag/config.json و/home/opentag/.local/state/opentag على كل شيء. يحتوي الأول على بيانات الاعتماد، بينما يحتوي الثاني على سجل التشغيل وملف قاعدة البيانات. أنشئ نسخة احتياطية من كليهما بالوضع 600، واحتفظ بها خارج الخادم. فقدانهما يعني إعادة إنشاء الرموز والارتباطات، لا إعادة بناء الخادم.
الترقية هي رفع رقم الإصدار ثم إعادة التشغيل.
sudo npm install -g @opentag/cli@0.9.0
sudo -iu opentag opentag service stop
sudo -iu opentag opentag service start
sudo -iu opentag opentag doctorثبّت الإصدار بدلاً من تتبّع @latest. يشغّل هذا البرنامج وكيل برمجة على مستودعك باستخدام رمز مميّز فعّال، لذلك فإن إصداراً يُنشر أثناء الليل يُعد تغييراً غير مُراجَع على ذلك المستودع. لا تنقل سياسة الأمان الإصلاحات إلى الإصدارات الأقدم، ولا تصل الإصلاحات إلا إلى أحدث إصدار. لذلك يعني تثبيت الإصدار أن تقرأ سجل التغييرات وتُجري الترقية عن قصد. ولا يعني ذلك البقاء على v0.9.0 إلى الأبد. يوضح السجل حتى July 2026 صدور عدة إصدارات كل شهر، وهذا سبب وجيه لقراءة ملاحظات الإصدار قبل كل ترقية.
FAQ
هل أحتاج إلى VPS لتشغيل OpenTag، أم يكفي حاسوب محمول؟
يكفي الحاسوب المحمول لتشغيل Slack وحده، لأن Socket Mode يفتح WebSocket صادراً ولا يحتاج إلى منفذ وارد. يختلف GitHub عن ذلك. تُرسِل webhooks الخاصة بالمستودع الطلبات عبر HTTP الوارد إلى URL تسجّله مرة واحدة، لذلك يجب أن يبقى العنوان ثابتاً وأن يستجيب أثناء نومك. يتغير عنوان tunnel host من الحساب المجاني عند كل إعادة تشغيل، ويواصل GitHub إرسال الطلبات إلى العنوان القديم. يظهر ذلك كإدخالات فاشلة في تبويب Recent Deliveries بالمستودع، وكصمت في سلسلة المحادثة. يزيل VPS ذو اسم DNS ثابت وشهادة هاتين المشكلتين.
ما أذونات GitHub التي يحتاج إليها OpenTag؟
يحتاج إلى personal access token دقيق الصلاحيات، ومقيّد بخيار Only select repositories، مع منح Issues: Read and write وPull requests: Read and write. يغطي ذلك قراءة الإشارة والرد في سلسلة المحادثة. لا يحتاج إلى صلاحية الكتابة على التعليمات البرمجية، إلا إذا ضبطت preparePullRequestBranch على true لكي يدفع OpenTag الفروع، كما يوجد githubApplyToken منفصل حتى يبقى token الخاص بكتابة التعليمات البرمجية منفصلاً عن token الخاص بالتعليق. تجنّب token يشمل جميع المستودعات مع صلاحية contents write، لأن أي شخص يستطيع التعليق على أي من تلك المستودعات سيتمكن عندها من توجيه agent يستطيع تنفيذ commit.
كيف أوقف عملية تشغيل تسير بشكل خاطئ؟
يوفّر Slack الأمر /stop لهذا الغرض تحديداً. على الخادم، يعرض opentag status ما هو قيد التشغيل، ويوقف opentag service stop الـdaemon، وبذلك ينهي خط المعالجة بالكامل بدلاً من عملية تشغيل واحدة. لتجنب الحاجة إلى أي منهما، اضبط approvalMode على ask حتى تتوقف عمليات التشغيل بانتظار موافقة شخص قبل تغيير أي شيء، واترك preparePullRequestBranch على false حتى تنتج عملية التشغيل السيئة تعليقاً بدلاً من فرع.
لماذا يعيد webhook لدي الرمز 502 بينما تبقى سلسلة المحادثة صامتة؟
يأتي الرمز 502 من nginx، وليس من OpenTag، ويعني أن الـproxy لم يتمكن من الوصول إلى listener. سيعرض /var/log/nginx/error.log القيمة connect() failed (111: Connection refused) while connecting to upstream. إما أن listener متوقف، أو أنه يعمل على منفذ مختلف عن المنفذ المذكور في السطر proxy_pass. شغّل sudo ss -tlnp وتحقق من أن هناك عملية تستمع على 127.0.0.1:3050 لصالح GitHub وعلى 127.0.0.1:3040 لصالح Slack، ثم شغّل opentag doctor للتحقق من bindings وexecutors.