Skip to content

Commit 380c708

Browse files
committed
Merge branch 'main' into feat/event-driven-messaging (awslabs#115)
Bring the event-driven architecture branch up to date with main (98 commits) and reconcile the rewrite with features that landed after it forked: eager inbox delivery (awslabs#251), the OpenCode poller, env-var forwarding (awslabs#259), memory curation (awslabs#254/awslabs#262), CORS auto-derive (awslabs#261), DNS host validation (awslabs#124), and the self-send guard (awslabs#24). Highlights: - Providers adopt the async initialize() + get_status(buffer) contract; copilot_cli/opencode_cli converted; kiro keeps colour-only ANSI stripping so carriage-return-redraw permission prompts aren't misread as idle. - Event-driven InboxService.deliver_pending with the awslabs#251 eager gate and message-sender attribution; OpenCode poller retained as a status-driven method; the watchdog (PollingObserver/LogFileHandler) is removed. - terminal_service.create_terminal is async (FIFO + StatusMonitor wiring); session_service.create_session, flow_service.execute_flow, the API endpoints, and `cao flow run` updated to await. - memory_service curated path and the flow CLI fixed to the new contract. Full unit suite green (1908 passed); black + isort clean.
2 parents 10a37ce + 946a5b2 commit 380c708

266 files changed

Lines changed: 48892 additions & 2186 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"id": "cao",
3+
"version": "2.2.0",
4+
"name": "CLI Agent Orchestrator",
5+
"description": "Multi-agent orchestration framework for CLI-based AI coding agents",
6+
"options": {
7+
"version": {
8+
"type": "string",
9+
"default": "latest",
10+
"description": "CAO version (git ref or 'latest')"
11+
},
12+
"webui": {
13+
"type": "boolean",
14+
"default": false,
15+
"description": "Build and include the web UI"
16+
},
17+
"port": {
18+
"type": "string",
19+
"default": "9889",
20+
"description": "Server port"
21+
},
22+
"autostart": {
23+
"type": "boolean",
24+
"default": false,
25+
"description": "Auto-start cao-server on container start"
26+
}
27+
},
28+
"dependsOn": {
29+
"ghcr.io/devcontainers/features/python:1": {}
30+
},
31+
"installsAfter": [
32+
"ghcr.io/devcontainers/features/node:1",
33+
"ghcr.io/devcontainers/features/python:1"
34+
],
35+
"entrypoint": "/usr/local/share/cao/entrypoint.sh"
36+
}
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#!/usr/bin/env bash
2+
# Devcontainer feature install script for CLI Agent Orchestrator (CAO)
3+
# https://github.com/awslabs/cli-agent-orchestrator
4+
set -euo pipefail
5+
6+
VERSION="${VERSION:-latest}"
7+
WEBUI="${WEBUI:-false}"
8+
PORT="${PORT:-9889}"
9+
AUTOSTART="${AUTOSTART:-false}"
10+
11+
REPO_URL="${REPO_URL:-https://github.com/awslabs/cli-agent-orchestrator.git}"
12+
INSTALL_DIR="/usr/local/share/cao"
13+
14+
echo "Installing CLI Agent Orchestrator (version: ${VERSION})..."
15+
16+
# Install system dependencies with distro-aware package manager detection.
17+
if command -v apt-get &>/dev/null; then
18+
apt-get update -y \
19+
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tmux git curl \
20+
&& rm -rf /var/lib/apt/lists/*
21+
elif command -v apk &>/dev/null; then
22+
apk add --no-cache tmux git curl
23+
else
24+
echo "ERROR: Unsupported base image. Expected apt-get or apk to install dependencies." >&2
25+
exit 1
26+
fi
27+
28+
read_tmux_version() {
29+
TMUX_VERSION="$(tmux -V | awk '{print $2}')"
30+
TMUX_MAJOR="$(printf '%s' "$TMUX_VERSION" | awk -F. '{print $1}')"
31+
TMUX_MINOR_RAW="$(printf '%s' "$TMUX_VERSION" | awk -F. '{print $2}')"
32+
TMUX_MINOR="${TMUX_MINOR_RAW%%[^0-9]*}"
33+
}
34+
35+
tmux_version_ok() {
36+
read_tmux_version
37+
if [[ -z "$TMUX_MAJOR" || ! "$TMUX_MAJOR" =~ ^[0-9]+$ || -z "$TMUX_MINOR" || ! "$TMUX_MINOR" =~ ^[0-9]+$ ]]; then
38+
return 1
39+
fi
40+
(( TMUX_MAJOR > 3 || (TMUX_MAJOR == 3 && TMUX_MINOR >= 3) ))
41+
}
42+
43+
install_tmux_build_deps() {
44+
if command -v apt-get &>/dev/null; then
45+
apt-get update -y \
46+
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
47+
libevent-dev libncurses-dev build-essential autoconf automake pkg-config bison \
48+
&& rm -rf /var/lib/apt/lists/*
49+
elif command -v apk &>/dev/null; then
50+
apk add --no-cache libevent-dev ncurses-dev build-base autoconf automake pkgconf bison
51+
else
52+
echo "ERROR: Unsupported base image for tmux source install." >&2
53+
return 1
54+
fi
55+
}
56+
57+
install_tmux_from_source() {
58+
local tmp_dir
59+
local tmux_ref="${TMUX_SOURCE_REF:-3.4}"
60+
tmp_dir="$(mktemp -d)"
61+
echo "Building tmux ${tmux_ref} from source in $tmp_dir..."
62+
if ! git clone --depth 1 --branch "$tmux_ref" https://github.com/tmux/tmux.git "$tmp_dir/tmux"; then
63+
rm -rf "$tmp_dir"
64+
echo "ERROR: Failed to clone tmux source repository at ref ${tmux_ref}." >&2
65+
return 1
66+
fi
67+
if ! (cd "$tmp_dir/tmux" && sh autogen.sh && ./configure && make && make install); then
68+
rm -rf "$tmp_dir"
69+
echo "ERROR: tmux source build failed." >&2
70+
return 1
71+
fi
72+
rm -rf "$tmp_dir"
73+
}
74+
75+
ensure_tmux_at_least_33() {
76+
if tmux_version_ok; then
77+
return 0
78+
fi
79+
80+
echo "tmux >= 3.3 is required; attempting package-manager upgrade..."
81+
if command -v apt-get &>/dev/null; then
82+
apt-get update -y \
83+
&& DEBIAN_FRONTEND=noninteractive apt-get install --only-upgrade -y tmux \
84+
&& rm -rf /var/lib/apt/lists/* || true
85+
elif command -v apk &>/dev/null; then
86+
apk upgrade tmux || true
87+
fi
88+
89+
if tmux_version_ok; then
90+
echo "tmux upgraded via package manager: $(tmux -V)"
91+
return 0
92+
fi
93+
94+
echo "Package-manager tmux is still too old; falling back to source install (see tmux-install.sh)..."
95+
install_tmux_build_deps || return 1
96+
install_tmux_from_source || return 1
97+
98+
if tmux_version_ok; then
99+
echo "tmux installed from source: $(tmux -V)"
100+
return 0
101+
fi
102+
103+
echo "ERROR: tmux >= 3.3 is required, but found $(tmux -V 2>/dev/null || echo 'none')." >&2
104+
return 1
105+
}
106+
107+
# Clone repository to a fixed location so editable install keeps
108+
# web UI asset paths correct relative to the Python package source.
109+
mkdir -p "$INSTALL_DIR"
110+
rm -rf "$INSTALL_DIR/repo"
111+
if [[ "$VERSION" = "latest" ]]; then
112+
git clone --depth 1 "$REPO_URL" "$INSTALL_DIR/repo"
113+
else
114+
# For branch/tag refs, prefer a shallow clone for faster image builds.
115+
if [[ ! "$VERSION" =~ ^[0-9a-fA-F]{7,40}$ ]] && git clone --depth 1 --branch "$VERSION" "$REPO_URL" "$INSTALL_DIR/repo"; then
116+
:
117+
else
118+
# For commit SHAs or unknown refs, try filtered clone first to reduce transfer cost.
119+
if ! git clone --filter=blob:none "$REPO_URL" "$INSTALL_DIR/repo"; then
120+
git clone "$REPO_URL" "$INSTALL_DIR/repo"
121+
fi
122+
if ! git -C "$INSTALL_DIR/repo" checkout "$VERSION"; then
123+
rm -rf "$INSTALL_DIR/repo"
124+
git clone "$REPO_URL" "$INSTALL_DIR/repo"
125+
if ! git -C "$INSTALL_DIR/repo" checkout "$VERSION"; then
126+
echo "ERROR: Version '${VERSION}' not found in repository ${REPO_URL}." >&2
127+
exit 1
128+
fi
129+
fi
130+
fi
131+
fi
132+
133+
ensure_tmux_at_least_33 || {
134+
echo "ERROR: Could not install tmux >= 3.3 (see repo/tmux-install.sh for manual setup)." >&2
135+
exit 1
136+
}
137+
138+
pip_install_editable() {
139+
local target="$1"
140+
local -a pip_args=(--no-cache-dir)
141+
if python3 -m pip install --help 2>/dev/null | grep -q break-system-packages; then
142+
pip_args+=(--break-system-packages)
143+
fi
144+
python3 -m pip install "${pip_args[@]}" -e "$target"
145+
}
146+
147+
# Editable install keeps server static asset resolution aligned with
148+
# the checked out source layout for the selected version.
149+
pip_install_editable "$INSTALL_DIR/repo"
150+
151+
# Build web UI if requested
152+
if [[ "$WEBUI" = "true" ]]; then
153+
if ! command -v npm &>/dev/null; then
154+
echo "ERROR: npm is not available. Install the Node.js devcontainer feature before this one, or set webui=false." >&2
155+
exit 1
156+
fi
157+
resolve_web_project_dir() {
158+
local repo="$1"
159+
local candidate
160+
for candidate in "$repo/web" "$repo/frontend" "$repo/ui"; do
161+
if [[ -f "$candidate/package.json" ]]; then
162+
printf '%s\n' "$candidate"
163+
return 0
164+
fi
165+
done
166+
echo "ERROR: Could not locate web UI npm project under $repo." >&2
167+
echo "Supported layouts include repo/web (package.json) and built artifacts under:" >&2
168+
echo " - repo/web/dist/index.html" >&2
169+
echo " - repo/src/cli_agent_orchestrator/web_ui/index.html" >&2
170+
return 1
171+
}
172+
173+
web_project_dir="$(resolve_web_project_dir "$INSTALL_DIR/repo")"
174+
echo "Building web UI in ${web_project_dir}..."
175+
cd "$web_project_dir"
176+
if [[ -f package-lock.json ]]; then
177+
npm ci
178+
else
179+
npm install
180+
fi
181+
npm run build
182+
echo "Web UI built successfully."
183+
fi
184+
185+
# Create entrypoint script that optionally starts cao-server on container start
186+
AUTOSTART_DEFAULT_LITERAL="$(printf '%q' "$AUTOSTART")"
187+
PORT_DEFAULT_LITERAL="$(printf '%q' "$PORT")"
188+
189+
{
190+
cat << EOF
191+
#!/usr/bin/env bash
192+
# CAO devcontainer entrypoint
193+
AUTOSTART_DEFAULT=${AUTOSTART_DEFAULT_LITERAL}
194+
PORT_DEFAULT=${PORT_DEFAULT_LITERAL}
195+
EOF
196+
197+
cat << 'EOF'
198+
set -euo pipefail
199+
200+
AUTOSTART_VALUE="${AUTOSTART:-$AUTOSTART_DEFAULT}"
201+
PORT_VALUE="${PORT:-$PORT_DEFAULT}"
202+
203+
if [[ "$AUTOSTART_VALUE" = "true" ]]; then
204+
echo "Starting cao-server on port $PORT_VALUE..."
205+
exec cao-server --host 0.0.0.0 --port "$PORT_VALUE"
206+
fi
207+
208+
if [[ "$#" -gt 0 ]]; then
209+
exec "$@"
210+
fi
211+
212+
exec tail -f /dev/null
213+
EOF
214+
} > "$INSTALL_DIR/entrypoint.sh"
215+
chmod +x "$INSTALL_DIR/entrypoint.sh"
216+
217+
echo "CLI Agent Orchestrator installed successfully."
218+
echo " - Run 'cao --help' to verify the CLI."
219+
echo " - Run 'cao-server --help' to see server options."
220+
if [[ "$WEBUI" = "true" ]]; then
221+
echo " - Web UI will be served at http://localhost:${PORT} when cao-server is running."
222+
fi

.github/workflows/ci.yml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
run: |
3939
uv run pytest test/ \
4040
--ignore=test/providers/test_q_cli_integration.py \
41+
--ignore=test/providers/test_kiro_cli_integration.py \
4142
--ignore=test/e2e \
4243
-m "not e2e" \
4344
--cov=src/cli_agent_orchestrator \
@@ -55,6 +56,37 @@ jobs:
5556
env:
5657
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
5758

59+
web-build:
60+
name: Web UI Build
61+
runs-on: ubuntu-latest
62+
63+
steps:
64+
- name: Checkout code
65+
uses: actions/checkout@v4
66+
67+
- name: Set up Node.js
68+
uses: actions/setup-node@v4
69+
with:
70+
node-version: "20"
71+
cache: "npm"
72+
cache-dependency-path: web/package-lock.json
73+
74+
- name: Install dependencies
75+
run: npm ci
76+
working-directory: web
77+
78+
- name: Type check
79+
run: npx tsc --noEmit
80+
working-directory: web
81+
82+
- name: Run tests
83+
run: npm test
84+
working-directory: web
85+
86+
- name: Build
87+
run: npm run build
88+
working-directory: web
89+
5890
lint:
5991
name: Code Quality
6092
runs-on: ubuntu-latest
@@ -101,7 +133,7 @@ jobs:
101133
uv export --format requirements-txt > requirements.txt
102134
103135
- name: Run Trivy vulnerability scanner
104-
uses: aquasecurity/trivy-action@master
136+
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.69.3
105137
with:
106138
scan-type: 'fs'
107139
scan-ref: '.'
@@ -130,3 +162,10 @@ jobs:
130162
with:
131163
fail-on-severity: high
132164
deny-licenses: GPL-3.0, AGPL-3.0
165+
166+
# NOTE: CodeQL runs via GitHub's "default setup" (visible as the `Analyze (...)`
167+
# checks on every PR). Adding a workflow-based CodeQL job here would conflict
168+
# with default setup ("CodeQL analyses from advanced configurations cannot be
169+
# processed when the default setup is enabled") and fail uploads. To widen the
170+
# query suite beyond the default, toggle default setup off in
171+
# Settings → Code security → CodeQL default setup and re-add a workflow job.

0 commit comments

Comments
 (0)