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,981 @@
|
|
|
1
|
+
flow_github_pr_merge() {
|
|
2
|
+
local repo_slug="${1:?repo slug required}"
|
|
3
|
+
local pr_number="${2:?pr number required}"
|
|
4
|
+
local merge_method="${3:-squash}"
|
|
5
|
+
local delete_branch="${4:-no}"
|
|
6
|
+
local pr_json=""
|
|
7
|
+
local head_ref=""
|
|
8
|
+
local encoded_ref=""
|
|
9
|
+
|
|
10
|
+
if flow_using_gitea; then
|
|
11
|
+
printf '%s' "$(
|
|
12
|
+
MERGE_METHOD="${merge_method}" DELETE_BRANCH="${delete_branch}" python3 - <<'PY'
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
method = os.environ.get("MERGE_METHOD", "squash")
|
|
17
|
+
delete_branch = os.environ.get("DELETE_BRANCH", "no") == "yes"
|
|
18
|
+
method_map = {"merge": "merge", "squash": "squash", "rebase": "rebase"}
|
|
19
|
+
print(json.dumps({
|
|
20
|
+
"Do": method_map.get(method, "squash"),
|
|
21
|
+
"delete_branch_after_merge": delete_branch,
|
|
22
|
+
}))
|
|
23
|
+
PY
|
|
24
|
+
)" | flow_github_api_repo "${repo_slug}" "pulls/${pr_number}/merge" --method POST --input - >/dev/null
|
|
25
|
+
return $?
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if gh pr merge "${pr_number}" -R "${repo_slug}" "--${merge_method}" $([[ "${delete_branch}" == "yes" ]] && printf '%s' '--delete-branch') --admin >/dev/null 2>&1; then
|
|
29
|
+
return 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
printf '{"merge_method":"%s"}' "${merge_method}" \
|
|
33
|
+
| flow_github_api_repo "${repo_slug}" "pulls/${pr_number}/merge" --method PUT --input - >/dev/null
|
|
34
|
+
|
|
35
|
+
if [[ "${delete_branch}" == "yes" ]]; then
|
|
36
|
+
pr_json="$(flow_github_pr_view_json "${repo_slug}" "${pr_number}" 2>/dev/null || printf '{}\n')"
|
|
37
|
+
head_ref="$(jq -r '.headRefName // ""' <<<"${pr_json}")"
|
|
38
|
+
if [[ -n "${head_ref}" ]]; then
|
|
39
|
+
encoded_ref="$(flow_github_urlencode "heads/${head_ref}")"
|
|
40
|
+
flow_github_api_repo "${repo_slug}" "git/refs/${encoded_ref}" --method DELETE >/dev/null 2>&1 || true
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
flow_config_get() {
|
|
46
|
+
local config_file="${1:?config file required}"
|
|
47
|
+
local target_path="${2:?target path required}"
|
|
48
|
+
|
|
49
|
+
python3 - "$config_file" "$target_path" <<'PY'
|
|
50
|
+
import sys
|
|
51
|
+
|
|
52
|
+
config_file = sys.argv[1]
|
|
53
|
+
target_path = sys.argv[2]
|
|
54
|
+
|
|
55
|
+
stack = []
|
|
56
|
+
found = False
|
|
57
|
+
|
|
58
|
+
with open(config_file, "r", encoding="utf-8") as fh:
|
|
59
|
+
for raw_line in fh:
|
|
60
|
+
stripped = raw_line.strip()
|
|
61
|
+
if not stripped or stripped.startswith("#") or stripped.startswith("- "):
|
|
62
|
+
continue
|
|
63
|
+
if ":" not in raw_line:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
indent = len(raw_line) - len(raw_line.lstrip())
|
|
67
|
+
key, value = stripped.split(":", 1)
|
|
68
|
+
key = key.strip()
|
|
69
|
+
value = value.strip().strip("\"'")
|
|
70
|
+
|
|
71
|
+
while stack and indent <= stack[-1][0]:
|
|
72
|
+
stack.pop()
|
|
73
|
+
|
|
74
|
+
stack.append((indent, key))
|
|
75
|
+
current_path = ".".join(part for _, part in stack)
|
|
76
|
+
|
|
77
|
+
if current_path == target_path and value:
|
|
78
|
+
print(value)
|
|
79
|
+
found = True
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
if not found:
|
|
83
|
+
print("")
|
|
84
|
+
PY
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
flow_kv_get() {
|
|
88
|
+
local payload="${1:-}"
|
|
89
|
+
local key="${2:?key required}"
|
|
90
|
+
|
|
91
|
+
awk -F= -v key="${key}" '$1 == key { print substr($0, length(key) + 2); exit }' <<<"${payload}"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
flow_env_or_config() {
|
|
95
|
+
local config_file="${1:?config file required}"
|
|
96
|
+
local env_names="${2:?env names required}"
|
|
97
|
+
local config_key="${3:?config key required}"
|
|
98
|
+
local default_value="${4:-}"
|
|
99
|
+
local env_name=""
|
|
100
|
+
local value=""
|
|
101
|
+
|
|
102
|
+
for env_name in ${env_names}; do
|
|
103
|
+
value="${!env_name:-}"
|
|
104
|
+
if [[ -n "${value}" ]]; then
|
|
105
|
+
printf '%s\n' "${value}"
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
done
|
|
109
|
+
|
|
110
|
+
if [[ -f "${config_file}" ]]; then
|
|
111
|
+
value="$(flow_config_get "${config_file}" "${config_key}")"
|
|
112
|
+
if [[ -n "${value}" ]]; then
|
|
113
|
+
printf '%s\n' "${value}"
|
|
114
|
+
return 0
|
|
115
|
+
fi
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
printf '%s\n' "${default_value}"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
flow_resolve_adapter_id() {
|
|
122
|
+
local config_file="${1:-}"
|
|
123
|
+
local default_value=""
|
|
124
|
+
if [[ -z "${config_file}" ]]; then
|
|
125
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
126
|
+
fi
|
|
127
|
+
default_value="$(flow_default_profile_id)"
|
|
128
|
+
flow_env_or_config "${config_file}" "ACP_PROJECT_ID AGENT_PROJECT_ID" "id" "${default_value}"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
flow_resolve_profile_notes_file() {
|
|
132
|
+
local config_file="${1:-}"
|
|
133
|
+
local config_dir=""
|
|
134
|
+
|
|
135
|
+
if [[ -z "${config_file}" ]]; then
|
|
136
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
config_dir="$(cd "$(dirname "${config_file}")" 2>/dev/null && pwd -P || dirname "${config_file}")"
|
|
140
|
+
printf '%s/README.md
|
|
141
|
+
' "${config_dir}"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
flow_default_issue_session_prefix() {
|
|
145
|
+
local config_file="${1:-}"
|
|
146
|
+
local adapter_id=""
|
|
147
|
+
|
|
148
|
+
if [[ -z "${config_file}" ]]; then
|
|
149
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
153
|
+
printf '%s-issue-\n' "${adapter_id}"
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
flow_default_pr_session_prefix() {
|
|
157
|
+
local config_file="${1:-}"
|
|
158
|
+
local adapter_id=""
|
|
159
|
+
|
|
160
|
+
if [[ -z "${config_file}" ]]; then
|
|
161
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
165
|
+
printf '%s-pr-\n' "${adapter_id}"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
flow_default_issue_branch_prefix() {
|
|
169
|
+
local config_file="${1:-}"
|
|
170
|
+
local adapter_id=""
|
|
171
|
+
|
|
172
|
+
if [[ -z "${config_file}" ]]; then
|
|
173
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
177
|
+
printf 'agent/%s/issue\n' "${adapter_id}"
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
flow_default_pr_worktree_branch_prefix() {
|
|
181
|
+
local config_file="${1:-}"
|
|
182
|
+
local adapter_id=""
|
|
183
|
+
|
|
184
|
+
if [[ -z "${config_file}" ]]; then
|
|
185
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
189
|
+
printf 'agent/%s/pr\n' "${adapter_id}"
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
flow_default_managed_pr_branch_globs() {
|
|
193
|
+
local config_file="${1:-}"
|
|
194
|
+
local adapter_id=""
|
|
195
|
+
|
|
196
|
+
if [[ -z "${config_file}" ]]; then
|
|
197
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
201
|
+
printf 'agent/%s/* codex/* openclaw/*\n' "${adapter_id}"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
flow_default_agent_root() {
|
|
205
|
+
local config_file="${1:-}"
|
|
206
|
+
local adapter_id=""
|
|
207
|
+
local platform_home="${AGENT_PLATFORM_HOME:-${HOME}/.agent-runtime}"
|
|
208
|
+
|
|
209
|
+
if [[ -z "${config_file}" ]]; then
|
|
210
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
214
|
+
printf '%s/projects/%s
|
|
215
|
+
' "${platform_home}" "${adapter_id}"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
flow_default_repo_slug() {
|
|
219
|
+
local config_file="${1:-}"
|
|
220
|
+
local adapter_id=""
|
|
221
|
+
|
|
222
|
+
if [[ -z "${config_file}" ]]; then
|
|
223
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
227
|
+
printf 'example/%s
|
|
228
|
+
' "${adapter_id}"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
flow_default_repo_id() {
|
|
232
|
+
printf '\n'
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
flow_default_repo_root() {
|
|
236
|
+
local config_file="${1:-}"
|
|
237
|
+
local agent_root=""
|
|
238
|
+
|
|
239
|
+
if [[ -z "${config_file}" ]]; then
|
|
240
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
agent_root="$(flow_default_agent_root "${config_file}")"
|
|
244
|
+
printf '%s/repo
|
|
245
|
+
' "${agent_root}"
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
flow_default_worktree_root() {
|
|
249
|
+
local config_file="${1:-}"
|
|
250
|
+
local agent_root=""
|
|
251
|
+
|
|
252
|
+
if [[ -z "${config_file}" ]]; then
|
|
253
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
agent_root="$(flow_default_agent_root "${config_file}")"
|
|
257
|
+
printf '%s/worktrees
|
|
258
|
+
' "${agent_root}"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
flow_default_retained_repo_root() {
|
|
262
|
+
local config_file="${1:-}"
|
|
263
|
+
local agent_root=""
|
|
264
|
+
|
|
265
|
+
if [[ -z "${config_file}" ]]; then
|
|
266
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
agent_root="$(flow_default_agent_root "${config_file}")"
|
|
270
|
+
printf '%s/retained
|
|
271
|
+
' "${agent_root}"
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
flow_default_vscode_workspace_file() {
|
|
275
|
+
local config_file="${1:-}"
|
|
276
|
+
local agent_root=""
|
|
277
|
+
|
|
278
|
+
if [[ -z "${config_file}" ]]; then
|
|
279
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
agent_root="$(flow_default_agent_root "${config_file}")"
|
|
283
|
+
printf '%s/workspace.code-workspace
|
|
284
|
+
' "${agent_root}"
|
|
285
|
+
}
|
|
286
|
+
flow_resolve_repo_slug() {
|
|
287
|
+
local config_file="${1:-}"
|
|
288
|
+
local default_value=""
|
|
289
|
+
if [[ -z "${config_file}" ]]; then
|
|
290
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
291
|
+
fi
|
|
292
|
+
default_value="$(flow_default_repo_slug "${config_file}")"
|
|
293
|
+
flow_env_or_config "${config_file}" "ACP_REPO_SLUG F_LOSNING_REPO_SLUG" "repo.slug" "${default_value}"
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
flow_resolve_repo_id() {
|
|
297
|
+
local config_file="${1:-}"
|
|
298
|
+
local default_value=""
|
|
299
|
+
if [[ -z "${config_file}" ]]; then
|
|
300
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
301
|
+
fi
|
|
302
|
+
default_value="$(flow_default_repo_id)"
|
|
303
|
+
flow_env_or_config "${config_file}" "ACP_REPO_ID F_LOSNING_REPO_ID ACP_GITHUB_REPOSITORY_ID F_LOSNING_GITHUB_REPOSITORY_ID" "repo.id" "${default_value}"
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
flow_resolve_default_branch() {
|
|
307
|
+
local config_file="${1:-}"
|
|
308
|
+
if [[ -z "${config_file}" ]]; then
|
|
309
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
310
|
+
fi
|
|
311
|
+
flow_env_or_config "${config_file}" "ACP_DEFAULT_BRANCH F_LOSNING_DEFAULT_BRANCH" "repo.default_branch" "main"
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
flow_resolve_project_label() {
|
|
315
|
+
local config_file="${1:-}"
|
|
316
|
+
local repo_slug=""
|
|
317
|
+
local adapter_id=""
|
|
318
|
+
local label=""
|
|
319
|
+
|
|
320
|
+
if [[ -z "${config_file}" ]]; then
|
|
321
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
322
|
+
fi
|
|
323
|
+
|
|
324
|
+
repo_slug="$(flow_resolve_repo_slug "${config_file}")"
|
|
325
|
+
adapter_id="$(flow_resolve_adapter_id "${config_file}")"
|
|
326
|
+
label="${repo_slug##*/}"
|
|
327
|
+
if [[ -n "${label}" ]]; then
|
|
328
|
+
printf '%s\n' "${label}"
|
|
329
|
+
else
|
|
330
|
+
printf '%s\n' "${adapter_id}"
|
|
331
|
+
fi
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
flow_resolve_repo_root() {
|
|
335
|
+
local config_file="${1:-}"
|
|
336
|
+
local default_value=""
|
|
337
|
+
if [[ -z "${config_file}" ]]; then
|
|
338
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
339
|
+
fi
|
|
340
|
+
default_value="$(flow_default_repo_root "${config_file}")"
|
|
341
|
+
flow_env_or_config "${config_file}" "ACP_REPO_ROOT F_LOSNING_REPO_ROOT" "repo.root" "${default_value}"
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
flow_resolve_agent_root() {
|
|
345
|
+
local config_file="${1:-}"
|
|
346
|
+
local default_value=""
|
|
347
|
+
|
|
348
|
+
if [[ -z "${config_file}" ]]; then
|
|
349
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
default_value="$(flow_default_agent_root "${config_file}")"
|
|
353
|
+
flow_env_or_config "${config_file}" "ACP_AGENT_ROOT F_LOSNING_AGENT_ROOT" "runtime.orchestrator_agent_root" "${default_value}"
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
flow_resolve_agent_repo_root() {
|
|
357
|
+
local config_file="${1:-}"
|
|
358
|
+
local default_value=""
|
|
359
|
+
|
|
360
|
+
if [[ -z "${config_file}" ]]; then
|
|
361
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
default_value="$(flow_resolve_repo_root "${config_file}")"
|
|
365
|
+
flow_env_or_config "${config_file}" "ACP_AGENT_REPO_ROOT F_LOSNING_AGENT_REPO_ROOT" "runtime.agent_repo_root" "${default_value}"
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
flow_resolve_worktree_root() {
|
|
369
|
+
local config_file="${1:-}"
|
|
370
|
+
local default_value=""
|
|
371
|
+
if [[ -z "${config_file}" ]]; then
|
|
372
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
373
|
+
fi
|
|
374
|
+
default_value="$(flow_default_worktree_root "${config_file}")"
|
|
375
|
+
flow_env_or_config "${config_file}" "ACP_WORKTREE_ROOT F_LOSNING_WORKTREE_ROOT" "runtime.worktree_root" "${default_value}"
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
flow_resolve_runs_root() {
|
|
379
|
+
local config_file="${1:-}"
|
|
380
|
+
local default_value=""
|
|
381
|
+
local explicit_root="${ACP_RUNS_ROOT:-${F_LOSNING_RUNS_ROOT:-}}"
|
|
382
|
+
local umbrella_root="${ACP_AGENT_ROOT:-${F_LOSNING_AGENT_ROOT:-}}"
|
|
383
|
+
|
|
384
|
+
if [[ -z "${config_file}" ]]; then
|
|
385
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
if [[ -n "${explicit_root}" ]]; then
|
|
389
|
+
printf '%s\n' "${explicit_root}"
|
|
390
|
+
return 0
|
|
391
|
+
fi
|
|
392
|
+
|
|
393
|
+
default_value="$(flow_resolve_agent_root "${config_file}")/runs"
|
|
394
|
+
if [[ -n "${umbrella_root}" ]]; then
|
|
395
|
+
printf '%s\n' "${default_value}"
|
|
396
|
+
return 0
|
|
397
|
+
fi
|
|
398
|
+
|
|
399
|
+
flow_env_or_config "${config_file}" "ACP_RUNS_ROOT F_LOSNING_RUNS_ROOT" "runtime.runs_root" "${default_value}"
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
flow_resolve_state_root() {
|
|
403
|
+
local config_file="${1:-}"
|
|
404
|
+
local default_value=""
|
|
405
|
+
local explicit_root="${ACP_STATE_ROOT:-${F_LOSNING_STATE_ROOT:-}}"
|
|
406
|
+
local umbrella_root="${ACP_AGENT_ROOT:-${F_LOSNING_AGENT_ROOT:-}}"
|
|
407
|
+
|
|
408
|
+
if [[ -z "${config_file}" ]]; then
|
|
409
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
410
|
+
fi
|
|
411
|
+
|
|
412
|
+
if [[ -n "${explicit_root}" ]]; then
|
|
413
|
+
printf '%s\n' "${explicit_root}"
|
|
414
|
+
return 0
|
|
415
|
+
fi
|
|
416
|
+
|
|
417
|
+
default_value="$(flow_resolve_agent_root "${config_file}")/state"
|
|
418
|
+
if [[ -n "${umbrella_root}" ]]; then
|
|
419
|
+
printf '%s\n' "${default_value}"
|
|
420
|
+
return 0
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
flow_env_or_config "${config_file}" "ACP_STATE_ROOT F_LOSNING_STATE_ROOT" "runtime.state_root" "${default_value}"
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
flow_resolve_history_root() {
|
|
427
|
+
local config_file="${1:-}"
|
|
428
|
+
local default_value=""
|
|
429
|
+
local explicit_root="${ACP_HISTORY_ROOT:-${F_LOSNING_HISTORY_ROOT:-}}"
|
|
430
|
+
local umbrella_root="${ACP_AGENT_ROOT:-${F_LOSNING_AGENT_ROOT:-}}"
|
|
431
|
+
|
|
432
|
+
if [[ -z "${config_file}" ]]; then
|
|
433
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
434
|
+
fi
|
|
435
|
+
|
|
436
|
+
if [[ -n "${explicit_root}" ]]; then
|
|
437
|
+
printf '%s\n' "${explicit_root}"
|
|
438
|
+
return 0
|
|
439
|
+
fi
|
|
440
|
+
|
|
441
|
+
default_value="$(flow_resolve_agent_root "${config_file}")/history"
|
|
442
|
+
if [[ -n "${umbrella_root}" ]]; then
|
|
443
|
+
printf '%s\n' "${default_value}"
|
|
444
|
+
return 0
|
|
445
|
+
fi
|
|
446
|
+
|
|
447
|
+
flow_env_or_config "${config_file}" "ACP_HISTORY_ROOT F_LOSNING_HISTORY_ROOT" "runtime.history_root" "${default_value}"
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
flow_resolve_retained_repo_root() {
|
|
451
|
+
local config_file="${1:-}"
|
|
452
|
+
local default_value=""
|
|
453
|
+
if [[ -z "${config_file}" ]]; then
|
|
454
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
455
|
+
fi
|
|
456
|
+
default_value="$(flow_default_retained_repo_root "${config_file}")"
|
|
457
|
+
flow_env_or_config "${config_file}" "ACP_RETAINED_REPO_ROOT F_LOSNING_RETAINED_REPO_ROOT" "runtime.retained_repo_root" "${default_value}"
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
flow_resolve_source_repo_root() {
|
|
461
|
+
local config_file="${1:-}"
|
|
462
|
+
local default_value=""
|
|
463
|
+
if [[ -z "${config_file}" ]]; then
|
|
464
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
465
|
+
fi
|
|
466
|
+
default_value="$(flow_resolve_retained_repo_root "${config_file}")"
|
|
467
|
+
flow_env_or_config "${config_file}" "ACP_SOURCE_REPO_ROOT F_LOSNING_SOURCE_REPO_ROOT" "runtime.source_repo_root" "${default_value}"
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
flow_resolve_vscode_workspace_file() {
|
|
471
|
+
local config_file="${1:-}"
|
|
472
|
+
local default_value=""
|
|
473
|
+
if [[ -z "${config_file}" ]]; then
|
|
474
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
475
|
+
fi
|
|
476
|
+
default_value="$(flow_default_vscode_workspace_file "${config_file}")"
|
|
477
|
+
flow_env_or_config "${config_file}" "ACP_VSCODE_WORKSPACE_FILE F_LOSNING_VSCODE_WORKSPACE_FILE" "runtime.vscode_workspace_file" "${default_value}"
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
flow_resolve_web_playwright_command() {
|
|
481
|
+
local config_file="${1:-}"
|
|
482
|
+
if [[ -z "${config_file}" ]]; then
|
|
483
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
484
|
+
fi
|
|
485
|
+
flow_env_or_config "${config_file}" "ACP_WEB_PLAYWRIGHT_COMMAND F_LOSNING_WEB_PLAYWRIGHT_COMMAND" "execution.verification.web_playwright_command" "pnpm exec playwright test"
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
flow_resolve_codex_quota_bin() {
|
|
489
|
+
local flow_root="${1:-}"
|
|
490
|
+
local shared_home=""
|
|
491
|
+
local explicit_bin="${ACP_CODEX_QUOTA_BIN:-${F_LOSNING_CODEX_QUOTA_BIN:-}}"
|
|
492
|
+
local candidate=""
|
|
493
|
+
|
|
494
|
+
if [[ -n "${explicit_bin}" ]]; then
|
|
495
|
+
printf '%s\n' "${explicit_bin}"
|
|
496
|
+
return 0
|
|
497
|
+
fi
|
|
498
|
+
|
|
499
|
+
if [[ -z "${flow_root}" ]]; then
|
|
500
|
+
flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
501
|
+
fi
|
|
502
|
+
shared_home="${SHARED_AGENT_HOME:-$(resolve_shared_agent_home "${flow_root}")}"
|
|
503
|
+
|
|
504
|
+
for candidate in \
|
|
505
|
+
"${flow_root}/tools/bin/codex-quota" \
|
|
506
|
+
"${shared_home}/tools/bin/codex-quota"; do
|
|
507
|
+
if [[ -x "${candidate}" ]]; then
|
|
508
|
+
printf '%s\n' "${candidate}"
|
|
509
|
+
return 0
|
|
510
|
+
fi
|
|
511
|
+
done
|
|
512
|
+
|
|
513
|
+
candidate="$(command -v codex-quota 2>/dev/null || true)"
|
|
514
|
+
if [[ -n "${candidate}" ]]; then
|
|
515
|
+
printf '%s\n' "${candidate}"
|
|
516
|
+
return 0
|
|
517
|
+
fi
|
|
518
|
+
|
|
519
|
+
printf '%s\n' "${flow_root}/tools/bin/codex-quota"
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
flow_resolve_codex_quota_manager_script() {
|
|
523
|
+
local flow_root="${1:-}"
|
|
524
|
+
local shared_home=""
|
|
525
|
+
local explicit_script="${ACP_CODEX_QUOTA_MANAGER_SCRIPT:-${F_LOSNING_CODEX_QUOTA_MANAGER_SCRIPT:-}}"
|
|
526
|
+
local candidate=""
|
|
527
|
+
|
|
528
|
+
if [[ -n "${explicit_script}" ]]; then
|
|
529
|
+
printf '%s\n' "${explicit_script}"
|
|
530
|
+
return 0
|
|
531
|
+
fi
|
|
532
|
+
|
|
533
|
+
if [[ -z "${flow_root}" ]]; then
|
|
534
|
+
flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
535
|
+
fi
|
|
536
|
+
shared_home="${SHARED_AGENT_HOME:-$(resolve_shared_agent_home "${flow_root}")}"
|
|
537
|
+
|
|
538
|
+
for candidate in \
|
|
539
|
+
"${flow_root}/tools/vendor/codex-quota-manager/scripts/auto-switch.sh" \
|
|
540
|
+
"${shared_home}/tools/vendor/codex-quota-manager/scripts/auto-switch.sh" \
|
|
541
|
+
"${shared_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh"; do
|
|
542
|
+
if [[ -x "${candidate}" ]]; then
|
|
543
|
+
printf '%s\n' "${candidate}"
|
|
544
|
+
return 0
|
|
545
|
+
fi
|
|
546
|
+
done
|
|
547
|
+
|
|
548
|
+
printf '%s\n' "${flow_root}/tools/vendor/codex-quota-manager/scripts/auto-switch.sh"
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
flow_resolve_template_file() {
|
|
552
|
+
local template_name="${1:?template name required}"
|
|
553
|
+
local workspace_dir="${2:-}"
|
|
554
|
+
local config_file="${3:-}"
|
|
555
|
+
local flow_root=""
|
|
556
|
+
local profile_id=""
|
|
557
|
+
local config_dir=""
|
|
558
|
+
local template_dir=""
|
|
559
|
+
local candidate=""
|
|
560
|
+
local workspace_real=""
|
|
561
|
+
local canonical_tools_real=""
|
|
562
|
+
|
|
563
|
+
if [[ -z "${config_file}" ]]; then
|
|
564
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
565
|
+
fi
|
|
566
|
+
|
|
567
|
+
flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[0]}")"
|
|
568
|
+
config_dir="$(cd "$(dirname "${config_file}")" 2>/dev/null && pwd -P || dirname "${config_file}")"
|
|
569
|
+
|
|
570
|
+
for template_dir in \
|
|
571
|
+
"${AGENT_CONTROL_PLANE_TEMPLATE_DIR:-}" \
|
|
572
|
+
"${ACP_TEMPLATE_DIR:-}" \
|
|
573
|
+
"${F_LOSNING_TEMPLATE_DIR:-}"; do
|
|
574
|
+
if [[ -n "${template_dir}" && -f "${template_dir}/${template_name}" ]]; then
|
|
575
|
+
printf '%s\n' "${template_dir}/${template_name}"
|
|
576
|
+
return 0
|
|
577
|
+
fi
|
|
578
|
+
done
|
|
579
|
+
|
|
580
|
+
if [[ -n "${workspace_dir}" && -f "${workspace_dir}/templates/${template_name}" ]]; then
|
|
581
|
+
workspace_real="$(cd "${workspace_dir}" && pwd -P)"
|
|
582
|
+
canonical_tools_real="$(cd "${flow_root}/tools" && pwd -P)"
|
|
583
|
+
if [[ "${workspace_real}" != "${canonical_tools_real}" ]]; then
|
|
584
|
+
printf '%s\n' "${workspace_dir}/templates/${template_name}"
|
|
585
|
+
return 0
|
|
586
|
+
fi
|
|
587
|
+
fi
|
|
588
|
+
|
|
589
|
+
candidate="${config_dir}/templates/${template_name}"
|
|
590
|
+
if [[ -f "${candidate}" ]]; then
|
|
591
|
+
printf '%s\n' "${candidate}"
|
|
592
|
+
return 0
|
|
593
|
+
fi
|
|
594
|
+
|
|
595
|
+
if [[ -n "${workspace_dir}" && -f "${workspace_dir}/templates/${template_name}" ]]; then
|
|
596
|
+
printf '%s\n' "${workspace_dir}/templates/${template_name}"
|
|
597
|
+
return 0
|
|
598
|
+
fi
|
|
599
|
+
|
|
600
|
+
printf '%s\n' "${flow_root}/tools/templates/${template_name}"
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
flow_resolve_retry_cooldowns() {
|
|
604
|
+
local config_file="${1:-}"
|
|
605
|
+
if [[ -z "${config_file}" ]]; then
|
|
606
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
607
|
+
fi
|
|
608
|
+
flow_env_or_config "${config_file}" "ACP_RETRY_COOLDOWNS F_LOSNING_RETRY_COOLDOWNS" "execution.retry.cooldowns" "300,900,1800,3600"
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
flow_resolve_provider_quota_cooldowns() {
|
|
612
|
+
local config_file="${1:-}"
|
|
613
|
+
if [[ -z "${config_file}" ]]; then
|
|
614
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
615
|
+
fi
|
|
616
|
+
flow_env_or_config "${config_file}" "ACP_PROVIDER_QUOTA_COOLDOWNS F_LOSNING_PROVIDER_QUOTA_COOLDOWNS" "execution.provider_quota.cooldowns" "300,900,1800,3600"
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
flow_resolve_provider_pool_order() {
|
|
620
|
+
local config_file="${1:-}"
|
|
621
|
+
if [[ -z "${config_file}" ]]; then
|
|
622
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
623
|
+
fi
|
|
624
|
+
flow_env_or_config "${config_file}" "ACP_PROVIDER_POOL_ORDER F_LOSNING_PROVIDER_POOL_ORDER" "execution.provider_pool_order" ""
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
flow_provider_pool_names() {
|
|
628
|
+
local config_file="${1:-}"
|
|
629
|
+
local order=""
|
|
630
|
+
local pool_name=""
|
|
631
|
+
|
|
632
|
+
if [[ -z "${config_file}" ]]; then
|
|
633
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
634
|
+
fi
|
|
635
|
+
|
|
636
|
+
order="$(flow_resolve_provider_pool_order "${config_file}")"
|
|
637
|
+
for pool_name in ${order}; do
|
|
638
|
+
[[ -n "${pool_name}" ]] || continue
|
|
639
|
+
printf '%s\n' "${pool_name}"
|
|
640
|
+
done
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
flow_provider_pools_enabled() {
|
|
644
|
+
local config_file="${1:-}"
|
|
645
|
+
[[ -n "$(flow_resolve_provider_pool_order "${config_file}")" ]]
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
flow_provider_pool_value() {
|
|
649
|
+
local config_file="${1:?config file required}"
|
|
650
|
+
local pool_name="${2:?pool name required}"
|
|
651
|
+
local relative_path="${3:?relative path required}"
|
|
652
|
+
|
|
653
|
+
flow_config_get "${config_file}" "execution.provider_pools.${pool_name}.${relative_path}"
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
flow_provider_pool_backend() {
|
|
657
|
+
local config_file="${1:?config file required}"
|
|
658
|
+
local pool_name="${2:?pool name required}"
|
|
659
|
+
|
|
660
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "coding_worker"
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
flow_provider_pool_safe_profile() {
|
|
664
|
+
local config_file="${1:?config file required}"
|
|
665
|
+
local pool_name="${2:?pool name required}"
|
|
666
|
+
|
|
667
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "safe_profile"
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
flow_provider_pool_bypass_profile() {
|
|
671
|
+
local config_file="${1:?config file required}"
|
|
672
|
+
local pool_name="${2:?pool name required}"
|
|
673
|
+
|
|
674
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "bypass_profile"
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
flow_provider_pool_claude_model() {
|
|
678
|
+
local config_file="${1:?config file required}"
|
|
679
|
+
local pool_name="${2:?pool name required}"
|
|
680
|
+
|
|
681
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "claude.model"
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
flow_provider_pool_claude_permission_mode() {
|
|
685
|
+
local config_file="${1:?config file required}"
|
|
686
|
+
local pool_name="${2:?pool name required}"
|
|
687
|
+
|
|
688
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "claude.permission_mode"
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
flow_provider_pool_claude_effort() {
|
|
692
|
+
local config_file="${1:?config file required}"
|
|
693
|
+
local pool_name="${2:?pool name required}"
|
|
694
|
+
|
|
695
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "claude.effort"
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
flow_provider_pool_claude_timeout_seconds() {
|
|
699
|
+
local config_file="${1:?config file required}"
|
|
700
|
+
local pool_name="${2:?pool name required}"
|
|
701
|
+
|
|
702
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "claude.timeout_seconds"
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
flow_provider_pool_claude_max_attempts() {
|
|
706
|
+
local config_file="${1:?config file required}"
|
|
707
|
+
local pool_name="${2:?pool name required}"
|
|
708
|
+
|
|
709
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "claude.max_attempts"
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
flow_provider_pool_claude_retry_backoff_seconds() {
|
|
713
|
+
local config_file="${1:?config file required}"
|
|
714
|
+
local pool_name="${2:?pool name required}"
|
|
715
|
+
|
|
716
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "claude.retry_backoff_seconds"
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
flow_provider_pool_openclaw_model() {
|
|
720
|
+
local config_file="${1:?config file required}"
|
|
721
|
+
local pool_name="${2:?pool name required}"
|
|
722
|
+
|
|
723
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "openclaw.model"
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
flow_provider_pool_openclaw_thinking() {
|
|
727
|
+
local config_file="${1:?config file required}"
|
|
728
|
+
local pool_name="${2:?pool name required}"
|
|
729
|
+
|
|
730
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "openclaw.thinking"
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
flow_provider_pool_openclaw_timeout_seconds() {
|
|
734
|
+
local config_file="${1:?config file required}"
|
|
735
|
+
local pool_name="${2:?pool name required}"
|
|
736
|
+
|
|
737
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "openclaw.timeout_seconds"
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
flow_provider_pool_ollama_model() {
|
|
741
|
+
local config_file="${1:?config file required}"
|
|
742
|
+
local pool_name="${2:?pool name required}"
|
|
743
|
+
|
|
744
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "ollama.model"
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
flow_provider_pool_ollama_base_url() {
|
|
748
|
+
local config_file="${1:?config file required}"
|
|
749
|
+
local pool_name="${2:?pool name required}"
|
|
750
|
+
|
|
751
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "ollama.base_url"
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
flow_provider_pool_ollama_timeout_seconds() {
|
|
755
|
+
local config_file="${1:?config file required}"
|
|
756
|
+
local pool_name="${2:?pool name required}"
|
|
757
|
+
|
|
758
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "ollama.timeout_seconds"
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
flow_provider_pool_pi_model() {
|
|
762
|
+
local config_file="${1:?config file required}"
|
|
763
|
+
local pool_name="${2:?pool name required}"
|
|
764
|
+
|
|
765
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "pi.model"
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
flow_provider_pool_pi_thinking() {
|
|
769
|
+
local config_file="${1:?config file required}"
|
|
770
|
+
local pool_name="${2:?pool name required}"
|
|
771
|
+
|
|
772
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "pi.thinking"
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
flow_provider_pool_pi_timeout_seconds() {
|
|
776
|
+
local config_file="${1:?config file required}"
|
|
777
|
+
local pool_name="${2:?pool name required}"
|
|
778
|
+
|
|
779
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "pi.timeout_seconds"
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
flow_provider_pool_opencode_model() {
|
|
783
|
+
local config_file="${1:?config file required}"
|
|
784
|
+
local pool_name="${2:?pool name required}"
|
|
785
|
+
|
|
786
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "opencode.model"
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
flow_provider_pool_opencode_timeout_seconds() {
|
|
790
|
+
local config_file="${1:?config file required}"
|
|
791
|
+
local pool_name="${2:?pool name required}"
|
|
792
|
+
|
|
793
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "opencode.timeout_seconds"
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
flow_provider_pool_kilo_model() {
|
|
797
|
+
local config_file="${1:?config file required}"
|
|
798
|
+
local pool_name="${2:?pool name required}"
|
|
799
|
+
|
|
800
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "kilo.model"
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
flow_provider_pool_kilo_timeout_seconds() {
|
|
804
|
+
local config_file="${1:?config file required}"
|
|
805
|
+
local pool_name="${2:?pool name required}"
|
|
806
|
+
|
|
807
|
+
flow_provider_pool_value "${config_file}" "${pool_name}" "kilo.timeout_seconds"
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
flow_sanitize_provider_key() {
|
|
811
|
+
local raw_key="${1:?raw key required}"
|
|
812
|
+
|
|
813
|
+
printf '%s' "${raw_key}" \
|
|
814
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
815
|
+
| sed -E 's/[^a-z0-9._-]+/-/g; s/^-+//; s/-+$//; s/-\+/-/g'
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
flow_provider_pool_model_identity() {
|
|
819
|
+
local config_file="${1:?config file required}"
|
|
820
|
+
local pool_name="${2:?pool name required}"
|
|
821
|
+
local backend=""
|
|
822
|
+
|
|
823
|
+
backend="$(flow_provider_pool_backend "${config_file}" "${pool_name}")"
|
|
824
|
+
case "${backend}" in
|
|
825
|
+
codex)
|
|
826
|
+
flow_provider_pool_safe_profile "${config_file}" "${pool_name}"
|
|
827
|
+
;;
|
|
828
|
+
claude)
|
|
829
|
+
flow_provider_pool_claude_model "${config_file}" "${pool_name}"
|
|
830
|
+
;;
|
|
831
|
+
openclaw)
|
|
832
|
+
flow_provider_pool_openclaw_model "${config_file}" "${pool_name}"
|
|
833
|
+
;;
|
|
834
|
+
ollama)
|
|
835
|
+
flow_provider_pool_ollama_model "${config_file}" "${pool_name}"
|
|
836
|
+
;;
|
|
837
|
+
pi)
|
|
838
|
+
flow_provider_pool_pi_model "${config_file}" "${pool_name}"
|
|
839
|
+
;;
|
|
840
|
+
opencode)
|
|
841
|
+
flow_provider_pool_opencode_model "${config_file}" "${pool_name}"
|
|
842
|
+
;;
|
|
843
|
+
kilo)
|
|
844
|
+
flow_provider_pool_kilo_model "${config_file}" "${pool_name}"
|
|
845
|
+
;;
|
|
846
|
+
*)
|
|
847
|
+
printf '\n'
|
|
848
|
+
;;
|
|
849
|
+
esac
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
# Check health of a specific provider pool
|
|
853
|
+
# Returns: 0 = healthy, 1 = unhealthy
|
|
854
|
+
flow_provider_pool_health_check() {
|
|
855
|
+
local config_file="${1:?config file required}"
|
|
856
|
+
local pool_name="${2:?pool name required}"
|
|
857
|
+
local backend=""
|
|
858
|
+
local adapter_script=""
|
|
859
|
+
local SCRIPT_DIR=""
|
|
860
|
+
|
|
861
|
+
backend="$(flow_provider_pool_backend "${config_file}" "${pool_name}")"
|
|
862
|
+
if [[ -z "${backend}" ]]; then
|
|
863
|
+
echo "ERROR: No backend found for pool ${pool_name}"
|
|
864
|
+
return 1
|
|
865
|
+
fi
|
|
866
|
+
|
|
867
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
868
|
+
adapter_script="${SCRIPT_DIR}/${backend}-adapter.sh"
|
|
869
|
+
|
|
870
|
+
if [[ ! -f "${adapter_script}" ]]; then
|
|
871
|
+
echo "WARN: Adapter script not found: ${adapter_script}"
|
|
872
|
+
return 1
|
|
873
|
+
fi
|
|
874
|
+
|
|
875
|
+
# Source adapter and run health check
|
|
876
|
+
source "${SCRIPT_DIR}/adapter-interface.sh"
|
|
877
|
+
source "${SCRIPT_DIR}/adapter-capabilities.sh"
|
|
878
|
+
source "${adapter_script}"
|
|
879
|
+
|
|
880
|
+
adapter_health_check
|
|
881
|
+
return $?
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
# Get healthy pools in priority order
|
|
885
|
+
# Returns list of pool names that are healthy
|
|
886
|
+
flow_healthy_pools() {
|
|
887
|
+
local config_file="${1:-}"
|
|
888
|
+
local pool_name=""
|
|
889
|
+
local healthy_pools=""
|
|
890
|
+
|
|
891
|
+
if [[ -z "${config_file}" ]]; then
|
|
892
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
893
|
+
fi
|
|
894
|
+
|
|
895
|
+
for pool_name in $(flow_provider_pool_names "${config_file}"); do
|
|
896
|
+
if flow_provider_pool_health_check "${config_file}" "${pool_name}" >/dev/null 2>&1; then
|
|
897
|
+
healthy_pools="${healthy_pools} ${pool_name}"
|
|
898
|
+
fi
|
|
899
|
+
done
|
|
900
|
+
|
|
901
|
+
printf '%s\n' "${healthy_pools}"
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
# Enhanced provider pool selection with failover
|
|
905
|
+
# Returns: environment variables for the best available provider pool
|
|
906
|
+
flow_selected_provider_pool_env() {
|
|
907
|
+
local config_file="${1:-}"
|
|
908
|
+
local pool_name=""
|
|
909
|
+
local selected_pool=""
|
|
910
|
+
local backend=""
|
|
911
|
+
local health_output=""
|
|
912
|
+
|
|
913
|
+
if [[ -z "${config_file}" ]]; then
|
|
914
|
+
config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
915
|
+
fi
|
|
916
|
+
|
|
917
|
+
# Try pools in order, pick first healthy one
|
|
918
|
+
for pool_name in $(flow_provider_pool_names "${config_file}"); do
|
|
919
|
+
health_output="$(flow_provider_pool_health_check "${config_file}" "${pool_name}" 2>&1)"
|
|
920
|
+
if [[ $? -eq 0 ]]; then
|
|
921
|
+
selected_pool="${pool_name}"
|
|
922
|
+
echo "INFO: Selected healthy provider pool: ${selected_pool}"
|
|
923
|
+
echo "${health_output}"
|
|
924
|
+
break
|
|
925
|
+
else
|
|
926
|
+
echo "WARN: Provider pool '${pool_name}' is unhealthy, trying next: ${health_output}"
|
|
927
|
+
fi
|
|
928
|
+
done
|
|
929
|
+
|
|
930
|
+
if [[ -z "${selected_pool}" ]]; then
|
|
931
|
+
echo "ERROR: No healthy provider pools available!"
|
|
932
|
+
return 1
|
|
933
|
+
fi
|
|
934
|
+
|
|
935
|
+
# Export environment for selected pool
|
|
936
|
+
backend="$(flow_provider_pool_backend "${config_file}" "${selected_pool}")"
|
|
937
|
+
echo "INFO: Active provider backend: ${backend}"
|
|
938
|
+
echo "ACP_PROVIDER_POOL_NAME=${selected_pool}"
|
|
939
|
+
echo "ACP_PROVIDER_POOL_BACKEND=${backend}"
|
|
940
|
+
|
|
941
|
+
# Export backend-specific settings
|
|
942
|
+
case "${backend}" in
|
|
943
|
+
claude)
|
|
944
|
+
echo "ACP_CLAUDE_MODEL=$(flow_provider_pool_claude_model "${config_file}" "${selected_pool}")"
|
|
945
|
+
echo "ACP_CLAUDE_PERMISSION_MODE=$(flow_provider_pool_claude_permission_mode "${config_file}" "${selected_pool}")"
|
|
946
|
+
echo "ACP_CLAUDE_EFFORT=$(flow_provider_pool_claude_effort "${config_file}" "${selected_pool}")"
|
|
947
|
+
echo "ACP_CLAUDE_TIMEOUT_SECONDS=$(flow_provider_pool_claude_timeout_seconds "${config_file}" "${selected_pool}")"
|
|
948
|
+
echo "ACP_CLAUDE_MAX_ATTEMPTS=$(flow_provider_pool_claude_max_attempts "${config_file}" "${selected_pool}")"
|
|
949
|
+
echo "ACP_CLAUDE_RETRY_BACKOFF_SECONDS=$(flow_provider_pool_claude_retry_backoff_seconds "${config_file}" "${selected_pool}")"
|
|
950
|
+
;;
|
|
951
|
+
openclaw)
|
|
952
|
+
echo "ACP_OPENCLAW_MODEL=$(flow_provider_pool_openclaw_model "${config_file}" "${selected_pool}")"
|
|
953
|
+
echo "ACP_OPENCLAW_THINKING=$(flow_provider_pool_openclaw_thinking "${config_file}" "${selected_pool}")"
|
|
954
|
+
echo "ACP_OPENCLAW_TIMEOUT_SECONDS=$(flow_provider_pool_openclaw_timeout_seconds "${config_file}" "${selected_pool}")"
|
|
955
|
+
;;
|
|
956
|
+
ollama)
|
|
957
|
+
echo "ACP_OLLAMA_MODEL=$(flow_provider_pool_ollama_model "${config_file}" "${selected_pool}")"
|
|
958
|
+
echo "ACP_OLLAMA_BASE_URL=$(flow_provider_pool_ollama_base_url "${config_file}" "${selected_pool}")"
|
|
959
|
+
echo "ACP_OLLAMA_TIMEOUT_SECONDS=$(flow_provider_pool_ollama_timeout_seconds "${config_file}" "${selected_pool}")"
|
|
960
|
+
;;
|
|
961
|
+
pi)
|
|
962
|
+
echo "ACP_PI_MODEL=$(flow_provider_pool_pi_model "${config_file}" "${selected_pool}")"
|
|
963
|
+
echo "ACP_PI_THINKING=$(flow_provider_pool_pi_thinking "${config_file}" "${selected_pool}")"
|
|
964
|
+
echo "ACP_PI_TIMEOUT_SECONDS=$(flow_provider_pool_pi_timeout_seconds "${config_file}" "${selected_pool}")"
|
|
965
|
+
;;
|
|
966
|
+
opencode)
|
|
967
|
+
echo "ACP_OPENCODE_MODEL=$(flow_provider_pool_opencode_model "${config_file}" "${selected_pool}")"
|
|
968
|
+
echo "ACP_OPENCODE_TIMEOUT_SECONDS=$(flow_provider_pool_opencode_timeout_seconds "${config_file}" "${selected_pool}")"
|
|
969
|
+
;;
|
|
970
|
+
kilo)
|
|
971
|
+
echo "ACP_KILO_MODEL=$(flow_provider_pool_kilo_model "${config_file}" "${selected_pool}")"
|
|
972
|
+
echo "ACP_KILO_TIMEOUT_SECONDS=$(flow_provider_pool_kilo_timeout_seconds "${config_file}" "${selected_pool}")"
|
|
973
|
+
;;
|
|
974
|
+
codex)
|
|
975
|
+
echo "INFO: Codex uses quota manager, no additional env needed"
|
|
976
|
+
;;
|
|
977
|
+
esac
|
|
978
|
+
|
|
979
|
+
return 0
|
|
980
|
+
}
|
|
981
|
+
|