agent-control-plane 0.1.2 → 0.1.3

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.
Files changed (179) hide show
  1. package/README.md +6 -0
  2. package/npm/bin/agent-control-plane.js +149 -5
  3. package/package.json +1 -2
  4. package/tools/tests/test-agent-control-plane-npm-cli.sh +0 -280
  5. package/tools/tests/test-agent-github-update-labels-falls-back-to-repository-id.sh +0 -56
  6. package/tools/tests/test-agent-project-claude-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -89
  7. package/tools/tests/test-agent-project-claude-session-wrapper-does-not-retry-provider-quota.sh +0 -82
  8. package/tools/tests/test-agent-project-claude-session-wrapper-retries-transient-failures.sh +0 -90
  9. package/tools/tests/test-agent-project-claude-session-wrapper-times-out.sh +0 -73
  10. package/tools/tests/test-agent-project-claude-session-wrapper.sh +0 -103
  11. package/tools/tests/test-agent-project-cleanup-session-orphan-fallback.sh +0 -90
  12. package/tools/tests/test-agent-project-cleanup-session-skip-worktree-cleanup.sh +0 -90
  13. package/tools/tests/test-agent-project-codex-live-thread-persist.sh +0 -76
  14. package/tools/tests/test-agent-project-codex-recovery.sh +0 -731
  15. package/tools/tests/test-agent-project-codex-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -105
  16. package/tools/tests/test-agent-project-codex-session-wrapper.sh +0 -97
  17. package/tools/tests/test-agent-project-open-pr-worktree-config-prefix.sh +0 -81
  18. package/tools/tests/test-agent-project-openclaw-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -109
  19. package/tools/tests/test-agent-project-openclaw-session-wrapper-infers-blocked-result-contract.sh +0 -89
  20. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-literal-env-artifacts.sh +0 -113
  21. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-version-mismatch.sh +0 -135
  22. package/tools/tests/test-agent-project-openclaw-session-wrapper-resident.sh +0 -179
  23. package/tools/tests/test-agent-project-openclaw-session-wrapper-reuses-existing-agent-after-add-race.sh +0 -119
  24. package/tools/tests/test-agent-project-openclaw-session-wrapper-terminates-rate-limit-hang.sh +0 -91
  25. package/tools/tests/test-agent-project-openclaw-session-wrapper.sh +0 -117
  26. package/tools/tests/test-agent-project-publish-issue-pr-prunes-stale-worktree-entry.sh +0 -148
  27. package/tools/tests/test-agent-project-publish-issue-pr-reads-archived-session.sh +0 -146
  28. package/tools/tests/test-agent-project-publish-issue-pr-recovers-final-head.sh +0 -145
  29. package/tools/tests/test-agent-project-publish-issue-pr-reuses-existing-worktree.sh +0 -147
  30. package/tools/tests/test-agent-project-reconcile-failure-reason.sh +0 -456
  31. package/tools/tests/test-agent-project-reconcile-issue-archived-session-fallback.sh +0 -96
  32. package/tools/tests/test-agent-project-reconcile-issue-before-blocked.sh +0 -90
  33. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery-uses-recovered-worktree.sh +0 -212
  34. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery.sh +0 -207
  35. package/tools/tests/test-agent-project-reconcile-issue-provider-quota-schedules-provider-cooldown.sh +0 -101
  36. package/tools/tests/test-agent-project-reconcile-issue-session-backfills-lane-metadata-from-worker-key.sh +0 -113
  37. package/tools/tests/test-agent-project-reconcile-issue-session-clears-stale-failed-summary.sh +0 -117
  38. package/tools/tests/test-agent-project-reconcile-issue-session-initializes-shared-agent-home.sh +0 -55
  39. package/tools/tests/test-agent-project-reconcile-issue-session-normalizes-runner-state.sh +0 -125
  40. package/tools/tests/test-agent-project-reconcile-issue-session-records-invalid-contract-summary.sh +0 -118
  41. package/tools/tests/test-agent-project-reconcile-issue-session-skips-duplicate-blocked-comment.sh +0 -144
  42. package/tools/tests/test-agent-project-reconcile-issue-session-standardizes-no-commits-blocker.sh +0 -145
  43. package/tools/tests/test-agent-project-reconcile-issue-session-synthesizes-blocked-comment.sh +0 -139
  44. package/tools/tests/test-agent-project-reconcile-pr-blocked-host-recovery.sh +0 -242
  45. package/tools/tests/test-agent-project-reconcile-pr-guard-blocked-no-commit.sh +0 -142
  46. package/tools/tests/test-agent-project-reconcile-pr-provider-quota-schedules-provider-cooldown.sh +0 -106
  47. package/tools/tests/test-agent-project-reconcile-pr-session-initializes-shared-agent-home.sh +0 -66
  48. package/tools/tests/test-agent-project-reconcile-pr-updated-branch-noop.sh +0 -129
  49. package/tools/tests/test-audit-agent-worktrees-active-launch-skips-git-inspection.sh +0 -69
  50. package/tools/tests/test-audit-agent-worktrees-broken-worktree.sh +0 -43
  51. package/tools/tests/test-audit-agent-worktrees-pending-launch-owner.sh +0 -46
  52. package/tools/tests/test-audit-agent-worktrees-unreconciled-owner.sh +0 -79
  53. package/tools/tests/test-audit-issue-routing-managed-branch-globs.sh +0 -56
  54. package/tools/tests/test-branch-verification-guard-generated-artifacts.sh +0 -72
  55. package/tools/tests/test-branch-verification-guard-targeted-coverage.sh +0 -125
  56. package/tools/tests/test-codex-quota-manager-failure-driven-rotation.sh +0 -178
  57. package/tools/tests/test-codex-quota-wrapper.sh +0 -37
  58. package/tools/tests/test-contribution-docs.sh +0 -18
  59. package/tools/tests/test-control-plane-dashboard-runtime-smoke.sh +0 -343
  60. package/tools/tests/test-create-follow-up-issue.sh +0 -73
  61. package/tools/tests/test-dashboard-launchd-bootstrap.sh +0 -55
  62. package/tools/tests/test-flow-export-execution-env-exports-repo-id.sh +0 -30
  63. package/tools/tests/test-flow-export-github-cli-auth-env-prefers-git-credential.sh +0 -48
  64. package/tools/tests/test-flow-github-api-repo-fallback-preserves-input.sh +0 -85
  65. package/tools/tests/test-flow-github-api-repo-prefers-explicit-repository-id.sh +0 -60
  66. package/tools/tests/test-flow-github-issue-list-falls-back-to-repository-id.sh +0 -64
  67. package/tools/tests/test-flow-github-pr-list-falls-back-to-repository-id.sh +0 -77
  68. package/tools/tests/test-flow-resident-can-reuse-does-not-leak-metadata.sh +0 -52
  69. package/tools/tests/test-flow-resident-reap-stale-controllers.sh +0 -63
  70. package/tools/tests/test-flow-resolve-codex-quota-tools.sh +0 -104
  71. package/tools/tests/test-flow-runtime-doctor-profile-selection.sh +0 -27
  72. package/tools/tests/test-heartbeat-codex-pr-linked-issue-exclusion.sh +0 -79
  73. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-idle-controller.sh +0 -115
  74. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-live-lane-controller.sh +0 -117
  75. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-claude.sh +0 -96
  76. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-codex.sh +0 -96
  77. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop.sh +0 -96
  78. package/tools/tests/test-heartbeat-loop-auth-wait-does-not-consume-capacity.sh +0 -170
  79. package/tools/tests/test-heartbeat-loop-blocked-recovery-lane.sh +0 -201
  80. package/tools/tests/test-heartbeat-loop-blocked-recovery-vs-pr-reservation.sh +0 -201
  81. package/tools/tests/test-heartbeat-loop-idle-resident-controller-does-not-block-launches.sh +0 -160
  82. package/tools/tests/test-heartbeat-loop-pr-launch-dedup.sh +0 -133
  83. package/tools/tests/test-heartbeat-loop-provider-cooldown-suppresses-launches.sh +0 -157
  84. package/tools/tests/test-heartbeat-loop-reaps-stale-resident-controller.sh +0 -181
  85. package/tools/tests/test-heartbeat-loop-waiting-provider-resident-controller-does-not-block-launches.sh +0 -160
  86. package/tools/tests/test-heartbeat-ready-issues-blocked-recovery.sh +0 -134
  87. package/tools/tests/test-heartbeat-safe-auto-dynamic-concurrency.sh +0 -162
  88. package/tools/tests/test-heartbeat-safe-auto-no-tmux-sessions.sh +0 -136
  89. package/tools/tests/test-heartbeat-safe-auto-openclaw-skips-codex-quota.sh +0 -139
  90. package/tools/tests/test-heartbeat-safe-auto-quota-health-signal.sh +0 -119
  91. package/tools/tests/test-heartbeat-safe-auto-stale-shared-loop-pid-does-not-skip.sh +0 -140
  92. package/tools/tests/test-heartbeat-safe-auto-static-capacity-without-quota-cache.sh +0 -142
  93. package/tools/tests/test-heartbeat-safe-auto-zero-healthy-pools.sh +0 -141
  94. package/tools/tests/test-heartbeat-sync-issue-labels-empty-schedule.sh +0 -65
  95. package/tools/tests/test-heartbeat-sync-open-agent-prs-terminal-clears-running.sh +0 -179
  96. package/tools/tests/test-install-dashboard-launchd.sh +0 -78
  97. package/tools/tests/test-install-project-launchd-adds-tool-paths.sh +0 -87
  98. package/tools/tests/test-install-project-launchd.sh +0 -110
  99. package/tools/tests/test-issue-local-workspace-install-policy.sh +0 -81
  100. package/tools/tests/test-issue-publish-scope-guard-docs-signal.sh +0 -70
  101. package/tools/tests/test-issue-reconcile-hooks-success-clears-blocked.sh +0 -36
  102. package/tools/tests/test-kick-scheduler-requires-explicit-profile.sh +0 -47
  103. package/tools/tests/test-label-follow-up-issues-falls-back-to-repository-id.sh +0 -132
  104. package/tools/tests/test-manual-operator-entrypoints-require-explicit-profile.sh +0 -64
  105. package/tools/tests/test-package-funding-metadata.sh +0 -21
  106. package/tools/tests/test-package-public-metadata.sh +0 -62
  107. package/tools/tests/test-placeholder-worker-adapters.sh +0 -38
  108. package/tools/tests/test-pr-reconcile-hooks-refreshes-recurring-issue-checklist.sh +0 -110
  109. package/tools/tests/test-pr-risk-cohesive-mobile-locale-scope.sh +0 -70
  110. package/tools/tests/test-pr-risk-fix-label-semantics.sh +0 -114
  111. package/tools/tests/test-pr-risk-local-first-no-checks.sh +0 -70
  112. package/tools/tests/test-prepare-worktree-simple-repo-baseline.sh +0 -67
  113. package/tools/tests/test-profile-activate.sh +0 -33
  114. package/tools/tests/test-profile-adopt-allow-missing-repo.sh +0 -68
  115. package/tools/tests/test-profile-adopt-skip-workspace-sync-missing-file.sh +0 -61
  116. package/tools/tests/test-profile-adopt-syncs-anchor-and-workspace.sh +0 -90
  117. package/tools/tests/test-profile-smoke-collision.sh +0 -44
  118. package/tools/tests/test-profile-smoke-invalid-claude-config.sh +0 -31
  119. package/tools/tests/test-profile-smoke-invalid-provider-pool.sh +0 -68
  120. package/tools/tests/test-profile-smoke-repo-slug-mismatch.sh +0 -36
  121. package/tools/tests/test-profile-smoke.sh +0 -45
  122. package/tools/tests/test-project-init-force-and-skip-sync.sh +0 -61
  123. package/tools/tests/test-project-init-repo-slug-mismatch.sh +0 -29
  124. package/tools/tests/test-project-init.sh +0 -66
  125. package/tools/tests/test-project-launchd-bootstrap.sh +0 -66
  126. package/tools/tests/test-project-remove.sh +0 -150
  127. package/tools/tests/test-project-runtime-supervisor.sh +0 -47
  128. package/tools/tests/test-project-runtimectl-launchd.sh +0 -115
  129. package/tools/tests/test-project-runtimectl-missing-profile.sh +0 -54
  130. package/tools/tests/test-project-runtimectl-start-falls-back-to-bootstrap.sh +0 -108
  131. package/tools/tests/test-project-runtimectl-status-reports-supervisor-as-heartbeat-parent.sh +0 -95
  132. package/tools/tests/test-project-runtimectl-status-supervisor-running.sh +0 -59
  133. package/tools/tests/test-project-runtimectl-stop-cancels-pending-kick.sh +0 -85
  134. package/tools/tests/test-project-runtimectl-stop-clears-running-labels.sh +0 -78
  135. package/tools/tests/test-project-runtimectl.sh +0 -212
  136. package/tools/tests/test-provider-cooldown-state-prefers-runtime-worker-context.sh +0 -39
  137. package/tools/tests/test-provider-cooldown-state.sh +0 -59
  138. package/tools/tests/test-public-repo-docs.sh +0 -161
  139. package/tools/tests/test-reconcile-pr-worker-acp-config-routing.sh +0 -75
  140. package/tools/tests/test-render-dashboard-snapshot.sh +0 -149
  141. package/tools/tests/test-render-flow-config-demo-profile.sh +0 -36
  142. package/tools/tests/test-render-flow-config-provider-pool-fallback.sh +0 -81
  143. package/tools/tests/test-render-flow-config.sh +0 -52
  144. package/tools/tests/test-run-codex-task-claude-routing.sh +0 -125
  145. package/tools/tests/test-run-codex-task-codex-resident-routing.sh +0 -108
  146. package/tools/tests/test-run-codex-task-kilo-routing.sh +0 -98
  147. package/tools/tests/test-run-codex-task-openclaw-resident-routing.sh +0 -117
  148. package/tools/tests/test-run-codex-task-openclaw-routing.sh +0 -113
  149. package/tools/tests/test-run-codex-task-opencode-routing.sh +0 -98
  150. package/tools/tests/test-run-codex-task-provider-pool-fallback-routing.sh +0 -146
  151. package/tools/tests/test-scaffold-profile.sh +0 -108
  152. package/tools/tests/test-serve-dashboard.sh +0 -93
  153. package/tools/tests/test-start-issue-worker-blocked-context.sh +0 -129
  154. package/tools/tests/test-start-issue-worker-blocks-complete-recurring-checklist.sh +0 -189
  155. package/tools/tests/test-start-issue-worker-local-install-routing.sh +0 -157
  156. package/tools/tests/test-start-issue-worker-profile-template-routing.sh +0 -149
  157. package/tools/tests/test-start-issue-worker-recurring-resident-reuse-codex.sh +0 -212
  158. package/tools/tests/test-start-issue-worker-recurring-resident-reuse.sh +0 -219
  159. package/tools/tests/test-start-issue-worker-renders-verification-snippet.sh +0 -155
  160. package/tools/tests/test-start-issue-worker-resident-reuse-falls-back-to-new-worktree.sh +0 -199
  161. package/tools/tests/test-start-pr-fix-worker-host-blocker-context.sh +0 -275
  162. package/tools/tests/test-start-resident-issue-loop-adopts-next-recurring-issue.sh +0 -185
  163. package/tools/tests/test-start-resident-issue-loop-clears-pending-while-waiting-due.sh +0 -152
  164. package/tools/tests/test-start-resident-issue-loop-consumes-queued-lease.sh +0 -186
  165. package/tools/tests/test-start-resident-issue-loop-fails-over-provider-pool.sh +0 -212
  166. package/tools/tests/test-start-resident-issue-loop-immediate-cycles.sh +0 -148
  167. package/tools/tests/test-start-resident-issue-loop-waits-for-provider.sh +0 -194
  168. package/tools/tests/test-start-resident-issue-loop-waits-for-terminal-reconcile-status.sh +0 -198
  169. package/tools/tests/test-start-resident-issue-loop-yields-to-live-lane-controller.sh +0 -145
  170. package/tools/tests/test-sync-pr-labels-fix-lane-uses-repair-queued.sh +0 -67
  171. package/tools/tests/test-sync-recurring-issue-checklist-backfills-workflow-complete-blocker.sh +0 -70
  172. package/tools/tests/test-sync-recurring-issue-checklist.sh +0 -95
  173. package/tools/tests/test-sync-shared-agent-home-local-source-root.sh +0 -66
  174. package/tools/tests/test-sync-shared-agent-home-preserves-unrelated-workflow-catalog-skill.sh +0 -47
  175. package/tools/tests/test-test-smoke.sh +0 -86
  176. package/tools/tests/test-uninstall-project-launchd.sh +0 -37
  177. package/tools/tests/test-update-github-labels-prefers-sibling-helper.sh +0 -49
  178. package/tools/tests/test-vendored-codex-quota-claude-oauth-only.sh +0 -38
  179. package/tools/tests/test-workflow-catalog.sh +0 -43
package/README.md CHANGED
@@ -302,6 +302,9 @@ During `setup`, ACP can:
302
302
  `openclaw` when ACP knows a safe install command, and otherwise show
303
303
  backend-specific next steps, install/auth/verify examples, and a docs URL it
304
304
  can open for you on interactive machines
305
+ - defer anchor repo sync automatically when ACP can scaffold the profile but
306
+ cannot reach the repo remote yet, so setup can still finish with a clear
307
+ follow-up instead of failing half way through
305
308
  - run one final fix-up summary at the end so you can clear whatever is still
306
309
  red instead of guessing which step to retry next
307
310
  - scaffold the profile, run doctor checks, and optionally start the runtime in
@@ -550,6 +553,9 @@ Use `--purge-paths` only when you want ACP-managed directories removed too.
550
553
  `launchd-uninstall`, and `remove`.
551
554
  - `gh` cannot access the repo
552
555
  Re-run `gh auth login` and confirm the repo slug in the profile is correct.
556
+ - setup deferred anchor repo sync
557
+ ACP could not reach the repo remote yet. Fix Git access or the remote URL,
558
+ then rerun `setup` or `init` without `--skip-anchor-sync`.
553
559
  - backend auth failures from `codex`, `claude`, or `openclaw`
554
560
  Authenticate that backend before starting ACP in the background.
555
561
  - `node` is older than `18`
@@ -191,12 +191,14 @@ function runCapture(command, args, options = {}) {
191
191
  const result = spawnSync(command, args, {
192
192
  encoding: "utf8",
193
193
  env: options.env || process.env,
194
- cwd: options.cwd || process.cwd()
194
+ cwd: options.cwd || process.cwd(),
195
+ timeout: options.timeoutMs || 0
195
196
  });
196
197
  return {
197
198
  status: typeof result.status === "number" ? result.status : (result.error ? 1 : 0),
198
199
  stdout: result.stdout || "",
199
- stderr: result.stderr || ""
200
+ stderr: result.stderr || "",
201
+ error: result.error || null
200
202
  };
201
203
  }
202
204
 
@@ -1044,6 +1046,92 @@ function printPrereqSummary(prereq) {
1044
1046
  console.log(`- backend note: ${backendReadinessHint(prereq.workerCommand)}`);
1045
1047
  }
1046
1048
 
1049
+ function probeAnchorSyncReadiness(repoRoot) {
1050
+ if (!repoRoot || !fs.existsSync(repoRoot)) {
1051
+ return {
1052
+ status: "deferred",
1053
+ reason: "source-repo-missing",
1054
+ remoteUrl: "",
1055
+ details: ""
1056
+ };
1057
+ }
1058
+
1059
+ const remoteResult = runCapture("git", ["remote", "get-url", "origin"], {
1060
+ cwd: repoRoot,
1061
+ timeoutMs: 5000
1062
+ });
1063
+ const remoteUrl = remoteResult.stdout.trim();
1064
+ if (remoteResult.status !== 0 || !remoteUrl) {
1065
+ return {
1066
+ status: "deferred",
1067
+ reason: "no-origin-remote",
1068
+ remoteUrl: "",
1069
+ details: `${remoteResult.stdout}${remoteResult.stderr}`.trim()
1070
+ };
1071
+ }
1072
+
1073
+ const probeResult = runCapture("git", ["ls-remote", "--exit-code", "origin", "HEAD"], {
1074
+ cwd: repoRoot,
1075
+ timeoutMs: 15000
1076
+ });
1077
+ if (probeResult.status === 0) {
1078
+ return {
1079
+ status: "ready",
1080
+ reason: "",
1081
+ remoteUrl,
1082
+ details: ""
1083
+ };
1084
+ }
1085
+
1086
+ const details = `${probeResult.stdout}${probeResult.stderr}`.trim();
1087
+ let reason = "git-remote-unreachable";
1088
+ if (probeResult.error && probeResult.error.code === "ETIMEDOUT") {
1089
+ reason = "git-remote-timeout";
1090
+ } else if (/could not read Username|Authentication failed|Permission denied|Repository not found|access denied|not found/i.test(details)) {
1091
+ reason = "git-remote-auth-or-access-error";
1092
+ } else if (/Could not resolve host|Name or service not known|Temporary failure in name resolution/i.test(details)) {
1093
+ reason = "git-remote-dns-error";
1094
+ }
1095
+
1096
+ return {
1097
+ status: "deferred",
1098
+ reason,
1099
+ remoteUrl,
1100
+ details
1101
+ };
1102
+ }
1103
+
1104
+ function buildAnchorSyncDecision(options, sourceRepoRoot) {
1105
+ if (options.skipAnchorSync) {
1106
+ return {
1107
+ status: "skipped",
1108
+ reason: "disabled",
1109
+ remoteUrl: "",
1110
+ details: "",
1111
+ skipAnchorSync: true
1112
+ };
1113
+ }
1114
+
1115
+ const probe = probeAnchorSyncReadiness(sourceRepoRoot);
1116
+ if (probe.status === "ready") {
1117
+ return {
1118
+ status: "ok",
1119
+ reason: "",
1120
+ remoteUrl: probe.remoteUrl,
1121
+ details: probe.details,
1122
+ skipAnchorSync: false
1123
+ };
1124
+ }
1125
+
1126
+ return {
1127
+ status: "deferred",
1128
+ reason: probe.reason,
1129
+ remoteUrl: probe.remoteUrl,
1130
+ details: probe.details,
1131
+ skipAnchorSync: true
1132
+ };
1133
+ }
1134
+
1047
1135
  function profileExists(profileRegistryRoot, profileId) {
1048
1136
  return fs.existsSync(path.join(profileRegistryRoot, profileId, "control-plane.yaml"));
1049
1137
  }
@@ -1082,6 +1170,7 @@ function buildSetupDryRunPlan(options, context, config) {
1082
1170
  const dependencyPlan = buildDependencyInstallPlan(prereq.missingRequired);
1083
1171
  const workerInstallPlan = buildWorkerBackendInstallPlan(prereq.workerCommand);
1084
1172
  const workerGuide = backendSetupGuide(prereq.workerCommand);
1173
+ const anchorSync = buildAnchorSyncDecision(options, config.paths.sourceRepoRoot);
1085
1174
 
1086
1175
  let dependencyAction = planStatusWithReason("not-needed");
1087
1176
  if (!prereq.coreToolsOk) {
@@ -1128,6 +1217,8 @@ function buildSetupDryRunPlan(options, context, config) {
1128
1217
  runtimeStartAction = planStatusWithReason("blocked", `missing-tools:${prereq.missingRequired.join(",")}`);
1129
1218
  } else if (!prereq.workerAvailable) {
1130
1219
  runtimeStartAction = planStatusWithReason("blocked", `missing-worker:${prereq.workerCommand}`);
1220
+ } else if (anchorSync.status !== "ok") {
1221
+ runtimeStartAction = planStatusWithReason("blocked", `anchor-sync-${anchorSync.reason}`);
1131
1222
  } else if (!prereq.ghAuthOk) {
1132
1223
  runtimeStartAction = planStatusWithReason("blocked", "gh-auth-not-ready");
1133
1224
  } else {
@@ -1156,6 +1247,7 @@ function buildSetupDryRunPlan(options, context, config) {
1156
1247
  workerInstallPlan,
1157
1248
  workerAction,
1158
1249
  workerGuide,
1250
+ anchorSync,
1159
1251
  githubAuthAction,
1160
1252
  runtimeStartAction,
1161
1253
  launchdAction
@@ -1170,6 +1262,7 @@ function printSetupDryRunPlan(context, config, plan) {
1170
1262
  console.log(`- profile registry root: ${context.profileRegistryRoot}`);
1171
1263
  console.log(`- sync packaged runtime: would-run`);
1172
1264
  console.log(`- scaffold/adopt profile: would-run`);
1265
+ console.log(`- anchor repo sync: ${plan.anchorSync.status === "ok" ? "would-run" : `${plan.anchorSync.status} (${plan.anchorSync.reason})`}`);
1173
1266
  console.log(`- doctor: would-run`);
1174
1267
  console.log(`- dependency install: ${plan.dependencyAction.status}${plan.dependencyAction.reason ? ` (${plan.dependencyAction.reason})` : ""}`);
1175
1268
  if (plan.dependencyAction.status !== "not-needed" && plan.dependencyPlan && plan.dependencyPlan.commands.length > 0) {
@@ -1202,6 +1295,11 @@ function buildSetupResultPayload(params) {
1202
1295
  status: params.coreToolsStatus,
1203
1296
  missingRequiredTools: params.missingRequiredTools
1204
1297
  },
1298
+ anchorSync: {
1299
+ status: params.anchorSyncStatus,
1300
+ reason: params.anchorSyncReason,
1301
+ remoteUrl: params.anchorSyncRemoteUrl
1302
+ },
1205
1303
  workerBackend: {
1206
1304
  command: params.workerBackendCommand,
1207
1305
  status: params.workerBackendStatus,
@@ -1252,6 +1350,9 @@ function emitSetupJsonPayload(payload) {
1252
1350
 
1253
1351
  function collectFinalSetupIssues(config, prereq, doctorKv, runtimeStartStatus) {
1254
1352
  const issues = [];
1353
+ if (config.anchorSync && config.anchorSync.status !== "ok") {
1354
+ issues.push(`anchor repo sync ${config.anchorSync.status}: ${config.anchorSync.reason}`);
1355
+ }
1255
1356
  if (!prereq.coreToolsOk) {
1256
1357
  issues.push(`missing core tools: ${prereq.missingRequired.join(", ")}`);
1257
1358
  }
@@ -1271,7 +1372,11 @@ function collectFinalSetupIssues(config, prereq, doctorKv, runtimeStartStatus) {
1271
1372
  }
1272
1373
 
1273
1374
  async function maybeRunFinalSetupFixups(options, scopedContext, config, currentState) {
1274
- const issues = collectFinalSetupIssues(config, currentState.prereq, currentState.doctorKv, currentState.runtimeStartStatus);
1375
+ const effectiveConfig = {
1376
+ ...config,
1377
+ anchorSync: currentState.anchorSync || config.anchorSync || null
1378
+ };
1379
+ const issues = collectFinalSetupIssues(effectiveConfig, currentState.prereq, currentState.doctorKv, currentState.runtimeStartStatus);
1275
1380
  if (issues.length === 0) {
1276
1381
  return {
1277
1382
  status: "not-needed",
@@ -1315,6 +1420,7 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
1315
1420
  let githubAuthStep = currentState.githubAuthStep;
1316
1421
  let workerSetupStep = currentState.workerSetupStep;
1317
1422
  let doctorKv = currentState.doctorKv;
1423
+ let anchorSync = currentState.anchorSync || effectiveConfig.anchorSync || null;
1318
1424
  let runtimeStartStatus = currentState.runtimeStartStatus;
1319
1425
  let runtimeStartReason = currentState.runtimeStartReason;
1320
1426
  let runtimeStatusKv = currentState.runtimeStatusKv;
@@ -1353,6 +1459,9 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
1353
1459
  } else if (!prereq.workerAvailable) {
1354
1460
  runtimeStartStatus = "skipped";
1355
1461
  runtimeStartReason = `missing-worker:${prereq.workerCommand}`;
1462
+ } else if (anchorSync && anchorSync.status !== "ok") {
1463
+ runtimeStartStatus = "skipped";
1464
+ runtimeStartReason = `anchor-sync-${anchorSync.reason}`;
1356
1465
  } else if (!prereq.ghAuthOk) {
1357
1466
  runtimeStartStatus = "skipped";
1358
1467
  runtimeStartReason = "gh-auth-not-ready";
@@ -1376,7 +1485,7 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
1376
1485
  launchdInstallReason = "";
1377
1486
  }
1378
1487
 
1379
- const finalIssues = collectFinalSetupIssues(config, prereq, doctorKv, runtimeStartStatus);
1488
+ const finalIssues = collectFinalSetupIssues({ ...effectiveConfig, anchorSync }, prereq, doctorKv, runtimeStartStatus);
1380
1489
  let status = "ok";
1381
1490
  if (dependencyInstall.status === "failed" || githubAuthStep.status === "failed" || workerSetupStep.installStatus === "failed") {
1382
1491
  status = "failed";
@@ -1387,6 +1496,7 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
1387
1496
  return {
1388
1497
  status,
1389
1498
  actions,
1499
+ anchorSync,
1390
1500
  prereq,
1391
1501
  dependencyInstall,
1392
1502
  githubAuthStep,
@@ -1564,6 +1674,9 @@ async function runSetupFlow(forwardedArgs) {
1564
1674
  worktreeRoot: config.paths.worktreeRoot,
1565
1675
  coreToolsStatus: plan.prereq.coreToolsOk ? "ok" : "missing",
1566
1676
  missingRequiredTools: plan.prereq.missingRequired,
1677
+ anchorSyncStatus: plan.anchorSync.status === "ok" ? "would-run" : plan.anchorSync.status,
1678
+ anchorSyncReason: plan.anchorSync.reason || "",
1679
+ anchorSyncRemoteUrl: plan.anchorSync.remoteUrl || "",
1567
1680
  workerBackendCommand: plan.prereq.workerCommand,
1568
1681
  workerBackendStatus: plan.prereq.workerAvailable ? "ok" : "missing",
1569
1682
  workerSetupGuideStatus: "planned",
@@ -1610,6 +1723,13 @@ async function runSetupFlow(forwardedArgs) {
1610
1723
  console.log(`PROFILE_EXISTS=${plan.profileExists ? "yes" : "no"}`);
1611
1724
  console.log(`CORE_TOOLS_STATUS=${plan.prereq.coreToolsOk ? "ok" : "missing"}`);
1612
1725
  console.log(`MISSING_REQUIRED_TOOLS=${plan.prereq.missingRequired.join(",")}`);
1726
+ console.log(`ANCHOR_SYNC_STATUS=${plan.anchorSync.status === "ok" ? "would-run" : plan.anchorSync.status}`);
1727
+ if (plan.anchorSync.reason) {
1728
+ console.log(`ANCHOR_SYNC_REASON=${plan.anchorSync.reason}`);
1729
+ }
1730
+ if (plan.anchorSync.remoteUrl) {
1731
+ console.log(`ANCHOR_SYNC_REMOTE_URL=${plan.anchorSync.remoteUrl}`);
1732
+ }
1613
1733
  console.log(`WORKER_BACKEND_COMMAND=${plan.prereq.workerCommand}`);
1614
1734
  console.log(`WORKER_BACKEND_STATUS=${plan.prereq.workerAvailable ? "ok" : "missing"}`);
1615
1735
  console.log(`GITHUB_AUTH_STATUS=${plan.prereq.ghAuthOk ? "ok" : "not-ready"}`);
@@ -1686,6 +1806,16 @@ async function runSetupFlow(forwardedArgs) {
1686
1806
  prereq = collectPrereqStatus(config.codingWorker);
1687
1807
 
1688
1808
  const scopedContext = buildScopedContext(context, config.profileId);
1809
+ const anchorSync = buildAnchorSyncDecision(options, config.paths.sourceRepoRoot);
1810
+
1811
+ if (anchorSync.status === "deferred") {
1812
+ console.log("\nAnchor repo sync will be deferred for this setup run.");
1813
+ if (anchorSync.remoteUrl) {
1814
+ console.log(`- remote: ${anchorSync.remoteUrl}`);
1815
+ }
1816
+ console.log(`- reason: ${anchorSync.reason}`);
1817
+ console.log("- next step: authenticate Git access or fix the repo remote, then rerun `setup` or `init` without skipping anchor sync.");
1818
+ }
1689
1819
 
1690
1820
  runSetupStep(scopedContext, "Sync packaged runtime into ~/.agent-runtime", "tools/bin/sync-shared-agent-home.sh", []);
1691
1821
 
@@ -1704,7 +1834,7 @@ async function runSetupFlow(forwardedArgs) {
1704
1834
  if (options.force) {
1705
1835
  initArgs.push("--force");
1706
1836
  }
1707
- if (options.skipAnchorSync) {
1837
+ if (anchorSync.skipAnchorSync) {
1708
1838
  initArgs.push("--skip-anchor-sync");
1709
1839
  }
1710
1840
  if (options.skipWorkspaceSync) {
@@ -1731,6 +1861,9 @@ async function runSetupFlow(forwardedArgs) {
1731
1861
  } else if (!prereq.workerAvailable) {
1732
1862
  runtimeStartReason = `missing-worker:${prereq.workerCommand}`;
1733
1863
  console.log(`runtime start skipped: ${prereq.workerCommand} is not available on PATH`);
1864
+ } else if (anchorSync.status !== "ok") {
1865
+ runtimeStartReason = `anchor-sync-${anchorSync.reason}`;
1866
+ console.log("runtime start skipped: ACP deferred anchor repo sync for this setup run.");
1734
1867
  } else if (!prereq.ghAuthOk) {
1735
1868
  runtimeStartReason = "gh-auth-not-ready";
1736
1869
  console.log("runtime start skipped: GitHub CLI is not authenticated yet. Run `gh auth login` and start the runtime afterwards.");
@@ -1760,6 +1893,7 @@ async function runSetupFlow(forwardedArgs) {
1760
1893
  }
1761
1894
 
1762
1895
  const finalFixup = await maybeRunFinalSetupFixups(options, scopedContext, config, {
1896
+ anchorSync,
1763
1897
  prereq,
1764
1898
  dependencyInstall,
1765
1899
  githubAuthStep,
@@ -1796,6 +1930,9 @@ async function runSetupFlow(forwardedArgs) {
1796
1930
  worktreeRoot: config.paths.worktreeRoot,
1797
1931
  coreToolsStatus: prereq.coreToolsOk ? "ok" : "missing",
1798
1932
  missingRequiredTools: prereq.missingRequired,
1933
+ anchorSyncStatus: anchorSync.status,
1934
+ anchorSyncReason: anchorSync.reason || "",
1935
+ anchorSyncRemoteUrl: anchorSync.remoteUrl || "",
1799
1936
  workerBackendCommand: prereq.workerCommand,
1800
1937
  workerBackendStatus: prereq.workerAvailable ? "ok" : "missing",
1801
1938
  workerSetupGuideStatus: workerSetupStep.status,
@@ -1846,6 +1983,13 @@ async function runSetupFlow(forwardedArgs) {
1846
1983
  console.log(`CODING_WORKER=${config.codingWorker}`);
1847
1984
  console.log(`CORE_TOOLS_STATUS=${prereq.coreToolsOk ? "ok" : "missing"}`);
1848
1985
  console.log(`MISSING_REQUIRED_TOOLS=${prereq.missingRequired.join(",")}`);
1986
+ console.log(`ANCHOR_SYNC_STATUS=${anchorSync.status}`);
1987
+ if (anchorSync.reason) {
1988
+ console.log(`ANCHOR_SYNC_REASON=${anchorSync.reason}`);
1989
+ }
1990
+ if (anchorSync.remoteUrl) {
1991
+ console.log(`ANCHOR_SYNC_REMOTE_URL=${anchorSync.remoteUrl}`);
1992
+ }
1849
1993
  console.log(`WORKER_BACKEND_COMMAND=${prereq.workerCommand}`);
1850
1994
  console.log(`WORKER_BACKEND_STATUS=${prereq.workerAvailable ? "ok" : "missing"}`);
1851
1995
  console.log(`WORKER_SETUP_GUIDE_STATUS=${workerSetupStep.status}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-control-plane",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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,7 +32,6 @@
32
32
  "tools/dashboard/server.py",
33
33
  "tools/dashboard/styles.css",
34
34
  "tools/templates",
35
- "tools/tests",
36
35
  "tools/vendor/codex-quota/LICENSE",
37
36
  "tools/vendor/codex-quota/codex-quota.js",
38
37
  "tools/vendor/codex-quota/lib",
@@ -1,280 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- FLOW_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
6
- CLI_SCRIPT="${FLOW_ROOT}/npm/bin/agent-control-plane.js"
7
- PACKAGE_VERSION="$(node -p "require('${FLOW_ROOT}/package.json').version")"
8
-
9
- tmpdir="$(mktemp -d)"
10
- trap 'rm -rf "${tmpdir}"' EXIT
11
-
12
- platform_home="${tmpdir}/platform"
13
- home_dir="${tmpdir}/home"
14
- fake_bin="${tmpdir}/fake-bin"
15
- mkdir -p "${platform_home}" "${home_dir}"
16
- mkdir -p "${fake_bin}"
17
-
18
- cat >"${fake_bin}/gh" <<'EOF'
19
- #!/usr/bin/env bash
20
- set -euo pipefail
21
-
22
- if [[ "${1:-}" == "auth" && "${2:-}" == "status" ]]; then
23
- echo "gh auth ok"
24
- exit 0
25
- fi
26
-
27
- if [[ "${1:-}" == "auth" && "${2:-}" == "login" ]]; then
28
- echo "gh auth login stub"
29
- exit 0
30
- fi
31
-
32
- echo "gh stub: unsupported invocation: $*" >&2
33
- exit 0
34
- EOF
35
-
36
- cat >"${fake_bin}/jq" <<'EOF'
37
- #!/usr/bin/env bash
38
- exit 0
39
- EOF
40
-
41
- cat >"${fake_bin}/codex" <<'EOF'
42
- #!/usr/bin/env bash
43
- if [[ "${1:-}" == "--version" ]]; then
44
- echo "codex stub 0.0.0"
45
- exit 0
46
- fi
47
-
48
- if [[ "${1:-}" == "login" && "${2:-}" == "status" ]]; then
49
- echo "codex login ok"
50
- exit 0
51
- fi
52
-
53
- echo "codex stub"
54
- exit 0
55
- EOF
56
-
57
- chmod +x "${fake_bin}/gh" "${fake_bin}/jq" "${fake_bin}/codex"
58
-
59
- help_output="$(
60
- HOME="${home_dir}" \
61
- AGENT_PLATFORM_HOME="${platform_home}" \
62
- node "${CLI_SCRIPT}" help
63
- )"
64
-
65
- grep -q '^Usage:$' <<<"${help_output}"
66
- grep -q '^ setup' <<<"${help_output}"
67
- grep -q '^ sync' <<<"${help_output}"
68
- grep -q '^ profile-smoke' <<<"${help_output}"
69
- grep -q '^ launchd-install' <<<"${help_output}"
70
- grep -q '^ remove' <<<"${help_output}"
71
-
72
- version_output="$(
73
- HOME="${home_dir}" \
74
- AGENT_PLATFORM_HOME="${platform_home}" \
75
- node "${CLI_SCRIPT}" version
76
- )"
77
-
78
- grep -q "^${PACKAGE_VERSION}$" <<<"${version_output}"
79
-
80
- HOME="${home_dir}" \
81
- AGENT_PLATFORM_HOME="${platform_home}" \
82
- node "${CLI_SCRIPT}" sync >/dev/null
83
-
84
- test -f "${platform_home}/runtime-home/skills/openclaw/agent-control-plane/SKILL.md"
85
- test -f "${platform_home}/runtime-home/tools/bin/flow-runtime-doctor.sh"
86
- test -f "${platform_home}/runtime-home/skills/openclaw/agent-control-plane/tools/bin/codex-quota"
87
- test -f "${platform_home}/runtime-home/skills/openclaw/agent-control-plane/tools/vendor/codex-quota-manager/scripts/auto-switch.sh"
88
-
89
- doctor_output="$(
90
- HOME="${home_dir}" \
91
- AGENT_PLATFORM_HOME="${platform_home}" \
92
- node "${CLI_SCRIPT}" doctor
93
- )"
94
-
95
- grep -q '^CONTROL_PLANE_NAME=agent-control-plane$' <<<"${doctor_output}"
96
- grep -q '^SOURCE_READY=yes$' <<<"${doctor_output}"
97
-
98
- init_output="$(
99
- HOME="${home_dir}" \
100
- AGENT_PLATFORM_HOME="${platform_home}" \
101
- node "${CLI_SCRIPT}" init \
102
- --profile-id alpha \
103
- --repo-slug example-owner/alpha \
104
- --allow-missing-repo \
105
- --skip-anchor-sync \
106
- --skip-workspace-sync
107
- )"
108
-
109
- grep -q '^PROJECT_INIT_STATUS=ok$' <<<"${init_output}"
110
- grep -q '^PROFILE_ID=alpha$' <<<"${init_output}"
111
- grep -q "^RUNTIME_HOME=${platform_home}/runtime-home$" <<<"${init_output}"
112
-
113
- profile_smoke_output="$(
114
- HOME="${home_dir}" \
115
- AGENT_PLATFORM_HOME="${platform_home}" \
116
- node "${CLI_SCRIPT}" profile-smoke --profile-id alpha
117
- )"
118
-
119
- grep -q '^PROFILE_ID=alpha$' <<<"${profile_smoke_output}"
120
- grep -q '^PROFILE_STATUS=ok$' <<<"${profile_smoke_output}"
121
-
122
- runtime_status_output="$(
123
- HOME="${home_dir}" \
124
- AGENT_PLATFORM_HOME="${platform_home}" \
125
- node "${CLI_SCRIPT}" runtime status --profile-id alpha
126
- )"
127
-
128
- grep -q '^PROFILE_ID=alpha$' <<<"${runtime_status_output}"
129
- grep -q "^CONFIG_YAML=${platform_home}/control-plane/profiles/alpha/control-plane.yaml$" <<<"${runtime_status_output}"
130
-
131
- launchd_help="$(
132
- HOME="${home_dir}" \
133
- AGENT_PLATFORM_HOME="${platform_home}" \
134
- node "${CLI_SCRIPT}" launchd-install --help
135
- )"
136
-
137
- grep -q '^Usage:$' <<<"${launchd_help}"
138
-
139
- remove_help="$(
140
- HOME="${home_dir}" \
141
- AGENT_PLATFORM_HOME="${platform_home}" \
142
- node "${CLI_SCRIPT}" remove --help
143
- )"
144
-
145
- grep -q '^Usage:$' <<<"${remove_help}"
146
-
147
- setup_help="$(
148
- HOME="${home_dir}" \
149
- AGENT_PLATFORM_HOME="${platform_home}" \
150
- node "${CLI_SCRIPT}" setup --help
151
- )"
152
-
153
- grep -q -- '--install-missing-deps' <<<"${setup_help}"
154
- grep -q -- '--no-install-missing-deps' <<<"${setup_help}"
155
- grep -q -- '--install-missing-backend' <<<"${setup_help}"
156
- grep -q -- '--no-install-missing-backend' <<<"${setup_help}"
157
- grep -q -- '--gh-auth-login' <<<"${setup_help}"
158
- grep -q -- '--no-gh-auth-login' <<<"${setup_help}"
159
- grep -q -- '--dry-run' <<<"${setup_help}"
160
- grep -q -- '--plan' <<<"${setup_help}"
161
- grep -q -- '--json' <<<"${setup_help}"
162
-
163
- setup_repo="${tmpdir}/setup-demo"
164
- mkdir -p "${setup_repo}"
165
- git -C "${setup_repo}" init -q -b main
166
- git -C "${setup_repo}" config user.name "ACP Test"
167
- git -C "${setup_repo}" config user.email "acp-test@example.com"
168
- printf '# setup demo\n' >"${setup_repo}/README.md"
169
- git -C "${setup_repo}" add README.md
170
- git -C "${setup_repo}" commit -q -m "init"
171
- git -C "${setup_repo}" remote add origin "https://github.com/example-owner/setup-demo.git"
172
-
173
- setup_output="$(
174
- HOME="${home_dir}" \
175
- AGENT_PLATFORM_HOME="${platform_home}" \
176
- PATH="${fake_bin}:${PATH}" \
177
- node "${CLI_SCRIPT}" setup \
178
- --non-interactive \
179
- --repo-root "${setup_repo}" \
180
- --no-start-runtime \
181
- --skip-anchor-sync \
182
- --skip-workspace-sync
183
- )"
184
-
185
- grep -q '^SETUP_STATUS=ok$' <<<"${setup_output}"
186
- grep -q '^PROFILE_ID=setup-demo$' <<<"${setup_output}"
187
- grep -q '^REPO_SLUG=example-owner/setup-demo$' <<<"${setup_output}"
188
- grep -q '^CODING_WORKER=' <<<"${setup_output}"
189
- grep -q '^CORE_TOOLS_STATUS=ok$' <<<"${setup_output}"
190
- grep -q '^WORKER_BACKEND_COMMAND=' <<<"${setup_output}"
191
- grep -q '^WORKER_BACKEND_STATUS=ok$' <<<"${setup_output}"
192
- grep -q '^WORKER_SETUP_GUIDE_STATUS=' <<<"${setup_output}"
193
- grep -q '^WORKER_BACKEND_INSTALL_STATUS=' <<<"${setup_output}"
194
- grep -q '^WORKER_SETUP_DOCS_OPENED=' <<<"${setup_output}"
195
- grep -q '^WORKER_BACKEND_DOCS_URL=' <<<"${setup_output}"
196
- grep -q '^WORKER_BACKEND_AUTH_EXAMPLE=' <<<"${setup_output}"
197
- grep -q '^WORKER_BACKEND_VERIFY_EXAMPLE=' <<<"${setup_output}"
198
- grep -q '^GITHUB_AUTH_STATUS=' <<<"${setup_output}"
199
- grep -q '^FINAL_FIXUP_STATUS=' <<<"${setup_output}"
200
- grep -q '^FINAL_FIXUP_ACTIONS=' <<<"${setup_output}"
201
- grep -q '^DEPENDENCY_INSTALL_STATUS=' <<<"${setup_output}"
202
- grep -q '^GITHUB_AUTH_STEP_STATUS=' <<<"${setup_output}"
203
- grep -q '^PROJECT_INIT_STATUS=ok$' <<<"${setup_output}"
204
- grep -q '^DOCTOR_STATUS=ok$' <<<"${setup_output}"
205
- grep -q '^RUNTIME_START_STATUS=skipped$' <<<"${setup_output}"
206
- grep -q '^RUNTIME_START_REASON=not-requested$' <<<"${setup_output}"
207
- test -f "${platform_home}/control-plane/profiles/setup-demo/control-plane.yaml"
208
-
209
- setup_dry_run_output="$(
210
- HOME="${home_dir}" \
211
- AGENT_PLATFORM_HOME="${platform_home}" \
212
- PATH="${fake_bin}:${PATH}" \
213
- node "${CLI_SCRIPT}" setup \
214
- --dry-run \
215
- --non-interactive \
216
- --repo-root "${setup_repo}" \
217
- --no-start-runtime \
218
- --skip-anchor-sync \
219
- --skip-workspace-sync
220
- )"
221
-
222
- grep -q '^SETUP_STATUS=dry-run$' <<<"${setup_dry_run_output}"
223
- grep -q '^SETUP_MODE=dry-run$' <<<"${setup_dry_run_output}"
224
- grep -q '^PROJECT_INIT_STATUS=would-run$' <<<"${setup_dry_run_output}"
225
- grep -q '^DOCTOR_STATUS=would-run$' <<<"${setup_dry_run_output}"
226
- grep -q '^FINAL_FIXUP_STATUS=planned$' <<<"${setup_dry_run_output}"
227
- grep -q '^FINAL_FIXUP_ACTIONS=review-plan$' <<<"${setup_dry_run_output}"
228
-
229
- setup_json_output="$(
230
- HOME="${home_dir}" \
231
- AGENT_PLATFORM_HOME="${platform_home}" \
232
- PATH="${fake_bin}:${PATH}" \
233
- node "${CLI_SCRIPT}" setup \
234
- --json \
235
- --repo-root "${setup_repo}" \
236
- --profile-id setup-json \
237
- --no-start-runtime \
238
- --skip-anchor-sync \
239
- --skip-workspace-sync
240
- )"
241
-
242
- printf '%s' "${setup_json_output}" | node -e '
243
- const fs = require("fs");
244
- const data = JSON.parse(fs.readFileSync(0, "utf8"));
245
- if (data.setupStatus !== "ok") process.exit(1);
246
- if (data.setupMode !== "run") process.exit(1);
247
- if (data.profileId !== "setup-json") process.exit(1);
248
- if (data.projectInitStatus !== "ok") process.exit(1);
249
- if (data.doctorStatus !== "ok") process.exit(1);
250
- if (!data.workerBackend || !data.githubAuth || !data.finalFixup) process.exit(1);
251
- '
252
-
253
- setup_dry_run_json_output="$(
254
- HOME="${home_dir}" \
255
- AGENT_PLATFORM_HOME="${platform_home}" \
256
- PATH="${fake_bin}:${PATH}" \
257
- node "${CLI_SCRIPT}" setup \
258
- --json \
259
- --dry-run \
260
- --repo-root "${setup_repo}" \
261
- --profile-id dry-run-json \
262
- --no-start-runtime \
263
- --skip-anchor-sync \
264
- --skip-workspace-sync
265
- )"
266
-
267
- printf '%s' "${setup_dry_run_json_output}" | node -e '
268
- const fs = require("fs");
269
- const data = JSON.parse(fs.readFileSync(0, "utf8"));
270
- if (data.setupStatus !== "dry-run") process.exit(1);
271
- if (data.setupMode !== "dry-run") process.exit(1);
272
- if (data.profileId !== "dry-run-json") process.exit(1);
273
- if (data.projectInitStatus !== "would-run") process.exit(1);
274
- if (data.doctorStatus !== "would-run") process.exit(1);
275
- if (!data.finalFixup || data.finalFixup.status !== "planned") process.exit(1);
276
- if (data.dependencyInstall && data.dependencyInstall.status === "not-needed" && data.dependencyInstall.command !== "") process.exit(1);
277
- if (data.workerBackend && data.workerBackend.installStatus === "not-needed" && data.workerBackend.installCommand !== "") process.exit(1);
278
- '
279
-
280
- test ! -e "${platform_home}/control-plane/profiles/dry-run-json/control-plane.yaml"
@@ -1,56 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
- SCRIPT_SRC="${FLOW_ROOT}/tools/bin/agent-github-update-labels"
6
- CONFIG_LIB_SRC="${FLOW_ROOT}/tools/bin/flow-config-lib.sh"
7
- SHELL_LIB_SRC="${FLOW_ROOT}/tools/bin/flow-shell-lib.sh"
8
-
9
- tmpdir="$(mktemp -d)"
10
- trap 'rm -rf "$tmpdir"' EXIT
11
-
12
- bin_dir="$tmpdir/bin"
13
- payload_file="$tmpdir/payload.json"
14
- mkdir -p "$bin_dir"
15
-
16
- cp "$SCRIPT_SRC" "$bin_dir/agent-github-update-labels"
17
- cp "$CONFIG_LIB_SRC" "$bin_dir/flow-config-lib.sh"
18
- cp "$SHELL_LIB_SRC" "$bin_dir/flow-shell-lib.sh"
19
-
20
- cat >"$bin_dir/gh" <<'EOF'
21
- #!/usr/bin/env bash
22
- set -euo pipefail
23
-
24
- if [[ "${1:-}" == "api" ]]; then
25
- route="${2:-}"
26
- if [[ "$route" == "repos/example/demo/issues/42" ]]; then
27
- echo '{"message":"Not Found"}' >&2
28
- exit 1
29
- fi
30
- if [[ "$route" == user/repos\?* ]]; then
31
- printf '[[{"id":123,"full_name":"example/demo"}]]\n'
32
- exit 0
33
- fi
34
- if [[ "$route" == "repositories/123/issues/42" ]]; then
35
- if [[ " $* " == *" --method PATCH "* ]]; then
36
- cat >"${TEST_PAYLOAD_FILE:?}"
37
- exit 0
38
- fi
39
- printf '{"labels":[{"name":"agent-blocked"}]}\n'
40
- exit 0
41
- fi
42
- fi
43
-
44
- echo "unexpected gh invocation: $*" >&2
45
- exit 1
46
- EOF
47
- chmod +x "$bin_dir/gh" "$bin_dir/agent-github-update-labels"
48
-
49
- TEST_PAYLOAD_FILE="$payload_file" \
50
- PATH="$bin_dir:$PATH" \
51
- bash "$bin_dir/agent-github-update-labels" --repo-slug example/demo --number 42 --add agent-running --remove agent-blocked
52
-
53
- test "$(jq -r '.labels[0]' "$payload_file")" = "agent-running"
54
- test "$(jq '.labels | length' "$payload_file")" -eq 1
55
-
56
- echo "agent github update labels falls back to repository id test passed"