-
Notifications
You must be signed in to change notification settings - Fork 186
Expand file tree
/
Copy pathcloud-codex-deployment.yaml
More file actions
236 lines (227 loc) · 10.1 KB
/
Copy pathcloud-codex-deployment.yaml
File metadata and controls
236 lines (227 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
{{- if .Values.agents.cloudCodex.enabled }}
{{- /*
cloud-codex — runs `commonly agent run <name>` + the codex CLI inside the
cluster, as an alternative to `sam-local-codex` (which runs the same pair
on an operator's laptop).
Why cluster-side codex matters:
ChatGPT binds OAuth sessions to the IP/device that completed device-auth.
A session device-auth'd on a laptop and then used by LiteLLM from the
cluster's IP gets `token_invalidated` immediately on first use. That's
why dev's LiteLLM Codex path has been flapping despite fresh tokens.
When `codex login --device-auth` runs INSIDE this pod, the cluster IP
is what ChatGPT sees both for the device-auth AND for subsequent CLI
calls — no mismatch, no anti-abuse revoke.
Operator flow (one-time per agent install):
1. Pre-create the AgentInstallation via `POST /api/registry/install`
and obtain a runtime token (cm_agent_*). Stash it in the secret
referenced by COMMONLY_AGENT_TOKEN below.
2. `helm upgrade` — pod boots; init container installs codex + commonly
CLIs to /tools; main container waits for ~/.codex/auth.json.
3. `kubectl exec -n commonly-dev deploy/cloud-codex-<agent> -- codex login --device-auth`
— operator completes device-auth in their browser; auth.json lands
on the PVC at /root/.codex/auth.json.
4. Main container picks up auth.json, runs `commonly agent run <name>`
in poll mode against CAP. Replies use codex CLI executing inside
this pod, so OpenAI sees one stable client = one stable session.
Identity continuity (ADR-001 §3):
The AgentInstallation row + the agent's User row predate this pod and
survive its restart/redeploy. The pod is just runtime — no identity
state lives here that isn't recoverable.
*/}}
{{- range $name, $cfg := .Values.agents.cloudCodex.agents }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cloud-codex-{{ $name }}
namespace: {{ include "commonly.namespace" $ }}
labels:
{{- include "commonly.labels" $ | nindent 4 }}
app: cloud-codex
agent: {{ $name }}
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: {{ $cfg.storage | default "1Gi" }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloud-codex-{{ $name }}
namespace: {{ include "commonly.namespace" $ }}
labels:
{{- include "commonly.labels" $ | nindent 4 }}
app: cloud-codex
agent: {{ $name }}
spec:
replicas: 1
# Recreate so the PVC (single-writer RWO) reattaches cleanly on update.
strategy:
type: Recreate
selector:
matchLabels:
{{- include "commonly.selectorLabels" $ | nindent 6 }}
app: cloud-codex
agent: {{ $name }}
template:
metadata:
labels:
{{- include "commonly.selectorLabels" $ | nindent 8 }}
app: cloud-codex
agent: {{ $name }}
spec:
initContainers:
# Mirror of clawdbot-deployment's codex-tools-installer init —
# installs the pinned codex + commonly CLIs into the shared
# /tools volume so the main container has the binaries available.
- name: codex-tools-installer
image: node:22-bookworm-slim
command:
- /bin/sh
- -c
- |
set -e
export NPM_CONFIG_PREFIX=/tools
apt-get update >/dev/null 2>&1
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
git ca-certificates >/dev/null 2>&1
npm install --global --no-audit --no-fund \
"@openai/codex@{{ $.Values.agents.cloudCodex.codexVersion | default "0.125.0" }}"
git clone --depth 1 --branch "{{ $.Values.agents.cloudCodex.commonlyCliRef | default "main" }}" \
https://github.com/Team-Commonly/commonly.git /tmp/commonly-src
mkdir -p /tools/lib
cp -r /tmp/commonly-src/cli /tools/lib/commonly-cli
cd /tools/lib/commonly-cli
npm install --omit=dev --no-audit --no-fund
ln -sf /tools/lib/commonly-cli/src/index.js /tools/bin/commonly
chmod +x /tools/lib/commonly-cli/src/index.js
/tools/bin/codex --version || true
/tools/bin/commonly --version || true
volumeMounts:
- name: tools
mountPath: /tools
containers:
- name: agent
image: node:22-bookworm-slim
command:
- /bin/sh
- -c
- |
set -e
export PATH="/tools/bin:$PATH"
export HOME=/state
mkdir -p /state/.codex /state/.commonly/tokens
# ca-certificates aren't included in node:22-bookworm-slim base,
# but codex CLI needs them for TLS to api.openai.com /
# auth.openai.com. Install once at boot (idempotent — apt skips
# what's already there) so the run loop's outbound HTTPS works.
if [ ! -d /etc/ssl/certs ] || [ -z "$(ls -A /etc/ssl/certs 2>/dev/null)" ]; then
echo "[cloud-codex] installing ca-certificates for TLS"
apt-get update >/dev/null 2>&1 || true
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates >/dev/null 2>&1 || true
update-ca-certificates >/dev/null 2>&1 || true
fi
# Seed ~/.commonly/tokens/<name>.json — `commonly agent run`
# uses loadAgentToken() to find the runtime credentials for this
# agent name. The record shape mirrors what `commonly agent
# attach` would write on a laptop.
# Token-file name uses the local wrapper alias (COMMONLY_AGENT_NAME);
# `agentName` and `instanceId` INSIDE the file are the registry-side
# identifiers the poller passes to /api/agents/runtime/events.
cat > /state/.commonly/tokens/${COMMONLY_AGENT_NAME}.json <<EOF
{
"agentName": "${COMMONLY_REGISTRY_AGENT_NAME}",
"instanceId": "${COMMONLY_AGENT_INSTANCE_ID:-$COMMONLY_AGENT_NAME}",
"podId": "${COMMONLY_POD_ID}",
"instanceUrl": "${COMMONLY_API_URL}",
"runtimeToken": "${COMMONLY_AGENT_TOKEN}",
"adapter": "${COMMONLY_ADAPTER:-codex}",
"savedAt": "$(date -u +%FT%TZ)"
}
EOF
chmod 600 /state/.commonly/tokens/${COMMONLY_AGENT_NAME}.json
# Seed ~/.codex/config.toml so codex CLI routes its model calls
# through LiteLLM instead of straight to chatgpt.com. The LiteLLM
# pod already holds cluster-IP-bound auth.json (rotator-managed,
# operator-device-auth'd), so this agent shares the same auth
# surface as every other openclaw moltbot agent — single quota
# pool, single rotation, single observability.
#
# Runtime stays codex: codex CLI still spawns, still sandboxes,
# still owns tool use and sessions. Only the HTTPS layer is proxied.
cat > /state/.codex/config.toml <<EOF
model = "gpt-5.4"
model_provider = "litellm"
[model_providers.litellm]
name = "LiteLLM"
base_url = "${COMMONLY_LITELLM_BASE_URL}"
wire_api = "responses"
env_key = "LITELLM_API_KEY"
EOF
# Codex CLI looks for LITELLM_API_KEY at call time. The virtual
# key is injected from a k8s Secret created at install time
# alongside COMMONLY_AGENT_TOKEN.
export LITELLM_API_KEY="${COMMONLY_LITELLM_KEY:-}"
if [ -z "$LITELLM_API_KEY" ]; then
echo "[cloud-codex] WARNING: COMMONLY_LITELLM_KEY is empty — model calls will 401 at LiteLLM"
fi
echo "[cloud-codex] config.toml seeded for LiteLLM provider; starting commonly agent run ${COMMONLY_AGENT_NAME}"
exec /tools/bin/commonly agent run "${COMMONLY_AGENT_NAME}"
env:
- name: COMMONLY_AGENT_NAME
value: {{ $name | quote }}
- name: COMMONLY_REGISTRY_AGENT_NAME
# The registry-side agentName (matches AgentInstallation.agentName).
# Defaults to "cloud-codex" since that's what this Helm template is
# for; override if you ever install under a different name.
value: {{ $cfg.registryAgentName | default "cloud-codex" | quote }}
- name: COMMONLY_AGENT_INSTANCE_ID
value: {{ $cfg.agentInstanceId | default $name | quote }}
- name: COMMONLY_POD_ID
value: {{ $cfg.podId | quote }}
- name: COMMONLY_ADAPTER
value: {{ $cfg.adapter | default "codex" | quote }}
- name: COMMONLY_API_URL
value: {{ $cfg.apiUrl | default $.Values.agents.cloudCodex.apiUrl | quote }}
- name: COMMONLY_AGENT_TOKEN
valueFrom:
secretKeyRef:
name: {{ $cfg.tokenSecret | default (printf "cloud-codex-%s-token" $name) }}
key: token
# Codex CLI is configured to call LiteLLM instead of chatgpt.com
# directly (see config.toml in the boot script). Two values needed:
# the base URL and a LiteLLM virtual key. ChatGPT auth itself lives
# on the LiteLLM pod's PVC, rotator-managed.
- name: COMMONLY_LITELLM_BASE_URL
value: {{ $cfg.litellmBaseUrl | default $.Values.agents.cloudCodex.litellmBaseUrl | default "http://litellm:4000/v1" | quote }}
- name: COMMONLY_LITELLM_KEY
valueFrom:
secretKeyRef:
name: {{ $cfg.litellmKeySecret | default (printf "cloud-codex-%s-litellm-key" $name) }}
key: key
# Optional so the deployment can start without a key (useful
# during initial helm-upgrade before the operator mints one);
# the boot script logs a warning and codex 401s at call time.
optional: true
volumeMounts:
- name: tools
mountPath: /tools
readOnly: true
- name: state
mountPath: /state
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: tools
emptyDir: {}
- name: state
persistentVolumeClaim:
claimName: cloud-codex-{{ $name }}
{{- end }}
{{- end }}