agent-control-plane 0.1.13 → 0.1.16
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 +250 -355
- package/SKILL.md +1 -1
- package/hooks/heartbeat-hooks.sh +16 -9
- package/npm/bin/agent-control-plane.js +117 -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 +23 -0
- package/tools/bin/agent-project-reconcile-pr-session +191 -22
- package/tools/bin/agent-project-run-codex-resilient +57 -2
- package/tools/bin/agent-project-run-openclaw-session +46 -0
- package/tools/bin/agent-project-worker-status +37 -0
- package/tools/bin/flow-config-lib.sh +7 -0
- package/tools/bin/flow-shell-lib.sh +2 -0
- package/tools/bin/heartbeat-safe-auto.sh +20 -10
- package/tools/bin/project-runtimectl.sh +1 -1
- package/tools/bin/provider-cooldown-state.sh +39 -1
- package/tools/bin/start-issue-worker.sh +35 -0
- package/tools/bin/start-pr-fix-worker.sh +3 -0
- package/tools/bin/start-pr-review-worker.sh +3 -0
- package/tools/bin/start-resident-issue-loop.sh +1 -0
- package/tools/dashboard/app.js +136 -0
- package/tools/dashboard/dashboard_snapshot.py +253 -3
- package/tools/dashboard/index.html +5 -1
- package/tools/dashboard/styles.css +97 -20
- package/tools/templates/pr-fix-template.md +6 -6
- package/tools/templates/pr-merge-repair-template.md +6 -6
- 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
|
@@ -95,6 +95,7 @@ heartbeat_open_agent_pr_issue_ids() {
|
|
|
95
95
|
local pr_issue_ids_json=""
|
|
96
96
|
pr_issue_ids_json="$(
|
|
97
97
|
flow_github_pr_list_json "$REPO_SLUG" open 100 \
|
|
98
|
+
2>/dev/null \
|
|
98
99
|
| jq --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" --arg branchIssueRegex "${AGENT_PR_ISSUE_CAPTURE_REGEX}" '
|
|
99
100
|
map(
|
|
100
101
|
. as $pr
|
|
@@ -119,7 +120,7 @@ heartbeat_open_agent_pr_issue_ids() {
|
|
|
119
120
|
| select(. != null and . != "")
|
|
120
121
|
)
|
|
121
122
|
| unique
|
|
122
|
-
'
|
|
123
|
+
' 2>/dev/null || true
|
|
123
124
|
)"
|
|
124
125
|
|
|
125
126
|
if [[ -z "${pr_issue_ids_json:-}" ]]; then
|
|
@@ -136,6 +137,7 @@ heartbeat_list_ready_issue_ids() {
|
|
|
136
137
|
|
|
137
138
|
ready_issue_rows="$(
|
|
138
139
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
140
|
+
2>/dev/null \
|
|
139
141
|
| jq -r --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
|
|
140
142
|
map(select(
|
|
141
143
|
(any(.labels[]?; .name == "agent-running") | not)
|
|
@@ -145,7 +147,7 @@ heartbeat_list_ready_issue_ids() {
|
|
|
145
147
|
| .[]
|
|
146
148
|
| [.number, (any(.labels[]?; .name == "agent-blocked"))]
|
|
147
149
|
| @tsv
|
|
148
|
-
'
|
|
150
|
+
' 2>/dev/null || true
|
|
149
151
|
)"
|
|
150
152
|
|
|
151
153
|
while IFS=$'\t' read -r issue_id is_blocked; do
|
|
@@ -170,6 +172,7 @@ heartbeat_list_blocked_recovery_issue_ids() {
|
|
|
170
172
|
|
|
171
173
|
blocked_issue_rows="$(
|
|
172
174
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
175
|
+
2>/dev/null \
|
|
173
176
|
| jq -r --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
|
|
174
177
|
map(select(
|
|
175
178
|
any(.labels[]?; .name == "agent-blocked")
|
|
@@ -178,7 +181,7 @@ heartbeat_list_blocked_recovery_issue_ids() {
|
|
|
178
181
|
))
|
|
179
182
|
| sort_by(.createdAt, .number)
|
|
180
183
|
| .[].number
|
|
181
|
-
'
|
|
184
|
+
' 2>/dev/null || true
|
|
182
185
|
)"
|
|
183
186
|
|
|
184
187
|
while IFS= read -r issue_id; do
|
|
@@ -268,6 +271,7 @@ heartbeat_list_exclusive_issue_ids() {
|
|
|
268
271
|
open_agent_pr_issue_ids="$(heartbeat_open_agent_pr_issue_ids)"
|
|
269
272
|
|
|
270
273
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
274
|
+
2>/dev/null \
|
|
271
275
|
| jq -r --arg exclusiveLabel "${AGENT_EXCLUSIVE_LABEL}" --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
|
|
272
276
|
map(select(
|
|
273
277
|
any(.labels[]?; .name == $exclusiveLabel)
|
|
@@ -277,20 +281,22 @@ heartbeat_list_exclusive_issue_ids() {
|
|
|
277
281
|
))
|
|
278
282
|
| sort_by(.createdAt, .number)
|
|
279
283
|
| .[].number
|
|
280
|
-
'
|
|
284
|
+
' 2>/dev/null || true
|
|
281
285
|
}
|
|
282
286
|
|
|
283
287
|
heartbeat_list_running_issue_ids() {
|
|
284
288
|
flow_github_issue_list_json "$REPO_SLUG" open 100 \
|
|
289
|
+
2>/dev/null \
|
|
285
290
|
| jq -r '
|
|
286
291
|
map(select(any(.labels[]?; .name == "agent-running")))
|
|
287
292
|
| sort_by(.createdAt, .number)
|
|
288
293
|
| .[].number
|
|
289
|
-
'
|
|
294
|
+
' 2>/dev/null || true
|
|
290
295
|
}
|
|
291
296
|
|
|
292
297
|
heartbeat_list_open_agent_pr_ids() {
|
|
293
298
|
flow_github_pr_list_json "$REPO_SLUG" open 100 \
|
|
299
|
+
2>/dev/null \
|
|
294
300
|
| jq -r --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" '
|
|
295
301
|
map(select(
|
|
296
302
|
. as $pr
|
|
@@ -302,11 +308,12 @@ heartbeat_list_open_agent_pr_ids() {
|
|
|
302
308
|
))
|
|
303
309
|
| sort_by(.createdAt)
|
|
304
310
|
| .[].number
|
|
305
|
-
'
|
|
311
|
+
' 2>/dev/null || true
|
|
306
312
|
}
|
|
307
313
|
|
|
308
314
|
heartbeat_list_exclusive_pr_ids() {
|
|
309
315
|
flow_github_pr_list_json "$REPO_SLUG" open 100 \
|
|
316
|
+
2>/dev/null \
|
|
310
317
|
| jq -r --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" --arg exclusiveLabel "${AGENT_EXCLUSIVE_LABEL}" '
|
|
311
318
|
map(select(
|
|
312
319
|
. as $pr
|
|
@@ -319,7 +326,7 @@ heartbeat_list_exclusive_pr_ids() {
|
|
|
319
326
|
))
|
|
320
327
|
| sort_by(.createdAt)
|
|
321
328
|
| .[].number
|
|
322
|
-
'
|
|
329
|
+
' 2>/dev/null || true
|
|
323
330
|
}
|
|
324
331
|
|
|
325
332
|
heartbeat_issue_is_heavy() {
|
|
@@ -438,9 +445,9 @@ heartbeat_mark_issue_running() {
|
|
|
438
445
|
local issue_id="${1:?issue id required}"
|
|
439
446
|
local is_heavy="${2:-no}"
|
|
440
447
|
if [[ "$is_heavy" == "yes" ]]; then
|
|
441
|
-
bash "${FLOW_TOOLS_DIR}/agent-github-update-labels" --repo-slug "$REPO_SLUG" --number "$issue_id" --remove agent-ready --remove agent-blocked --add agent-running --add agent-e2e-heavy >/dev/null
|
|
448
|
+
bash "${FLOW_TOOLS_DIR}/agent-github-update-labels" --repo-slug "$REPO_SLUG" --number "$issue_id" --remove agent-ready --remove agent-blocked --add agent-running --add agent-e2e-heavy >/dev/null || true
|
|
442
449
|
else
|
|
443
|
-
bash "${FLOW_TOOLS_DIR}/agent-github-update-labels" --repo-slug "$REPO_SLUG" --number "$issue_id" --remove agent-ready --remove agent-blocked --add agent-running >/dev/null
|
|
450
|
+
bash "${FLOW_TOOLS_DIR}/agent-github-update-labels" --repo-slug "$REPO_SLUG" --number "$issue_id" --remove agent-ready --remove agent-blocked --add agent-running >/dev/null || true
|
|
444
451
|
fi
|
|
445
452
|
}
|
|
446
453
|
|
|
@@ -22,6 +22,7 @@ Commands:
|
|
|
22
22
|
help Show this help
|
|
23
23
|
version Print package version
|
|
24
24
|
setup Guided setup flow for one repo profile
|
|
25
|
+
onboard Alias for setup
|
|
25
26
|
sync Publish the packaged runtime into ~/.agent-runtime
|
|
26
27
|
install Alias for sync
|
|
27
28
|
init Scaffold and adopt a project profile
|
|
@@ -503,6 +504,20 @@ function createPromptInterface() {
|
|
|
503
504
|
});
|
|
504
505
|
}
|
|
505
506
|
|
|
507
|
+
function printWizardBanner() {
|
|
508
|
+
console.log("============================================================");
|
|
509
|
+
console.log(" Agent Control Plane — Setup Wizard");
|
|
510
|
+
console.log("============================================================");
|
|
511
|
+
console.log("");
|
|
512
|
+
console.log("This wizard will guide you through setting up one repo profile.");
|
|
513
|
+
console.log("Press Enter at any prompt to accept the value shown in [brackets].");
|
|
514
|
+
console.log("");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function printWizardStep(step, total, title) {
|
|
518
|
+
console.log(`\n[${step}/${total}] ${title}`);
|
|
519
|
+
}
|
|
520
|
+
|
|
506
521
|
function question(rl, prompt) {
|
|
507
522
|
return new Promise((resolve) => rl.question(prompt, resolve));
|
|
508
523
|
}
|
|
@@ -1341,7 +1356,9 @@ function printSetupDryRunPlan(context, config, plan) {
|
|
|
1341
1356
|
}
|
|
1342
1357
|
console.log(`- GitHub auth step: ${plan.githubAuthAction.status}${plan.githubAuthAction.reason ? ` (${plan.githubAuthAction.reason})` : ""}`);
|
|
1343
1358
|
console.log(`- runtime start: ${plan.runtimeStartAction.status}${plan.runtimeStartAction.reason ? ` (${plan.runtimeStartAction.reason})` : ""}`);
|
|
1344
|
-
|
|
1359
|
+
if (process.platform === "darwin") {
|
|
1360
|
+
console.log(`- launchd install: ${plan.launchdAction.status}${plan.launchdAction.reason ? ` (${plan.launchdAction.reason})` : ""}`);
|
|
1361
|
+
}
|
|
1345
1362
|
}
|
|
1346
1363
|
|
|
1347
1364
|
function buildSetupResultPayload(params) {
|
|
@@ -1457,6 +1474,23 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
|
|
|
1457
1474
|
console.log(`- ${issue}`);
|
|
1458
1475
|
}
|
|
1459
1476
|
|
|
1477
|
+
// Always show actionable hints so operators know what to fix,
|
|
1478
|
+
// even when running non-interactively (--yes / --json / CI).
|
|
1479
|
+
if (!currentState.prereq.coreToolsOk) {
|
|
1480
|
+
const missing = currentState.prereq.missingRequired.join(", ");
|
|
1481
|
+
console.log(` Fix: install missing core tools (${missing})`);
|
|
1482
|
+
}
|
|
1483
|
+
if (!currentState.prereq.workerAvailable) {
|
|
1484
|
+
const worker = currentState.prereq.workerCommand;
|
|
1485
|
+
if (worker === "codex") console.log(" Fix: npm install -g @openai/codex && codex login");
|
|
1486
|
+
else if (worker === "openclaw") console.log(" Fix: npm install -g openclaw && openclaw setup");
|
|
1487
|
+
else if (worker === "claude") console.log(" Fix: npm install -g @anthropic-ai/claude-code && claude auth login");
|
|
1488
|
+
else console.log(` Fix: install ${worker} and add it to PATH`);
|
|
1489
|
+
}
|
|
1490
|
+
if (!currentState.prereq.ghAuthOk) {
|
|
1491
|
+
console.log(" Fix: run gh auth login");
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1460
1494
|
if (!options.interactive) {
|
|
1461
1495
|
return {
|
|
1462
1496
|
status: "skipped",
|
|
@@ -1597,19 +1631,20 @@ async function collectSetupConfig(options, context) {
|
|
|
1597
1631
|
throw new Error("setup could not detect --repo-slug automatically; pass --repo-slug <owner/repo> or run interactively inside a git checkout with origin set");
|
|
1598
1632
|
}
|
|
1599
1633
|
} else {
|
|
1634
|
+
printWizardBanner();
|
|
1600
1635
|
const rl = createPromptInterface();
|
|
1601
1636
|
try {
|
|
1602
|
-
|
|
1603
|
-
console.log("Press Enter to accept the suggested value shown in brackets.\n");
|
|
1637
|
+
printWizardStep(1, 4, "Project details");
|
|
1604
1638
|
|
|
1605
1639
|
repoRoot = path.resolve(await promptText(rl, "Local repo root", detectedRepoRoot));
|
|
1606
1640
|
repoSlug = await promptText(rl, "GitHub repo slug", repoSlug || "");
|
|
1607
1641
|
profileId = sanitizeProfileId(await promptText(rl, "Profile id", profileId));
|
|
1608
|
-
codingWorker = await promptText(rl, "Coding worker (codex|claude|openclaw)", codingWorker);
|
|
1609
1642
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1643
|
+
let workerInput = codingWorker;
|
|
1644
|
+
while (!["codex", "claude", "openclaw"].includes(workerInput)) {
|
|
1645
|
+
workerInput = await promptText(rl, "Coding worker (codex / claude / openclaw)", codingWorker || "openclaw");
|
|
1612
1646
|
}
|
|
1647
|
+
codingWorker = workerInput;
|
|
1613
1648
|
} finally {
|
|
1614
1649
|
rl.close();
|
|
1615
1650
|
}
|
|
@@ -1630,6 +1665,9 @@ async function collectSetupConfig(options, context) {
|
|
|
1630
1665
|
prereq
|
|
1631
1666
|
};
|
|
1632
1667
|
|
|
1668
|
+
if (options.interactive) {
|
|
1669
|
+
printWizardStep(2, 4, "Review plan");
|
|
1670
|
+
}
|
|
1633
1671
|
renderSetupSummary(config);
|
|
1634
1672
|
|
|
1635
1673
|
if (options.interactive) {
|
|
@@ -1641,7 +1679,7 @@ async function collectSetupConfig(options, context) {
|
|
|
1641
1679
|
}
|
|
1642
1680
|
const shouldContinue = await promptYesNo(rl, "Continue with these values", true);
|
|
1643
1681
|
if (!shouldContinue) {
|
|
1644
|
-
|
|
1682
|
+
return null;
|
|
1645
1683
|
}
|
|
1646
1684
|
if (options.startRuntime === null) {
|
|
1647
1685
|
options.startRuntime = await promptYesNo(rl, "Start the runtime after setup", true);
|
|
@@ -1733,6 +1771,10 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
1733
1771
|
|
|
1734
1772
|
try {
|
|
1735
1773
|
const config = await collectSetupConfig(options, context);
|
|
1774
|
+
if (config === null) {
|
|
1775
|
+
console.log("\nSetup cancelled. Run again when you are ready.");
|
|
1776
|
+
return 0;
|
|
1777
|
+
}
|
|
1736
1778
|
if (options.dryRun) {
|
|
1737
1779
|
const plan = buildSetupDryRunPlan(options, context, config);
|
|
1738
1780
|
printSetupDryRunPlan(context, config, plan);
|
|
@@ -1869,6 +1911,10 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
1869
1911
|
return 1;
|
|
1870
1912
|
}
|
|
1871
1913
|
|
|
1914
|
+
if (options.interactive) {
|
|
1915
|
+
printWizardStep(3, 4, "Prerequisites");
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1872
1918
|
let prereq = config.prereq;
|
|
1873
1919
|
let dependencyInstall = await maybeInstallMissingDependencies(options, prereq);
|
|
1874
1920
|
if (dependencyInstall.status === "failed") {
|
|
@@ -1886,6 +1932,35 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
1886
1932
|
let workerSetupStep = await maybeShowWorkerSetupGuide(options, prereq);
|
|
1887
1933
|
prereq = collectPrereqStatus(config.codingWorker);
|
|
1888
1934
|
|
|
1935
|
+
// Check OpenRouter API key when openclaw is selected
|
|
1936
|
+
if (config.codingWorker === "openclaw" && !process.env.OPENROUTER_API_KEY) {
|
|
1937
|
+
console.log("\nOpenClaw requires an OpenRouter API key (OPENROUTER_API_KEY).");
|
|
1938
|
+
console.log("- Get a free key at: https://openrouter.ai/keys");
|
|
1939
|
+
if (options.interactive) {
|
|
1940
|
+
const rl = createPromptInterface();
|
|
1941
|
+
let apiKey = "";
|
|
1942
|
+
try {
|
|
1943
|
+
apiKey = (await promptText(rl, "OpenRouter API key (Enter to skip)", "")).trim();
|
|
1944
|
+
} finally {
|
|
1945
|
+
rl.close();
|
|
1946
|
+
}
|
|
1947
|
+
if (apiKey) {
|
|
1948
|
+
process.env.OPENROUTER_API_KEY = apiKey;
|
|
1949
|
+
console.log("API key set for this session.");
|
|
1950
|
+
console.log("To persist it, add the following to your shell profile (~/.zshrc or ~/.bashrc):");
|
|
1951
|
+
console.log(` export OPENROUTER_API_KEY=${JSON.stringify(apiKey)}`);
|
|
1952
|
+
} else {
|
|
1953
|
+
console.log("Skipped. Set OPENROUTER_API_KEY before starting the runtime.");
|
|
1954
|
+
}
|
|
1955
|
+
} else {
|
|
1956
|
+
console.log("Set OPENROUTER_API_KEY in your environment before starting the runtime.");
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
if (options.interactive) {
|
|
1961
|
+
printWizardStep(4, 4, "Install");
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1889
1964
|
const scopedContext = buildScopedContext(context, config.profileId);
|
|
1890
1965
|
const anchorSync = buildAnchorSyncDecision(options, config.paths.sourceRepoRoot);
|
|
1891
1966
|
|
|
@@ -2047,12 +2122,45 @@ async function runSetupFlow(forwardedArgs) {
|
|
|
2047
2122
|
|
|
2048
2123
|
if (options.json) {
|
|
2049
2124
|
emitSetupJsonPayload(runPayload);
|
|
2125
|
+
} else if (options.interactive) {
|
|
2126
|
+
// Human-friendly summary for interactive terminal runs
|
|
2127
|
+
console.log("\n============================================================");
|
|
2128
|
+
console.log(" Setup complete!");
|
|
2129
|
+
console.log("============================================================");
|
|
2130
|
+
console.log(` Profile : ${config.profileId}`);
|
|
2131
|
+
console.log(` Repo : ${config.repoSlug}`);
|
|
2132
|
+
console.log(` Worker : ${config.codingWorker}`);
|
|
2133
|
+
console.log(` Runtime : ${context.runtimeHome}`);
|
|
2134
|
+
|
|
2135
|
+
const pendingItems = [];
|
|
2136
|
+
if (!prereq.ghAuthOk) pendingItems.push("GitHub CLI not authenticated — run: gh auth login");
|
|
2137
|
+
if (!prereq.workerAvailable) pendingItems.push(`${config.codingWorker} not found on PATH — install it before starting`);
|
|
2138
|
+
if (config.codingWorker === "openclaw" && !process.env.OPENROUTER_API_KEY) {
|
|
2139
|
+
pendingItems.push("OPENROUTER_API_KEY not set — required for openclaw workers");
|
|
2140
|
+
}
|
|
2141
|
+
if (anchorSync.status !== "ok") pendingItems.push(`Anchor repo sync deferred (${anchorSync.reason}) — fix git access and re-run setup`);
|
|
2142
|
+
if ((doctorKv.DOCTOR_STATUS || "") !== "ok") pendingItems.push(`Doctor check flagged issues — run: npx agent-control-plane@latest doctor`);
|
|
2143
|
+
|
|
2144
|
+
if (pendingItems.length > 0) {
|
|
2145
|
+
console.log("\n Pending items before starting:");
|
|
2146
|
+
for (const item of pendingItems) {
|
|
2147
|
+
console.log(` - ${item}`);
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
console.log("\n Next commands:");
|
|
2152
|
+
if (runtimeStartStatus !== "ok") {
|
|
2153
|
+
console.log(` npx agent-control-plane@latest runtime start --profile-id ${config.profileId}`);
|
|
2154
|
+
}
|
|
2155
|
+
console.log(` npx agent-control-plane@latest runtime status --profile-id ${config.profileId}`);
|
|
2156
|
+
console.log(` npx agent-control-plane@latest doctor`);
|
|
2157
|
+
console.log("");
|
|
2050
2158
|
} else {
|
|
2159
|
+
// Machine-readable KV output for non-interactive / scripted runs
|
|
2051
2160
|
console.log("\nSetup complete.");
|
|
2052
2161
|
console.log(`- profile: ${config.profileId}`);
|
|
2053
2162
|
console.log(`- repo: ${config.repoSlug}`);
|
|
2054
2163
|
console.log(`- runtime home: ${context.runtimeHome}`);
|
|
2055
|
-
console.log(`- next status command: npx agent-control-plane@latest runtime status --profile-id ${config.profileId}`);
|
|
2056
2164
|
|
|
2057
2165
|
console.log(`SETUP_STATUS=ok`);
|
|
2058
2166
|
console.log(`PROFILE_ID=${config.profileId}`);
|
|
@@ -2170,6 +2278,7 @@ async function main() {
|
|
|
2170
2278
|
console.log(packageJson.version);
|
|
2171
2279
|
return 0;
|
|
2172
2280
|
case "setup":
|
|
2281
|
+
case "onboard":
|
|
2173
2282
|
return runSetupFlow(forwardedArgs);
|
|
2174
2283
|
case "sync":
|
|
2175
2284
|
case "install":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-control-plane",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
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
|
}
|
|
@@ -357,6 +359,16 @@ infer_issue_runtime_failure_from_log() {
|
|
|
357
359
|
return 0
|
|
358
360
|
fi
|
|
359
361
|
|
|
362
|
+
if grep -Eiq 'stale-run no-agent-output-before-stall-threshold|no-agent-output-before-stall-threshold' "${log_file}" 2>/dev/null; then
|
|
363
|
+
printf 'no-agent-output-before-stall-threshold\n'
|
|
364
|
+
return 0
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
if grep -Eiq 'stale-run no-agent-progress-before-stall-threshold|no-agent-progress-before-stall-threshold' "${log_file}" 2>/dev/null; then
|
|
368
|
+
printf 'no-agent-progress-before-stall-threshold\n'
|
|
369
|
+
return 0
|
|
370
|
+
fi
|
|
371
|
+
|
|
360
372
|
if grep -Eiq 'Ignoring invalid cwd .* No such file or directory|/tmp is absolute|Custom tool call output is missing' "${log_file}" 2>/dev/null; then
|
|
361
373
|
printf 'worker-environment-blocked\n'
|
|
362
374
|
return 0
|
|
@@ -985,6 +997,17 @@ if (explicitFailureReason) {
|
|
|
985
997
|
reason = 'scope-guard-blocked';
|
|
986
998
|
} else if (/^# Blocker: Provider quota is currently exhausted$/im.test(body)) {
|
|
987
999
|
reason = 'provider-quota-limit';
|
|
1000
|
+
} else if (
|
|
1001
|
+
/blocked on external network access/i.test(body) &&
|
|
1002
|
+
(/What I ran:/i.test(body) ||
|
|
1003
|
+
/`pnpm audit`/i.test(body) ||
|
|
1004
|
+
/`gh issue view`/i.test(body)) &&
|
|
1005
|
+
(/failed with `ENOTFOUND`/i.test(body) ||
|
|
1006
|
+
/Exact failure:/i.test(body) ||
|
|
1007
|
+
/registry\.npmjs\.org/i.test(body) ||
|
|
1008
|
+
/api\.github\.com/i.test(body))
|
|
1009
|
+
) {
|
|
1010
|
+
reason = 'worker-preflight-network-blocked';
|
|
988
1011
|
} else if (
|
|
989
1012
|
/blocked on external network access/i.test(body) ||
|
|
990
1013
|
/could not perform a safe offline bump/i.test(body) ||
|