کدکس یک مسیر کامل برای اجرای غیرتعاملی دارد: codex exec. در حالت پیش‌فرض روی سرور اشتراکی ما فقط خواندن می‌دهد، پیشرفت را به stderr می‌فرستد و متن نهایی را به یک فایل می‌نویسد. آنچه در راهنما نوشته نشده این است که این پیش‌فرض با تغییر یک خط در پرونده‌ی تنظیمات عوض می‌شود. در آزمونی که با خانه‌ی تازه اجرا کردم، سندباکس فقط‌خواندنی بود؛ همان فرمان در خانه‌ای که پروژه را مورد اعتماد کرده بود، دسترسی نوشتن به کار می‌داد. برای خط لوله‌ی ساخت این تفاوت یعنی رفتار شما به تاریخچه‌ی ماشین وابسته است، نه به کد شما.

برای اینکه کدکس را در خط لوله بگذارید، پنج چیز باید روشن باشد: چه چیزی به آن می‌دهید، چه چیزی از آن می‌گیرید، چه سطح دسترسی‌ای دارد، چگونه خرابی‌اش را تشخیص می‌دهید، و چرا نباید به آن اجازه‌ی نوشتن بی‌حد بدهید. این نوشته هر پنج را با اجرای واقعی نسخه‌ی ۰٫۱۵۴ نشان می‌دهد.

سه جریان، نه یکی

اولین چیزی که باید بدانید این است که خروجی خط فرمان یک جریان نیست، سه جریان جداست. سربرگ نشست و تمام پیام‌های پیشرفت روی جریان خطا می‌روند، متن نهایی روی جریان خروجی استاندارد، و اگر حالت ساختاریافته را روشن کنید، روی جریان خروجی استاندارد به‌صورت هر سطر یک شیء جیسون می‌آید.

<span class="code-comment"># جریان خطا: سربرگ نشست و هر پیام پیشرفت</span>
$ codex exec --ephemeral "این یک آزمون است" 2>&1 1>/dev/null | head -8
Reading prompt from stdin...
OpenAI Codex v0.154.0
--------
workdir: /root/probe-repo
model: gpt-6-astra
provider: openai
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR]
reasoning effort: none
--------

این تفکیک برای خط لوله حیاتی است. اگر همه را در یک فایل بریزید، متن نهایی مدل با ده خط سربرگ و پنجاه خط لاگ مخلوط می‌شود و هر چیزی که می‌خواهید بخوانید یا هر چیزی که می‌خواهید به‌عنوان خروجی ذخیره کنید، آلوده می‌شود. تفکیک درست سه مقصد است: سربرگ و لاگ در لاگ ساخت، و متن نهایی در یک متغیر یا یک فایل جدا.

حالت ساختاریافته برای پردازش برنامه‌ای لازم است. هر سطر یک شیء جیسون است و سه نوع رویداد دیده می‌شود: آغاز رشته، آغاز نوبت، و رویدادهای خطا. این قرارداد متعلق به خود کدکس است و در مستند app-server در مخزن رسمی تعریف شده است؛ اگر روزی نوع چهارمی اضافه شود، شما آن را از یک قرارداد مستند می‌خوانید نه از حدس.

<span class="code-comment"># هر سطر یک شیء مستقل است، پس با jq یا هر پردازشگر خطی خوانده می‌شود</span>
$ codex exec --ephemeral --json "این را اصلاح کن" 2>/dev/null | head -3
{"type":"thread.started","thread_id":"01a0e80f-ed34-7331-bb3a-0a907715b7e6"}
{"type":"turn.started"}
{"type":"error","message":"Reconnecting... 2/5 (unexpected status 401 Unauthorized...)"}

نکته‌ای که اینجا مهم است: رویداد خطا هم روی همین جریان می‌آید و روی جریان خطا نیست. یعنی اگر خط لوله شما فقط جریان خطا را بگیرد، درباره‌ی شکست هیچ نمی‌فهمد. در حالت جیسون باید رویدادهای نوع خطا را خودتان بشمارید و در صورت وجود، شکست را اعلام کنید.

پیش‌فرضی که با یک خط تنظیمات عوض می‌شود

مهم‌ترین یافته‌ی این بررسی رفتار پیش‌فرض سندباکس است. راهنما سه حالت را نام می‌برد: فقط‌خواندنی، نوشتن در فضای کار، و دسترسی کامل. در اجرای اول با یک خانه‌ی تازه‌ی تنظیمات، حالت فقط‌خواندنی بود. اما پس از آنکه همان ریپازیتوری در همان خانه مورد اعتماد قرار گرفت، همان فرمان با همان آرگومان‌ها نوشتن را فعال کرد.

مقصر یک خط در پرونده‌ی تنظیمات است که کدکس خودش نوشته بود. این تنها محتوای فایل بود و دقیقاً همان چیزی را نشان می‌دهد که تفاوت را توضیح می‌دهد.

<span class="code-comment"># پس از یک اجرای موفق، این فایل خودکار ساخته شده بود</span>
$ cat $CODEX_HOME/config.toml
[projects."/root/probe-repo"]
trust_level = "trusted"

آزمون را شش حالت اجرا کردم، هر کدام با خانه‌ی تازه تا هیچ تنظیم قبلی وارد نشود. نتیجه یک جدول است و از آن یک قاعده درمی‌آید.

حالت اجراسندباکس گزارش‌شدهسطح دسترسی
خانه‌ی تازه، ریپازیتوری بی‌اعتمادفقط‌خواندنینوشتن ممنوع
خانه‌ی تازه با پرچم پاک‌کردن بررسی گیتفقط‌خواندنینوشتن ممنوع
خانه‌ی تازه با پرچم صریح فقط‌خواندنیفقط‌خواندنینوشتن ممنوع
خانه‌ی تازه با پرچم صریح نوشتننوشتن در فضای کار، مسیر موقت و متغیر آننوشتن در ریپازیتوری
خانه‌ی تازه با دسترسی کاملدسترسی کاملبدون سندباکس
خانه‌ای که پروژه در آن مورد اعتماد استنوشتن در فضای کار، مسیر موقت و متغیر آننوشتن در ریپازیتوری

قاعده روشن است: به‌جز وقتی خودتان پرچم صریح می‌دهید، رفتار به مورد اعتماد بودن پروژه بستگی دارد. برای خط لوله این یعنی رفتار شما به تاریخچه‌ی ماشین وابسته است. یک اجرای محلی روی لپ‌تاپ شما می‌تواند پروژه را مورد اعتماد کند و همان‌جا بماند؛ اما کارگر تازه در خط لوله با همان فرمان، فقط‌خواندنی می‌ماند. یا برعکس، اگر کش را در حجم کار مشترک نگه دارید، همه‌ی کارگرها نوشتن می‌گیرند، حتی آن‌هایی که نباید.

این تنها چیزی نیست که به ماشین وابسته باشد. اگر همان فرمان را روی مسیری غیر از یک ریپازیتوری گیت اجرا کنید، خود فرمان خطا می‌دهد و کد بازگشت یک است، چون بررسی ریپازیتوری را رد نکرده‌اید. این رفتار خوبی است: در خط لوله، نبودِ ریپازیتوری باید یک شکست باشد نه یک هشدار.

نسخه‌ی ۰٫۱۵۴ راه دیگری هم دارد: پرچم نادیده‌گرفتن پرونده‌ی تنظیمات کاربر. با آن، احراز هویت همچنان از همان خانه خوانده می‌شود اما هیچ تنظیمی بارگذاری نمی‌شود. این پرچم برای خط لوله دقیقاً همان چیزی است که می‌خواهید، چون تضمین می‌کند پیکربندی محلی شما وارد اجرا نمی‌شود. همین منطق برای هر پیکربندی دیگری هم صدق می‌کند، و مستند mcp-server جایی است که می‌توانید ببینید این پیکربندی از چه فایلی و با چه ترتیبی بارگذاری می‌شود.

<span class="code-comment"># بدون پرونده‌ی تنظیمات، رفتار قابل پیش‌بینی می‌شود</span>
$ codex exec --ephemeral --ignore-user-config "hi" 2>&1 | grep -E "^sandbox:"
sandbox: read-only

کد بازگشت و تشخیص خرابی

خط لوله به یک چیز نیاز دارد که بتواند به آن تکیه کند: کد بازگشت. آزمون‌های من نشان دادند که رفتار بدون احراز هویت همیشه کد یک برمی‌گرداند، حتی وقتی سربرگ نشست را با موفقیت چاپ کرده است. یعنی یک اجرای ناموفق به‌راحتی می‌تواند شبیه یک اجرای موفق به نظر برسد اگر فقط به متن نگاه کنید.

<span class="code-comment"># هر سه حالت کد یک می‌دهند، و هر سه متفاوت‌اند</span>
$ codex exec --ephemeral "hi" >/dev/null 2>&1; echo "untrusted  -> $?"
1
$ cd /root && codex exec --ephemeral "hi" >/dev/null 2>&1; echo "no git     -> $?"
1
$ codex --strict-config -c bogus_key_xyz=1 exec "hi" 2>&1 | head -1
Error loading config.toml: unknown configuration field `bogus_key_xyz` in -c/--config override

سه شکست متفاوت، سه نشانه‌ی متفاوت. نبودِ ریپازیتوری پیش از هر درخواستی متوقف می‌شود. نبودِ اعتماد همان‌طور. و کلید ناشناخته در تنظیمات، پیش از ارسال هر چیزی به شبکه، خطا می‌دهد.

آخری برای خط لوله ارزشمندترین است، چون یک دروازه‌ی واقعی است. اگر نسخه‌ای از کدکس را ارتقا دهید و نام یک کلید تنظیمات را عوض کرده باشد، این حالت ساخت شکست می‌خورد پیش از آنکه سیلی از هزینه تولید شود، به‌جای آنکه بی‌صدا تنظیمات را نادیده بگیرد. تاریخچه‌ی تغییرات دقیقاً جایی است که این جابه‌جایی‌ها را می‌بینید، و یادداشت انتشار ۰٫۱۵۴ نشان می‌دهد هر انتشار چقدر کوچک و چقدر پرریسک است. همان نسخه را از زاویه‌ی ویژگی‌های تازه در سطح تلاش استدلال و چک‌اوت سنجیده‌ایم.

خروجی نهایی را در فایل بنویسید. پرچم مربوطه مسیر می‌گیرد و متن آخرین پاسخ مدل را آنجا می‌گذارد، که بعد از شکست هم فایل خالی ساخته می‌شود. این برای گزارش ساخت مفید است، چون متن نهایی را از لاگ‌های میانی جدا می‌کند.

اگر واقعاً باید بنویسد

بیشتر کارهایی که می‌خواهید به کدکس بسپارید، نیاز به نوشتن دارند: تولید کد، اصلاح اشکال، به‌روزرسانی وابستگی. در آن حالت سطح دسترسی را صریح بدهید و مسیر نوشتن را محدود کنید. اگر کارتان از جنس تماس با سرویس بیرونی است، همان قاعده را روی اتصال هم بگذارید: پیکربندی اتصال بیرون از خط لوله بماند و داخل خط لوله صریح داده شود، دقیقاً همان کاری که پرچم نادیده‌گرفتن پرونده‌ی تنظیمات می‌کند. راهنمای سرور MCP کدکس و مستند mcp-server هر دو نشان می‌دهند این پیکربندی از کجا بارگذاری می‌شود.

  • پرچم سطح سندباکس را همیشه صریح بدهید، حتی وقتی مطمئنید پیش‌فرض همان است. این کار خود را از تاریخچه‌ی ماشین جدا می‌کند.
  • پوشه‌ی کاری را با پرچم مسیر تعیین کنید تا نوشتن همان‌جا بماند، نه هرجا که فرایند دسترسی دارد.
  • برای نوشتن در مسیرهای بیرون از ریپازیتوری، پرچم افزودن مسیر را به‌کار ببرید و هر مسیر را جداگانه نام ببرید.
  • هرگز در خط لوله از پرچم دور زدن تأییدها و سندباکس استفاده نکنید، مگر اینکه بیرون از خط لوله خودتان یک سندباکس واقعی ساخته باشید.

اگر می‌خواهید کدکس تغییرات را بسازد و خودش آن‌ها را اعمال نکند، راه درست‌تر این است که اجازه‌ی نوشتن ندهید و از او بخواهید وصله را چاپ کند، سپس خودتان آن را اعمال کنید. این کار یک مزیت دارد که در هیچ پیکربندی‌ای نیست: اگر مدل اشتباه کند، شما آن را قبل از دیدن می‌بینید، و گام بعدی شما تصمیم می‌گیرد.

دو گزینه‌ی دیگر هم وجود دارد که در آزمون دیدم. پرچم بررسی خودکار، درخواست‌های تأیید را از مسیر بازبینی خودکار با سندباکس نوشتن عبور می‌دهد، که برای هر کاری که خودش تأیید می‌خواهد کند است. و پرچم اعتماد قلاب‌ها، اجازه می‌دهد قلاب‌های فعال بدون نیاز به اعتماد ذخیره‌شده اجرا شوند؛ این را فقط به‌کار ببرید اگر منبع قلاب‌ها را خودتان بازبینی کرده‌اید، چون این پرچم عمداً یک دروازه را کنار می‌زند.

در پایان، یک محدودیت را صریح بگویم. همه‌ی عددهای این نوشته از اجرای نسخه‌ی ۰٫۱۵۴ روی یک ماشین بدون احراز هویه به دست آمده‌اند. شکست‌های شبکه‌ای که دیدم با خطای مجوز همراه بودند، پس مسیر خرابی احراز هویه و مسیر خرابی کار را کامل از هم جدا نکرده‌ام. آنچه اندازه‌گیری شده رفتار محلی است: کد بازگشت، گزارش سربرگ، مسیرها و مرزهای سندباکس. این‌ها در نسخه‌ی بعد می‌توانند عوض شوند، پس اگر خط لوله‌ی شما به آن‌ها تکیه می‌کند، آن را در آزمون خودتان هم نگه دارید.

منابع

  1. یادداشت انتشار نسخه‌ی ۰٫۱۵۴٫۰ — نسخه‌ای که همه‌ی عددهای این نوشته از اجرای آن به دست آمده‌اند
  2. مستند app-server — جایی که قرارداد رویدادهای ساختاریافته تعریف می‌شود
  3. راهنمای app-server — توضیح ساختار نشست و جریان رویدادها
  4. تاریخچه‌ی تغییرات — جایی که می‌شود دید یک پرچم چه زمانی پیش‌فرض شده است
  5. انتشار ۰٫۱۵۷٫۱ — تازه‌ترین نسخه‌ای که رفتار غیرفوقی آن سنجیده شد
  6. انتشار ۰٫۱۵۶٫۰ — یک پله در فاصله‌ی همین بازه
  7. انتشار ۰٫۱۵۵٫۰ — یک پله در فاصله‌ی همین بازه
  8. انتشار ۰٫۱۵۳٫۴ — آخرین نسخه‌ی پیش از ۰٫۱۵۴
  9. درخواست ادغام ۳۹۶۵۷ — نمونه‌ی عینی هشدار منسوخ‌شدن یک زیرفرمان
  10. درخواست ادغام ۴۲۹۹۳ — نمونه‌ی عینی حذف کامل همان زیرفرمان در نسخه‌ای بعد