agent-control-plane 0.6.0 → 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.
- package/README.md +38 -5
- package/package.json +1 -1
- package/tools/bin/flow-config-lib.sh +13 -3508
- package/tools/bin/flow-execution-lib.sh +243 -0
- package/tools/bin/flow-forge-lib.sh +1770 -0
- package/tools/bin/flow-profile-lib.sh +335 -0
- package/tools/bin/flow-provider-lib.sh +981 -0
- package/tools/bin/flow-session-lib.sh +317 -0
- package/tools/dashboard/app.js +40 -3
- package/tools/dashboard/requirements.txt +3 -0
- package/tools/dashboard/server.py +250 -152
|
@@ -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
|
+
|