Implement Canvas V2 subgraphs, assets and asynchronous AI execution
Foundry Go checks / check (push) Successful in 2m40s
Foundry Go checks / check (pull_request) Successful in 2m36s

This commit is contained in:
2026-11-06 18:36:00 +08:00
parent 7847167043
commit d6ee8942f1
62 changed files with 8339 additions and 35 deletions
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Explicit remote-runner opt-in: invokes real models using the dedicated CLI login.
set -euo pipefail
umask 077
binary=$(realpath "$1")
cli=${FOUNDRY_REAL_CODEX_INSTALL:-/opt/endoleap-infra/codex-cli}
login=${FOUNDRY_REAL_CODEX_HOME:-/opt/endoleap-infra/foundry/codex/home}
: "${FOUNDRY_CANVAS_IMAGE_MODELS:?Set only backend model names verified for image output}"
test -x "$binary"
test -s "$login/auth.json"
test -s "$login/models_cache.json"
suffix=$(openssl rand -hex 8)
database=foundry-real-db-$suffix
worker=foundry-real-worker-$suffix
scratch=$(mktemp -d /tmp/foundry-real.XXXXXXXX)
cleanup(){ docker rm -f "$worker" "$database" >/dev/null 2>&1 || true; rm -rf "$scratch"; }
trap cleanup EXIT
install -d -m 700 "$scratch/run"
chown 1000:1000 "$scratch/run"
docker run -d --name "$database" --network none -e POSTGRES_HOST_AUTH_METHOD=trust postgres:17-alpine >/dev/null
for attempt in {1..30}; do if docker exec "$database" pg_isready -h 127.0.0.1 -U postgres >/dev/null 2>&1; then break; fi; sleep 1; done
docker exec -i "$database" psql -X -U postgres -v ON_ERROR_STOP=1 < db/001-auth.sql
docker exec "$database" psql -X -U postgres -v ON_ERROR_STOP=1 -c 'CREATE ROLE foundry_fixture LOGIN'
python3 deploy/migrate.py --container "$database" --database postgres --role foundry_fixture
# Only the CLI container sees the login. Core/tests receive the private socket,
# never the login, runtime configuration or Docker socket.
docker run -d --name "$worker" --init --network bridge --user 1000:1000 \
--cap-drop ALL --security-opt no-new-privileges --read-only --pids-limit 128 --memory 1g \
--tmpfs /tmp:rw,nosuid,nodev,size=128m --tmpfs /work:ro,nosuid,nodev \
--tmpfs /home/node/.codex:rw,nosuid,nodev,size=32m,uid=1000,gid=1000 --workdir /work \
-e "FOUNDRY_CANVAS_IMAGE_MODELS=$FOUNDRY_CANVAS_IMAGE_MODELS" \
--mount "type=bind,source=$binary,target=/opt/foundry,readonly" \
--mount "type=bind,source=$cli,target=/opt/codex,readonly" \
--mount "type=bind,source=$login/auth.json,target=/home/node/.codex/auth.json,readonly" \
--mount "type=bind,source=$login/models_cache.json,target=/home/node/.codex/models_cache.json,readonly" \
--mount "type=bind,source=$scratch/run,target=/run/foundry-codex" \
--mount type=bind,source=/etc/ssl/certs/ca-certificates.crt,target=/etc/ssl/certs/ca-certificates.crt,readonly \
node:24-bookworm-slim /opt/foundry codex-worker >/dev/null
for attempt in {1..30}; do if [[ -S "$scratch/run/worker.sock" ]]; then break; fi; sleep 1; done
test -S "$scratch/run/worker.sock"
CGO_ENABLED=0 go test -mod=readonly -c -o bin/platform-real-tests ./internal/platform
docker run --rm --network "container:$database" --user 1000:1000 \
--read-only --cap-drop ALL --security-opt no-new-privileges \
--tmpfs /tmp:rw,nosuid,nodev,noexec,size=16m,mode=1777 \
--mount "type=bind,source=$(pwd)/bin/platform-real-tests,target=/opt/integration-tests,readonly" \
--mount "type=bind,source=$scratch/run,target=/run/canvas-real,readonly" \
-e PLATFORM_TEST_DATABASE_URL=postgres://foundry_fixture@127.0.0.1:5432/postgres?sslmode=disable \
-e FOUNDRY_REAL_CODEX_SOCKET=/run/canvas-real/worker.sock \
--entrypoint /opt/integration-tests postgres:17-alpine -test.v -test.run '^TestCanvasAIRealWorker$' -test.timeout 6m
+1
View File
@@ -71,6 +71,7 @@ for package in platform auth; do
CGO_ENABLED=0 go test -mod=readonly -c -o "bin/$package-tests" "./internal/$package"
docker run --rm --network "container:$container" --user 1000:1000 \
--read-only --cap-drop ALL --security-opt no-new-privileges \
--tmpfs /tmp:rw,nosuid,nodev,noexec,size=16m,mode=1777 \
--mount "type=bind,source=$(pwd)/bin/$package-tests,target=/opt/integration-tests,readonly" \
-e PLATFORM_TEST_DATABASE_URL=postgres://foundry_fixture@127.0.0.1:5432/postgres?sslmode=disable \
--entrypoint /opt/integration-tests postgres:17-alpine -test.v
+5 -1
View File
@@ -10,15 +10,19 @@ test -s "$dir/grants/projects.json"
install -d -m 700 "$dir/run"
chown 1000:1000 "$dir/run"
canvas_env=()
if [[ -s "$dir/canvas.env" ]]; then canvas_env=(--env-file "$dir/canvas.env"); fi
# The host operator supplies an isolated Codex home and starts a single worker.
# The Docker default bridge supplies egress independently of Foundry's network.
docker run -d --name foundry-codex-worker --restart unless-stopped --init \
--network bridge --user 1000:1000 --cap-drop ALL --security-opt no-new-privileges \
--read-only --pids-limit 128 --memory 384m --tmpfs /tmp:rw,nosuid,nodev \
--read-only --pids-limit 128 --memory 1g --tmpfs /tmp:rw,nosuid,nodev \
--tmpfs /work:ro,nosuid,nodev --workdir /work \
--mount "type=bind,source=$binary,target=/opt/foundry,readonly" \
--mount "type=bind,source=$base/codex-cli,target=/opt/codex,readonly" \
--mount "type=bind,source=$dir/home,target=/home/node/.codex" \
--mount "type=bind,source=$dir/run,target=/run/foundry-codex" \
--mount "type=bind,source=/etc/ssl/certs/ca-certificates.crt,target=/etc/ssl/certs/ca-certificates.crt,readonly" \
"${canvas_env[@]}" \
node:24-bookworm-slim /opt/foundry codex-worker
+34 -3
View File
@@ -70,9 +70,40 @@ fi
codex_mounts=()
codex_dir=$foundry/codex
if [[ -d "$codex_dir" ]]; then
if ! docker inspect -f '{{.State.Running}}' foundry-codex-worker 2>/dev/null | grep -qx true; then
bash "$(dirname "$0")/codex-worker.sh" "$release/foundry" >/dev/null
# Core and its private worker must speak the same Canvas artifact protocol.
# Keep the previous container definition for exact startup rollback.
worker_backup=
if docker inspect foundry-codex-worker >/dev/null 2>&1; then
worker_backup=foundry-codex-worker-previous-$(date +%s)-$$
docker stop -t 5 foundry-codex-worker >/dev/null
docker rename foundry-codex-worker "$worker_backup"
fi
worker_ready=false
if bash "$(dirname "$0")/codex-worker.sh" "$release/foundry" >/dev/null; then
for attempt in {1..20}; do
if docker exec foundry-codex-worker node -e '
const http = require("node:http");
const req = http.get({socketPath:"/run/foundry-codex/worker.sock",path:"/canvas/models"}, res => {
let body = "";
res.on("data", chunk => { body += chunk; if(body.length > 262144) req.destroy(); });
res.on("end", () => { try { const value=JSON.parse(body); process.exit(res.statusCode===200 && Array.isArray(value.models) && value.models.length>0 ? 0 : 1); } catch { process.exit(1); } });
});
req.setTimeout(2000, () => req.destroy());
req.on("error", () => process.exit(1));
' >/dev/null 2>&1; then worker_ready=true; break; fi
sleep 0.25
done
fi
if [[ "$worker_ready" != true ]]; then
docker rm -f foundry-codex-worker >/dev/null 2>&1 || true
if [[ -n "$worker_backup" ]]; then
docker rename "$worker_backup" foundry-codex-worker
docker start foundry-codex-worker >/dev/null
fi
printf 'Canvas worker startup/catalog verification failed; previous worker restored.\n' >&2
exit 1
fi
if [[ -n "$worker_backup" ]]; then docker rm "$worker_backup" >/dev/null; fi
for attempt in {1..20}; do
[[ ! -S "$codex_dir/run/worker.sock" ]] || break
sleep 0.25
@@ -91,7 +122,7 @@ docker run -d --name "$candidate" "${codex_mounts[@]}" "${mail_env[@]}" "${slack
--env-file "$foundry/database.env" -e HOST -e PORT -e PUBLIC_ORIGIN \
-e GOOGLE_CLIENT_ID -e GOOGLE_CLIENT_SECRET -e FOUNDRY_PROJECTS -e FOUNDRY_ONBOARDING \
--user 1000:1000 --cap-drop ALL --security-opt no-new-privileges \
--read-only --memory 128m \
--read-only --memory 1g \
--mount "type=bind,source=$ca_bundle,target=/etc/ssl/certs/ca-certificates.crt,readonly" \
-e SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
--mount "type=bind,source=$release/foundry,target=/opt/foundry,readonly" \