agent-control-plane 0.4.9 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +109 -13
  2. package/npm/bin/agent-control-plane.js +1 -1
  3. package/package.json +39 -33
  4. package/tools/bin/debug-session.sh +106 -0
  5. package/tools/bin/flow-config-lib.sh +13 -3508
  6. package/tools/bin/flow-execution-lib.sh +243 -0
  7. package/tools/bin/flow-forge-lib.sh +1770 -0
  8. package/tools/bin/flow-profile-lib.sh +335 -0
  9. package/tools/bin/flow-provider-lib.sh +981 -0
  10. package/tools/bin/flow-runtime-doctor-linux.sh +136 -0
  11. package/tools/bin/flow-runtime-doctor.sh +5 -1
  12. package/tools/bin/flow-session-lib.sh +317 -0
  13. package/tools/bin/install-project-systemd.sh +255 -0
  14. package/tools/bin/project-runtimectl.sh +45 -0
  15. package/tools/bin/project-systemd-bootstrap.sh +74 -0
  16. package/tools/bin/uninstall-project-systemd.sh +87 -0
  17. package/tools/dashboard/app.js +238 -8
  18. package/tools/dashboard/issue_queue_state.py +101 -0
  19. package/tools/dashboard/requirements.txt +3 -0
  20. package/tools/dashboard/server.py +250 -30
  21. package/tools/dashboard/styles.css +526 -455
  22. package/tools/bin/agent-cleanup-worktree +0 -247
  23. package/tools/bin/agent-github-update-labels +0 -105
  24. package/tools/bin/agent-init-worktree +0 -216
  25. package/tools/bin/agent-project-archive-run +0 -52
  26. package/tools/bin/agent-project-capture-worker +0 -46
  27. package/tools/bin/agent-project-catch-up-issue-pr-links +0 -118
  28. package/tools/bin/agent-project-catch-up-merged-prs +0 -195
  29. package/tools/bin/agent-project-catch-up-scheduled-issue-retries +0 -123
  30. package/tools/bin/agent-project-cleanup-session +0 -513
  31. package/tools/bin/agent-project-detached-launch +0 -127
  32. package/tools/bin/agent-project-heartbeat-loop +0 -1029
  33. package/tools/bin/agent-project-open-issue-worktree +0 -89
  34. package/tools/bin/agent-project-open-pr-worktree +0 -80
  35. package/tools/bin/agent-project-publish-issue-pr +0 -468
  36. package/tools/bin/agent-project-reconcile-issue-session +0 -1409
  37. package/tools/bin/agent-project-reconcile-pr-session +0 -1288
  38. package/tools/bin/agent-project-retry-state +0 -158
  39. package/tools/bin/agent-project-run-claude-session +0 -805
  40. package/tools/bin/agent-project-run-codex-resilient +0 -963
  41. package/tools/bin/agent-project-run-codex-session +0 -435
  42. package/tools/bin/agent-project-run-kilo-session +0 -369
  43. package/tools/bin/agent-project-run-ollama-session +0 -658
  44. package/tools/bin/agent-project-run-openclaw-session +0 -1309
  45. package/tools/bin/agent-project-run-opencode-session +0 -377
  46. package/tools/bin/agent-project-run-pi-session +0 -479
  47. package/tools/bin/agent-project-sync-anchor-repo +0 -139
  48. package/tools/bin/agent-project-sync-source-repo-main +0 -163
  49. package/tools/bin/agent-project-worker-status +0 -188
  50. package/tools/bin/branch-verification-guard.sh +0 -364
  51. package/tools/bin/capture-worker.sh +0 -18
  52. package/tools/bin/cleanup-worktree.sh +0 -52
  53. package/tools/bin/codex-quota +0 -31
  54. package/tools/bin/create-follow-up-issue.sh +0 -114
  55. package/tools/bin/dashboard-launchd-bootstrap.sh +0 -50
  56. package/tools/bin/issue-publish-localization-guard.sh +0 -142
  57. package/tools/bin/issue-publish-scope-guard.sh +0 -242
  58. package/tools/bin/issue-requires-local-workspace-install.sh +0 -31
  59. package/tools/bin/issue-resource-class.sh +0 -12
  60. package/tools/bin/kick-scheduler.sh +0 -75
  61. package/tools/bin/label-follow-up-issues.sh +0 -14
  62. package/tools/bin/new-pr-worktree.sh +0 -50
  63. package/tools/bin/new-worktree.sh +0 -49
  64. package/tools/bin/pr-risk.sh +0 -12
  65. package/tools/bin/prepare-worktree.sh +0 -142
  66. package/tools/bin/provider-cooldown-state.sh +0 -204
  67. package/tools/bin/publish-issue-worker.sh +0 -31
  68. package/tools/bin/reconcile-bootstrap-lib.sh +0 -113
  69. package/tools/bin/reconcile-issue-worker.sh +0 -34
  70. package/tools/bin/reconcile-pr-worker.sh +0 -34
  71. package/tools/bin/record-verification.sh +0 -71
  72. package/tools/bin/render-flow-config.sh +0 -98
  73. package/tools/bin/resident-issue-controller-lib.sh +0 -448
  74. package/tools/bin/retry-state.sh +0 -31
  75. package/tools/bin/reuse-issue-worktree.sh +0 -121
  76. package/tools/bin/run-codex-bypass.sh +0 -3
  77. package/tools/bin/run-codex-safe.sh +0 -3
  78. package/tools/bin/run-codex-task.sh +0 -280
  79. package/tools/bin/serve-dashboard.sh +0 -5
  80. package/tools/bin/start-issue-worker.sh +0 -943
  81. package/tools/bin/start-pr-fix-worker.sh +0 -528
  82. package/tools/bin/start-pr-merge-repair-worker.sh +0 -8
  83. package/tools/bin/start-pr-review-worker.sh +0 -261
  84. package/tools/bin/start-resident-issue-loop.sh +0 -499
  85. package/tools/bin/update-github-labels.sh +0 -14
  86. package/tools/bin/worker-status.sh +0 -19
  87. package/tools/bin/workflow-catalog.sh +0 -77
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ # shellcheck source=/dev/null
6
+ source "${SCRIPT_DIR}/flow-shell-lib.sh"
7
+ flow_export_project_env_aliases
8
+
9
+ flow_explicit_github_repo_id() {
10
+ local requested_repo_slug="${1:-}"
11
+ local configured_repo_slug="${ACP_REPO_SLUG:-${F_LOSNING_REPO_SLUG:-}}"
12
+ local explicit_repo_id="${ACP_REPO_ID:-${F_LOSNING_REPO_ID:-${ACP_GITHUB_REPOSITORY_ID:-${F_LOSNING_GITHUB_REPOSITORY_ID:-}}}}"
13
+
14
+ [[ -n "${explicit_repo_id}" ]] || return 1
15
+ if [[ -n "${requested_repo_slug}" && -n "${configured_repo_slug}" && "${configured_repo_slug}" != "${requested_repo_slug}" ]]; then
16
+ return 1
17
+ fi
18
+
19
+ printf '%s\n' "${explicit_repo_id}"
20
+ }
21
+
22
+ flow_explicit_profile_id() {
23
+ printf '%s\n' "${ACP_PROJECT_ID:-${AGENT_PROJECT_ID:-}}"
24
+ }
25
+
26
+ resolve_flow_profile_registry_root() {
27
+ local platform_home="${AGENT_PLATFORM_HOME:-${HOME}/.agent-runtime}"
28
+ printf '%s\n' "${AGENT_CONTROL_PLANE_PROFILE_ROOT:-${ACP_PROFILE_REGISTRY_ROOT:-${platform_home}/control-plane/profiles}}"
29
+ }
30
+
31
+ flow_list_profiles_in_root() {
32
+ local profiles_root="${1:-}"
33
+ local profile_file=""
34
+ local profile_id=""
35
+
36
+ [[ -d "${profiles_root}" ]] || return 0
37
+
38
+ while IFS= read -r profile_file; do
39
+ [[ -n "${profile_file}" ]] || continue
40
+ profile_id="$(basename "$(dirname "${profile_file}")")"
41
+ [[ -n "${profile_id}" ]] || continue
42
+ printf '%s\n' "${profile_id}"
43
+ done < <(find "${profiles_root}" -mindepth 2 -maxdepth 2 -type f -name 'control-plane.yaml' 2>/dev/null | sort)
44
+ }
45
+
46
+ flow_list_installed_profile_ids() {
47
+ flow_list_profiles_in_root "$(resolve_flow_profile_registry_root)"
48
+ }
49
+
50
+ flow_find_profile_dir_by_id() {
51
+ local flow_root="${1:-}"
52
+ local profile_id="${2:?profile id required}"
53
+ local registry_root=""
54
+ local candidate=""
55
+
56
+ if [[ -z "${flow_root}" ]]; then
57
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
58
+ fi
59
+
60
+ registry_root="$(resolve_flow_profile_registry_root)"
61
+ candidate="${registry_root}/${profile_id}"
62
+ if [[ -f "${candidate}/control-plane.yaml" ]]; then
63
+ printf '%s\n' "${candidate}"
64
+ return 0
65
+ fi
66
+
67
+ printf '%s/%s\n' "${registry_root}" "${profile_id}"
68
+ }
69
+
70
+ flow_profile_count() {
71
+ local flow_root="${1:-}"
72
+
73
+ if [[ -z "${flow_root}" ]]; then
74
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
75
+ fi
76
+
77
+ flow_list_profile_ids "${flow_root}" | awk 'NF { count += 1 } END { print count + 0 }'
78
+ }
79
+
80
+ flow_default_profile_id() {
81
+ local flow_root="${1:-}"
82
+ local preferred_profile="${AGENT_CONTROL_PLANE_DEFAULT_PROFILE_ID:-${ACP_DEFAULT_PROFILE_ID:-${AGENT_PROJECT_DEFAULT_PROFILE_ID:-}}}"
83
+ local candidate=""
84
+
85
+ if [[ -z "${flow_root}" ]]; then
86
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
87
+ fi
88
+
89
+ for candidate in "${preferred_profile}" "default"; do
90
+ [[ -n "${candidate}" ]] || continue
91
+ if [[ -f "$(flow_find_profile_dir_by_id "${flow_root}" "${candidate}")/control-plane.yaml" ]]; then
92
+ printf '%s\n' "${candidate}"
93
+ return 0
94
+ fi
95
+ done
96
+
97
+ candidate="$(flow_list_profile_ids "${flow_root}" | grep -v '^demo$' | head -n 1 || true)"
98
+ if [[ -n "${candidate}" ]]; then
99
+ printf '%s\n' "${candidate}"
100
+ return 0
101
+ fi
102
+
103
+ candidate="$(flow_list_profile_ids "${flow_root}" | head -n 1 || true)"
104
+ if [[ -n "${candidate}" ]]; then
105
+ printf '%s\n' "${candidate}"
106
+ return 0
107
+ fi
108
+
109
+ printf 'default\n'
110
+ }
111
+
112
+ flow_profile_selection_mode() {
113
+ local flow_root="${1:-}"
114
+ local explicit_profile=""
115
+ local profile_count="0"
116
+
117
+ if [[ -z "${flow_root}" ]]; then
118
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
119
+ fi
120
+
121
+ explicit_profile="$(flow_explicit_profile_id)"
122
+ if [[ -n "${explicit_profile}" ]]; then
123
+ printf 'explicit\n'
124
+ return 0
125
+ fi
126
+
127
+ profile_count="$(flow_profile_count "${flow_root}")"
128
+ if [[ "${profile_count}" -gt 1 ]]; then
129
+ printf 'implicit-default\n'
130
+ return 0
131
+ fi
132
+
133
+ printf 'single-profile-default\n'
134
+ }
135
+
136
+ flow_profile_selection_hint() {
137
+ local flow_root="${1:-}"
138
+ local mode=""
139
+
140
+ if [[ -z "${flow_root}" ]]; then
141
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
142
+ fi
143
+
144
+ mode="$(flow_profile_selection_mode "${flow_root}")"
145
+ if [[ "${mode}" == "implicit-default" ]]; then
146
+ printf 'Set ACP_PROJECT_ID=<id> or AGENT_PROJECT_ID=<id> when multiple available profiles exist.\n'
147
+ fi
148
+ }
149
+
150
+ flow_profile_guard_message() {
151
+ local flow_root="${1:-}"
152
+ local command_name="${2:-this command}"
153
+ local hint=""
154
+
155
+ if [[ -z "${flow_root}" ]]; then
156
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
157
+ fi
158
+
159
+ hint="$(flow_profile_selection_hint "${flow_root}")"
160
+ printf 'explicit profile selection required for %s when multiple available profiles exist.\n' "${command_name}"
161
+ if [[ -n "${hint}" ]]; then
162
+ printf '%s\n' "${hint}"
163
+ fi
164
+ }
165
+
166
+ flow_require_explicit_profile_selection() {
167
+ local flow_root="${1:-}"
168
+ local command_name="${2:-this command}"
169
+
170
+ if [[ -z "${flow_root}" ]]; then
171
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
172
+ fi
173
+
174
+ if [[ "${ACP_ALLOW_IMPLICIT_PROFILE_SELECTION:-0}" == "1" ]]; then
175
+ return 0
176
+ fi
177
+
178
+ if [[ "$(flow_profile_selection_mode "${flow_root}")" == "implicit-default" ]]; then
179
+ flow_profile_guard_message "${flow_root}" "${command_name}" >&2
180
+ return 1
181
+ fi
182
+
183
+ return 0
184
+ }
185
+
186
+ resolve_flow_config_yaml() {
187
+ local script_path="${1:-${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}}"
188
+ local flow_root
189
+ local profile_id=""
190
+ local candidate=""
191
+ flow_root="$(resolve_flow_skill_dir "${script_path}")"
192
+ profile_id="${ACP_PROJECT_ID:-${AGENT_PROJECT_ID:-$(flow_default_profile_id "${flow_root}")}}"
193
+
194
+ for candidate in \
195
+ "${AGENT_CONTROL_PLANE_CONFIG:-}" \
196
+ "${ACP_CONFIG:-}" \
197
+ "${AGENT_PROJECT_CONFIG_PATH:-}" \
198
+ "${F_LOSNING_FLOW_CONFIG:-}"; do
199
+ if [[ -n "${candidate}" && -f "${candidate}" ]]; then
200
+ printf '%s\n' "${candidate}"
201
+ return 0
202
+ fi
203
+ done
204
+
205
+ candidate="$(flow_find_profile_dir_by_id "${flow_root}" "${profile_id}")/control-plane.yaml"
206
+ if [[ -f "${candidate}" ]]; then
207
+ printf '%s\n' "${candidate}"
208
+ return 0
209
+ fi
210
+
211
+ printf '%s\n' "${candidate}"
212
+ }
213
+
214
+ flow_list_profile_ids() {
215
+ local flow_root="${1:-}"
216
+ local found_any=""
217
+
218
+ if [[ -z "${flow_root}" ]]; then
219
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
220
+ fi
221
+
222
+ found_any="$(
223
+ {
224
+ flow_list_installed_profile_ids
225
+ } | awk 'NF { print }' | sort -u
226
+ )"
227
+
228
+ if [[ -z "${found_any}" ]]; then
229
+ return 0
230
+ fi
231
+
232
+ printf '%s\n' "${found_any}"
233
+ }
234
+
235
+ flow_git_remote_repo_slug() {
236
+ local repo_root="${1:-}"
237
+ local remote_name="${2:-origin}"
238
+ local remote_url=""
239
+ local normalized=""
240
+
241
+ [[ -n "${repo_root}" && -d "${repo_root}" ]] || return 1
242
+ remote_url="$(git -C "${repo_root}" remote get-url "${remote_name}" 2>/dev/null || true)"
243
+ [[ -n "${remote_url}" ]] || return 1
244
+
245
+ normalized="${remote_url%.git}"
246
+ case "${normalized}" in
247
+ ssh://*@*/*)
248
+ normalized="${normalized#ssh://}"
249
+ normalized="${normalized#*@}"
250
+ normalized="${normalized#*/}"
251
+ ;;
252
+ *@*:*/*)
253
+ normalized="${normalized#*@}"
254
+ normalized="${normalized#*:}"
255
+ ;;
256
+ https://*/*|http://*/*)
257
+ normalized="${normalized#http://}"
258
+ normalized="${normalized#https://}"
259
+ normalized="${normalized#*/}"
260
+ ;;
261
+ *)
262
+ return 1
263
+ ;;
264
+ esac
265
+
266
+ if [[ "${normalized}" == */*/* ]]; then
267
+ normalized="${normalized#*/}"
268
+ fi
269
+
270
+ if [[ "${normalized}" =~ ^[^/]+/[^/]+$ ]]; then
271
+ printf '%s\n' "${normalized}"
272
+ return 0
273
+ fi
274
+
275
+ return 1
276
+ }
277
+
278
+ flow_git_has_remote() {
279
+ local repo_root="${1:-}"
280
+ local remote_name="${2:-}"
281
+
282
+ [[ -n "${repo_root}" && -d "${repo_root}" && -n "${remote_name}" ]] || return 1
283
+ git -C "${repo_root}" remote get-url "${remote_name}" >/dev/null 2>&1
284
+ }
285
+
286
+ flow_resolve_forge_primary_remote() {
287
+ local repo_root="${1:-}"
288
+ local repo_slug="${2:-}"
289
+ local remote_name=""
290
+ local override="${ACP_SOURCE_SYNC_REMOTE:-${F_LOSNING_SOURCE_SYNC_REMOTE:-}}"
291
+ local forge_provider=""
292
+
293
+ [[ -n "${repo_root}" && -d "${repo_root}" ]] || return 1
294
+
295
+ if [[ -n "${override}" ]] && flow_git_has_remote "${repo_root}" "${override}"; then
296
+ printf '%s\n' "${override}"
297
+ return 0
298
+ fi
299
+
300
+ forge_provider="$(flow_forge_provider)"
301
+ case "${forge_provider}" in
302
+ gitea)
303
+ if flow_git_has_remote "${repo_root}" "gitea"; then
304
+ printf 'gitea\n'
305
+ return 0
306
+ fi
307
+ ;;
308
+ github)
309
+ if flow_git_has_remote "${repo_root}" "origin"; then
310
+ printf 'origin\n'
311
+ return 0
312
+ fi
313
+ ;;
314
+ esac
315
+
316
+ if [[ -n "${repo_slug}" ]]; then
317
+ while IFS= read -r remote_name; do
318
+ [[ -n "${remote_name}" ]] || continue
319
+ if [[ "$(flow_git_remote_repo_slug "${repo_root}" "${remote_name}" 2>/dev/null || true)" == "${repo_slug}" ]]; then
320
+ printf '%s\n' "${remote_name}"
321
+ return 0
322
+ fi
323
+ done < <(git -C "${repo_root}" remote)
324
+ fi
325
+
326
+ for remote_name in origin gitea; do
327
+ if flow_git_has_remote "${repo_root}" "${remote_name}"; then
328
+ printf '%s\n' "${remote_name}"
329
+ return 0
330
+ fi
331
+ done
332
+
333
+ return 1
334
+ }
335
+