روی این ماشین فرمان codex remote-control pair هرگز کد جفتشدن نمیسازد، و دلیلش نه نبودِ مجوز و نه خرابی نصب است. آن فرمان از سوکت یونیکسِ یک دیمون app-server حرف میزند که «کنترل از راه دور» در آن روشن نگه داشته شده، اما هرگز در ثبتنام ChatGPT وارد نشده است. کل خطا در یک جمله است: remote control pairing is unavailable until enrollment completes. در این نوشته دایمون را بالا میآوریم، خطا را میبینیم، و دو عددی را که دیمون را از خط فرمان جدا نگه میدارند میسنجیم. همهی عددها لحظهی خواندن: ۶ اکتبر ۲۰۲۶.
کد که دست آمد، آماد
کدکس دو چیز جدا را یکی «کدکس» مینامد، و همینجا تمام ریشهی این خطاست. یکی فرمان codex است که در ترمینال اجرا میکنید. دیگری یک دیمون دائمی app-server است که روی سوکت یونیکس گوش میدهد و کلاینتهای راه دور به آن وصل میشوند. مستندات رسمی codex remote-control را «یک جایگزین برای codex app-server --listen» نمیدانند و صریح میگویند جای ساختن کلاینت پروتکل محلی، برای کلاینتهای مدیریتشدهی راه دور و گردشکارهای SSH است [1].
همین تفکیک را رابط خط فرمان هم نشان میدهد. زیرشاخهی codex remote-control چهار فرمان دارد: start، stop، pair و اجرای مستقیم بدون زیرشاخه. هر کدام پرچم --json را میپذیرد و خروجی ماشینخوان میدهد.
پس اول دستور را میزنیم و نتیجه را همانجا میبینیم. پرچم --json روی خطا بیاثر است، چون خطا پیش از ساخت شیء JSON رخ میدهد.
# نسخهی فرمان خط فرمان را میخوانیم، نه نسخهی دیمون
$ codex --version
codex-cli 0.158.0
$ codex remote-control pair --json
Error: failed to connect to /root/.codex/app-server-control/app-server-control.sock
Caused by:
No such file or directory (os error 2)
# فرمان تشخیصی هم همان خطا را میدهد، چون از یک سوکت رد میشود
$ codex app-server daemon version
Error: failed to connect to /root/.codex/app-server-control/app-server-control.sock
Caused by:
No such file or directory (os error 2)
خروجی بالا دو چیز را با هم میگوید. سوکتی که خطا نام میبرد یک مسیر ثابت در ~/.codex/app-server-control/ است، و خطای os error 2 یعنی آن فایل که باید سوکت باشد هنوز وجود ندارد. نه دسترسی رد شده، نه سوکت شلوغ است. هیچ چیزی روی این مسیر نیست، چون دیمون هنوز بالا نیامده.
دیمون را بالا بیاورید
برای بالا آوردن، زیرشاخهی جداگانهی codex app-server daemon را داریم که کارش مدیریت چرخهی حیات همین سرور است. فرمان start بستهی دیمون را از مسیر خودش بالا میآورد.
# دیمون را بالا میآوریم؛ pid تازه و مسیر بستهی خودش برمیگردد
$ codex app-server daemon start
{"status":"started","backend":"pid","pid":1293038,"managedCodexPath":"/root/.codex/packages/app-server-daemon/current/bin/codex","managedCodexVersion":"0.160.1","socketPath":"/root/.codex/app-server-control/app-server-control.sock","cliVersion":"0.158.0","appServerVersion":"0.160.1"}
# حالا status از started به running عوض شد، ولی دو نسخه سر جایشان ماندند
$ codex app-server daemon version
{"status":"running","backend":"pid","managedCodexPath":"/root/.codex/packages/app-server-daemon/current/bin/codex","managedCodexVersion":"0.160.1","socketPath":"/root/.codex/app-server-control/app-server-control.sock","cliVersion":"0.158.0","appServerVersion":"0.160.1"}
تفاوت status در دو خط همان چیزی است که این پست را میسازد. در خط اول started داریم و یک pid تازه، در خط دوم running. در هر دو، دو نسخهی متفاوت در یک شیء JSON نشستهاند: cliVersion برابر 0.158.0 و appServerVersion برابر 0.160.1. این دو عدد از دو مسیر فایل جدا میآیند.
جدول زیر هر سه عددی را نشان میدهد که در همین اجرا خوانده شد. ستون آخر میگوید هر عدد از کجا آمده.
| مسیر گزارش | عدد | معنا |
|---|---|---|
فرمان codex | 0.158.0 | نشستی که شما در ترمینال باز میکنید |
| بستهی دیمون | 0.160.1 | فایل اجرایی که سرور را اجرا میکند |
| سرور در حال اجرا | 0.160.1 | نسخهای که همین حالا روی سوکت گوش میدهد |
نکتهای که عدد سوم را از عدد دوم جدا میکند این است که managedCodexVersion میگوید کدام بسته انتخاب شده و appServerVersion میگوید کدام نسخه الان روی سوکت زندگی میکند. این دو میتوانند از هم جدا باشند، دقیقا مثل دو عددی که در نوشتهی قفل کردن نسخهی دیمون کدکس روی خط فرمان اندازه گرفتیم. آن نوشته اختلاف نسخه را توضیح داد؛ اینجا میگوییم آن اختلاف دقیقا چه چیزی را از کار انداخته است.
خطای ثبتنام، نه خطای دسترسی
حالا که دیمون بالا است، همان فرمان را دوباره میزنیم. خطا عوض میشود، و این عوضشدن مهمترین درس این پست است.
# خطا عوض شد: دیمون زنده است، ثبتنام حساب نیست
$ codex remote-control pair --json
Error: remoteControl/pairing/start failed: remote control pairing is unavailable until enrollment completes
# این یکی نام میزبان را داخل خود پیام میگذارد
$ codex remote-control start --json
Error: Remote control is enabled on srv6473684731 but the connection is errored.
خطای اول دیگر از جنس «سوکت نیست» نیست. سوکت هست، دیمون زنده است، و کدکس توانسته درخواست را به لایهی راه دور برساند. چیزی که کم است یک ثبتنام در سمت حساب ChatGPT است، و این کار از ترمینال شما ساخته نیست.
مستندات رسمی میگویند پیوند از راه دور با نرمافزار دسکتاپ ChatGPT راهاندازی میشود، نه با فرمان. مسیر اعلامشده در تنظیمات همان نرمافزار است: مسیر Connections و گزینهی کنترل این رایانه، سپس راهاندازی یا افزودن. کد جفتشدن با اسکن از گوشی به دست میآید و هر دستگاه را باید جداگانه با هر میزبان جفت کنید [2].
برای همین نام فرمان دوم هم راهنماست. remote-control start روی این ماشین یعنی «کنترل از راه دور روشن است ولی اتصال خراب است» و نام میزبان srv6473684731 را داخل خود پیام میگذارد. این فرمان دیمون را از مسیر خودش بالا میآورد؛ کاری که app-server daemon start هم میکرد. یعنی خطا از خودِ اتصال میآید، نه از نبودِ دیمون.
اگر پیام خطا از این خانواده بود، راهحل هم در مستندات آمده است. اگر پس از ورود دوباره به حساب، کنترل از راه دور خاموش شد، خروج از حساب آن را خاموش میکند ولی جفتهای قبلی را پاک نمیکند، پس باید دوباره روشنش کنید. اگر باز هم خطا گرفتید، نرمافزار دسکتاپ را روی میزبان ریاستارت کنید [2].
کنترل از راه دور را خاموش کنید
پرچم روی دیسک زندگی میکند و دستکاریاش از راه فرمان انجام میشود. همان پوشهای که سوکت در آن ساخته میشود یک فایل تنظیمات هم دارد که فقط یک کلید دارد.
# پرچم روی دیسک است، پس اول وضعیتش را میبینیم
$ cat ~/.codex/app-server-daemon/settings.json
{
"remoteControlEnabled": true
}
# خاموشکردن؛ همان یک کلید در خروجی هم دیده میشود
$ codex app-server daemon disable-remote-control
{"status":"disabled","backend":"pid","remoteControlEnabled":false,"socketPath":"/root/.codex/app-server-control/app-server-control.sock","cliVersion":"0.158.0","appServerVersion":"0.160.1"}
# تغییر واقعی روی دیسک
$ cat ~/.codex/app-server-daemon/settings.json
{
"remoteControlEnabled": false
}
تغییر واقعی همین است: همان یک کلید از true به false رفت و مقدار status در خروجی فرمان هم با آن هماهنگ شد. اگر دیمون از قبل روشن بود، enable-remote-control وضعیت alreadyEnabled میدهد و چیزی را تغییر نمیدهد.
نکتهی مهم این است که خاموشکردن پرچم، خطا را عوض میکند ولی pair را سالم نمیکند. پس از خاموشکردن، pair همان پیام ثبتنام را میدهد، چون هیچکدام از دو چیزی که لازم است را فراهم نمیکند: نه ثبتنام حساب، نه اتصال درست.
| فرمان | وقتی دیمون پایین است | وقتی دیمون بالاست |
|---|---|---|
app-server daemon version | خطای سوکت | گزارش کامل دو نسخه |
remote-control pair | خطای سوکت | خطای ثبتنام |
remote-control start | خطای سوکت | خطای اتصال با نام میزبان |
این جدول همان چیزی است که دیاگراست. سه فرمان وقتی دیمون پایین است دقیقا یک خطا میدهند، چون هر سه از یک مسیر، یعنی همان سوکت، رد میشوند. وقتی دیمون بالا میآید، هر فرمان خطای مخصوص خودش را میدهد و دیگر نمیشود از روی پیام نتیجه گرفت که مشکل از کجاست.
مقدار راه دور
این فرمانها سطح حمله را کم میکنند، اما شبکه را باز نمیکنند. مستندات هشدار میدهند که اتصال از راه دور با SSH کار میکند و نباید انتقال app-server را مستقیم روی یک شبکهی مشترک یا عمومی بگذارید. اگر لازم دارید از بیرون شبکهی خودتان به یک ماشین دور برسید، راه درست ابزار VPN یا مش است، نه باز کردن خودِ app-server [2].
از آنطرف، منابعی که از میزبان به دستگاه شما میآیند همان منابع محلیاند. پروژهها، فایلها، فرمانها، سرورهای MCP، اسکیلها و دسترسی مرورگر همگی از همان میزبان خوانده میشوند. تنظیمات سندباکس و تأییدها هم روی نشست راه دور اعمال میشوند، یعنی باز بودن درِ ورود به پیوند راه دور به معنی دور زدن مجوزها نیست [2].
یک عدد دیگر هم هست که به درک این مسیر کمک میکند. یادداشت انتشار 0.160.0 که در ۱ اکتبر ۲۰۲۶ منتشر شد، مینویسد نشستهای محلیِ واجد شرایط میتوانند از بازیابی مجوزهای ذخیرهشده هنگام از سرگیری استفاده کنند و نشستها را بیرون از یک پروژه هم شروع کنند [3]. یعنی وضعیت این دیمون و نشستهای محلی به هم گره خوردهاند، و خرابی یکی روی دیگری اثر میگذارد.
سرانجام، خود فرمانها هم جای خودشان را در راهنمای رسمی دارند. مستندات فرمانها فهرستی از پرچمهای سراسری و زیرشاخهها را نگه میدارد، و صفحهی codex نشان میدهد نصب با نصبکنندهی مستقل برای لینوکس و مک از چه مسیری انجام میشود [7]. آن راهنما این دستورها را کنار فرمانهای تعاملی نشست، مثل فرمانهای اسلش برای مدیریت مدل و مجوز، میگذارد [4].
یک سؤال که بیپاسخ ماند
پیام خطای اتصال، نام میزبان را داخل متن خطا گذاشت: srv6473684731. روی ماشینی که این آزمون رویش اجرا شد، چنین میزبانی وجود نداشت، و پیش از خطا هم چیزی ساخته نشد که بتوان نامش را از آن خواند. از این رو نپرسیدیم این شناسه از کجا آمده و فقط همان رشته را همانطور که در خروجی بود نقل کردیم.
همچنین راه بستنِ pair روی یک حساب واقعی را آزمودیم، چون از خط فرمان ساخته نیست و به نرمافزار دسکتاپ ChatGPT نیاز دارد. هر چیزی دربارهی اینکه پس از ثبتنام چه شکلی از کد برمیگردد، در این نوشته از خودِ خروجی نیامده است. تنها چیزی که مستندات وعده میدهند این است که خروجی JSON فرمان pair چهار فیلد دارد: pairingCode، manualPairingCode، environmentId و expiresAt [1].
یک ادعای دیگر هم از خودِ حساب رسمی آمده و فقط اعلام است، نه اثبات. OpenAI در ۲۴ ژوئیه ۲۰۲۶ نوشت صدا در کدکس از اپ iOS با دسترسی راه دورِ جفتشده در دسترس است [6]. آنچه این اجرا نشان داد این است که بدون ثبتنام حساب، این فرمانها پیش از هر کار دیگری به خطا میخورند.
منابع
- Codex CLI — راهنمای فرمانها و گزینهها — بخش
codex remote-control - اتصالهای راه دور — راهاندازی، دامنهی دسترسی و عیبیابی
- یادداشت انتشار Codex 0.160.0 — ۱ اکتبر ۲۰۲۶
- فرمانهای اسلش در Codex CLI
- گفتوگوی کدکس: کنترل کدکس از اپ ChatGPT — ۱۷ سپتامبر ۲۰۲۶
- OpenAI در X: صدا در کدکس از اپ iOS با دسترسی راه دورِ جفتشده — ۲۴ ژوئیه ۲۰۲۶
- Codex CLI — نصب و راهاندازی
دیدگاهها
۰ موردهنوز دیدگاهی ثبت نشده. اولین نفر باشید.