agent-control-plane 0.3.0 → 0.6.0
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 +141 -28
- package/assets/workflow-catalog.json +1 -1
- package/bin/pr-risk.sh +22 -7
- package/bin/sync-pr-labels.sh +1 -1
- package/hooks/heartbeat-hooks.sh +125 -12
- package/hooks/issue-reconcile-hooks.sh +1 -1
- package/hooks/pr-reconcile-hooks.sh +1 -1
- package/npm/bin/agent-control-plane.js +257 -59
- package/package.json +39 -32
- package/tools/bin/debug-session.sh +106 -0
- package/tools/bin/flow-config-lib.sh +1203 -60
- package/tools/bin/flow-runtime-doctor-linux.sh +136 -0
- package/tools/bin/flow-runtime-doctor.sh +5 -1
- package/tools/bin/flow-shell-lib.sh +32 -0
- package/tools/bin/github-core-rate-limit-state.sh +77 -0
- package/tools/bin/github-write-outbox.sh +470 -0
- package/tools/bin/heartbeat-loop-scheduling-lib.sh +7 -7
- package/tools/bin/heartbeat-safe-auto.sh +42 -0
- package/tools/bin/install-project-launchd.sh +17 -2
- package/tools/bin/install-project-systemd.sh +255 -0
- package/tools/bin/project-init.sh +21 -1
- package/tools/bin/project-launchd-bootstrap.sh +5 -1
- package/tools/bin/project-runtimectl.sh +91 -2
- package/tools/bin/project-systemd-bootstrap.sh +74 -0
- package/tools/bin/scaffold-profile.sh +61 -3
- package/tools/bin/uninstall-project-systemd.sh +87 -0
- package/tools/dashboard/app.js +228 -6
- package/tools/dashboard/dashboard_snapshot.py +55 -0
- package/tools/dashboard/issue_queue_state.py +101 -0
- package/tools/dashboard/server.py +123 -1
- package/tools/dashboard/styles.css +526 -455
- package/tools/templates/pr-fix-template.md +3 -1
- package/tools/templates/pr-merge-repair-template.md +2 -1
- package/references/architecture.md +0 -217
- package/references/commands.md +0 -128
- package/references/control-plane-map.md +0 -124
- package/references/docs-map.md +0 -73
- package/references/release-checklist.md +0 -65
- package/references/repo-map.md +0 -36
- package/tools/bin/agent-cleanup-worktree +0 -247
- package/tools/bin/agent-github-update-labels +0 -71
- package/tools/bin/agent-init-worktree +0 -216
- package/tools/bin/agent-project-archive-run +0 -52
- package/tools/bin/agent-project-capture-worker +0 -46
- package/tools/bin/agent-project-catch-up-issue-pr-links +0 -118
- package/tools/bin/agent-project-catch-up-merged-prs +0 -194
- package/tools/bin/agent-project-catch-up-scheduled-issue-retries +0 -123
- package/tools/bin/agent-project-cleanup-session +0 -513
- package/tools/bin/agent-project-detached-launch +0 -127
- package/tools/bin/agent-project-heartbeat-loop +0 -1029
- package/tools/bin/agent-project-open-issue-worktree +0 -89
- package/tools/bin/agent-project-open-pr-worktree +0 -80
- package/tools/bin/agent-project-publish-issue-pr +0 -465
- package/tools/bin/agent-project-reconcile-issue-session +0 -1398
- package/tools/bin/agent-project-reconcile-pr-session +0 -1230
- package/tools/bin/agent-project-retry-state +0 -147
- package/tools/bin/agent-project-run-claude-session +0 -805
- package/tools/bin/agent-project-run-codex-resilient +0 -955
- package/tools/bin/agent-project-run-codex-session +0 -435
- package/tools/bin/agent-project-run-kilo-session +0 -369
- package/tools/bin/agent-project-run-ollama-session +0 -658
- package/tools/bin/agent-project-run-openclaw-session +0 -1309
- package/tools/bin/agent-project-run-opencode-session +0 -377
- package/tools/bin/agent-project-run-pi-session +0 -479
- package/tools/bin/agent-project-sync-anchor-repo +0 -139
- package/tools/bin/agent-project-worker-status +0 -188
- package/tools/bin/branch-verification-guard.sh +0 -364
- package/tools/bin/capture-worker.sh +0 -18
- package/tools/bin/cleanup-worktree.sh +0 -52
- package/tools/bin/codex-quota +0 -31
- package/tools/bin/create-follow-up-issue.sh +0 -114
- package/tools/bin/dashboard-launchd-bootstrap.sh +0 -50
- package/tools/bin/issue-publish-localization-guard.sh +0 -142
- package/tools/bin/issue-publish-scope-guard.sh +0 -242
- package/tools/bin/issue-requires-local-workspace-install.sh +0 -31
- package/tools/bin/issue-resource-class.sh +0 -12
- package/tools/bin/kick-scheduler.sh +0 -75
- package/tools/bin/label-follow-up-issues.sh +0 -14
- package/tools/bin/new-pr-worktree.sh +0 -50
- package/tools/bin/new-worktree.sh +0 -49
- package/tools/bin/pr-risk.sh +0 -12
- package/tools/bin/prepare-worktree.sh +0 -142
- package/tools/bin/provider-cooldown-state.sh +0 -204
- package/tools/bin/publish-issue-worker.sh +0 -31
- package/tools/bin/reconcile-bootstrap-lib.sh +0 -113
- package/tools/bin/reconcile-issue-worker.sh +0 -34
- package/tools/bin/reconcile-pr-worker.sh +0 -34
- package/tools/bin/record-verification.sh +0 -71
- package/tools/bin/render-flow-config.sh +0 -98
- package/tools/bin/resident-issue-controller-lib.sh +0 -448
- package/tools/bin/resident-issue-queue-status.py +0 -35
- package/tools/bin/retry-state.sh +0 -31
- package/tools/bin/reuse-issue-worktree.sh +0 -121
- package/tools/bin/run-codex-bypass.sh +0 -3
- package/tools/bin/run-codex-safe.sh +0 -3
- package/tools/bin/run-codex-task.sh +0 -280
- package/tools/bin/serve-dashboard.sh +0 -5
- package/tools/bin/split-retained-slice.sh +0 -124
- package/tools/bin/start-issue-worker.sh +0 -943
- package/tools/bin/start-pr-fix-worker.sh +0 -491
- package/tools/bin/start-pr-merge-repair-worker.sh +0 -8
- package/tools/bin/start-pr-review-worker.sh +0 -261
- package/tools/bin/start-resident-issue-loop.sh +0 -499
- package/tools/bin/update-github-labels.sh +0 -14
- package/tools/bin/worker-status.sh +0 -19
- package/tools/bin/workflow-catalog.sh +0 -77
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
cat <<'EOF'
|
|
6
|
+
Usage:
|
|
7
|
+
install-project-systemd.sh --profile-id <id> [options]
|
|
8
|
+
|
|
9
|
+
Install a per-user systemd service so one ACP project runtime starts
|
|
10
|
+
automatically after login/restart on Linux.
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--profile-id <id> Installed profile id to manage
|
|
14
|
+
--unit-name <name> Override systemd unit name (default: agent-project-<id>.service)
|
|
15
|
+
--delay-seconds <n> Initial supervisor delay before first bootstrap (default: 0)
|
|
16
|
+
--interval-seconds <n> Supervisor interval between bootstrap passes (default: 15)
|
|
17
|
+
--help Show this help
|
|
18
|
+
EOF
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
# shellcheck source=/dev/null
|
|
23
|
+
source "${script_dir}/flow-config-lib.sh"
|
|
24
|
+
|
|
25
|
+
append_path_dir() {
|
|
26
|
+
local value_name="${1:?value name required}"
|
|
27
|
+
local candidate="${2:-}"
|
|
28
|
+
local current=""
|
|
29
|
+
|
|
30
|
+
[[ -n "${candidate}" && -d "${candidate}" ]] || return 0
|
|
31
|
+
current="${!value_name:-}"
|
|
32
|
+
case ":${current}:" in
|
|
33
|
+
*":${candidate}:"*) return 0 ;;
|
|
34
|
+
esac
|
|
35
|
+
if [[ -n "${current}" ]]; then
|
|
36
|
+
printf -v "${value_name}" '%s:%s' "${current}" "${candidate}"
|
|
37
|
+
else
|
|
38
|
+
printf -v "${value_name}" '%s' "${candidate}"
|
|
39
|
+
fi
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
resolved_tool_dir() {
|
|
43
|
+
local tool_name="${1:-}"
|
|
44
|
+
local tool_path=""
|
|
45
|
+
|
|
46
|
+
[[ -n "${tool_name}" ]] || return 1
|
|
47
|
+
tool_path="$(command -v "${tool_name}" 2>/dev/null || true)"
|
|
48
|
+
[[ -n "${tool_path}" ]] || return 1
|
|
49
|
+
dirname "${tool_path}"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
build_systemd_base_path() {
|
|
53
|
+
local path_value="${ACP_PROJECT_RUNTIME_PATH:-/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin}"
|
|
54
|
+
local tool_name=""
|
|
55
|
+
local tool_dir=""
|
|
56
|
+
|
|
57
|
+
for tool_name in node gh git python3 openclaw codex claude ollama pi crush kilo; do
|
|
58
|
+
tool_dir="$(resolved_tool_dir "${tool_name}" || true)"
|
|
59
|
+
append_path_dir path_value "${tool_dir}"
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
printf '%s\n' "${path_value}"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
profile_id_override=""
|
|
66
|
+
unit_name_override=""
|
|
67
|
+
delay_seconds="0"
|
|
68
|
+
interval_seconds="15"
|
|
69
|
+
profile_registry_root_override="${ACP_PROJECT_RUNTIME_PROFILE_REGISTRY_ROOT:-${ACP_PROFILE_REGISTRY_ROOT:-}}"
|
|
70
|
+
|
|
71
|
+
while [[ $# -gt 0 ]]; do
|
|
72
|
+
case "$1" in
|
|
73
|
+
--profile-id) profile_id_override="${2:-}"; shift 2 ;;
|
|
74
|
+
--unit-name) unit_name_override="${2:-}"; shift 2 ;;
|
|
75
|
+
--delay-seconds) delay_seconds="${2:-}"; shift 2 ;;
|
|
76
|
+
--interval-seconds) interval_seconds="${2:-}"; shift 2 ;;
|
|
77
|
+
--help|-h) usage; exit 0 ;;
|
|
78
|
+
*) echo "Unknown argument: $1" >&2; usage >&2; exit 64 ;;
|
|
79
|
+
esac
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
if [[ -z "${profile_id_override}" ]]; then
|
|
83
|
+
usage >&2
|
|
84
|
+
exit 64
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
case "${delay_seconds}" in
|
|
88
|
+
''|*[!0-9]*) echo "--delay-seconds must be numeric" >&2; exit 64 ;;
|
|
89
|
+
esac
|
|
90
|
+
|
|
91
|
+
case "${interval_seconds}" in
|
|
92
|
+
''|*[!0-9]*) echo "--interval-seconds must be numeric" >&2; exit 64 ;;
|
|
93
|
+
esac
|
|
94
|
+
|
|
95
|
+
export ACP_PROJECT_ID="${profile_id_override}"
|
|
96
|
+
export AGENT_PROJECT_ID="${profile_id_override}"
|
|
97
|
+
|
|
98
|
+
if [[ -n "${profile_registry_root_override}" ]]; then
|
|
99
|
+
export ACP_PROFILE_REGISTRY_ROOT="${profile_registry_root_override}"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
flow_skill_dir="$(resolve_flow_skill_dir "${BASH_SOURCE[0]}")"
|
|
103
|
+
if ! flow_require_explicit_profile_selection "${flow_skill_dir}" "install-project-systemd.sh"; then
|
|
104
|
+
exit 64
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
config_yaml="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
|
|
108
|
+
if [[ ! -f "${config_yaml}" ]]; then
|
|
109
|
+
printf 'profile not installed: %s\n' "${profile_id_override}" >&2
|
|
110
|
+
exit 66
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
profile_id="$(flow_resolve_adapter_id "${config_yaml}")"
|
|
114
|
+
profile_slug="$(printf '%s' "${profile_id}" | tr -c 'A-Za-z0-9._-' '-')"
|
|
115
|
+
home_dir="${ACP_PROJECT_RUNTIME_HOME_DIR:-${HOME:-}}"
|
|
116
|
+
source_home="${ACP_PROJECT_RUNTIME_SOURCE_HOME:-}"
|
|
117
|
+
|
|
118
|
+
if [[ -z "${source_home}" ]]; then
|
|
119
|
+
if flow_is_skill_root "${flow_skill_dir}"; then
|
|
120
|
+
source_home="${flow_skill_dir}"
|
|
121
|
+
else
|
|
122
|
+
source_home="$(cd "${flow_skill_dir}/../../.." && pwd)"
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
runtime_home="${ACP_PROJECT_RUNTIME_RUNTIME_HOME:-${home_dir}/.agent-runtime/runtime-home}"
|
|
127
|
+
workspace_dir="${ACP_PROJECT_RUNTIME_WORKSPACE_DIR:-${home_dir}/.agent-runtime/control-plane/workspace}"
|
|
128
|
+
profile_registry_root="${ACP_PROJECT_RUNTIME_PROFILE_REGISTRY_ROOT:-${ACP_PROFILE_REGISTRY_ROOT:-${home_dir}/.agent-runtime/control-plane/profiles}}"
|
|
129
|
+
systemd_dir="${ACP_PROJECT_RUNTIME_SYSTEMD_DIR:-${home_dir}/.config/systemd/user}"
|
|
130
|
+
log_dir="${ACP_PROJECT_RUNTIME_LOG_DIR:-${home_dir}/.agent-runtime/logs}"
|
|
131
|
+
unit_name="${unit_name_override:-${ACP_PROJECT_RUNTIME_SYSTEMD_UNIT:-agent-project-${profile_slug}.service}}"
|
|
132
|
+
base_path="$(build_systemd_base_path)"
|
|
133
|
+
coding_worker_override="${ACP_PROJECT_RUNTIME_CODING_WORKER:-${ACP_CODING_WORKER:-}}"
|
|
134
|
+
sync_script="${ACP_PROJECT_RUNTIME_SYNC_SCRIPT:-${flow_skill_dir}/tools/bin/sync-shared-agent-home.sh}"
|
|
135
|
+
runtime_skill_dir="${runtime_home}/skills/openclaw/agent-control-plane"
|
|
136
|
+
bootstrap_script="${ACP_PROJECT_RUNTIME_BOOTSTRAP_SCRIPT:-}"
|
|
137
|
+
|
|
138
|
+
if [[ -z "${bootstrap_script}" ]]; then
|
|
139
|
+
if [[ -x "${runtime_skill_dir}/tools/bin/project-systemd-bootstrap.sh" ]]; then
|
|
140
|
+
bootstrap_script="${runtime_skill_dir}/tools/bin/project-systemd-bootstrap.sh"
|
|
141
|
+
else
|
|
142
|
+
bootstrap_script="${flow_skill_dir}/tools/bin/project-systemd-bootstrap.sh"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
supervisor_script="${ACP_PROJECT_RUNTIME_SUPERVISOR_SCRIPT:-}"
|
|
147
|
+
if [[ -z "${supervisor_script}" ]]; then
|
|
148
|
+
if [[ -x "${runtime_skill_dir}/tools/bin/project-runtime-supervisor.sh" ]]; then
|
|
149
|
+
supervisor_script="${runtime_skill_dir}/tools/bin/project-runtime-supervisor.sh"
|
|
150
|
+
else
|
|
151
|
+
supervisor_script="${flow_skill_dir}/tools/bin/project-runtime-supervisor.sh"
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
state_root="$(flow_resolve_state_root "${config_yaml}")"
|
|
156
|
+
supervisor_pid_file="${state_root}/runtime-supervisor.pid"
|
|
157
|
+
env_file="${ACP_PROJECT_RUNTIME_ENV_FILE:-${profile_registry_root}/${profile_id}/runtime.env}"
|
|
158
|
+
wrapper_path="${workspace_dir}/bin/agent-project-${profile_slug}-systemd.sh"
|
|
159
|
+
unit_file="${systemd_dir}/${unit_name}"
|
|
160
|
+
stdout_log="${log_dir}/agent-project-${profile_slug}.stdout.log"
|
|
161
|
+
stderr_log="${log_dir}/agent-project-${profile_slug}.stderr.log"
|
|
162
|
+
|
|
163
|
+
if [[ -z "${home_dir}" ]]; then
|
|
164
|
+
echo "install-project-systemd requires HOME or ACP_PROJECT_RUNTIME_HOME_DIR" >&2
|
|
165
|
+
exit 64
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Check if systemd is available
|
|
169
|
+
if ! command -v systemctl &>/dev/null; then
|
|
170
|
+
echo "systemctl not found. Is systemd installed?" >&2
|
|
171
|
+
exit 1
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
mkdir -p "${workspace_dir}/bin" "${systemd_dir}" "${log_dir}" "$(dirname "${supervisor_pid_file}")"
|
|
175
|
+
|
|
176
|
+
# Create wrapper script
|
|
177
|
+
cat >"${wrapper_path}" <<EOF
|
|
178
|
+
#!/usr/bin/env bash
|
|
179
|
+
set -euo pipefail
|
|
180
|
+
export ACP_PROJECT_RUNTIME_HOME_DIR='${home_dir}'
|
|
181
|
+
export ACP_PROJECT_RUNTIME_SOURCE_HOME='${source_home}'
|
|
182
|
+
export ACP_PROJECT_RUNTIME_RUNTIME_HOME='${runtime_home}'
|
|
183
|
+
export ACP_PROJECT_RUNTIME_PROFILE_REGISTRY_ROOT='${profile_registry_root}'
|
|
184
|
+
export ACP_PROJECT_RUNTIME_PROFILE_ID='${profile_id}'
|
|
185
|
+
export ACP_PROJECT_RUNTIME_ENV_FILE='${env_file}'
|
|
186
|
+
export ACP_PROJECT_ID='${profile_id}'
|
|
187
|
+
export AGENT_PROJECT_ID='${profile_id}'
|
|
188
|
+
export ACP_PROJECT_RUNTIME_PATH='${base_path}'
|
|
189
|
+
export ACP_PROJECT_RUNTIME_SYNC_SCRIPT='${sync_script}'
|
|
190
|
+
export ACP_PROFILE_REGISTRY_ROOT='${profile_registry_root}'
|
|
191
|
+
EOF
|
|
192
|
+
|
|
193
|
+
if [[ -n "${coding_worker_override}" ]]; then
|
|
194
|
+
cat >>"${wrapper_path}" <<EOF
|
|
195
|
+
export ACP_CODING_WORKER='${coding_worker_override}'
|
|
196
|
+
EOF
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
cat >>"${wrapper_path}" <<EOF
|
|
200
|
+
exec bash '${supervisor_script}' --bootstrap-script '${bootstrap_script}' --pid-file '${supervisor_pid_file}' --delay-seconds '${delay_seconds}' --interval-seconds '${interval_seconds}'
|
|
201
|
+
EOF
|
|
202
|
+
chmod +x "${wrapper_path}"
|
|
203
|
+
|
|
204
|
+
# Create systemd unit file
|
|
205
|
+
cat >"${unit_file}" <<EOF
|
|
206
|
+
[Unit]
|
|
207
|
+
Description=Agent Control Plane - Project ${profile_id}
|
|
208
|
+
After=default.target
|
|
209
|
+
Wants=default.target
|
|
210
|
+
|
|
211
|
+
[Service]
|
|
212
|
+
Type=simple
|
|
213
|
+
ExecStart=${wrapper_path}
|
|
214
|
+
WorkingDirectory=${workspace_dir}
|
|
215
|
+
StandardOutput=append:${stdout_log}
|
|
216
|
+
StandardError=append:${stderr_log}
|
|
217
|
+
Restart=always
|
|
218
|
+
RestartSec=10
|
|
219
|
+
Environment=HOME=${home_dir}
|
|
220
|
+
Environment=PATH=${base_path}
|
|
221
|
+
Environment=ACP_PROFILE_REGISTRY_ROOT=${profile_registry_root}
|
|
222
|
+
|
|
223
|
+
[Install]
|
|
224
|
+
WantedBy=default.target
|
|
225
|
+
EOF
|
|
226
|
+
|
|
227
|
+
# Enable and start the service
|
|
228
|
+
if [[ "${ACP_PROJECT_RUNTIME_SKIP_SYSTEMCTL:-0}" == "1" ]]; then
|
|
229
|
+
printf 'SYSTEMD_INSTALL_STATUS=skipped-systemctl\n'
|
|
230
|
+
printf 'PROFILE_ID=%s\n' "${profile_id}"
|
|
231
|
+
printf 'UNIT_NAME=%s\n' "${unit_name}"
|
|
232
|
+
printf 'UNIT_FILE=%s\n' "${unit_file}"
|
|
233
|
+
printf 'WRAPPER=%s\n' "${wrapper_path}"
|
|
234
|
+
exit 0
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# Enable user service (create symlink)
|
|
238
|
+
systemctl --user enable "${unit_name}" 2>&1 || true
|
|
239
|
+
|
|
240
|
+
# Start the service
|
|
241
|
+
systemctl --user restart "${unit_name}" 2>&1 || true
|
|
242
|
+
|
|
243
|
+
# Wait briefly for service to start
|
|
244
|
+
for _ in $(seq 1 10); do
|
|
245
|
+
if systemctl --user is-active --quiet "${unit_name}" 2>/dev/null; then
|
|
246
|
+
break
|
|
247
|
+
fi
|
|
248
|
+
sleep 1
|
|
249
|
+
done
|
|
250
|
+
|
|
251
|
+
printf 'SYSTEMD_INSTALL_STATUS=ok\n'
|
|
252
|
+
printf 'PROFILE_ID=%s\n' "${profile_id}"
|
|
253
|
+
printf 'UNIT_NAME=%s\n' "${unit_name}"
|
|
254
|
+
printf 'UNIT_FILE=%s\n' "${unit_file}"
|
|
255
|
+
printf 'WRAPPER=%s\n' "${wrapper_path}"
|
|
@@ -34,7 +34,12 @@ runtime copy.
|
|
|
34
34
|
|
|
35
35
|
Common options:
|
|
36
36
|
--profile-id <id> Profile id, e.g. billing-api
|
|
37
|
-
--repo-slug <owner/repo>
|
|
37
|
+
--repo-slug <owner/repo> Forge repo slug
|
|
38
|
+
--forge-provider <github|gitea> Forge provider for this profile
|
|
39
|
+
--gitea-base-url <url> Base URL for a local/self-hosted Gitea instance
|
|
40
|
+
--gitea-token <token> Gitea API token written to profile runtime.env
|
|
41
|
+
--gitea-username <user> Gitea username written to profile runtime.env
|
|
42
|
+
--gitea-password <pass> Gitea password written to profile runtime.env
|
|
38
43
|
--profile-home <path> Installed profile registry root
|
|
39
44
|
--repo-root <path> Canonical repo root
|
|
40
45
|
--agent-repo-root <path> Agent-owned anchor repo root
|
|
@@ -68,6 +73,11 @@ EOF
|
|
|
68
73
|
|
|
69
74
|
profile_id=""
|
|
70
75
|
repo_slug=""
|
|
76
|
+
forge_provider=""
|
|
77
|
+
gitea_base_url=""
|
|
78
|
+
gitea_token=""
|
|
79
|
+
gitea_username=""
|
|
80
|
+
gitea_password=""
|
|
71
81
|
profile_home=""
|
|
72
82
|
repo_root=""
|
|
73
83
|
agent_repo_root=""
|
|
@@ -98,6 +108,11 @@ while [[ $# -gt 0 ]]; do
|
|
|
98
108
|
case "$1" in
|
|
99
109
|
--profile-id) profile_id="${2:-}"; shift 2 ;;
|
|
100
110
|
--repo-slug) repo_slug="${2:-}"; shift 2 ;;
|
|
111
|
+
--forge-provider) forge_provider="${2:-}"; shift 2 ;;
|
|
112
|
+
--gitea-base-url) gitea_base_url="${2:-}"; shift 2 ;;
|
|
113
|
+
--gitea-token) gitea_token="${2:-}"; shift 2 ;;
|
|
114
|
+
--gitea-username) gitea_username="${2:-}"; shift 2 ;;
|
|
115
|
+
--gitea-password) gitea_password="${2:-}"; shift 2 ;;
|
|
101
116
|
--profile-home) profile_home="${2:-}"; shift 2 ;;
|
|
102
117
|
--repo-root) repo_root="${2:-}"; shift 2 ;;
|
|
103
118
|
--agent-repo-root) agent_repo_root="${2:-}"; shift 2 ;;
|
|
@@ -144,6 +159,11 @@ SOURCE_HOME="${source_home:-${ACP_PROJECT_INIT_SOURCE_HOME:-$(cd "${FLOW_SKILL_D
|
|
|
144
159
|
RUNTIME_HOME="${runtime_home:-${ACP_PROJECT_INIT_RUNTIME_HOME:-${HOME}/.agent-runtime/runtime-home}}"
|
|
145
160
|
|
|
146
161
|
scaffold_cmd=(bash "${SCAFFOLD_SCRIPT}" --profile-id "${profile_id}" --repo-slug "${repo_slug}")
|
|
162
|
+
[[ -n "${forge_provider}" ]] && scaffold_cmd+=(--forge-provider "${forge_provider}")
|
|
163
|
+
[[ -n "${gitea_base_url}" ]] && scaffold_cmd+=(--gitea-base-url "${gitea_base_url}")
|
|
164
|
+
[[ -n "${gitea_token}" ]] && scaffold_cmd+=(--gitea-token "${gitea_token}")
|
|
165
|
+
[[ -n "${gitea_username}" ]] && scaffold_cmd+=(--gitea-username "${gitea_username}")
|
|
166
|
+
[[ -n "${gitea_password}" ]] && scaffold_cmd+=(--gitea-password "${gitea_password}")
|
|
147
167
|
[[ -n "${profile_home}" ]] && scaffold_cmd+=(--profile-home "${profile_home}")
|
|
148
168
|
[[ -n "${repo_root}" ]] && scaffold_cmd+=(--repo-root "${repo_root}")
|
|
149
169
|
[[ -n "${agent_repo_root}" ]] && scaffold_cmd+=(--agent-repo-root "${agent_repo_root}")
|
|
@@ -54,7 +54,11 @@ if [[ -x "${ENSURE_SYNC_SCRIPT}" ]]; then
|
|
|
54
54
|
if [[ "${ALWAYS_SYNC}" == "1" ]]; then
|
|
55
55
|
ensure_args=(--force "${ensure_args[@]}")
|
|
56
56
|
fi
|
|
57
|
-
|
|
57
|
+
if [[ "${FLOW_SKILL_DIR}" == "${RUNTIME_HOME}"/* ]]; then
|
|
58
|
+
printf 'RUNTIME_SYNC_SKIPPED=active-runtime-home\n'
|
|
59
|
+
else
|
|
60
|
+
bash "${ENSURE_SYNC_SCRIPT}" "${ensure_args[@]}"
|
|
61
|
+
fi
|
|
58
62
|
elif [[ "${ALWAYS_SYNC}" == "1" || ! -x "${RUNTIME_HEARTBEAT_SCRIPT}" ]]; then
|
|
59
63
|
if [[ -z "${SOURCE_HOME}" ]]; then
|
|
60
64
|
SOURCE_HOME="${FLOW_SKILL_DIR}"
|
|
@@ -107,9 +107,13 @@ LAUNCHCTL_BIN="${ACP_PROJECT_RUNTIME_LAUNCHCTL_BIN:-$(command -v launchctl || tr
|
|
|
107
107
|
LAUNCH_AGENTS_DIR="${ACP_PROJECT_RUNTIME_LAUNCH_AGENTS_DIR:-${HOME}/Library/LaunchAgents}"
|
|
108
108
|
LAUNCHD_LABEL="${ACP_PROJECT_RUNTIME_LAUNCHD_LABEL:-ai.agent.project.${PROFILE_ID_SLUG}}"
|
|
109
109
|
LAUNCHD_PLIST="${ACP_PROJECT_RUNTIME_LAUNCHD_PLIST:-${LAUNCH_AGENTS_DIR}/${LAUNCHD_LABEL}.plist}"
|
|
110
|
+
SYSTEMCTL_BIN="${ACP_PROJECT_RUNTIME_SYSTEMCTL_BIN:-$(command -v systemctl || true)}"
|
|
111
|
+
SYSTEMD_DIR="${ACP_PROJECT_RUNTIME_SYSTEMD_DIR:-${HOME}/.config/systemd/user}"
|
|
112
|
+
SYSTEMD_UNIT_NAME="${ACP_PROJECT_RUNTIME_SYSTEMD_UNIT:-agent-project-${PROFILE_ID_SLUG}.service}"
|
|
110
113
|
SOURCE_HOME="${ACP_PROJECT_RUNTIME_SOURCE_HOME:-}"
|
|
111
114
|
RUNTIME_HOME="${ACP_PROJECT_RUNTIME_RUNTIME_HOME:-$(resolve_runtime_home)}"
|
|
112
115
|
SYNC_STAMP_FILE="${RUNTIME_HOME}/.agent-control-plane-runtime-sync.env"
|
|
116
|
+
SOURCE_REPO_SYNC_STATE_FILE="${STATE_ROOT}/source-repo-main-sync.env"
|
|
113
117
|
|
|
114
118
|
case "${delay_seconds}" in
|
|
115
119
|
''|*[!0-9]*) echo "--delay-seconds must be numeric" >&2; exit 64 ;;
|
|
@@ -199,6 +203,13 @@ sync_stamp_value() {
|
|
|
199
203
|
| sed -e "s/^'//" -e "s/'$//"
|
|
200
204
|
}
|
|
201
205
|
|
|
206
|
+
source_repo_sync_value() {
|
|
207
|
+
local key="${1:?key required}"
|
|
208
|
+
[[ -f "${SOURCE_REPO_SYNC_STATE_FILE}" ]] || return 1
|
|
209
|
+
awk -F= -v target="${key}" '$1 == target {print $2; exit}' "${SOURCE_REPO_SYNC_STATE_FILE}" 2>/dev/null \
|
|
210
|
+
| sed -e "s/^'//" -e "s/'$//"
|
|
211
|
+
}
|
|
212
|
+
|
|
202
213
|
shared_loop_status_value() {
|
|
203
214
|
local key="${1:?key required}"
|
|
204
215
|
local file="${STATE_ROOT}/shared-heartbeat-loop.env"
|
|
@@ -377,6 +388,24 @@ launchd_service_state() {
|
|
|
377
388
|
fi
|
|
378
389
|
}
|
|
379
390
|
|
|
391
|
+
systemd_service_enabled_for_profile() {
|
|
392
|
+
[[ -n "${SYSTEMCTL_BIN}" && -x "${SYSTEMCTL_BIN}" ]] || return 1
|
|
393
|
+
[[ -f "${SYSTEMD_DIR}/${SYSTEMD_UNIT_NAME}" ]] || return 1
|
|
394
|
+
return 0
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
systemd_service_state() {
|
|
398
|
+
if ! systemd_service_enabled_for_profile; then
|
|
399
|
+
printf 'n/a\n'
|
|
400
|
+
return 0
|
|
401
|
+
fi
|
|
402
|
+
if "${SYSTEMCTL_BIN}" --user is-active --quiet "${SYSTEMD_UNIT_NAME}" 2>/dev/null; then
|
|
403
|
+
printf 'running\n'
|
|
404
|
+
else
|
|
405
|
+
printf 'stopped\n'
|
|
406
|
+
fi
|
|
407
|
+
}
|
|
408
|
+
|
|
380
409
|
print_status() {
|
|
381
410
|
local heartbeat=""
|
|
382
411
|
local shared_loop=""
|
|
@@ -399,6 +428,15 @@ print_status() {
|
|
|
399
428
|
local shared_loop_last_status=""
|
|
400
429
|
local shared_loop_started_at=""
|
|
401
430
|
local shared_loop_updated_at=""
|
|
431
|
+
local source_repo_sync_status=""
|
|
432
|
+
local source_repo_sync_updated_at=""
|
|
433
|
+
local source_repo_sync_root=""
|
|
434
|
+
local source_repo_sync_branch=""
|
|
435
|
+
local source_repo_sync_remote=""
|
|
436
|
+
local source_repo_sync_remote_sha=""
|
|
437
|
+
local source_repo_sync_local_sha=""
|
|
438
|
+
local source_repo_sync_detail=""
|
|
439
|
+
local source_repo_sync_aligned="unknown"
|
|
402
440
|
|
|
403
441
|
heartbeat="$(heartbeat_pid)"
|
|
404
442
|
shared_loop="$(shared_loop_pid)"
|
|
@@ -422,6 +460,7 @@ print_status() {
|
|
|
422
460
|
fi
|
|
423
461
|
|
|
424
462
|
launchd_state="$(launchd_service_state)"
|
|
463
|
+
systemd_state="$(systemd_service_state)"
|
|
425
464
|
runtime_sync_status="$(sync_stamp_value "SYNC_STATUS" || true)"
|
|
426
465
|
runtime_sync_updated_at="$(sync_stamp_value "UPDATED_AT" || true)"
|
|
427
466
|
runtime_sync_fingerprint="$(sync_stamp_value "SOURCE_FINGERPRINT" || true)"
|
|
@@ -429,6 +468,23 @@ print_status() {
|
|
|
429
468
|
shared_loop_last_status="$(shared_loop_status_value "STATUS" || true)"
|
|
430
469
|
shared_loop_started_at="$(shared_loop_status_value "STARTED_AT" || true)"
|
|
431
470
|
shared_loop_updated_at="$(shared_loop_status_value "UPDATED_AT" || true)"
|
|
471
|
+
source_repo_sync_status="$(source_repo_sync_value "STATUS" || true)"
|
|
472
|
+
source_repo_sync_updated_at="$(source_repo_sync_value "UPDATED_AT" || true)"
|
|
473
|
+
source_repo_sync_root="$(source_repo_sync_value "SOURCE_REPO_ROOT" || true)"
|
|
474
|
+
source_repo_sync_branch="$(source_repo_sync_value "DEFAULT_BRANCH" || true)"
|
|
475
|
+
source_repo_sync_remote="$(source_repo_sync_value "REMOTE_NAME" || true)"
|
|
476
|
+
source_repo_sync_remote_sha="$(source_repo_sync_value "REMOTE_SHA" || true)"
|
|
477
|
+
source_repo_sync_local_sha="$(source_repo_sync_value "LOCAL_SHA" || true)"
|
|
478
|
+
source_repo_sync_detail="$(source_repo_sync_value "DETAIL" || true)"
|
|
479
|
+
if [[ -n "${source_repo_sync_status}" ]]; then
|
|
480
|
+
if [[ -n "${source_repo_sync_remote_sha}" && -n "${source_repo_sync_local_sha}" && "${source_repo_sync_remote_sha}" == "${source_repo_sync_local_sha}" ]]; then
|
|
481
|
+
source_repo_sync_aligned="yes"
|
|
482
|
+
elif [[ "${source_repo_sync_status}" == "blocked" || "${source_repo_sync_status}" == "failed" ]]; then
|
|
483
|
+
source_repo_sync_aligned="no"
|
|
484
|
+
else
|
|
485
|
+
source_repo_sync_aligned="unknown"
|
|
486
|
+
fi
|
|
487
|
+
fi
|
|
432
488
|
|
|
433
489
|
printf 'PROFILE_ID=%s\n' "${PROFILE_ID}"
|
|
434
490
|
printf 'CONFIG_YAML=%s\n' "${CONFIG_YAML}"
|
|
@@ -437,6 +493,7 @@ print_status() {
|
|
|
437
493
|
printf 'STATE_ROOT=%s\n' "${STATE_ROOT}"
|
|
438
494
|
printf 'RUNTIME_STATUS=%s\n' "${runtime_status}"
|
|
439
495
|
printf 'LAUNCHD_STATE=%s\n' "${launchd_state}"
|
|
496
|
+
printf 'SYSTEMD_STATE=%s\n' "${systemd_state}"
|
|
440
497
|
printf 'LAUNCHD_LABEL=%s\n' "${LAUNCHD_LABEL}"
|
|
441
498
|
printf 'LAUNCHD_PLIST=%s\n' "${LAUNCHD_PLIST}"
|
|
442
499
|
printf 'HEARTBEAT_PID=%s\n' "${heartbeat}"
|
|
@@ -459,6 +516,16 @@ print_status() {
|
|
|
459
516
|
printf 'RUNTIME_SYNC_STATUS=%s\n' "${runtime_sync_status}"
|
|
460
517
|
printf 'RUNTIME_SYNC_UPDATED_AT=%s\n' "${runtime_sync_updated_at}"
|
|
461
518
|
printf 'RUNTIME_SYNC_FINGERPRINT=%s\n' "${runtime_sync_fingerprint}"
|
|
519
|
+
printf 'SOURCE_REPO_SYNC_STATE_FILE=%s\n' "${SOURCE_REPO_SYNC_STATE_FILE}"
|
|
520
|
+
printf 'SOURCE_REPO_SYNC_STATUS=%s\n' "${source_repo_sync_status}"
|
|
521
|
+
printf 'SOURCE_REPO_SYNC_UPDATED_AT=%s\n' "${source_repo_sync_updated_at}"
|
|
522
|
+
printf 'SOURCE_REPO_SYNC_ROOT=%s\n' "${source_repo_sync_root}"
|
|
523
|
+
printf 'SOURCE_REPO_SYNC_BRANCH=%s\n' "${source_repo_sync_branch}"
|
|
524
|
+
printf 'SOURCE_REPO_SYNC_REMOTE=%s\n' "${source_repo_sync_remote}"
|
|
525
|
+
printf 'SOURCE_REPO_SYNC_REMOTE_SHA=%s\n' "${source_repo_sync_remote_sha}"
|
|
526
|
+
printf 'SOURCE_REPO_SYNC_LOCAL_SHA=%s\n' "${source_repo_sync_local_sha}"
|
|
527
|
+
printf 'SOURCE_REPO_SYNC_DETAIL=%s\n' "${source_repo_sync_detail}"
|
|
528
|
+
printf 'SOURCE_REPO_SYNC_ALIGNED=%s\n' "${source_repo_sync_aligned}"
|
|
462
529
|
}
|
|
463
530
|
|
|
464
531
|
terminate_pid_list() {
|
|
@@ -518,7 +585,7 @@ clear_running_labels_after_stop() {
|
|
|
518
585
|
fi
|
|
519
586
|
|
|
520
587
|
issue_json="$(flow_github_issue_list_json "${REPO_SLUG}" open 100 2>/dev/null || printf '[]\n')"
|
|
521
|
-
if [[ "${issue_json}" == "[]" ]]; then
|
|
588
|
+
if [[ "${issue_json}" == "[]" ]] && ! flow_using_gitea; then
|
|
522
589
|
issue_json="$(gh issue list -R "${REPO_SLUG}" --state open --limit 100 --json number,labels 2>/dev/null || printf '[]\n')"
|
|
523
590
|
fi
|
|
524
591
|
while IFS= read -r number; do
|
|
@@ -527,7 +594,7 @@ clear_running_labels_after_stop() {
|
|
|
527
594
|
done < <(jq -r '.[] | select(any(.labels[]?; .name == "agent-running")) | .number' <<<"${issue_json}" 2>/dev/null || true)
|
|
528
595
|
|
|
529
596
|
pr_json="$(flow_github_pr_list_json "${REPO_SLUG}" open 100 2>/dev/null || printf '[]\n')"
|
|
530
|
-
if [[ "${pr_json}" == "[]" ]]; then
|
|
597
|
+
if [[ "${pr_json}" == "[]" ]] && ! flow_using_gitea; then
|
|
531
598
|
pr_json="$(gh pr list -R "${REPO_SLUG}" --state open --limit 100 --json number,labels 2>/dev/null || printf '[]\n')"
|
|
532
599
|
fi
|
|
533
600
|
while IFS= read -r number; do
|
|
@@ -543,6 +610,7 @@ stop_runtime() {
|
|
|
543
610
|
local session=""
|
|
544
611
|
local pid=""
|
|
545
612
|
local launchd_stopped="no"
|
|
613
|
+
local systemd_stopped="no"
|
|
546
614
|
|
|
547
615
|
while IFS= read -r session; do
|
|
548
616
|
[[ -n "${session}" ]] || continue
|
|
@@ -575,6 +643,11 @@ stop_runtime() {
|
|
|
575
643
|
launchd_stopped="yes"
|
|
576
644
|
fi
|
|
577
645
|
|
|
646
|
+
if systemd_service_enabled_for_profile; then
|
|
647
|
+
"${SYSTEMCTL_BIN}" --user stop "${SYSTEMD_UNIT_NAME}" >/dev/null 2>&1 || true
|
|
648
|
+
systemd_stopped="yes"
|
|
649
|
+
fi
|
|
650
|
+
|
|
578
651
|
if [[ -n "${TMUX_BIN}" ]]; then
|
|
579
652
|
for session in "${tmux_sessions[@]+"${tmux_sessions[@]}"}"; do
|
|
580
653
|
"${TMUX_BIN}" kill-session -t "${session}" >/dev/null 2>&1 || true
|
|
@@ -592,6 +665,7 @@ stop_runtime() {
|
|
|
592
665
|
printf 'ACTION=stop\n'
|
|
593
666
|
printf 'PROFILE_ID=%s\n' "${PROFILE_ID}"
|
|
594
667
|
printf 'LAUNCHD_STOPPED=%s\n' "${launchd_stopped}"
|
|
668
|
+
printf 'SYSTEMD_STOPPED=%s\n' "${systemd_stopped}"
|
|
595
669
|
printf 'STOPPED_PID_COUNT=%s\n' "$(printf '%s\n' "${pid_targets[@]+"${pid_targets[@]}"}" | awk 'NF {c+=1} END {print c+0}')"
|
|
596
670
|
printf 'STOPPED_TMUX_SESSION_COUNT=%s\n' "$(printf '%s\n' "${tmux_sessions[@]+"${tmux_sessions[@]}"}" | awk 'NF {c+=1} END {print c+0}')"
|
|
597
671
|
printf 'STOPPED_STALE_TMUX_SESSION_COUNT=%s\n' "$(printf '%s\n' "${stale_tmux_sessions[@]+"${stale_tmux_sessions[@]}"}" | awk 'NF {c+=1} END {print c+0}')"
|
|
@@ -635,6 +709,21 @@ start_runtime() {
|
|
|
635
709
|
return 0
|
|
636
710
|
fi
|
|
637
711
|
|
|
712
|
+
if systemd_service_enabled_for_profile; then
|
|
713
|
+
"${SYSTEMCTL_BIN}" --user restart "${SYSTEMD_UNIT_NAME}" >/dev/null 2>&1 || true
|
|
714
|
+
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
715
|
+
if "${SYSTEMCTL_BIN}" --user is-active --quiet "${SYSTEMD_UNIT_NAME}" 2>/dev/null; then
|
|
716
|
+
break
|
|
717
|
+
fi
|
|
718
|
+
sleep 1
|
|
719
|
+
done
|
|
720
|
+
printf 'ACTION=start\n'
|
|
721
|
+
printf 'PROFILE_ID=%s\n' "${PROFILE_ID}"
|
|
722
|
+
printf 'START_MODE=systemd\n'
|
|
723
|
+
printf 'SYSTEMD_UNIT=%s\n' "${SYSTEMD_UNIT_NAME}"
|
|
724
|
+
return 0
|
|
725
|
+
fi
|
|
726
|
+
|
|
638
727
|
kick_output="$(ACP_PROJECT_ID="${PROFILE_ID}" AGENT_PROJECT_ID="${PROFILE_ID}" bash "${KICK_SCRIPT}" "${delay_seconds}")"
|
|
639
728
|
if wait_for_runtime_start "${start_timeout}"; then
|
|
640
729
|
runtime_started_after_kick="1"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
flow_skill_dir="$(cd "${script_dir}/../.." && pwd)"
|
|
6
|
+
home_dir="${ACP_PROJECT_RUNTIME_HOME_DIR:-${HOME:-}}"
|
|
7
|
+
profile_registry_root="${ACP_PROJECT_RUNTIME_PROFILE_REGISTRY_ROOT:-${ACP_PROFILE_REGISTRY_ROOT:-${home_dir}/.agent-runtime/control-plane/profiles}}"
|
|
8
|
+
profile_id="${ACP_PROJECT_RUNTIME_PROFILE_ID:-${ACP_PROJECT_ID:-${AGENT_PROJECT_ID:-}}}"
|
|
9
|
+
env_file="${ACP_PROJECT_RUNTIME_ENV_FILE:-${profile_registry_root}/${profile_id}/runtime.env}"
|
|
10
|
+
|
|
11
|
+
if [[ -z "${home_dir}" ]]; then
|
|
12
|
+
echo "project systemd bootstrap requires HOME or ACP_PROJECT_RUNTIME_HOME_DIR" >&2
|
|
13
|
+
exit 64
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [[ -z "${profile_id}" ]]; then
|
|
17
|
+
echo "project systemd bootstrap requires ACP_PROJECT_RUNTIME_PROFILE_ID or ACP_PROJECT_ID" >&2
|
|
18
|
+
exit 64
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
export HOME="${home_dir}"
|
|
22
|
+
export ACP_PROFILE_REGISTRY_ROOT="${profile_registry_root}"
|
|
23
|
+
export ACP_PROJECT_ID="${profile_id}"
|
|
24
|
+
export AGENT_PROJECT_ID="${profile_id}"
|
|
25
|
+
|
|
26
|
+
if [[ -f "${env_file}" ]]; then
|
|
27
|
+
set -a
|
|
28
|
+
# shellcheck source=/dev/null
|
|
29
|
+
source "${env_file}"
|
|
30
|
+
set +a
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Resolve launch paths after runtime.env overrides are loaded so systemd can
|
|
34
|
+
# pin the project runtime to a source checkout or alternate runtime home.
|
|
35
|
+
source_home="${ACP_PROJECT_RUNTIME_SOURCE_HOME:-}"
|
|
36
|
+
runtime_home="${ACP_PROJECT_RUNTIME_RUNTIME_HOME:-${home_dir}/.agent-runtime/runtime-home}"
|
|
37
|
+
base_path="${ACP_PROJECT_RUNTIME_PATH:-/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin}"
|
|
38
|
+
sync_script="${ACP_PROJECT_RUNTIME_SYNC_SCRIPT:-${flow_skill_dir}/tools/bin/sync-shared-agent-home.sh}"
|
|
39
|
+
ensure_sync_script="${ACP_PROJECT_RUNTIME_ENSURE_SYNC_SCRIPT:-${flow_skill_dir}/tools/bin/ensure-runtime-sync.sh}"
|
|
40
|
+
runtime_heartbeat_script="${ACP_PROJECT_RUNTIME_HEARTBEAT_SCRIPT:-${runtime_home}/skills/openclaw/agent-control-plane/tools/bin/heartbeat-safe-auto.sh}"
|
|
41
|
+
always_sync="${ACP_PROJECT_RUNTIME_ALWAYS_SYNC:-0}"
|
|
42
|
+
export PATH="${base_path}"
|
|
43
|
+
|
|
44
|
+
if [[ ! -x "${ensure_sync_script}" && ! -x "${sync_script}" ]]; then
|
|
45
|
+
echo "project systemd bootstrap missing sync helper: ${ensure_sync_script}" >&2
|
|
46
|
+
exit 65
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [[ -x "${ensure_sync_script}" ]]; then
|
|
50
|
+
ensure_args=(--runtime-home "${runtime_home}" --quiet)
|
|
51
|
+
if [[ -n "${source_home}" ]]; then
|
|
52
|
+
ensure_args=(--source-home "${source_home}" "${ensure_args[@]}")
|
|
53
|
+
fi
|
|
54
|
+
if [[ "${always_sync}" == "1" ]]; then
|
|
55
|
+
ensure_args=(--force "${ensure_args[@]}")
|
|
56
|
+
fi
|
|
57
|
+
if [[ "${flow_skill_dir}" == "${runtime_home}"/* ]]; then
|
|
58
|
+
printf 'RUNTIME_SYNC_SKIPPED=active-runtime-home\n'
|
|
59
|
+
else
|
|
60
|
+
bash "${ensure_sync_script}" "${ensure_args[@]}"
|
|
61
|
+
fi
|
|
62
|
+
elif [[ "${always_sync}" == "1" || ! -x "${runtime_heartbeat_script}" ]]; then
|
|
63
|
+
if [[ -z "${source_home}" ]]; then
|
|
64
|
+
source_home="${flow_skill_dir}"
|
|
65
|
+
fi
|
|
66
|
+
bash "${sync_script}" "${source_home}" "${runtime_home}" >/dev/null
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if [[ ! -x "${runtime_heartbeat_script}" ]]; then
|
|
70
|
+
echo "project systemd bootstrap missing runtime heartbeat: ${runtime_heartbeat_script}" >&2
|
|
71
|
+
exit 66
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
exec bash "${runtime_heartbeat_script}"
|