agent-control-plane 0.1.12 → 0.1.14
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/SKILL.md +1 -1
- package/hooks/heartbeat-hooks.sh +64 -7
- package/hooks/issue-reconcile-hooks.sh +46 -0
- package/npm/bin/agent-control-plane.js +89 -8
- package/package.json +3 -1
- package/references/commands.md +2 -2
- package/references/control-plane-map.md +1 -1
- package/tools/bin/agent-project-reconcile-issue-session +2 -0
- package/tools/bin/agent-project-reconcile-pr-session +166 -22
- package/tools/bin/agent-project-run-codex-session +58 -0
- package/tools/bin/branch-verification-guard.sh +15 -2
- package/tools/bin/ensure-runtime-sync.sh +5 -5
- package/tools/bin/heartbeat-safe-auto.sh +20 -10
- package/tools/bin/provider-cooldown-state.sh +39 -1
- package/tools/bin/start-issue-worker.sh +66 -24
- package/tools/bin/sync-shared-agent-home.sh +24 -10
- package/tools/dashboard/app.js +74 -0
- package/tools/dashboard/dashboard_snapshot.py +202 -1
- package/tools/templates/issue-prompt-template.md +4 -1
- package/tools/vendor/codex-quota-manager/scripts/auto-switch.sh +8 -6
- package/tools/bin/render-dashboard-snapshot.py +0 -16
- package/tools/templates/legacy/issue-prompt-template-pre-slim.md +0 -109
package/SKILL.md
CHANGED
|
@@ -22,7 +22,7 @@ external profile registry, not inside this repository.
|
|
|
22
22
|
- installed project profiles in `~/.agent-runtime/control-plane/profiles/*/control-plane.yaml`
|
|
23
23
|
- installed profile notes in `~/.agent-runtime/control-plane/profiles/*/README.md`
|
|
24
24
|
- workflow catalog in `assets/workflow-catalog.json`
|
|
25
|
-
- worker dashboard in `tools/dashboard/` with
|
|
25
|
+
- worker dashboard in `tools/dashboard/` with launcher at `tools/dashboard/dashboard_snapshot.py`
|
|
26
26
|
and `tools/bin/serve-dashboard.sh`
|
|
27
27
|
- dashboard autostart helpers in `tools/bin/dashboard-launchd-bootstrap.sh` and
|
|
28
28
|
`tools/bin/install-dashboard-launchd.sh`
|
package/hooks/heartbeat-hooks.sh
CHANGED
|
@@ -15,6 +15,8 @@ FLOW_TOOLS_DIR="${FLOW_SKILL_DIR}/tools/bin"
|
|
|
15
15
|
DETACHED_LAUNCH_BIN="${FLOW_TOOLS_DIR}/agent-project-detached-launch"
|
|
16
16
|
RESIDENT_ISSUE_LOOP_BIN="${FLOW_TOOLS_DIR}/start-resident-issue-loop.sh"
|
|
17
17
|
REPO_SLUG="$(flow_resolve_repo_slug "${CONFIG_YAML}")"
|
|
18
|
+
AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
|
|
19
|
+
DEFAULT_BRANCH="$(flow_resolve_default_branch "${CONFIG_YAML}")"
|
|
18
20
|
STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
|
|
19
21
|
PENDING_LAUNCH_DIR="${ACP_PENDING_LAUNCH_DIR:-${F_LOSNING_PENDING_LAUNCH_DIR:-${STATE_ROOT}/pending-launches}}"
|
|
20
22
|
AGENT_PR_PREFIXES_JSON="$(flow_managed_pr_prefixes_json "${CONFIG_YAML}")"
|
|
@@ -24,6 +26,51 @@ AGENT_EXCLUSIVE_LABEL="${AGENT_EXCLUSIVE_LABEL:-agent-exclusive}"
|
|
|
24
26
|
CODING_WORKER="${ACP_CODING_WORKER:-${F_LOSNING_CODING_WORKER:-codex}}"
|
|
25
27
|
HEARTBEAT_ISSUE_JSON_CACHE_DIR="${TMPDIR:-/tmp}/heartbeat-issue-json.$$"
|
|
26
28
|
|
|
29
|
+
heartbeat_issue_retry_state_file() {
|
|
30
|
+
local issue_id="${1:?issue id required}"
|
|
31
|
+
printf '%s/retries/issues/%s.env\n' "${STATE_ROOT}" "${issue_id}"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
heartbeat_reason_requires_baseline_change() {
|
|
35
|
+
local reason="${1:-}"
|
|
36
|
+
case "${reason}" in
|
|
37
|
+
verification-guard-blocked|no-publishable-commits|no-publishable-delta)
|
|
38
|
+
return 0
|
|
39
|
+
;;
|
|
40
|
+
*)
|
|
41
|
+
return 1
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
heartbeat_current_baseline_head_sha() {
|
|
47
|
+
local head_sha=""
|
|
48
|
+
if [[ -d "${AGENT_REPO_ROOT}" ]]; then
|
|
49
|
+
head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "origin/${DEFAULT_BRANCH}" 2>/dev/null || true)"
|
|
50
|
+
if [[ -z "${head_sha}" ]]; then
|
|
51
|
+
head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "${DEFAULT_BRANCH}" 2>/dev/null || true)"
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
printf '%s\n' "${head_sha}"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
heartbeat_retry_reason_is_baseline_blocked() {
|
|
58
|
+
local issue_id="${1:?issue id required}"
|
|
59
|
+
local reason="${2:-}"
|
|
60
|
+
local state_file baseline_head current_head
|
|
61
|
+
|
|
62
|
+
heartbeat_reason_requires_baseline_change "${reason}" || return 1
|
|
63
|
+
state_file="$(heartbeat_issue_retry_state_file "${issue_id}")"
|
|
64
|
+
[[ -f "${state_file}" ]] || return 1
|
|
65
|
+
|
|
66
|
+
baseline_head="$(awk -F= '/^BASELINE_HEAD_SHA=/{print substr($0, index($0, "=") + 1); exit}' "${state_file}" 2>/dev/null | tr -d '\r' || true)"
|
|
67
|
+
[[ -n "${baseline_head}" ]] || return 1
|
|
68
|
+
current_head="$(heartbeat_current_baseline_head_sha)"
|
|
69
|
+
[[ -n "${current_head}" ]] || return 1
|
|
70
|
+
|
|
71
|
+
[[ "${baseline_head}" == "${current_head}" ]]
|
|
72
|
+
}
|
|
73
|
+
|
|
27
74
|
heartbeat_issue_json_cached() {
|
|
28
75
|
local issue_id="${1:?issue id required}"
|
|
29
76
|
local cache_file=""
|
|
@@ -48,6 +95,7 @@ heartbeat_open_agent_pr_issue_ids() {
|
|
|
48
95
|
local pr_issue_ids_json=""
|
|
49
96
|
pr_issue_ids_json="$(
|
|
50
97
|
flow_github_pr_list_json "$REPO_SLUG" open 100 \
|
|
98
|
+
2>/dev/null \
|
|
51
99
|
| jq --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" --arg branchIssueRegex "${AGENT_PR_ISSUE_CAPTURE_REGEX}" '
|
|
52
100
|
map(
|
|
53
101
|
. as $pr
|
|
@@ -72,7 +120,7 @@ heartbeat_open_agent_pr_issue_ids() {
|
|
|
72
120
|
| select(. != null and . != "")
|
|
73
121
|
)
|
|
74
122
|
| unique
|
|
75
|
-
'
|
|
123
|
+
' 2>/dev/null || true
|
|
76
124
|
)"
|
|
77
125
|
|
|
78
126
|
if [[ -z "${pr_issue_ids_json:-}" ]]; then
|
|
@@ -89,6 +137,7 @@ heartbeat_list_ready_issue_ids() {
|
|
|
89
137
|
|
|
90
138
|
ready_issue_rows="$(
|
|
91
139
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
140
|
+
2>/dev/null \
|
|
92
141
|
| jq -r --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
|
|
93
142
|
map(select(
|
|
94
143
|
(any(.labels[]?; .name == "agent-running") | not)
|
|
@@ -98,7 +147,7 @@ heartbeat_list_ready_issue_ids() {
|
|
|
98
147
|
| .[]
|
|
99
148
|
| [.number, (any(.labels[]?; .name == "agent-blocked"))]
|
|
100
149
|
| @tsv
|
|
101
|
-
'
|
|
150
|
+
' 2>/dev/null || true
|
|
102
151
|
)"
|
|
103
152
|
|
|
104
153
|
while IFS=$'\t' read -r issue_id is_blocked; do
|
|
@@ -123,6 +172,7 @@ heartbeat_list_blocked_recovery_issue_ids() {
|
|
|
123
172
|
|
|
124
173
|
blocked_issue_rows="$(
|
|
125
174
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
175
|
+
2>/dev/null \
|
|
126
176
|
| jq -r --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
|
|
127
177
|
map(select(
|
|
128
178
|
any(.labels[]?; .name == "agent-blocked")
|
|
@@ -131,7 +181,7 @@ heartbeat_list_blocked_recovery_issue_ids() {
|
|
|
131
181
|
))
|
|
132
182
|
| sort_by(.createdAt, .number)
|
|
133
183
|
| .[].number
|
|
134
|
-
'
|
|
184
|
+
' 2>/dev/null || true
|
|
135
185
|
)"
|
|
136
186
|
|
|
137
187
|
while IFS= read -r issue_id; do
|
|
@@ -152,6 +202,9 @@ heartbeat_issue_blocked_recovery_reason() {
|
|
|
152
202
|
retry_out="$("${FLOW_TOOLS_DIR}/retry-state.sh" issue "$issue_id" get 2>/dev/null || true)"
|
|
153
203
|
retry_reason="$(awk -F= '/^LAST_REASON=/{print $2}' <<<"${retry_out:-}")"
|
|
154
204
|
if [[ -n "${retry_reason:-}" && "${retry_reason}" != "issue-worker-blocked" ]]; then
|
|
205
|
+
if heartbeat_retry_reason_is_baseline_blocked "${issue_id}" "${retry_reason}"; then
|
|
206
|
+
return 0
|
|
207
|
+
fi
|
|
155
208
|
printf '%s\n' "$retry_reason"
|
|
156
209
|
return 0
|
|
157
210
|
fi
|
|
@@ -218,6 +271,7 @@ heartbeat_list_exclusive_issue_ids() {
|
|
|
218
271
|
open_agent_pr_issue_ids="$(heartbeat_open_agent_pr_issue_ids)"
|
|
219
272
|
|
|
220
273
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
274
|
+
2>/dev/null \
|
|
221
275
|
| jq -r --arg exclusiveLabel "${AGENT_EXCLUSIVE_LABEL}" --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
|
|
222
276
|
map(select(
|
|
223
277
|
any(.labels[]?; .name == $exclusiveLabel)
|
|
@@ -227,20 +281,22 @@ heartbeat_list_exclusive_issue_ids() {
|
|
|
227
281
|
))
|
|
228
282
|
| sort_by(.createdAt, .number)
|
|
229
283
|
| .[].number
|
|
230
|
-
'
|
|
284
|
+
' 2>/dev/null || true
|
|
231
285
|
}
|
|
232
286
|
|
|
233
287
|
heartbeat_list_running_issue_ids() {
|
|
234
288
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
289
|
+
2>/dev/null \
|
|
235
290
|
| jq -r '
|
|
236
291
|
map(select(any(.labels[]?; .name == "agent-running")))
|
|
237
292
|
| sort_by(.createdAt, .number)
|
|
238
293
|
| .[].number
|
|
239
|
-
'
|
|
294
|
+
' 2>/dev/null || true
|
|
240
295
|
}
|
|
241
296
|
|
|
242
297
|
heartbeat_list_open_agent_pr_ids() {
|
|
243
298
|
flow_github_pr_list_json "$REPO_SLUG" open 100 \
|
|
299
|
+
2>/dev/null \
|
|
244
300
|
| jq -r --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" '
|
|
245
301
|
map(select(
|
|
246
302
|
. as $pr
|
|
@@ -252,11 +308,12 @@ heartbeat_list_open_agent_pr_ids() {
|
|
|
252
308
|
))
|
|
253
309
|
| sort_by(.createdAt)
|
|
254
310
|
| .[].number
|
|
255
|
-
'
|
|
311
|
+
' 2>/dev/null || true
|
|
256
312
|
}
|
|
257
313
|
|
|
258
314
|
heartbeat_list_exclusive_pr_ids() {
|
|
259
315
|
flow_github_pr_list_json "$REPO_SLUG" open 100 \
|
|
316
|
+
2>/dev/null \
|
|
260
317
|
| jq -r --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" --arg exclusiveLabel "${AGENT_EXCLUSIVE_LABEL}" '
|
|
261
318
|
map(select(
|
|
262
319
|
. as $pr
|
|
@@ -269,7 +326,7 @@ heartbeat_list_exclusive_pr_ids() {
|
|
|
269
326
|
))
|
|
270
327
|
| sort_by(.createdAt)
|
|
271
328
|
| .[].number
|
|
272
|
-
'
|
|
329
|
+
' 2>/dev/null || true
|
|
273
330
|
}
|
|
274
331
|
|
|
275
332
|
heartbeat_issue_is_heavy() {
|
|
@@ -12,7 +12,9 @@ ADAPTER_BIN_DIR="${FLOW_SKILL_DIR}/bin"
|
|
|
12
12
|
FLOW_TOOLS_DIR="${FLOW_SKILL_DIR}/tools/bin"
|
|
13
13
|
REPO_SLUG="$(flow_resolve_repo_slug "${CONFIG_YAML}")"
|
|
14
14
|
AGENT_ROOT="$(flow_resolve_agent_root "${CONFIG_YAML}")"
|
|
15
|
+
AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
|
|
15
16
|
STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
|
|
17
|
+
DEFAULT_BRANCH="$(flow_resolve_default_branch "${CONFIG_YAML}")"
|
|
16
18
|
RUNS_ROOT="$(flow_resolve_runs_root "${CONFIG_YAML}")"
|
|
17
19
|
BLOCKED_RECOVERY_STATE_DIR="${STATE_ROOT}/blocked-recovery-issues"
|
|
18
20
|
|
|
@@ -26,6 +28,49 @@ issue_clear_blocked_recovery_state() {
|
|
|
26
28
|
rm -f "${BLOCKED_RECOVERY_STATE_DIR}/${ISSUE_ID}.env" 2>/dev/null || true
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
issue_retry_state_file() {
|
|
32
|
+
printf '%s/retries/issues/%s.env\n' "${STATE_ROOT}" "${ISSUE_ID}"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
issue_reason_requires_baseline_change() {
|
|
36
|
+
local reason="${1:-}"
|
|
37
|
+
case "${reason}" in
|
|
38
|
+
verification-guard-blocked|no-publishable-commits|no-publishable-delta)
|
|
39
|
+
return 0
|
|
40
|
+
;;
|
|
41
|
+
*)
|
|
42
|
+
return 1
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
issue_current_baseline_head_sha() {
|
|
48
|
+
local head_sha=""
|
|
49
|
+
if [[ -d "${AGENT_REPO_ROOT}" ]]; then
|
|
50
|
+
head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "origin/${DEFAULT_BRANCH}" 2>/dev/null || true)"
|
|
51
|
+
if [[ -z "${head_sha}" ]]; then
|
|
52
|
+
head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "${DEFAULT_BRANCH}" 2>/dev/null || true)"
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
printf '%s\n' "${head_sha}"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
issue_record_retry_baseline_gate() {
|
|
59
|
+
local reason="${1:-}"
|
|
60
|
+
local state_file head_sha tmp_file
|
|
61
|
+
|
|
62
|
+
issue_reason_requires_baseline_change "${reason}" || return 0
|
|
63
|
+
state_file="$(issue_retry_state_file)"
|
|
64
|
+
[[ -f "${state_file}" ]] || return 0
|
|
65
|
+
head_sha="$(issue_current_baseline_head_sha)"
|
|
66
|
+
[[ -n "${head_sha}" ]] || return 0
|
|
67
|
+
|
|
68
|
+
tmp_file="$(mktemp)"
|
|
69
|
+
grep -v '^BASELINE_HEAD_SHA=' "${state_file}" >"${tmp_file}" || true
|
|
70
|
+
printf 'BASELINE_HEAD_SHA=%s\n' "${head_sha}" >>"${tmp_file}"
|
|
71
|
+
mv "${tmp_file}" "${state_file}"
|
|
72
|
+
}
|
|
73
|
+
|
|
29
74
|
issue_has_schedule_cadence() {
|
|
30
75
|
local issue_json issue_body
|
|
31
76
|
issue_json="$(flow_github_issue_view_json "${REPO_SLUG}" "${ISSUE_ID}" 2>/dev/null || true)"
|
|
@@ -155,6 +200,7 @@ issue_schedule_retry() {
|
|
|
155
200
|
return 0
|
|
156
201
|
fi
|
|
157
202
|
"${FLOW_TOOLS_DIR}/retry-state.sh" issue "$ISSUE_ID" schedule "$reason" >/dev/null || true
|
|
203
|
+
issue_record_retry_baseline_gate "${reason}"
|
|
158
204
|
}
|
|
159
205
|
|
|
160
206
|
issue_mark_ready() {
|
|
@@ -156,7 +156,7 @@ function createExecutionContext(stage) {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
function runScriptWithContext(context, scriptRelativePath, forwardedArgs, options = {}) {
|
|
159
|
-
const scriptPath = path.join(packageRoot, scriptRelativePath);
|
|
159
|
+
const scriptPath = options.scriptPath || path.join(packageRoot, scriptRelativePath);
|
|
160
160
|
const stdio = options.stdio || "inherit";
|
|
161
161
|
const result = spawnSync("bash", [scriptPath, ...forwardedArgs], {
|
|
162
162
|
stdio,
|
|
@@ -175,11 +175,78 @@ function runScriptWithContext(context, scriptRelativePath, forwardedArgs, option
|
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
function resolvePersistentSourceHome(context) {
|
|
179
|
+
if (process.env.ACP_PROJECT_RUNTIME_SOURCE_HOME) {
|
|
180
|
+
return process.env.ACP_PROJECT_RUNTIME_SOURCE_HOME;
|
|
181
|
+
}
|
|
182
|
+
if (fs.existsSync(path.join(packageRoot, ".git"))) {
|
|
183
|
+
return packageRoot;
|
|
184
|
+
}
|
|
185
|
+
return context.runtimeHome;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function runtimeSkillRoot(context) {
|
|
189
|
+
return path.join(context.runtimeHome, "skills", "openclaw", skillName);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function createRuntimeExecutionContext(context) {
|
|
193
|
+
const stableSkillRoot = runtimeSkillRoot(context);
|
|
194
|
+
const persistentSourceHome = resolvePersistentSourceHome(context);
|
|
195
|
+
const runtimeScriptEnv = {
|
|
196
|
+
ACP_PROJECT_RUNTIME_SYNC_SCRIPT:
|
|
197
|
+
context.env.ACP_PROJECT_RUNTIME_SYNC_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "sync-shared-agent-home.sh"),
|
|
198
|
+
ACP_PROJECT_RUNTIME_ENSURE_SYNC_SCRIPT:
|
|
199
|
+
context.env.ACP_PROJECT_RUNTIME_ENSURE_SYNC_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "ensure-runtime-sync.sh"),
|
|
200
|
+
ACP_PROJECT_RUNTIME_BOOTSTRAP_SCRIPT:
|
|
201
|
+
context.env.ACP_PROJECT_RUNTIME_BOOTSTRAP_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "project-launchd-bootstrap.sh"),
|
|
202
|
+
ACP_PROJECT_RUNTIME_SUPERVISOR_SCRIPT:
|
|
203
|
+
context.env.ACP_PROJECT_RUNTIME_SUPERVISOR_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "project-runtime-supervisor.sh"),
|
|
204
|
+
ACP_PROJECT_RUNTIME_KICK_SCRIPT:
|
|
205
|
+
context.env.ACP_PROJECT_RUNTIME_KICK_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "kick-scheduler.sh")
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
...context,
|
|
209
|
+
stableSkillRoot,
|
|
210
|
+
persistentSourceHome,
|
|
211
|
+
env: {
|
|
212
|
+
...context.env,
|
|
213
|
+
SHARED_AGENT_HOME: context.runtimeHome,
|
|
214
|
+
AGENT_CONTROL_PLANE_ROOT: stableSkillRoot,
|
|
215
|
+
ACP_ROOT: stableSkillRoot,
|
|
216
|
+
AGENT_FLOW_SOURCE_ROOT: stableSkillRoot,
|
|
217
|
+
ACP_PROJECT_INIT_SOURCE_HOME: persistentSourceHome,
|
|
218
|
+
ACP_PROJECT_RUNTIME_SOURCE_HOME: persistentSourceHome,
|
|
219
|
+
ACP_DASHBOARD_SOURCE_HOME: persistentSourceHome,
|
|
220
|
+
...runtimeScriptEnv
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function syncRuntimeHome(context, options = {}) {
|
|
226
|
+
const result = runScriptWithContext(context, "tools/bin/sync-shared-agent-home.sh", [], {
|
|
227
|
+
stdio: options.stdio || "inherit"
|
|
228
|
+
});
|
|
229
|
+
if (result.status !== 0) {
|
|
230
|
+
throw new Error("failed to sync runtime home before command execution");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
178
234
|
function runCommand(scriptRelativePath, forwardedArgs) {
|
|
179
235
|
const stage = stageSharedHome();
|
|
180
236
|
const context = createExecutionContext(stage);
|
|
181
237
|
|
|
182
238
|
try {
|
|
239
|
+
if (scriptRelativePath !== "tools/bin/sync-shared-agent-home.sh") {
|
|
240
|
+
syncRuntimeHome(context, { stdio: "inherit" });
|
|
241
|
+
const runtimeContext = createRuntimeExecutionContext(context);
|
|
242
|
+
const runtimeScriptPath = path.join(runtimeContext.stableSkillRoot, scriptRelativePath);
|
|
243
|
+
const result = runScriptWithContext(runtimeContext, scriptRelativePath, forwardedArgs, {
|
|
244
|
+
stdio: "inherit",
|
|
245
|
+
scriptPath: runtimeScriptPath
|
|
246
|
+
});
|
|
247
|
+
return result.status;
|
|
248
|
+
}
|
|
249
|
+
|
|
183
250
|
const result = runScriptWithContext(context, scriptRelativePath, forwardedArgs, { stdio: "inherit" });
|
|
184
251
|
return result.status;
|
|
185
252
|
} finally {
|
|
@@ -1470,8 +1537,8 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
|
|
|
1470
1537
|
runtimeStartReason = `doctor-${doctorKv.DOCTOR_STATUS || "not-ok"}`;
|
|
1471
1538
|
} else {
|
|
1472
1539
|
actions.push("runtime-start");
|
|
1473
|
-
runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId]);
|
|
1474
|
-
const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId]);
|
|
1540
|
+
runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
1541
|
+
const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
1475
1542
|
runtimeStatusKv = parseKvOutput(runtimeStatusOutput);
|
|
1476
1543
|
runtimeStartStatus = "ok";
|
|
1477
1544
|
runtimeStartReason = "";
|
|
@@ -1480,7 +1547,7 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
|
|
|
1480
1547
|
|
|
1481
1548
|
if (config.installLaunchd && process.platform === "darwin" && launchdInstallStatus !== "ok" && runtimeStartStatus === "ok") {
|
|
1482
1549
|
actions.push("launchd-install");
|
|
1483
|
-
runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId]);
|
|
1550
|
+
runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
1484
1551
|
launchdInstallStatus = "ok";
|
|
1485
1552
|
launchdInstallReason = "";
|
|
1486
1553
|
}
|
|
@@ -1603,7 +1670,21 @@ async function collectSetupConfig(options, context) {
|
|
|
1603
1670
|
|
|
1604
1671
|
function runSetupStep(context, title, scriptRelativePath, args, options = {}) {
|
|
1605
1672
|
console.log(`\n== ${title} ==`);
|
|
1606
|
-
|
|
1673
|
+
let executionContext = context;
|
|
1674
|
+
let scriptPath = undefined;
|
|
1675
|
+
|
|
1676
|
+
if (options.useRuntimeCopy) {
|
|
1677
|
+
syncRuntimeHome(context, { stdio: "pipe" });
|
|
1678
|
+
executionContext = createRuntimeExecutionContext(context);
|
|
1679
|
+
scriptPath = path.join(executionContext.stableSkillRoot, scriptRelativePath);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
const result = runScriptWithContext(executionContext, scriptRelativePath, args, {
|
|
1683
|
+
stdio: "pipe",
|
|
1684
|
+
env: options.env,
|
|
1685
|
+
cwd: options.cwd,
|
|
1686
|
+
scriptPath
|
|
1687
|
+
});
|
|
1607
1688
|
if (result.status !== 0) {
|
|
1608
1689
|
printFailureDetails(result);
|
|
1609
1690
|
throw new Error(`${title} failed`);
|
|
@@ -1868,8 +1949,8 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
1868
1949
|
runtimeStartReason = "gh-auth-not-ready";
|
|
1869
1950
|
console.log("runtime start skipped: GitHub CLI is not authenticated yet. Run `gh auth login` and start the runtime afterwards.");
|
|
1870
1951
|
} else {
|
|
1871
|
-
runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId]);
|
|
1872
|
-
const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId]);
|
|
1952
|
+
runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
1953
|
+
const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
1873
1954
|
runtimeStatusKv = parseKvOutput(runtimeStatusOutput);
|
|
1874
1955
|
runtimeStartStatus = "ok";
|
|
1875
1956
|
runtimeStartReason = "";
|
|
@@ -1886,7 +1967,7 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
1886
1967
|
launchdInstallReason = "runtime-not-started";
|
|
1887
1968
|
console.log("launchd install skipped: runtime was not started successfully in this setup run.");
|
|
1888
1969
|
} else {
|
|
1889
|
-
runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId]);
|
|
1970
|
+
runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId], { useRuntimeCopy: true });
|
|
1890
1971
|
launchdInstallStatus = "ok";
|
|
1891
1972
|
launchdInstallReason = "";
|
|
1892
1973
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-control-plane",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
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": {
|
|
@@ -32,12 +32,14 @@
|
|
|
32
32
|
"tools/bin",
|
|
33
33
|
"!tools/bin/audit-*.sh",
|
|
34
34
|
"!tools/bin/check-skill-contracts.sh",
|
|
35
|
+
"!tools/bin/render-dashboard-snapshot.py",
|
|
35
36
|
"tools/dashboard/app.js",
|
|
36
37
|
"tools/dashboard/dashboard_snapshot.py",
|
|
37
38
|
"tools/dashboard/index.html",
|
|
38
39
|
"tools/dashboard/server.py",
|
|
39
40
|
"tools/dashboard/styles.css",
|
|
40
41
|
"tools/templates",
|
|
42
|
+
"!tools/templates/legacy/",
|
|
41
43
|
"tools/vendor/codex-quota/LICENSE",
|
|
42
44
|
"tools/vendor/codex-quota/codex-quota.js",
|
|
43
45
|
"tools/vendor/codex-quota/lib",
|
package/references/commands.md
CHANGED
|
@@ -85,7 +85,7 @@ tools/bin/uninstall-project-launchd.sh --profile-id <id>
|
|
|
85
85
|
tools/bin/project-remove.sh --profile-id <id>
|
|
86
86
|
tools/bin/project-remove.sh --profile-id <id> --purge-paths
|
|
87
87
|
tools/bin/sync-shared-agent-home.sh
|
|
88
|
-
python3 tools/
|
|
88
|
+
python3 tools/dashboard/dashboard_snapshot.py --pretty
|
|
89
89
|
bash tools/bin/serve-dashboard.sh --host 127.0.0.1 --port 8765
|
|
90
90
|
bash tools/bin/install-dashboard-launchd.sh --host 127.0.0.1 --port 8765
|
|
91
91
|
```
|
|
@@ -97,7 +97,7 @@ prompts live under `tools/templates/`.
|
|
|
97
97
|
## Dashboard
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
python3 tools/
|
|
100
|
+
python3 tools/dashboard/dashboard_snapshot.py --pretty
|
|
101
101
|
bash tools/bin/serve-dashboard.sh --host 127.0.0.1 --port 8765
|
|
102
102
|
bash tools/bin/install-dashboard-launchd.sh --host 127.0.0.1 --port 8765
|
|
103
103
|
```
|
|
@@ -64,7 +64,7 @@ roots, labels, worker preferences, prompts, and project-specific guardrails.
|
|
|
64
64
|
before scheduler use.
|
|
65
65
|
- `tools/bin/test-smoke.sh`
|
|
66
66
|
Runs the main shared-package smoke gates in one operator-facing command.
|
|
67
|
-
- `tools/
|
|
67
|
+
- `tools/dashboard/dashboard_snapshot.py`
|
|
68
68
|
Emits a JSON snapshot of active runs, resident controllers, cooldown state,
|
|
69
69
|
queue depth, and scheduled issues across installed profiles.
|
|
70
70
|
- `tools/bin/serve-dashboard.sh`
|
|
@@ -307,12 +307,14 @@ schedule_provider_quota_cooldown() {
|
|
|
307
307
|
local reason="${1:-provider-quota-limit}"
|
|
308
308
|
[[ "${reason}" == "provider-quota-limit" ]] || return 0
|
|
309
309
|
[[ -x "${provider_cooldown_script}" ]] || return 0
|
|
310
|
+
[[ "${CODING_WORKER:-}" == "codex" ]] && return 0
|
|
310
311
|
|
|
311
312
|
"${provider_cooldown_script}" schedule "${reason}" >/dev/null || true
|
|
312
313
|
}
|
|
313
314
|
|
|
314
315
|
clear_provider_quota_cooldown() {
|
|
315
316
|
[[ -x "${provider_cooldown_script}" ]] || return 0
|
|
317
|
+
[[ "${CODING_WORKER:-}" == "codex" ]] && return 0
|
|
316
318
|
|
|
317
319
|
"${provider_cooldown_script}" clear >/dev/null || true
|
|
318
320
|
}
|