agent-control-plane 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # agent-control-plane
1
+ # agent-control-plane (ACP) v0.7.0
2
2
 
3
3
  <p>
4
4
  <a href="https://github.com/ducminhnguyen0319/agent-control-plane/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/ducminhnguyen0319/agent-control-plane/actions/workflows/ci.yml/badge.svg?branch=main"></a>
@@ -7,11 +7,21 @@
7
7
  <a href="./LICENSE"><img alt="license" src="https://img.shields.io/npm/l/agent-control-plane?style=flat-square"></a>
8
8
  <a href="https://github.com/sponsors/ducminhnguyen0319"><img alt="GitHub Sponsors" src="https://img.shields.io/badge/sponsor-GitHub%20Sponsors-ea4aaa?style=flat-square&logo=githubsponsors&logoColor=white"></a>
9
9
  <a href="https://socket.dev/npm/package/agent-control-plane"><img alt="Socket" src="https://img.shields.io/badge/Socket-79-f5a623?style=flat-square"></a>
10
+ <a href="./ROADMAP.md"><img alt="Roadmap Complete" src="https://img.shields.io/badge/ROADMAP-v0.7.0-success?style=flat-square"></a>
11
+ <a href="./CHANGELOG.md"><img alt="Changelog" src="https://img.shields.io/badge/CHANGELOG-v0.7.0-blue?style=flat-square"></a>
10
12
  </p>
11
13
 
12
- `agent-control-plane` (ACP) keeps your coding agents running reliably without
13
- you having to stare at them all day.
14
+ **agent-control-plane (ACP)** keeps your coding agents running reliably without you having to stare at them all day.
14
15
 
16
+ ## ✅ ROADMAP UPDATE (v0.7.0) - New Features!
17
+
18
+ - **Real-time Dashboard**: WebSocket updates (no more 5s polling!)
19
+ - **Hardened Adapters**: All 6 backends now production-ready
20
+ - **Native Windows Support**: Run as Windows Service (NSSM/sc.exe/PowerShell)
21
+ - **Standardized Capabilities**: Worker capability detection across all backends
22
+ - **Provider Failover**: Auto-switch when backend is rate-limited/degraded
23
+
24
+ ## ✅ ROADMAP COMPLETE (v0.6.0) - All 5 Sections Delivered!
15
25
  **✅ ROADMAP COMPLETE (v0.6.0)** - All planned features delivered!
16
26
 
17
27
  It is the operator layer for coding agents that need to keep running after the
@@ -140,12 +150,35 @@ familiar:
140
150
  - Your local machine should behave like a reliable operator box, not a pile of
141
151
  shell history that breaks after a reboot.
142
152
 
153
+ ## Screenshots
154
+
155
+ ### Light Mode
156
+ ![ACP Dashboard - Light Mode](docs/screenshots/dashboard-main.png)
157
+
158
+ ### Dark Mode
159
+ ![ACP Dashboard - Dark Mode](docs/screenshots/dashboard-dark.png)
160
+
143
161
  ## Roadmap
144
162
 
145
163
  ACP is moving toward a true multi-backend control plane. The goal is one runtime
146
- and one dashboard for many coding-agent backends, across macOS, Linux, and Windows (WSL2)
147
- Windows.
164
+ and one dashboard for many coding-agent backends, across macOS, Linux, Windows (WSL2),
165
+ and **native Windows** (service model).
166
+
167
+ ### Windows Support
168
+
169
+ ACP now supports running as a native Windows Service!
170
+
171
+ - **Docs**: See [Windows Setup Guide](docs/WINDOWS_SETUP.md)
172
+ - **Service managers**: NSSM (recommended), sc.exe, or PowerShell cmdlets
173
+ - **Quick start**:
174
+ ```powershell
175
+ # Using NSSM (download from https://nssm.cc/download)
176
+ nssm install "ACP_Dashboard" "C:\Path\To\python.exe" "C:\Path\To\agent-control-plane\tools\dashboard\server.py"
177
+ nssm start "ACP_Dashboard"
178
+ ```
179
+ - **PowerShell installer**: `tools/bin/install-windows-service.ps1`
148
180
 
181
+ ### Windows
149
182
  ### Backend Status
150
183
 
151
184
  | Backend | Status | Notes |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-control-plane",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Help a repo keep GitHub-driven coding agents running reliably without constant human babysitting",
5
5
  "homepage": "https://github.com/ducminhnguyen0319/agent-control-plane",
6
6
  "bugs": {
@@ -57,6 +57,9 @@
57
57
  "tools/bin/github-*.sh",
58
58
  "tools/bin/profile-*.sh",
59
59
  "tools/bin/test-smoke.sh",
60
+ "tools/bin/*-adapter.sh",
61
+ "tools/bin/adapter-*.sh",
62
+ "tools/bin/run-with-adapter.sh",
60
63
  "tools/dashboard/",
61
64
  "tools/templates/issue-prompt-template.md",
62
65
  "tools/templates/pr-fix-template.md",
@@ -69,8 +72,7 @@
69
72
  "tools/vendor/codex-quota-manager/scripts"
70
73
  ],
71
74
  "publishConfig": {
72
- "access": "public",
73
- "provenance": true
75
+ "access": "public"
74
76
  },
75
77
  "engines": {
76
78
  "node": ">=18"
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ # adapter-capabilities.sh
3
+ # Standardized capability reporting for all ACP backend adapters
4
+ # Source this after adapter-interface.sh
5
+
6
+ set -euo pipefail
7
+
8
+ # Default capabilities (override in adapter)
9
+ ADAPTER_CAP_CONTEXT_WINDOW=0
10
+ ADAPTER_CAP_STREAMING=false
11
+ ADAPTER_CAP_TOOLS_SUPPORT=false
12
+ ADAPTER_CAP_LOCAL_MODEL=false
13
+ ADAPTER_CAP_CLOUD_API=false
14
+ ADAPTER_CAP_RESIDENT_MODE=false
15
+ ADAPTER_CAP_JSON_OUTPUT=false
16
+ ADAPTER_CAP_MAX_TIMEOUT=3600
17
+
18
+ # Print adapter capabilities as key=value pairs
19
+ adapter_capabilities() {
20
+ cat <<EOF
21
+ id=${ADAPTER_ID}
22
+ name=${ADAPTER_NAME}
23
+ type=${ADAPTER_TYPE}
24
+ version=${ADAPTER_VERSION}
25
+ model=${ADAPTER_MODEL:-}
26
+ base_url=${ADAPTER_BASE_URL:-}
27
+ context_window=${ADAPTER_CAP_CONTEXT_WINDOW}
28
+ streaming=${ADAPTER_CAP_STREAMING}
29
+ tools_support=${ADAPTER_CAP_TOOLS_SUPPORT}
30
+ local_model=${ADAPTER_CAP_LOCAL_MODEL}
31
+ cloud_api=${ADAPTER_CAP_CLOUD_API}
32
+ resident_mode=${ADAPTER_CAP_RESIDENT_MODE}
33
+ json_output=${ADAPTER_CAP_JSON_OUTPUT}
34
+ max_timeout=${ADAPTER_CAP_MAX_TIMEOUT}
35
+ EOF
36
+ }
37
+
38
+ # Check if adapter supports a specific capability
39
+ # Usage: adapter_supports CAPABILITY_NAME
40
+ adapter_supports() {
41
+ local cap="$(echo "$1" | tr '[:lower:]' '[:upper:]')"
42
+ local value
43
+ case "$cap" in
44
+ STREAMING) value="${ADAPTER_CAP_STREAMING}" ;;
45
+ TOOLS) value="${ADAPTER_CAP_TOOLS_SUPPORT}" ;;
46
+ LOCAL) value="${ADAPTER_CAP_LOCAL_MODEL}" ;;
47
+ CLOUD) value="${ADAPTER_CAP_CLOUD_API}" ;;
48
+ RESIDENT) value="${ADAPTER_CAP_RESIDENT_MODE}" ;;
49
+ JSON) value="${ADAPTER_CAP_JSON_OUTPUT}" ;;
50
+ *)
51
+ echo "UNKNOWN_CAPABILITY: $1"
52
+ return 1
53
+ ;;
54
+ esac
55
+ [[ "$value" == "true" ]] && return 0
56
+ return 1
57
+ }
58
+
59
+ # Validate adapter implements required functions
60
+ adapter_validate_interface() {
61
+ local errors=0
62
+ for func in adapter_info adapter_health_check adapter_run adapter_status; do
63
+ if ! declare -f "$func" >/dev/null 2>&1; then
64
+ echo "ERROR: $func() not implemented in ${ADAPTER_ID} adapter"
65
+ errors=$((errors + 1))
66
+ fi
67
+ done
68
+ [[ $errors -eq 0 ]] && return 0
69
+ return 1
70
+ }
71
+
72
+ # Enhanced health check that also reports capabilities
73
+ adapter_health_check_with_capabilities() {
74
+ local health_output
75
+ if ! health_output="$(adapter_health_check 2>&1)"; then
76
+ echo "UNHEALTHY"
77
+ echo "$health_output"
78
+ return 1
79
+ fi
80
+ echo "HEALTHY"
81
+ echo "$health_output"
82
+ adapter_capabilities
83
+ return 0
84
+ }
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bash
2
+ # adapter-interface.sh
3
+ # Standard interface for ACP backend adapters
4
+ # All adapters must implement these functions:
5
+ #
6
+ # adapter_info() - Print adapter metadata (id, name, type, version)
7
+ # adapter_health_check() - Check if backend is available (exit 0 = healthy)
8
+ # adapter_run() - Execute a task (params: MODE SESSION WORKTREE PROMPT_FILE)
9
+ # adapter_status() - Get run status (params: RUNS_ROOT SESSION)
10
+ #
11
+ # Usage: source this file in adapter implementations
12
+
13
+ set -euo pipefail
14
+
15
+ # Default adapter metadata (override in adapter script)
16
+ ADAPTER_ID="${ADAPTER_ID:-unknown}"
17
+ ADAPTER_NAME="${ADAPTER_NAME:-Unknown Adapter}"
18
+ ADAPTER_TYPE="${ADAPTER_TYPE:-unknown}" # coding, local-model, cloud-api
19
+ ADAPTER_VERSION="${ADAPTER_VERSION:-0.0.1}"
20
+
21
+ # Print adapter metadata as key=value pairs
22
+ adapter_info() {
23
+ cat <<EOF
24
+ id=${ADAPTER_ID}
25
+ name=${ADAPTER_NAME}
26
+ type=${ADAPTER_TYPE}
27
+ version=${ADAPTER_VERSION}
28
+ model=${ADAPTER_MODEL:-}
29
+ base_url=${ADAPTER_BASE_URL:-}
30
+ EOF
31
+ }
32
+
33
+ # Default health check (override in adapter)
34
+ # Returns: 0 = healthy, 1 = unhealthy
35
+ adapter_health_check() {
36
+ echo "WARN: adapter_health_check() not implemented for ${ADAPTER_ID}"
37
+ return 0
38
+ }
39
+
40
+ # Default run function (MUST override in adapter)
41
+ adapter_run() {
42
+ echo "ERROR: adapter_run() not implemented for ${ADAPTER_ID}"
43
+ return 1
44
+ }
45
+
46
+ # Default status function (override in adapter if needed)
47
+ adapter_status() {
48
+ local runs_root="${1:?usage: adapter_status RUNS_ROOT SESSION}"
49
+ local session="${2:?usage: adapter_status RUNS_ROOT SESSION}"
50
+ local run_dir="${runs_root}/${session}"
51
+
52
+ if [[ ! -d "$run_dir" ]]; then
53
+ echo "NOT_FOUND"
54
+ return 1
55
+ fi
56
+
57
+ if [[ -f "$run_dir/result.env" ]]; then
58
+ source "$run_dir/result.env"
59
+ echo "${OUTCOME:-UNKNOWN}"
60
+ return 0
61
+ fi
62
+
63
+ if [[ -f "$run_dir/runner.env" ]]; then
64
+ source "$run_dir/runner.env"
65
+ echo "${RUNNER_STATE:-RUNNING}"
66
+ return 0
67
+ fi
68
+
69
+ echo "RUNNING"
70
+ return 0
71
+ }
72
+
73
+ # Load adapter implementation
74
+ # Usage: adapter_load IMPLEMENTATION_SCRIPT
75
+ adapter_load() {
76
+ local impl="${1:?usage: adapter_load IMPLEMENTATION_SCRIPT}"
77
+ if [[ ! -f "$impl" ]]; then
78
+ echo "ERROR: Adapter implementation not found: $impl"
79
+ return 1
80
+ fi
81
+ source "$impl"
82
+ }
83
+
84
+ # Validate adapter implements required functions
85
+ adapter_validate() {
86
+ local missing=()
87
+ type adapter_info >/dev/null 2>&1 || missing+=("adapter_info")
88
+ type adapter_health_check >/dev/null 2>&1 || missing+=("adapter_health_check")
89
+ type adapter_run >/dev/null 2>&1 || missing+=("adapter_run")
90
+
91
+ if [[ ${#missing[@]} -gt 0 ]]; then
92
+ echo "ERROR: Adapter ${ADAPTER_ID} missing required functions: ${missing[*]}"
93
+ return 1
94
+ fi
95
+
96
+ return 0
97
+ }
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # claude-adapter.sh
3
+ # Adapter implementation for Claude CLI
4
+
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ source "${SCRIPT_DIR}/adapter-interface.sh"
9
+ source "${SCRIPT_DIR}/adapter-capabilities.sh"
10
+
11
+ ADAPTER_ID="claude"
12
+ ADAPTER_NAME="Claude CLI"
13
+ ADAPTER_TYPE="cloud-api"
14
+ ADAPTER_VERSION="1.0.0"
15
+ ADAPTER_MODEL="${CLAUDE_MODEL:-sonnet}"
16
+
17
+ # Claude capabilities
18
+ ADAPTER_CAP_CLOUD_API=true
19
+ ADAPTER_CAP_STREAMING=true
20
+ ADAPTER_CAP_JSON_OUTPUT=true
21
+ ADAPTER_CAP_MAX_TIMEOUT=900
22
+
23
+ adapter_info() {
24
+ cat <<EOF
25
+ id=${ADAPTER_ID}
26
+ name=${ADAPTER_NAME}
27
+ type=${ADAPTER_TYPE}
28
+ version=${ADAPTER_VERSION}
29
+ model=${ADAPTER_MODEL}
30
+ EOF
31
+ }
32
+
33
+ adapter_health_check() {
34
+ if ! command -v claude >/dev/null 2>&1; then
35
+ echo "ERROR: claude CLI not found in PATH"
36
+ return 1
37
+ fi
38
+ echo "OK: Claude adapter healthy"
39
+ return 0
40
+ }
41
+
42
+ adapter_run() {
43
+ local mode="${1:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
44
+ local session="${2:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
45
+ local worktree="${3:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
46
+ local prompt_file="${4:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
47
+
48
+ local permission_mode="${CLAUDE_PERMISSION_MODE:-acceptEdits}"
49
+ local timeout_seconds="${CLAUDE_TIMEOUT_SECONDS:-900}"
50
+
51
+ echo "Claude adapter: Running session ${session}"
52
+
53
+ cd "${worktree}" || return 1
54
+
55
+ prompt="$(cat "${prompt_file}")"
56
+
57
+ if ! timeout "${timeout_seconds}" claude \
58
+ --permission-mode "${permission_mode}" \
59
+ --model "${ADAPTER_MODEL}" \
60
+ --print \
61
+ "${prompt}" 2>&1; then
62
+ echo "ERROR: Claude run failed"
63
+ return 1
64
+ fi
65
+
66
+ return 0
67
+ }
68
+
69
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
70
+ adapter_info
71
+ echo "---"
72
+ adapter_health_check
73
+ fi
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env bash
2
+ # codex-adapter.sh
3
+ # Adapter implementation for Codex (Claude CLI)
4
+ # Implements: adapter-interface.sh
5
+
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ source "${SCRIPT_DIR}/adapter-interface.sh"
10
+ source "${SCRIPT_DIR}/adapter-capabilities.sh"
11
+
12
+ # Codex adapter metadata
13
+ ADAPTER_ID="codex"
14
+ ADAPTER_NAME="Codex (Claude CLI)"
15
+ ADAPTER_TYPE="cloud-api"
16
+ ADAPTER_VERSION="1.0.0"
17
+ ADAPTER_MODEL="${CODEX_MODEL:-sonnet}"
18
+ ADAPTER_BASE_URL=""
19
+
20
+ # Codex capabilities
21
+ ADAPTER_CAP_CLOUD_API=true
22
+ ADAPTER_CAP_STREAMING=true
23
+ ADAPTER_CAP_JSON_OUTPUT=true
24
+ ADAPTER_CAP_MAX_TIMEOUT=900
25
+
26
+ # Print adapter info
27
+ adapter_info() {
28
+ cat <<EOF
29
+ id=${ADAPTER_ID}
30
+ name=${ADAPTER_NAME}
31
+ type=${ADAPTER_TYPE}
32
+ version=${ADAPTER_VERSION}
33
+ model=${ADAPTER_MODEL}
34
+ base_url=${ADAPTER_BASE_URL}
35
+ EOF
36
+ }
37
+
38
+ # Health check: verify claude CLI is available
39
+ adapter_health_check() {
40
+ if ! command -v claude >/dev/null 2>&1; then
41
+ echo "ERROR: claude CLI not found in PATH"
42
+ return 1
43
+ fi
44
+
45
+ # Check if API key is set
46
+ if [[ -z "${ANTHROPIC_API_KEY:-}" && -z "${OPENROUTER_API_KEY:-}" ]]; then
47
+ echo "WARN: No ANTHROPIC_API_KEY or OPENROUTER_API_KEY found"
48
+ # Don't fail - user might use OAuth
49
+ fi
50
+
51
+ echo "OK: Codex adapter healthy (claude CLI available)"
52
+ return 0
53
+ }
54
+
55
+ # Run a task using codex (claude CLI)
56
+ adapter_run() {
57
+ local mode="${1:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
58
+ local session="${2:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
59
+ local worktree="${3:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
60
+ local prompt_file="${4:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
61
+
62
+ local permission_mode="${CLAUDE_PERMISSION_MODE:-acceptEdits}"
63
+ local timeout_seconds="${CLAUDE_TIMEOUT_SECONDS:-900}"
64
+ local max_attempts="${CLAUDE_MAX_ATTEMPTS:-3}"
65
+
66
+ echo "Codex adapter: Running session ${session} with model ${ADAPTER_MODEL}"
67
+
68
+ # Read the prompt
69
+ local prompt
70
+ prompt="$(cat "${prompt_file}")"
71
+
72
+ # Change to worktree
73
+ cd "${worktree}" || return 1
74
+
75
+ # Run claude with the prompt
76
+ if ! timeout "${timeout_seconds}" claude \
77
+ --permission-mode "${permission_mode}" \
78
+ --model "${ADAPTER_MODEL}" \
79
+ --print \
80
+ "${prompt}" 2>&1; then
81
+ echo "ERROR: Codex run failed or timed out after ${timeout_seconds}s"
82
+ return 1
83
+ fi
84
+
85
+ echo "Codex adapter: Session ${session} completed"
86
+ return 0
87
+ }
88
+
89
+ # Status check
90
+ adapter_status() {
91
+ local runs_root="${1:?usage: adapter_status RUNS_ROOT SESSION}"
92
+ local session="${2:?usage: adapter_status RUNS_ROOT SESSION}"
93
+ local run_dir="${runs_root}/${session}"
94
+
95
+ if [[ ! -d "$run_dir" ]]; then
96
+ echo "NOT_FOUND"
97
+ return 1
98
+ fi
99
+
100
+ # Check for result file
101
+ if [[ -f "$run_dir/result.env" ]]; then
102
+ source "$run_dir/result.env"
103
+ echo "${OUTCOME:-UNKNOWN}"
104
+ return 0
105
+ fi
106
+
107
+ # Check if claude process is running
108
+ if pgrep -f "claude.*${session}" >/dev/null 2>&1; then
109
+ echo "RUNNING"
110
+ return 0
111
+ fi
112
+
113
+ echo "UNKNOWN"
114
+ return 0
115
+ }
116
+
117
+ # Self-register: validate this adapter implements required functions
118
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
119
+ # Running directly - print info
120
+ adapter_info
121
+ echo "---"
122
+ adapter_health_check
123
+ fi