Am scris deja tirada cu „de ce am smuls instalația din propriile repo-uri și am construit o imagine de bază” în postarea despre aicodebox, deci n-o mai fac o dată aici. Pe scurt: am construit claudebox pentru Claude Code, i-am crescut un API HTTP, un endpoint compatibil cu OpenAI, un server MCP, un bot de Telegram și un scheduler de cron, apoi am vrut exact același lucru pentru un al doilea agent și am refuzat să copiez nouă sute de linii de FastAPI și de randare de markdown pentru Telegram într-un repo nou a treia oară. Așa că instalația a fost smulsă în aicodebox, o imagine de bază agnostică la agent, iar fiecare agent anume a devenit o imagine copil subțire care doar implementează o clasă de adaptor.
codexbox e adaptorul ăla îndreptat spre Codex CLI de la OpenAI. Aceeași bază, aceeași suprafață REST, același bot de Telegram, același scheduler de cron, singurul lucru chiar nou aici e codul care traduce o cerere generică de „rulează promptul ăsta” în ce combinație nebună de flaguri vrea Codex în ziua aia, plus dansul de autentificare din jurul a două feluri complet diferite de a plăti pentru el.
Problema Anume Cu CLI-ul de la OpenAI
Codex CLI e în regulă. Ergonomia lui Codex CLI ca ceva la care dai shell programatic e altă poveste. Câteva puncte din cititul efectiv al chestiei în loc să am încredere în documentație:
- Crapă la pornire din cauza unui director care nu există. Setezi
CODEX_HOMEpe o cale și nu o creezi dinainte, iar codex pur și simplu dă eroare în loc să facămkdir -pîntr-o linie, cum face literalmente orice alt CLI de pe planetă pentru tine. Mai multe mai jos, pentru că e cel mai bun și cel mai prost detaliu din tot repo-ul. - Flagul de ocolire a sandboxului se citește ca o notă legală.
--dangerously-bypass-approvals-and-sandbox. Nu--yolo, nu-y, o propoziție întreagă, probabil ca să nu poată pretinde nimeni că nu știa ce face când containerul lui s-a apucat să dea rm -rf pe ceva. - Reluarea e o subcomandă, nu un flag. Orice alt CLI de agent pe care l-am legat ia
--continuesau--resume <id>ca flag pe comanda normală de rulare. Codex te pune să chemiexec resume <id>sauexec resume --lastca verb distinct, ceea ce înseamnă că adaptorul trebuie să construiască o formă de argv complet diferită după cum reiei sau nu. - Nu există un comutator de „oprește toate uneltele”.
update_plane necondiționat, iarapply_patchrămâne cât timp există un mediu local, deci un mod fără unelte adevărat înseamnă să scoți uneltele de shell și de căutare web din config și să forțezi sandboxul doar în citire ca măsură de siguranță dublă, pentru că doar configul nu îl poate castra complet. - Ieșirea structurată ia doar o cale de fișier. Codex are validare nativă de schemă JSON prin
--output-schema, ceea ce e sincer mai bine decât ce primesc celelalte adaptoare de pe baza asta, nu sunt necesare reîncercări de autocorectare, dar acceptă doar un fișier de pe disc, nu o schemă inline, deci adaptorul trebuie să îți scrie schema într-un fișier temporar la fiecare apel. - Propriul lui flux JSON te minte că e JSON. Rulezi
codex exec --jsonși primești un flux JSONL deThreadEventpe stdout, doar că tot codex intercalează și linii de log în text simplu de formaERROR ...direct în același stdout. N-ai voie să presupui că fiecare linie se parsează. Parsezi, prinzi eșecul de decodare, îl numeri, mergi mai departe.
Nimic din asta nu îl face pe Codex prost. Îl face un CLI construit pentru un om care scrie la un terminal, nu pentru un program care îi dă shell în buclă, adică exact golul pe care adaptorul există ca să îl acopere.
Patru Variabile de Mediu Și Un mkdir
Dockerfile-ul setează exact patru variabile de mediu ca să lege adaptorul la imaginea de bază, toate într-un singur bloc:
ENV AICODEBOX_ADAPTER=codexbox.adapter:CodexAdapter
AICODEBOX_AGENT_BINARY=codexbox-agent
CODEXBOX_IMAGE_VARIANT=minimal
CODEX_HOME=/home/aicode/.codexAICODEBOX_ADAPTER îndreaptă mașinăria de încărcare de adaptoare a bazei spre CodexAdapter, ca să știe cum să construiască argv anume pentru codex. AICODEBOX_AGENT_BINARY bagă un script de lansare în loc să cheme codex direct pentru uz interactiv și de passthrough (de ce, mai jos). CODEXBOX_IMAGE_VARIANT doar semnalează pe ce imagine ești (minimal aici, full în buildul cu toolchain). Iar CODEX_HOME e cea care chiar contează, din cauza crashului ăla la pornire de care ziceam.
Codex citește auth.json, config.toml și fișierele de rulare a sesiunilor din $CODEX_HOME. Dacă variabila aia nu e setată, codex cade pe o valoare implicită rezonabilă. Dar dacă e setată, și trebuie să fie aici, pentru că tot rostul e să o montezi de pe host ca un login să supraviețuiască unui container făcut praf și recreat, iar directorul spre care arată nu există încă, codex refuză să pornească. Nu un avertisment, nu o creare automată, o eroare dură. Așa că imediat după blocul ENV, Dockerfile-ul face asta:
RUN mkdir -p /home/aicode/.codex && chown -R aicode:aicode /home/aicode/.codexUn singur mkdir -p și un chown, coapte în imagine la build, pur și simplu ca un CLI scris de o companie cu o sută de miliarde de dolari în bancă să nu se prăbușească prima dată când îl îndrepți spre un bind mount proaspăt. Ăsta e sincer detaliul meu preferat din tot repo-ul, nu pentru că ar fi deștept, e opusul a deștept, e o cârpeală pentru un apel de mkdir care lipsește. Dar e genul de lucru pe care îl găsești doar citind efectiv Dockerfile-ul în loc să crezi un README, și explică de ce CODEX_HOME e precreat în loc să fie doar declarat și lăsat pe seama lui codex.
Ce Implementează De Fapt CodexAdapter
Contractul de adaptor din imaginea de bază îți dă o mână de metode de completat, iar CodexAdapter le implementează pe toate: validate (respinge valori necunoscute de efort de raționament, avertizează și ignoră o listă de unelte permise pentru care codex n-are echivalent), build_argv (carnea, traduce o cerere generică de rulare în supa reală de flaguri a lui codex), translate_auth (nu face nimic, pentru că codex citește nativ OPENAI_API_KEY / OPENAI_BASE_URL / auth.json, fără aliasare), parse_output și parse_events (decodează fluxul JSONL într-un rezultat normalizat, sărind peste liniile de log intercalate care nu sunt JSON), parse_stream_event (transformă liniile individuale în delte canonice de flux pentru endpointurile de streaming live), interactive_argv și passthrough_argv (invocare brută a binarului codex pentru TUI și passthrough) și auth_paths (spune imaginii de bază unde stă fișierul de credențiale, ca să poată fi verificat dacă există).
build_argv e locul unde stau deciziile interesante. O rulare fără resume și fără noContinue setate cade implicit pe exec resume --last, continuând implicit cea mai recentă sesiune a workspace-ului, aceeași idee ca la claudebox. systemPrompt se mapează pe -c instructions=..., care înlocuiește promptul de sistem încorporat al lui codex; appendSystemPrompt se mapează pe -c developer_instructions=..., care adaugă în schimb un mesaj cu rol de developer lângă el. Efortul de raționament trece prin -c model_reasoning_effort=<level>, nu printr-un flag dedicat de gândire. Și fiecare dintre astea e trimis ca un singur element de argv, nu interpolat prin shell, deci textul de prompt pe mai multe linii și prompturile de sistem supraviețuiesc ca atare, fără să fie mutilate de vreun shell de pe traseu.
Ce Restaurează codexbox-agent.sh Din Ce Scapă Baza
Modul de passthrough al lui aicodebox e deliberat prost: pentru invocări interactive și dintr-un foc rulează doar exec $AICODEBOX_AGENT_BINARY "$@" și se dă la o parte. E în regulă pentru o imagine de bază generică, dar înseamnă că valorile implicite de pe partea containerului, fără prompturi de aprobare, continuare de sesiune rezonabilă, nu se aplică automat la TUI-ul interactiv propriu al lui Codex, ci doar la modurile de server conduse de adaptor. codexbox-agent.sh e scriptul băgat prin AICODEBOX_AGENT_BINARY ca să pună la loc valorile alea implicite anume pentru căile de TUI și de passthrough de CLI:
case "${1:-}" in
login | logout | mcp | mcp-server | doctor | completion | update | resume | review | apply | sandbox | debug | features | help | -V | --version | -h | --help)
exec "$CODEX_BIN" "$@"
;;
exec | e)
# inject --dangerously-bypass-approvals-and-sandbox unless already present
exec "$CODEX_BIN" "$sub" "$BYPASS" "$@"
;;
esac
# bare interactive TUI — defaults to resuming the workspace's last session
exec "$CODEX_BIN" resume --last "$BYPASS" ${args[@]+"${args[@]}"}Subcomenzile de autentificare și de întreținere (login, logout, mcp, doctor, update și așa mai departe) rulează complet ca atare, fără flaguri injectate, iar asta e ce îi permite lui codexbox login --device-auth să conducă fluxul OAuth de ChatGPT neatins. Orice altceva, o sesiune interactivă simplă sau un apel de exec, primește automat flagul de ocolire injectat, ca să nu scrii --dangerously-bypass-approvals-and-sandbox de mână de fiecare dată, iar TUI-ul simplu primește în plus aceeași valoare implicită de „continuă ultima sesiune pentru directorul ăsta” pe care o folosește adaptorul în modurile de server. Modurile de server (API, Telegram, cron, MCP) nu ating deloc scriptul ăsta, ele trec prin CodexAdapter.build_argv și nasc codex direct.
Autentificare Dublă: Cheie de API sau Abonament ChatGPT, Un Singur Fișier
Codex suportă două moduri de autentificare, iar codexbox trebuie să le țină pe amândouă funcționale fără să se calce în picioare. Un script de init care rulează o dată pe boot (10-codex-auth-config.sh) citește orice auth.json existent, îi verifică câmpul auth_mode și seamănă o cheie de API prin codex login --with-api-key doar când nu există autentificare sau când cea existentă e deja în mod apikey. Un login OAuth de abonament ChatGPT câștigă mereu și nu e suprascris niciodată, chiar dacă se nimerește ca OPENAI_API_KEY să fie setată în mediu. Motivul pentru care cheia de API are nevoie totuși de un pas de login, în loc să fie citită direct din variabila de mediu: subcomanda exec a lui codex nu acceptă un OPENAI_API_KEY gol pentru autentificare, are nevoie de auth.json scris pe disc mai întâi.
Ambele moduri de autentificare converg spre exact același fișier, ceea ce e și exact motivul pentru care CODEX_HOME trebuie să supraviețuiască unui container făcut praf. CodexAdapter.auth_paths() întoarce o singură cale:
def auth_paths(self) -> list[str]:
home = os.environ.get("HOME", "/home/aicode")
cfg = os.environ.get("CODEX_HOME", f"{home}/.codex")
return [f"{cfg}/auth.json"]Montezi ~/.codex de pe host și, indiferent cum te-ai autentificat, cu o cheie de API stocată sau cu un token OAuth de ChatGPT, supraviețuiește fiecărei recreări de container, pentru că nu stă în stratul scriibil al containerului, stă pe mountul pe care directorul precreat și chownuit al bazei l-a făcut sigur pentru CODEX_HOME de la bun început.
Minimală față de Full: Aceeași Autentificare, Același Adaptor, Mai Mult Toolchain
Imaginea implicită psyb0t/codexbox:latest e codex plus Node, Python, uv, Docker, git, jq și curl, cât să rulezi agentul și nu cu mult mai mult. Dockerfile.full construiește o a doua imagine peste cea minimală, fixată pe digest la un tag publicat anume, și pune deasupra același toolchain de dezvoltare de uz general pe care îl livrează imaginea full a lui claudebox: Go cu golangci-lint, un toolchain de Python prin pyenv, un toolchain de Node instalat dintr-un lockfile pnpm comis, cu scripturile de ciclu de viață dezactivate, GitHub CLI, Terraform, kubectl, Helm și mormanul obișnuit de unelte de build, clienți de baze de date și depanatoare, despărțite în directoarele de dependințe full-go/, full-node/ și full-python/, ca intrările fiecărui toolchain să fie fixate pe versiune și verificate prin sumă de control, nu „ce are chef apt să instaleze azi”. Același adaptor de Codex, același entrypoint, același comportament de autentificare, buildul full doar adaugă unelte în jur, iar fiecare descărcare din el e verificată prin sumă de control pe un SHA256 fixat înainte să fie crezută.
Partea de Host: wrapper.sh și install.sh
install.sh trage imaginea aleasă (minimală implicit, full prin CODEXBOX_FULL=1), creează ~/.codex și un director de chei SSH pentru git peste SSH în interiorul containerului, descarcă wrapper.sh, coace tagul de imagine rezolvat în el și îl instalează pe PATH ca codexbox. De acolo:
export OPENAI_API_KEY=sk-... # or: codexbox login --device-auth
codexbox # interactive TUI, continues last session for this dir
codexbox --no-continue # same, but forces a fresh session
codexbox exec "fix the failing test" # one-shot exec, output to your terminal
echo "summarize README.md" | codexbox exec -
codexbox stop # kill this dir's running container(s)
codexbox clear-session # drop saved codex sessions, keep auth + configTreaba wrapper-ului e deliberat îngustă: rezolvă imaginea, montează workspace-ul, ~/.codex-ul persistat și socketul de docker, transmite autentificarea și orice variabile CODEXBOX_ENV_* / CODEXBOX_MOUNT_* și administrează un container per director, după nume. Fiecare decizie reală de flag de codex, injecția de ocolire, reluare față de sesiune proaspătă, passthroughul de subcomenzi, stă înăuntrul imaginii, în codexbox-agent.sh, nu în scriptul de pe host. Wrapper-ul îți dă argumentele mai departe neatinse și lasă containerul să decidă ce face cu ele.
Cum Instalezi Ce Tocmai Ai Construit, Nu Ce E pe Docker Hub
Calea de instalare presupunea că vrei imaginea publicată. În regulă dacă o folosești, inutilă dacă lucrezi la ea, pentru că ai construi o imagine locală, ai rula instalatorul și ăsta s-ar duce să tragă copia din registry peste modificările tale.
Deci există ținte pentru asta acum:
make install # build the minimal image, then install the wrapper against it
make install-full # same, full variant
make install-wrapper # wrapper only, against an image you already builtToate trei trec prin CODEXBOX_SRC_LOCAL=true, care îi spune lui install.sh să sară peste docker pull și să folosească imaginea locală aleasă. Pică zgomotos dacă imaginea aia n-a fost construită, în loc să cadă în tăcere pe tras, fiindcă tot rostul e să nu o iei pe cea publicată. Flagul ăla merge și de sine stătător dacă chemi install.sh direct.
make install-wrapper e cel spre care chiar o să te întinzi în mod repetat: reparațiile de wrapper de pe host nu mai cer reconstruirea imaginii doar ca să testezi o schimbare într-un script de shell.
Comenzile de Administrare Au Încetat Să Se Aciueze În Sesiunea Ta
Wrapper-ul rula înainte totul prin containerul interactiv persistent. Corect pentru o sesiune de cod adevărată și greșit pentru subcomenzile de gospodărie, plugin, doctor, sandbox, debug, review, apply, resume, archive, delete, care primesc acum fiecare câte un container --rm de aruncat.
Legat de asta: fiecare subcomandă Codex de nivel superior suportată e transmisă acum direct mai departe, în loc să fie tratată ca un prompt interactiv de reluare. Dacă scriai o subcomandă adevărată, primeai înainte o sesiune pe care n-o ceruseși.
Poți păstra ce a zis Codex de fapt
Implicit, răspunsul e rezumatul folositor. Setezi eventMode pe "full" la rulare și primești în schimb înregistrările proprii ale lui Codex neatinse: raționament, execuție de comenzi, modificări de fișiere, activitate MCP, activitate web, actualizări de todo, consum. Nimic comprimat într-o tură, nimic trunchiat ca să fie încărcătura ordonată. Contează cel mai mult atunci când o rulare a făcut ceva la care nu te așteptai, iar rezumatul e exact stratul care a aruncat dovezile.
Trei reparații pentru care merită să treci peste pin
codex update pica cu EACCES. codex era instalat în prefixul global implicit al lui npm, sub /usr, ceea ce lasă pachetul deținut de root în /usr/lib/node_modules, în timp ce containerul rulează ca utilizatorul neprivilegiat aicode. Deci propriul autoactualizator al CLI-ului nu putea scrie în propria lui instalare. Acum se instalează sub /home/aicode/.local, cu un /home/aicode/.npmrc care fixează același prefix peste coborârea de privilegii a entrypointului.
Imaginea full moștenea o fosilă. Dockerfile.full avea ca implicit o bază care derivase înapoi la v0.2.0, deci latest-full era construită pe o imagine cu câteva luni în urma lui latest. Fluxul de lucru etapizat publică acum întâi imaginea minimală, iar varianta full o moștenește pe cea curentă, ceea ce ar fi trebuit să însemne „aceeași bază, mai mult toolchain” de la bun început. Toată clasa aia de bug a dispărut de la v0.5.11, pentru că codexbox a încetat în primul rând să dețină un toolchain care să derive: uneltele comune de Go, Node, Python, editoare, clienți de baze de date și ops s-au mutat în sus, în psyb0t/aicodebox:v0.15.0-full, iar imaginea full a lui codexbox pornește acum doar de la ea. Ștergând cam 15.800 de linii de lockfile-uri pe drum. Nu mai e nicio a doua copie care să rămână în urmă.
Subagenții furau conversația. Continuarea lui Codex se reia per workspace, iar o rulare mai nouă de subagent putea deturna firul pe care îl continuau cronul, API-ul, MCP-ul sau Telegramul. Acum persistă firul de exec de nivel superior confirmat per workspace canonic și reia exact acel ID, deci o rulare de subagent nu poate deveni pe tăcute chestia din care continuă mâine jobul tău de cron.
Cutii care lansează cutii
Instalezi codexbox, claudebox și pibox în același director și fiecare wrapper le montează pe celelalte două doar în citire la /usr/local/bin/<name>. Codex poate atunci să cheme alt agent în mijlocul sesiunii, ceea ce e util mai ales când vrei un al doilea model care să se uite la același diff.
Rulările imbricate duc un context de host versionat, pentru că „unde e workspace-ul” are alt răspuns înăuntrul unui container decât pe mașina care l-a pornit: AICODEBOX_LAUNCH_CONTEXT_VERSION=1, plus AICODEBOX_HOST_HOME, AICODEBOX_HOST_WORKSPACE, AICODEBOX_HOST_WRAPPER_DIR și câte un home și o cale de wrapper per agent, pentru fiecare dintre cei trei. O rulare imbricată sare și peste scrierile de fișiere de autentificare pe care le face una de nivel superior, deci un copil nu poate rescrie credențialele celui care l-a chemat.
AICODEBOX_ENV_* și AICODEBOX_MOUNT_* transmit mediu și mounturi spre fiecare cutie deodată, stând lângă CODEXBOX_ENV_* și CODEXBOX_MOUNT_*, care sunt per cutie. AICODEBOX_MANAGED_INSTALL=1 dă scripturilor de provisioning o instalare neinteractivă care nu va suprascrie o cheie SSH existentă, iar CODEXBOX_INSTALL_DIR și CODEXBOX_BIN_NAME decid unde aterizează și cum se numește. Instalatorul aduce acum și wrapper.sh din tagul de release potrivit, nu din master, deci o instalare fixată primește wrapper-ul versiunii ăleia.
Toate Căile Înăuntrul Cutiei
Dincolo de shellul interactiv și de exec-ul dintr-un foc, aceleași suprafețe de server din aicodebox se aplică și aici, doar că rulează codex pe sub capotă:
- Modul API: un server FastAPI cu
/run(rulări de agent sincrone sau asincrone),/files/*(listare, citire, scriere, ștergere înăuntrul workspace-ului, cu verificare de traversare de cale) și un endpoint/openai/v1/chat/completionscompatibil cu OpenAI. Cere caCODEXBOX_AVAILABLE_MODELSsă fie setată explicit, pentru că codex n-are o listă de modele hardcodată, e condusă de server, deci nu există o valoare implicită rezonabilă pe care să cazi. - Modul Telegram: intră text, rulează codex, iar Markdownul e randat înapoi în HTML. Override-uri per chat pentru model, efort de raționament și prompturi de sistem, persistate peste reporniri.
- Modul Cron: joburi programate conduse de croniter, care trag codex cu o instrucțiune fixă după un program, fiecare rulare logată într-un director de istoric per job.
- Modul MCP: suprafața MCP proprie a imaginii de bază, cu operații pe fișiere și rulare de prompturi, montată la
/mcpcând modul API e pornit, sau ca proces sidecar altfel. Asta e separat de capabilitățile proprii de client și server MCP ale lui codex, care nu sunt legate în nimic din toate astea.
Singurul lucru care merită spus explicit: toolsAllowlist și noTools sunt acceptate pe /run pentru compatibilitate de API cu ceilalți agenți de pe baza asta, dar codex n-are o listă de unelte încorporate cu nume. O listă de permise e logată și ignorată. noTools e singura excepție chiar onorată, scoate uneltele de shell și de căutare web din cerere și forțează sandboxul doar în citire, pentru că doar configul nu poate scoate apply_patch sau update_plan.
A Meritat
codexbox nu e o rescriere a nimic. E cât ar trebui să coste „mai adaugă un agent” odată ce munca reală de infrastructură e făcută în altă parte: un Dockerfile, patru variabile de mediu, un mkdir -p pentru un director pe care propriul CLI al OpenAI nu și-l creează singur, și o clasă de Python care știe vocabularul de flaguri anume al lui Codex. Tot restul, API-ul, botul de Telegram, schedulerul de cron, suprafața MCP, tot wrapper-ul de host, e al lui aicodebox, neatins.
Dacă vrei toată argumentația imaginii de bază, citește postarea despre aicodebox. Dacă vrei fratele cu Claude Code, ăla e claudebox. Dacă vrei doar o cutie care rulează Codex fără ca CLI-ul OpenAI să se dea cu botul de pământ pe un director lipsă, e pe GitHub. Face o singură treabă și a încetat să se dea cu botul de pământ, ceea ce e tot ce am vrut vreodată de la el.
Cum Îl Instalezi în Agentul Tău
Codex poate instala chestia care rulează Codex. Tot ce e sub .agents/ e catalogat într-un singur marketplace, deci sunt două comenzi:
claude plugin marketplace add psyb0t/agents
claude plugin install codexbox@psyb0tCodex folosește același marketplace cu alt verb, codex plugin add codexbox@psyb0t, pentru că nu există codex plugin install. Găsește singur și skillul într-un checkout al repo-ului, pentru că scanează .agents/skills/ nativ, fără să fie instalat absolut nimic. E listat acum și pe MCP Registry-ul oficial, deci un client care rezolvă servere de acolo îl poate găsi fără să i se dea un URL.