Files
OmniRoute/scripts/devin-bridge/common
Lucas Israel 2a94cbfe14 feat(executors): add isolated Claude Code bridge over Devin ACP (#8914)
Validated in post-merge-train sweep (boards clean on release/v3.8.50 tip)
2026-08-06 11:08:37 -03:00

132 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
BRIDGE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
BRIDGE_COMPOSE="$BRIDGE_ROOT/docker/devin-bridge/compose.yml"
BRIDGE_SANDBOX="$BRIDGE_ROOT/.sandbox"
BRIDGE_GUARD_AUDIT_ROOT="$BRIDGE_SANDBOX/guard-audit"
BRIDGE_CLAUDE_AUDIT="$BRIDGE_GUARD_AUDIT_ROOT/claude/egress.jsonl"
BRIDGE_DEVIN_AUDIT="$BRIDGE_GUARD_AUDIT_ROOT/devin/egress.jsonl"
BRIDGE_RUNTIME_POLICY="$BRIDGE_ROOT/scripts/devin-bridge/runtime-policy.mjs"
bridge_prepare_sandbox() {
mkdir -p "$BRIDGE_SANDBOX/home" "$BRIDGE_SANDBOX/test-data" \
"$BRIDGE_SANDBOX/e2e-workspace" "$BRIDGE_SANDBOX/live-workspace" \
"$BRIDGE_SANDBOX/evidence" "$BRIDGE_GUARD_AUDIT_ROOT/claude" \
"$BRIDGE_GUARD_AUDIT_ROOT/devin"
chmod 0777 "$BRIDGE_SANDBOX/e2e-workspace" "$BRIDGE_SANDBOX/live-workspace" \
"$BRIDGE_SANDBOX/evidence"
chmod 01777 "$BRIDGE_GUARD_AUDIT_ROOT/claude" "$BRIDGE_GUARD_AUDIT_ROOT/devin"
}
bridge_reset_guard_audit() {
local audit_path="$1"
local audit_dir
local temp_path
bridge_prepare_sandbox
audit_dir="$(dirname "$audit_path")"
temp_path="$(mktemp "$audit_dir/.egress.jsonl.XXXXXX")"
chmod 0666 "$temp_path"
mv -f "$temp_path" "$audit_path"
}
bridge_reset_claude_egress_audit() {
bridge_reset_guard_audit "$BRIDGE_CLAUDE_AUDIT"
}
bridge_reset_devin_egress_audit() {
bridge_reset_guard_audit "$BRIDGE_DEVIN_AUDIT"
}
bridge_reset_e2e_fixture() {
bridge_prepare_sandbox
cp -R "$BRIDGE_ROOT/tests/fixtures/devin-bridge/e2e-workspace/." \
"$BRIDGE_SANDBOX/e2e-workspace/"
rm -f "$BRIDGE_SANDBOX/e2e-workspace/.e2e-hook.log" \
"$BRIDGE_SANDBOX/evidence/claude-stream.jsonl" \
"$BRIDGE_SANDBOX/evidence/mock-acp.jsonl"
bridge_reset_claude_egress_audit
}
bridge_reset_live_fixture() {
bridge_prepare_sandbox
cp -R "$BRIDGE_ROOT/tests/fixtures/devin-bridge/e2e-workspace/." \
"$BRIDGE_SANDBOX/live-workspace/"
rm -f "$BRIDGE_SANDBOX/live-workspace/.e2e-hook.log" \
"$BRIDGE_SANDBOX/evidence/live-analysis.jsonl" \
"$BRIDGE_SANDBOX/evidence/live-fix.jsonl" \
"$BRIDGE_SANDBOX/evidence/live-command.jsonl" \
"$BRIDGE_SANDBOX/evidence/live-models.json" \
"$BRIDGE_SANDBOX/evidence/egress.jsonl"
bridge_reset_claude_egress_audit
bridge_reset_devin_egress_audit
}
bridge_test_env() {
bridge_prepare_sandbox
env HOME="$BRIDGE_SANDBOX/home" DATA_DIR="$BRIDGE_SANDBOX/test-data" SQLITE_FILE="$BRIDGE_SANDBOX/test-data/storage.sqlite" DEVIN_AGENTIC_HOME="$BRIDGE_SANDBOX/home" "$@"
}
bridge_run_devin() {
docker compose -f "$BRIDGE_COMPOSE" --profile live-devin run --rm --no-deps \
omniroute-live sh -ceu '
trusted_proxy=http://network-guard:8080
test "${DEVIN_BRIDGE_PROXY_URL:-}" = "$trusted_proxy"
export HTTP_PROXY="$trusted_proxy" HTTPS_PROXY="$trusted_proxy"
unset ALL_PROXY NO_PROXY http_proxy https_proxy all_proxy no_proxy
exec devin "$@"
' bridge-devin "$@"
}
bridge_assert_devin_auth_status() {
local exit_status="$1"
local output="$2"
printf '%s' "$output" | node --input-type=module -e '
import { pathToFileURL } from "node:url";
import fs from "node:fs";
const policy = await import(pathToFileURL(process.argv[1]));
const result = policy.validateDevinAuthStatus(process.argv[2], fs.readFileSync(0, "utf8"));
if (!result.ok) throw new Error(result.error);
' "$BRIDGE_RUNTIME_POLICY" "$exit_status"
}
bridge_check_devin_auth() {
local output
local exit_status
set +e
output="$(bridge_run_devin auth status 2>&1)"
exit_status=$?
set -e
bridge_assert_devin_auth_status "$exit_status" "$output"
printf 'PASS: Devin authentication confirmed\n'
}
bridge_assert_zero_claude_egress() {
local audit_path="$1"
bridge_validate_guard_audit claude-zero "$audit_path"
}
bridge_assert_claude_guard_denials() {
local audit_path="$1"
bridge_validate_guard_audit claude-denials "$audit_path"
}
bridge_assert_devin_guard_audit() {
local audit_path="$1"
bridge_validate_guard_audit devin-allowed "$audit_path"
}
bridge_validate_guard_audit() {
local kind="$1"
local audit_path="$2"
node --input-type=module -e '
import { pathToFileURL } from "node:url";
const policy = await import(pathToFileURL(process.argv[1]));
policy.validateAuditFile(process.argv[2], process.argv[3], process.argv[4]);
' "$BRIDGE_RUNTIME_POLICY" "$kind" "$audit_path" "$(id -u)"
}
bridge_export_guard_audit() {
local audit_path="$1"
local evidence_name="$2"
cp "$audit_path" "$BRIDGE_SANDBOX/evidence/$evidence_name"
chmod 0644 "$BRIDGE_SANDBOX/evidence/$evidence_name"
}
bridge_cleanup_compose() {
docker compose -f "$BRIDGE_COMPOSE" --profile offline --profile live-devin \
down --remove-orphans >/dev/null 2>&1 || true
}