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.
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env bash
2
+ # opencode-adapter.sh
3
+ # Adapter implementation for OpenCode (Crush)
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="opencode"
12
+ ADAPTER_NAME="OpenCode (Crush)"
13
+ ADAPTER_TYPE="cloud-api"
14
+ ADAPTER_VERSION="1.0.0"
15
+ ADAPTER_MODEL="${OPENCODE_MODEL:-anthropic/claude-sonnet-4-20250514}"
16
+
17
+ # OpenCode 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 crush >/dev/null 2>&1; then
35
+ echo "ERROR: crush CLI not found in PATH"
36
+ return 1
37
+ fi
38
+
39
+ # Verify crush can actually run (version check)
40
+ if ! crush --version >/dev/null 2>&1; then
41
+ echo "ERROR: crush CLI cannot run (check installation)"
42
+ return 1
43
+ fi
44
+
45
+ local version
46
+ version="$(crush --version 2>/dev/null || true)"
47
+ if [[ -z "$version" ]]; then
48
+ echo "WARN: Could not detect crush version"
49
+ else
50
+ echo "INFO: Crush version: $version"
51
+ fi
52
+
53
+ # Verify model is specified
54
+ if [[ -z "${ADAPTER_MODEL}" ]]; then
55
+ echo "WARN: No model specified for OpenCode adapter"
56
+ fi
57
+
58
+ echo "OK: OpenCode adapter healthy (model: ${ADAPTER_MODEL})"
59
+ return 0
60
+ }
61
+
62
+ adapter_run() {
63
+ local mode="${1:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
64
+ local session="${2:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
65
+ local worktree="${3:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
66
+ local prompt_file="${4:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
67
+
68
+ # Validate prompt file
69
+ if [[ ! -f "${prompt_file}" ]]; then
70
+ echo "ERROR: Prompt file not found: ${prompt_file}"
71
+ return 1
72
+ fi
73
+ if [[ ! -s "${prompt_file}" ]]; then
74
+ echo "ERROR: Prompt file is empty: ${prompt_file}"
75
+ return 1
76
+ fi
77
+
78
+ local timeout_seconds="${OPENCODE_TIMEOUT_SECONDS:-900}"
79
+
80
+ echo "OpenCode adapter: Running session ${session} with model ${ADAPTER_MODEL}"
81
+
82
+ cd "${worktree}" || return 1
83
+
84
+ prompt="$(cat "${prompt_file}")"
85
+
86
+ if ! timeout "${timeout_seconds}" crush --model "${ADAPTER_MODEL}" "${prompt}" 2>&1; then
87
+ echo "ERROR: OpenCode run failed"
88
+ return 1
89
+ fi
90
+
91
+ return 0
92
+ }
93
+
94
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
95
+ adapter_info
96
+ echo "---"
97
+ adapter_health_check
98
+ fi
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ # pi-adapter.sh
3
+ # Adapter implementation for Pi CLI (OpenRouter)
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="pi"
12
+ ADAPTER_NAME="Pi Coding Agent"
13
+ ADAPTER_TYPE="cloud-api"
14
+ ADAPTER_VERSION="1.0.0"
15
+ ADAPTER_MODEL="${PI_MODEL:-openrouter/qwen/qwen3.5-plus:free}"
16
+
17
+ # Pi 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 pi >/dev/null 2>&1; then
35
+ echo "ERROR: pi CLI not found in PATH"
36
+ return 1
37
+ fi
38
+
39
+ if [[ -z "${OPENROUTER_API_KEY:-}" ]]; then
40
+ echo "ERROR: OPENROUTER_API_KEY is required for Pi adapter"
41
+ return 1
42
+ fi
43
+
44
+ # Test API connectivity with a simple model list call
45
+ if ! pi models 2>/dev/null | grep -q "."; then
46
+ echo "ERROR: Pi CLI cannot connect to OpenRouter API (check OPENROUTER_API_KEY)"
47
+ return 1
48
+ fi
49
+
50
+ # Verify configured model is available
51
+ if [[ -n "${ADAPTER_MODEL}" ]] && ! pi models 2>/dev/null | grep -q "${ADAPTER_MODEL}"; then
52
+ echo "WARN: Configured model ${ADAPTER_MODEL} not found in available models"
53
+ fi
54
+
55
+ echo "OK: Pi adapter healthy (model: ${ADAPTER_MODEL})"
56
+ return 0
57
+ }
58
+
59
+ adapter_run() {
60
+ local mode="${1:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
61
+ local session="${2:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
62
+ local worktree="${3:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
63
+ local prompt_file="${4:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
64
+
65
+ # Validate prompt file
66
+ if [[ ! -f "${prompt_file}" ]]; then
67
+ echo "ERROR: Prompt file not found: ${prompt_file}"
68
+ return 1
69
+ fi
70
+ if [[ ! -s "${prompt_file}" ]]; then
71
+ echo "ERROR: Prompt file is empty: ${prompt_file}"
72
+ return 1
73
+ fi
74
+
75
+ local timeout_seconds="${PI_TIMEOUT_SECONDS:-900}"
76
+
77
+ echo "Pi adapter: Running session ${session}"
78
+
79
+ cd "${worktree}" || return 1
80
+
81
+ prompt="$(cat "${prompt_file}")"
82
+
83
+ if ! timeout "${timeout_seconds}" pi --model "${ADAPTER_MODEL}" "${prompt}" 2>&1; then
84
+ echo "ERROR: Pi run failed"
85
+ return 1
86
+ fi
87
+
88
+ return 0
89
+ }
90
+
91
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
92
+ adapter_info
93
+ echo "---"
94
+ adapter_health_check
95
+ fi
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ # run-with-adapter.sh
3
+ # Generic runner that uses the adapter interface
4
+ # Usage: run-with-adapter.sh ADAPTER_SCRIPT MODE SESSION WORKTREE PROMPT_FILE
5
+
6
+ set -euo pipefail
7
+
8
+ ADAPTER_SCRIPT="${1:?usage: run-with-adapter.sh ADAPTER_SCRIPT MODE SESSION WORKTREE PROMPT_FILE}"
9
+ MODE="${2:?usage: run-with-adapter.sh ADAPTER_SCRIPT MODE SESSION WORKTREE PROMPT_FILE}"
10
+ SESSION="${3:?usage: run-with-adapter.sh ADAPTER_SCRIPT MODE SESSION WORKTREE PROMPT_FILE}"
11
+ WORKTREE="${4:?usage: run-with-adapter.sh ADAPTER_SCRIPT MODE SESSION WORKTREE PROMPT_FILE}"
12
+ PROMPT_FILE="${5:?usage: run-with-adapter.sh ADAPTER_SCRIPT MODE SESSION WORKTREE PROMPT_FILE}"
13
+
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+
16
+ # Load adapter interface
17
+ source "${SCRIPT_DIR}/adapter-interface.sh"
18
+
19
+ # Load adapter implementation
20
+ adapter_load "${ADAPTER_SCRIPT}"
21
+
22
+ # Validate adapter
23
+ if ! adapter_validate; then
24
+ echo "ERROR: Adapter validation failed"
25
+ exit 1
26
+ fi
27
+
28
+ # Print adapter info
29
+ echo "=== Using Adapter ==="
30
+ adapter_info
31
+ echo "===================="
32
+
33
+ # Run the task
34
+ adapter_run "${MODE}" "${SESSION}" "${WORKTREE}" "${PROMPT_FILE}"
@@ -734,6 +734,43 @@ refreshButton.addEventListener("click", () => {
734
734
 
735
735
  initializeTheme();
736
736
  void loadSnapshot();
737
- window.setInterval(() => {
738
- void loadSnapshot();
739
- }, 5000);
737
+
738
+ // WebSocket live updates
739
+ let wsReconnectDelay = 1000;
740
+ let wsConnectionActive = false;
741
+
742
+ function connectWebSocket() {
743
+ const protocol = location.protocol === "https:" ? "wss:" : "ws:";
744
+ const wsUrl = `${protocol}//${location.host}/ws`;
745
+ const ws = new WebSocket(wsUrl);
746
+
747
+ ws.onopen = () => {
748
+ wsReconnectDelay = 1000;
749
+ wsConnectionActive = true;
750
+ console.log("ACP Dashboard: WebSocket connected");
751
+ };
752
+
753
+ ws.onmessage = (event) => {
754
+ try {
755
+ const snapshot = JSON.parse(event.data);
756
+ window._acpSnapshot = snapshot;
757
+ renderFromSnapshot(snapshot);
758
+ maybeNotifyAlerts(snapshot);
759
+ } catch (error) {
760
+ console.error("ACP Dashboard: Failed to parse WebSocket message", error);
761
+ }
762
+ };
763
+
764
+ ws.onclose = () => {
765
+ wsConnectionActive = false;
766
+ console.log(`ACP Dashboard: WebSocket disconnected, reconnecting in ${wsReconnectDelay}ms`);
767
+ setTimeout(connectWebSocket, wsReconnectDelay);
768
+ wsReconnectDelay = Math.min(wsReconnectDelay * 2, 30000);
769
+ };
770
+
771
+ ws.onerror = (error) => {
772
+ console.error("ACP Dashboard: WebSocket error", error);
773
+ };
774
+ }
775
+
776
+ connectWebSocket();
@@ -0,0 +1,3 @@
1
+ aiohttp>=3.8
2
+ aiohttp-cors>=0.7
3
+ websockets>=10.0