عامل کدنویسی OpenAI، Codex، در سطوح مختلفی موجود است: برنامه وب(در یک پنجره جدید باز میشود)، رابط خط فرمان (CLI)(در یک پنجره جدید باز میشود)، افزونه IDE(در یک پنجره جدید باز میشود) و برنامه جدید Codex برای macOS. در پشت صحنه، همه آنها با همان چارچوب Codex—حلقهٔ عامل و منطقی که زیربنای همهٔ تجربههای Codex است—تأمین میشوند. پیوند حیاتی بین آنها چیست؟ Codex App Server(در یک پنجره جدید باز میشود)، یک API JSON-RPC1 دوطرفه و کاربرپسند.
در این پست، Codex App Server را معرفی میکنیم؛ آموختههای خود تا اینجا را درباره بهترین راهها برای آوردن قابلیتهای Codex به محصول شما به اشتراک میگذاریم تا به کاربران شما کمک کنیم گردشکارهایشان را بهطور چشمگیری تقویت کنند. ما به بررسی معماری و پروتکل App Server و نحوهٔ یکپارچهسازی آن با سطوح مختلف Codex خواهیم پرداخت، همچنین نکاتی دربارهٔ استفاده از Codex ارائه خواهیم داد، چه بخواهید Codex را به یک بازبین کد، یک عامل SRE، یا یک دستیار کدنویسی تبدیل کنید.
قبل از اینکه وارد معماری شوید، بهتر است پیشزمینه App Server را بدانید. در ابتدا، App Server راهی عملی برای استفاده مجدد از هارنس Codex در محصولات مختلف بود که بهتدریج تکامل یافت و به پروتکل استاندارد ما تبدیل شد.
Codex CLI بهعنوان یک TUI (رابط کاربری ترمینال) شروع شد، به این معنی که Codex از طریق ترمینال قابل دسترسی است. هنگامی که افزونه VS Code را ساختیم (روشی سازگارتر با IDE برای تعامل با عاملهای Codex)، به روشی نیاز داشتیم تا از همان ابزار استفاده کنیم تا بتوانیم همان حلقه عامل را از یک رابط کاربری در IDE بدون پیادهسازی مجدد آن راهاندازی کنیم. این به معنای پشتیبانی از الگوهای تعامل غنی فراتر از درخواست/پاسخ بود؛ مانند کاوش در فضای کاری، پخش پیشرفت همزمان با استدلال عامل، و تولید تفاوتها. ما ابتدا با آزمایش Codex بهعنوان یک سرور MCP(در یک پنجره جدید باز میشود) شروع کردیم، اما حفظ معناشناسی MCP به گونهای که برای VS Code منطقی باشد، دشوار بود. در عوض، ما یک پروتکل JSON-RPC معرفی کردیم که حلقهٔ TUI را بازتاب میداد و به اولین نسخهٔ غیر رسمی(در یک پنجره جدید باز میشود) از App Server تبدیل شد. در آن زمان، انتظار نداشتیم که مشتریان دیگر به App Server وابسته شوند، بنابراین بهعنوان یک API پایدار طراحی نشده بود.
با افزایش پذیرش Codex در چند ماه آینده، تیمهای داخلی و شرکای خارجی خواستار این بودند که بتوانند همان ابزار را در محصولات خود تعبیه کنند تا جریانهای کاری توسعه نرمافزار کاربرانشان را تسریع بخشند. برای مثال، JetBrains و Xcode خواستار تجربهای در سطح IDE برای عاملها بودند، در حالی که اپلیکیشن دسکتاپ Codex نیاز داشت تا بسیاری از عاملهای Codex را بهصورت موازی هماهنگ کند. آن تقاضاها ما را وادار کرد تا سطحی از پلتفرم را طراحی کنیم که هم محصولات ما و هم یکپارچهسازیهای شرکایمان بتوانند در طول زمان با اطمینان به آن تکیه کنند. لازم بود ادغام آن آسان باشد و با نسخههای قبلی سازگار باشد، به این معنا که میتوانستیم پروتکل را بدون اینکه کلاینتهای موجود را مختل کنیم، توسعه دهیم.
در ادامه، توضیح خواهیم داد که چگونه معماری و پروتکل را طراحی کردیم تا مشتریان مختلف بتوانند از همان هارنس استفاده کنند.
ابتدا، بیایید روی آنچه داخل مهار Codex است و اینکه Codex App Server چگونه آن را در اختیار کلاینتها قرار میدهد، زوم کنیم. در آخرین وبلاگ Codex، ما حلقه عامل اصلی را که تعامل بین کاربر، مدل و ابزارها را هماهنگ میکند، تحلیل کردیم. این منطق اصلی هستهای Codex است، اما تجربهٔ کامل عامل شامل موارد بیشتری است:
1. چرخه عمر و ماندگاریِ رشته. یک رشته مکالمهای در Codex بین یک کاربر و یک عامل است. Codex رشتهها را ایجاد، از سرگیری، فورک و بایگانی میکند و تاریخچه رویدادها را حفظ میکند تا مشتریان بتوانند دوباره متصل شوند و یک خط زمانی سازگار را ارائه دهند.
2. پیکربندی و احراز هویت. Codex پیکربندی را بارگذاری میکند، پیشفرضها را مدیریت میکند و جریانهای احراز هویت مانند «Sign in with ChatGPT» را اجراء میکند، از جمله وضعیت اعتبار نامه.
3. اجرای ابزار و توسعهها. Codex ابزارهای shell/file را در یک محیط ایزوله اجراء میکند و یکپارچهسازیهایی مانند سرورهای MCP و مهارتها را متصل میکند تا بتوانند تحت یک مدل سیاستگذاری یکسان در چرخه عامل مشارکت کنند.
تمام منطق عامل که اینجا به آن اشاره کردیم، از جمله حلقهٔ اصلی عامل، در بخشی از کدبیس Codex CLI قرار دارد که «Codex core(در یک پنجره جدید باز میشود)» نامیده میشود. هستهٔ Codex هم یک کتابخانه است که تمام کدهای عامل در آن قرار دارد و هم یک زماناجراء که میتوان آن را راهاندازی کرد تا حلقهٔ عامل را اجراء کند و پایداری یک رشتهٔ Codex (گفت و گو) را مدیریت کند.
برای اینکه مفید باشد، Codex باید برای مشتریان قابل دسترسی باشد. اینجاست که سرور اپلیکیشن وارد عمل میشود.
سرور برنامه هم پروتکل JSON-RPC بین کلاینت و سرور است و هم یک فرآیند طولانیمدت که رشتههای اصلی Codex را میزبانی میکند. همانطور که از نمودار بالا مشاهده میکنیم، یک فرآیند App Server چهار مؤلفهٔ اصلی دارد: خوانندهٔ stdio، پردازشگر پیام Codex، مدیر ترد، و تردهای هستهای. مدیر رشته برای هر رشته یک جلسه هستهای را راهاندازی میکند و سپس پردازشگر پیام Codex بهطور مستقیم با هر جلسه هستهای ارتباط برقرار میکند تا درخواستهای مشتری را ارسال کند و بهروزرسانیها را دریافت کند.
یک درخواست مشتری میتواند به بهروز رسانیهای متعدد رویداد منجر شود، و این رویدادهای دقیق همان چیزی هستند که به ما امکان میدهند یک رابط کاربری غنی بر روی سرور اپلیکیشن بسازیم. علاوه بر این، خوانندهٔ stdio و پردازشگر پیام Codex بهعنوان لایهٔ ترجمه بین مشتری و رشتههای اصلی Codex عمل میکنند. آنها درخواستهای JSON-RPC مشتری را به عملیاتهای اصلی Codex تبدیل میکنند، به جریان رویداد داخلی هستهٔ Codex گوش میدهند، و سپس آن رویدادهای سطح پایین را به مجموعهای کوچک از اعلانهای JSON-RPC پایدار و آمادهٔ رابط کاربری تبدیل میکنند.
پروتکل JSON-RPC بین کلاینت و سرور اپلیکیشن کاملاً دو طرفه است. یک رشته معمولی شامل یک درخواست مشتری و اعلانهای متعدد سرور است. علاوه بر این، سرور میتواند زمانی که عامل به ورودی نیاز دارد، مانند یک تأیید، درخواستهایی را آغاز کند و سپس نوبت را تا زمانی که مشتری پاسخ دهد، متوقف نماید.
سپس، اجزای اولیه مکالمه، یعنی بلوکهای سازنده پروتکل App Server را تجزیه و تحلیل خواهیم کرد. طراحی یک API برای حلقه عامل چالشبرانگیز است زیرا تعامل کاربر/عامل به سادگی یک درخواست/پاسخ نیست. یک درخواست کاربر میتواند به یک دنبالهٔ ساختار یافته از اقدامها تبدیل شود که کلاینت باید آن را بهطور وفادارانه بازنمایی کند: ورودی کاربر، پیشرفت تدریجی عامل، مصنوعات تولید شده در طول مسیر (مثلاً تفاوتها). برای اینکه آن جریان تعامل بهراحتی قابل ادغام باشد و در سراسر رابطهای کاربری مقاوم بماند، به سه عنصر اصلی با مرزها و چرخههای عمر مشخص رسیدیم:
۱. آیتم: آیتم واحد اتمیِ ورودی/خروجی در Codex است. اقلام دستهبندی میشوند (مثلاً، پیام کاربر، پیام عامل، اجرای ابزار، درخواست تأیید، diff) و هرکدام چرخهٔ عمر مشخصی دارند:
آیتم/شروع شدهزمانی که آیتم شروع میشود- رویدادهای اختیاری
آیتم/*/دلتابهعنوان جریانهای محتوا برای (انواع آیتمهای جریانی) آیتم/تکمیل شدهزمانی که مورد با بار نهایی خود به پایان میرسد
این چرخهٔ عمر به مشتریان اجازه میدهد بلافاصله روی شروع شده رندر را شروع کنند، بهروزرسانیهای افزایشی را روی دلتا بهصورت جریانی دریافت کنند، و روی تکمیل شده نهاییسازی کنند.
۲. نوبت: نوبت یک واحد از کار عامل است که با ورودی کاربر آغاز میشود. این فرآیند زمانی آغاز میشود که مشتری یک ورودی ارسال میکند (برای مثال، «اجرای تستها و خلاصهسازی خطاها») و زمانی پایان مییابد که عامل تولید خروجیها برای آن ورودی را به پایان میرساند. یک نوبت شامل دنبالهای از موارد است که گامهای میانی و خروجیهای تولید شده در طول مسیر را نشان میدهد.
۳. رشته: تاپیک، ظرفِ بادوام برای یک جلسه در حال انجام Codex بین یک کاربر و یک عامل است. این شامل چندین چرخش است. رشتهها میتوانند ایجاد شوند، از سر گرفته شوند، منشعب شوند و بایگانی شوند. تاریخچهٔ رشته ذخیره میشود تا مشتریان بتوانند دوباره متصل شوند و یک جدول زمانی سازگار را نمایش دهند.
اکنون، به یک مکالمه سادهشده بین یک مشتری و یک عامل نگاه میکنیم، که در آن مکالمه با عناصر ابتدایی نمایش داده میشود:
در ابتدای مکالمه، کلاینت و سرور باید دستدهی آماده سازی را برقرار نمایند. مشتری باید پیش از هر روش دیگری یک درخواست آماده سازی واحد ارسال کند و سرور با یک پاسخ آن را تأیید میکند. این به سرور فرصتی میدهد تا قابلیتها را اعلام کند و به هر دو طرف اجازه میدهد پیش از آغاز کار واقعی، درباره نسخهبندی پروتکل، پرچمهای ویژگی و پیشفرضها به توافق برسند. در اینجا یک نمونه بار مفید از افزونه VS Code شرکت OpenAI آمده است:
این همان چیزی است که سرور برمیگرداند:
هنگامی که یک کلاینت درخواست جدیدی میکند، ابتدا یک ترد و سپس یک نوبت ایجاد میکند. سرور اعلانهایی را برای پیشرفت ارسال خواهد کرد (رشته/شروع و چرخش/شروع شده). همچنین ورودیهایی را که بهعنوان اقلام ثبت میکند، مانند پیام کاربر در اینجا، بازمیفرستد.
فراخوانیهای ابزار همچنین بهعنوان آیتمها به مشتری بازگردانده میشوند. علاوه بر این، سرور ممکن است قبل از اینکه بتواند با ارسال یک درخواست سرور یک اقدام را اجراء کند، تأیید مشتری را درخواست کند. تأیید، نوبت را تا زمانی که مشتری با یکی از گزینههای «اجازه» یا «رد» پاسخ دهد، متوقف میکند. این همان چیزی است که جریان تأیید در افزونه VS Code به نظر میرسد:

در نهایت، سرور یک پیام عامل ارسال میکند و سپس نوبت را با چرخش/تکمیل شده به پایان میرساند. رویدادهای دلتا پیام عامل، بخشهایی از پیام را بهصورت جریانی بازمیگردانند تا زمانی که پیام با آیتم/تکمیل شده نهایی شود.
پیامها در نمودار برای افزایش خوانایی سادهسازی شدهاند. اگر میخواهید JSON مربوط به یک نوبت کامل را ببینید، میتوانید کلاینت تست را از مخزن Codex CLI اجراء نمایید:
اکنون، بیایید بررسی کنیم که سطوح مختلف کلاینت چگونه Codex را از طریق App Server تعبیه میکنند. ما سه الگو را پوشش خواهیم داد: برنامههای محلی و IDEها، محیط اجرای وب Codex، و TUI.
در هر سه مورد، انتقال JSON-RPC از طریق stdio (JSONL) انجام میشود. JSON-RPC ساخت اتصالهای کلاینت را در زبان دلخواه شما آسان میکند. سطوح Codex و یکپارچهسازیهای شریک، کلاینتهای App Server را در زبانهایی از جمله Go، Python، TypeScript، Swift و Kotlin پیادهسازی کردهاند. برای TypeScript، شما میتوانید با اجرای دستور زیر، تعریفها را مستقیماً از پروتکل Rust تولید کنید:
برای زبانهای دیگر، میتوانید یک بستهٔ JSON الگو تولید کنید و آن را به مولد کد دلخواه خود با اجرای دستور زیر بدهید:

کلاینتهای محلی معمولاً یک باینری App Server مخصوص پلتفرم را بستهبندی یا واکشی میکنند، آن را بهعنوان یک فرآیند فرزند طولانیمدت اجراء میکنند و یک کانال ورودی/خروجی استاندارد دوطرفه را برای JSON-RPC باز نگه میدارند. برای مثال، در افزونه VS Code و برنامه دسکتاپ ما، آرتیفکت ارسالشده شامل باینری Codex مخصوص پلتفرم است و به یک نسخه آزمایششده پین میشود تا مشتری همیشه دقیقاً همان بیتهایی را اجراء کند که ما اعتبارسنجی کردهایم.
هر ادغامی نمیتواند بهروزرسانیهای مشتری را بهطور مکرر منتشر کند. برخی از شرکا مانند Xcode چرخههای انتشار را با ثابت نگه داشتن کلاینت و اجازه دادن به آن برای اشاره به یک باینری جدیدتر از سرور اپلیکیشن در صورت نیاز، از هم جدا میکنند. به این ترتیب آنها میتوانند بهبودهای سمت سرور (برای مثال، فشردهسازی خودکار بهتر در هستهٔ Codex یا کلیدهای پیکربندیِ تازه پشتیبانیشده) را به کار بگیرند و رفع باگها را بدون انتظار برای انتشار نسخهٔ کلاینت عرضه کنند. سطح JSON-RPC سرور اپلیکیشن به گونهای طراحی شده است که با نسخههای قبلی سازگار باشد، بنابراین کلاینتهای قدیمیتر میتوانند با اطمینان با سرورهای جدیدتر ارتباط برقرار کنند.

Codex وب از مهار Codex استفاده میکند، اما آن را در یک محیط کانتینری اجراء میکند. یک کارگر یک کانتینر را با فضای کاری بررسیشده فراهم میکند، فایل اجرایی سرور برنامه را درون آن اجراء میکند و یک JSON-RPC طولانیمدت را بر روی کانال stdio2 حفظ میکند. اپلیکیشن وب (که در تب مرورگر کاربر اجراء میشود) از طریق HTTP و SSE با بکاند Codex ارتباط برقرار میکند و رویدادهای وظیفهای که توسط کارگر تولید میشوند را بهصورت جریانی ارسال میکند. این کار رابط کاربری سمت مرورگر را سبک نگه میدارد و در عین حال یک زمان اجرای یکپارچه در دسکتاپ و وب به ما ارائه میدهد.
زیرا نشستهای وب موقتی هستند (تبها بسته میشوند، شبکهها قطع میشوند)، اپلیکیشن وب نمیتواند منبع معتبر برای وظایف طولانیمدت باشد. نگهداشتن وضعیت و پیشرفت روی سرور به این معناست که کار حتی اگر تب ناپدید شود نیز ادامه مییابد. پروتکل استریمینگ و نشستهای ذخیرهشدهٔ رشته باعث میشوند یک جلسهٔ جدید بهراحتی دوباره متصل شود، از همان جایی که متوقف شده بود ادامه دهد و بدون بازسازی وضعیت در کلاینت، عقبماندگی را جبران کند.

از نظر تاریخی، TUI یک کلاینت «بومی» بود که در همان فرآیند حلقه عامل اجراء میشد و بهجای پروتکل app-server، مستقیماً با انواع اصلی Rust ارتباط برقرار میکرد. این کار تکرارهای اولیه را سریع کرد، اما همچنین باعث شد TUI به یک سطح خاص تبدیل شود.
اکنون که App Server وجود دارد، قصد داریم TUI را بازسازی کنیم(در یک پنجره جدید باز میشود) تا از آن استفاده کند و مانند هر کلاینت دیگری عمل کند: یک فرآیند فرزند App Server را راهاندازی کند، JSON-RPC را از طریق stdio اجراء کند، و همان رویدادهای پخش جریانی و تأییدیهها را رندر کند. این امکان گردشکارهایی را فراهم میکند که در آن TUI میتواند به یک سرور Codex که روی یک ماشین راهدور در حال اجراست متصل شود، عامل را نزدیک به منابع محاسباتی نگه دارد و حتی اگر لپتاپ به حالت خواب برود یا قطع اتصال کند، کار را ادامه دهد، در حالی که همچنان بهصورت محلی بهروزرسانیهای زنده و کنترلها را ارائه میدهد.
سرور Codex App به عنوان روش یکپارچهسازی درجهیک و اصلی که از این پس نگهداری خواهیم کرد، خواهد بود، اما روشهای دیگری نیز با قابلیتهای محدودتر وجود دارند. بهطور پیشفرض، توصیه میکنیم مشتریان برای یکپارچهسازی با Codex از Codex App Server استفاده کنند، اما ارزش دارد روشهای مختلف یکپارچهسازی را بررسی نمایید و مزایا و معایب آنها را درک نمایید. در زیر رایجترین روشها برای هدایت Codex و زمان مناسب بودن هر یک آمده است.
دستورات codex mcp-server(در یک پنجره جدید باز میشود) را اجراء کنید و از هر کلاینت MCP که از سرورهای stdio پشتیبانی میکند (مانند OpenAI Agents SDK(در یک پنجره جدید باز میشود)) متصل شوید. این گزینه انتخاب مناسبی است اگر از قبل یک جریان کاری مبتنی بر MCP دارید و میخواهید Codex را بهعنوان یک ابزار قابل فراخوانی فراخوانی کنید. نکته منفی این است که شما فقط به آنچه MCP در معرض قرار میدهد دسترسی دارید، بنابراین تعاملات خاص Codex که به معناشناسی غنیتر نشست متکی هستند (مثلاً بهروزرسانیهای diff) ممکن است از طریق نقاط پایان MCP بهخوبی نگاشت نشوند.
برخی اکوسیستمها یک رابط قابل حمل ارائه میدهند که میتواند چندین ارائهدهنده مدل و زمانهای اجراء را هدف قرار دهد. اگر به یک انتزاع واحد نیاز دارید که چندین عامل را هماهنگ کند، این میتواند گزینه مناسبی باشد. معامله این است که این پروتکلها اغلب بر روی زیرمجموعهٔ مشترک قابلیتها همگرا میشوند، که میتواند نمایش تعاملات غنیتر را دشوارتر کند، بهویژه زمانی که معناشناسی ابزار و جلسهٔ خاص ارائهدهنده اهمیت دارد. این فضا بهسرعت در حال تکامل است و ما انتظار داریم که با مشخص شدن بهترین اجزای پایه برای نمایش گردشهای کاری عامل در دنیای واقعی، استانداردهای رایجتری پدیدار شوند (مهارتها(در یک پنجره جدید باز میشود) نمونهٔ خوبی از این موضوع است).
App Server را انتخاب کنید وقتی که میخواهید مهار کامل Codex بهصورت یک جریان رویداد پایدار و سازگار با UI در معرض قرار گیرد. شما به تمامی قابلیتهای کامل حلقه عامل و سایر ویژگیهای پشتیبان مانند ورود با ChatGPT، کشف مدل و مدیریت پیکربندی دسترسی دارید. هزینهٔ اصلی کارِ یکپارچهسازی است، زیرا باید binding سمتِ کلاینتِ JSON-RPC را به زبان خودتان بسازید. در عمل، با این حال، Codex قادر است بخش زیادی از کارهای سنگین را انجام دهد اگر الگوی JSON و مستندات را به آن ارائه دهید. بسیاری از تیمهایی که با آنها کار کردیم توانستند با استفاده از Codex بهسرعت به یکپارچهسازی عملی دست یابند.
یک حالت CLI سبک و قابل اسکریپتنویسی برای وظایف یکباره و اجرای CI. این گزینه برای اتوماسیون و پایپلاینهایی که میخواهید یک فرمان واحد بهصورت غیر تعاملی تا پایان اجراء شود، خروجی های دارای ساختار را برای لاگها بهصورت جریانی ارسال کند و با یک سیگنال واضح از موفقیت یا شکست خارج شود، بسیار مناسب است.
یک کتابخانهٔ TypeScript برای کنترل برنامهنویسی عاملهای محلی Codex از درون برنامهٔ خودتان. بهترین گزینه است زمانی که میخواهید برای ابزارها و گردشهای کاری سمت سرور، یک رابط کتابخانهای بومی داشته باشید، بدون اینکه یک کلاینت JSON-RPC جداگانه بسازید. از آنجا که زودتر از App Server عرضه شد، در حال حاضر از زبانهای کمتری پشتیبانی میکند و سطح پوشش کمتری دارد. اگر توسعهدهندگان علاقهمند باشند، ممکن است SDKهای بیشتری اضافه کنیم که پروتکل App Server را بپوشانند تا تیمها بتوانند بدون نوشتن bindingهای JSON-RPC، بخش بیشتری از سطح harness را بپوشانند.
در این پست، به اشتراک گذاشتیم که چگونه به طراحی یک استاندارد جدید برای تعامل با عاملها نزدیک میشویم و چگونه زیرساخت Codex را به یک پروتکل پایدار و مشتریپسند تبدیل کنیم. ما توضیح دادیم که چگونه سرور اپلیکیشن هستهٔ Codex را در معرض قرار میدهد، به مشتریان اجازه میدهد حلقهٔ کامل عامل را هدایت کنند، و طیف گستردهای از سطوح از جمله TUI، یکپارچهسازیهای محلی IDE و زمان اجرای وب را قدرت میبخشد.
اگر این موضوع ایدههایی برای یکپارچهسازی Codex در جریانهای کاریتان ایجاد کرد، ارزشش را دارد که App Server را امتحان کنید. تمام کد منبع در مخزن متنباز Codex CLI ریپو(در یک پنجره جدید باز میشود) قرار دارد. لطفاً بازخورد و درخواستهای ویژگی خود را به اشتراک بگذارید. ما هیجانزدهایم که از شما بشنویم و به قابلدسترستر کردن نمایندگان برای همه ادامه دهیم.
نویسنده
قدردانیها
تشکر ویژه از Michael Bolin، Owen Lin، Eric Traut و Rasmus Rygaard که در این پست مشارکت داشتند، و از کل تیم Codex که روی سرور اپلیکیشن کار کردند.
پاورقی
- 1
ما از یک گونه «JSON‑RPC lite» استفاده میکنیم: شکل درخواست/پاسخ/اعلان را حفظ میکند، اما
«jsonrpc»: «2.0»را حذف میکند هدر و بهصورت JSONL روی stdio قاببندی شده است، نه بهصورت JSON‑RPC 2.0 سختگیرانه. - 2
«stdio» به stdin/stdout سرور اپلیکیشن در داخل کانتینر اشاره دارد. در تنظیمات میزبانیشده، این جریانها اغلب از طریق یک اتصال شبکه پایدار (مثلاً شبیه WebSocket) به زمان اجرای کانتینر تونل میشوند—بنابراین طوری رفتار میکند که گویی stdio است، حتی اگر یک لوله محلی واقعی نباشد.


