aidevops 3.13.95 → 3.14.1
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 +0 -1
- package/VERSION +1 -1
- package/aidevops.sh +44 -26
- package/package.json +1 -1
- package/setup.sh +25 -21
- package/aidevops-init-lib.sh +0 -1411
- package/aidevops-repos-lib.sh +0 -700
- package/aidevops-skills-plugin-lib.sh +0 -697
- package/aidevops-status-lib.sh +0 -141
- package/aidevops-update-lib.sh +0 -512
- package/aidevops-upgrade-planning-lib.sh +0 -370
- package/setup-modules/agent-deploy.sh +0 -1035
- package/setup-modules/agent-runtime.sh +0 -287
- package/setup-modules/config.sh +0 -478
- package/setup-modules/core.sh +0 -736
- package/setup-modules/mcp-setup.sh +0 -947
- package/setup-modules/migrations.sh +0 -1688
- package/setup-modules/plugins.sh +0 -728
- package/setup-modules/post-setup.sh +0 -301
- package/setup-modules/schedulers-linux.sh +0 -386
- package/setup-modules/schedulers-platform.sh +0 -1072
- package/setup-modules/schedulers-pulse.sh +0 -978
- package/setup-modules/schedulers.sh +0 -565
- package/setup-modules/shell-env.sh +0 -1240
- package/setup-modules/tool-beads.sh +0 -324
- package/setup-modules/tool-install.sh +0 -2134
|
@@ -1,1072 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
|
|
4
|
-
# =============================================================================
|
|
5
|
-
# Schedulers Platform Sub-Library -- Platform scheduler setup for contribution
|
|
6
|
-
# watch, complexity scan, pulse merge routine, profile README, OAuth token
|
|
7
|
-
# refresh, OpenCode DB maintenance, repo sync, repo health, and peer
|
|
8
|
-
# productivity monitor.
|
|
9
|
-
# =============================================================================
|
|
10
|
-
# This sub-library is sourced by setup-modules/schedulers.sh (the orchestrator).
|
|
11
|
-
# It covers:
|
|
12
|
-
# - Contribution watch (t1554): passive monitoring of external issues/PRs
|
|
13
|
-
# - Complexity scan (t2903): decoupled weekly complexity scan
|
|
14
|
-
# - Pulse merge routine (t2862, GH#20919): fast 120s merge pass
|
|
15
|
-
# - Draft responses (t1555): private repo + local draft storage
|
|
16
|
-
# - Profile README: auto-create and scheduled update
|
|
17
|
-
# - OAuth token refresh: launchd/systemd/cron/schtasks
|
|
18
|
-
# - OpenCode DB maintenance (r913, t2183): weekly checkpoint/vacuum
|
|
19
|
-
# - Repo sync: daily fast-forward pull
|
|
20
|
-
# - Repo aidevops health (r914): daily drift keeper
|
|
21
|
-
# - Peer productivity monitor (t2932): adaptive cross-runner coordination
|
|
22
|
-
#
|
|
23
|
-
# Usage: source "${SCRIPT_DIR}/schedulers-platform.sh"
|
|
24
|
-
#
|
|
25
|
-
# Dependencies:
|
|
26
|
-
# - shared-constants.sh (print_info, print_warning, print_error)
|
|
27
|
-
# - schedulers-linux.sh (_install_scheduler_linux, _uninstall_scheduler)
|
|
28
|
-
# - schedulers-pulse.sh (_resolve_modern_bash)
|
|
29
|
-
#
|
|
30
|
-
# Part of aidevops framework: https://aidevops.sh
|
|
31
|
-
|
|
32
|
-
# Apply strict mode only when executed directly (not when sourced)
|
|
33
|
-
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && set -euo pipefail
|
|
34
|
-
|
|
35
|
-
# Include guard
|
|
36
|
-
[[ -n "${_SCHEDULERS_PLATFORM_LIB_LOADED:-}" ]] && return 0
|
|
37
|
-
_SCHEDULERS_PLATFORM_LIB_LOADED=1
|
|
38
|
-
|
|
39
|
-
# SCRIPT_DIR fallback — needed when sourced from test harnesses that don't set it.
|
|
40
|
-
if [[ -z "${SCRIPT_DIR:-}" ]]; then
|
|
41
|
-
_sched_platform_lib_path="${BASH_SOURCE[0]%/*}"
|
|
42
|
-
[[ "$_sched_platform_lib_path" == "${BASH_SOURCE[0]}" ]] && _sched_platform_lib_path="."
|
|
43
|
-
SCRIPT_DIR="$(cd "$_sched_platform_lib_path" && pwd)"
|
|
44
|
-
unset _sched_platform_lib_path
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
# --- Functions ---
|
|
48
|
-
|
|
49
|
-
# Resolve and validate the log directory from config for contribution watch.
|
|
50
|
-
# Delegates resolution to _resolve_log_dir (shared-constants.sh), then applies
|
|
51
|
-
# install-time character validation (safe for shell paths and cron lines).
|
|
52
|
-
# Prints the resolved absolute path. Returns 1 on invalid characters.
|
|
53
|
-
_resolve_cw_log_dir() {
|
|
54
|
-
local _cw_log_dir
|
|
55
|
-
_cw_log_dir=$(_resolve_log_dir)
|
|
56
|
-
# Whitelist: only allow characters safe in shell paths and cron lines.
|
|
57
|
-
# Reject anything outside [A-Za-z0-9_./ -] (tilde already expanded by _resolve_log_dir).
|
|
58
|
-
# Store regex in variable — bash [[ =~ ]] requires unquoted RHS for regex,
|
|
59
|
-
# and a variable avoids quoting issues with special chars in the pattern.
|
|
60
|
-
local _cw_log_dir_re='^[A-Za-z0-9_./ -]+$'
|
|
61
|
-
if ! [[ "$_cw_log_dir" =~ $_cw_log_dir_re ]]; then
|
|
62
|
-
# Redirect to stderr so $() captures only the path result
|
|
63
|
-
print_error "Invalid characters in paths.log_dir (only [A-Za-z0-9_./ -] allowed): $_cw_log_dir" >&2
|
|
64
|
-
return 1
|
|
65
|
-
fi
|
|
66
|
-
printf '%s' "$_cw_log_dir"
|
|
67
|
-
return 0
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
# Install contribution watch via launchd (macOS).
|
|
71
|
-
# Args: $1=label, $2=script path, $3=log dir
|
|
72
|
-
_install_cw_launchd() {
|
|
73
|
-
local cw_label="$1"
|
|
74
|
-
local cw_script="$2"
|
|
75
|
-
local _cw_log_dir="$3"
|
|
76
|
-
local cw_plist="$HOME/Library/LaunchAgents/${cw_label}.plist"
|
|
77
|
-
|
|
78
|
-
local _xml_cw_script _xml_cw_home _xml_cw_log_dir
|
|
79
|
-
_xml_cw_script=$(_xml_escape "$cw_script")
|
|
80
|
-
_xml_cw_home=$(_xml_escape "$HOME")
|
|
81
|
-
_xml_cw_log_dir=$(_xml_escape "$_cw_log_dir")
|
|
82
|
-
|
|
83
|
-
local cw_plist_content
|
|
84
|
-
cw_plist_content=$(
|
|
85
|
-
cat <<CW_PLIST
|
|
86
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
87
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
88
|
-
<plist version="1.0">
|
|
89
|
-
<dict>
|
|
90
|
-
<key>Label</key>
|
|
91
|
-
<string>${cw_label}</string>
|
|
92
|
-
<key>ProgramArguments</key>
|
|
93
|
-
<array>
|
|
94
|
-
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
95
|
-
<string>${_xml_cw_script}</string>
|
|
96
|
-
<string>scan</string>
|
|
97
|
-
</array>
|
|
98
|
-
<key>StartInterval</key>
|
|
99
|
-
<integer>3600</integer>
|
|
100
|
-
<key>StandardOutPath</key>
|
|
101
|
-
<string>${_xml_cw_log_dir}/contribution-watch.log</string>
|
|
102
|
-
<key>StandardErrorPath</key>
|
|
103
|
-
<string>${_xml_cw_log_dir}/contribution-watch.log</string>
|
|
104
|
-
<key>EnvironmentVariables</key>
|
|
105
|
-
<dict>
|
|
106
|
-
<key>PATH</key>
|
|
107
|
-
<string>$(aidevops_launchd_sanitized_path)</string>
|
|
108
|
-
<key>HOME</key>
|
|
109
|
-
<string>${_xml_cw_home}</string>
|
|
110
|
-
</dict>
|
|
111
|
-
<key>RunAtLoad</key>
|
|
112
|
-
<false/>
|
|
113
|
-
<key>KeepAlive</key>
|
|
114
|
-
<false/>
|
|
115
|
-
<key>ProcessType</key>
|
|
116
|
-
<string>Background</string>
|
|
117
|
-
<key>LowPriorityBackgroundIO</key>
|
|
118
|
-
<true/>
|
|
119
|
-
<key>Nice</key>
|
|
120
|
-
<integer>10</integer>
|
|
121
|
-
</dict>
|
|
122
|
-
</plist>
|
|
123
|
-
CW_PLIST
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
if _launchd_install_if_changed "$cw_label" "$cw_plist" "$cw_plist_content"; then
|
|
127
|
-
print_info "Contribution watch enabled (launchd, hourly scan)"
|
|
128
|
-
else
|
|
129
|
-
print_warning "Failed to load contribution watch LaunchAgent"
|
|
130
|
-
fi
|
|
131
|
-
return 0
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
# Install contribution watch via systemd or cron (Linux).
|
|
135
|
-
# Args: $1=script path, $2=log dir
|
|
136
|
-
_install_cw_linux() {
|
|
137
|
-
local cw_script="$1"
|
|
138
|
-
local _cw_log_dir="$2"
|
|
139
|
-
local cw_systemd="aidevops-contribution-watch"
|
|
140
|
-
_install_scheduler_linux \
|
|
141
|
-
"$cw_systemd" \
|
|
142
|
-
"aidevops: contribution-watch" \
|
|
143
|
-
"$CRON_HOURLY" \
|
|
144
|
-
"\"${cw_script}\" scan" \
|
|
145
|
-
"3600" \
|
|
146
|
-
"${_cw_log_dir}/contribution-watch.log" \
|
|
147
|
-
"" \
|
|
148
|
-
"Contribution watch enabled (hourly scan)" \
|
|
149
|
-
"Failed to install contribution watch scheduler" \
|
|
150
|
-
"false" \
|
|
151
|
-
"true"
|
|
152
|
-
return 0
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
# Setup contribution watch — monitors external issues/PRs for new activity (t1554).
|
|
156
|
-
# Auto-seeds on first run (discovers authored/commented issues/PRs), then installs
|
|
157
|
-
# a launchd/systemd/cron job to scan periodically. Requires gh CLI authenticated.
|
|
158
|
-
# No consent needed — this is passive monitoring (read-only notifications API),
|
|
159
|
-
# not autonomous action. Comment bodies are never processed by LLM in automated context.
|
|
160
|
-
# Respects config: aidevops config set orchestration.contribution_watch false
|
|
161
|
-
setup_contribution_watch() {
|
|
162
|
-
local cw_script="$HOME/.aidevops/agents/scripts/contribution-watch-helper.sh"
|
|
163
|
-
local cw_label="sh.aidevops.contribution-watch"
|
|
164
|
-
local cw_state="$HOME/.aidevops/cache/contribution-watch.json"
|
|
165
|
-
if ! [[ -x "$cw_script" ]] || ! is_feature_enabled orchestration.contribution_watch 2>/dev/null || ! command -v gh &>/dev/null || ! gh auth status &>/dev/null 2>&1; then
|
|
166
|
-
return 0
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
# Resolve and validate log directory
|
|
170
|
-
local _cw_log_dir
|
|
171
|
-
_cw_log_dir=$(_resolve_cw_log_dir) || return 1
|
|
172
|
-
mkdir -p "$HOME/.aidevops/cache" "$_cw_log_dir"
|
|
173
|
-
|
|
174
|
-
# Auto-seed on first run (populates state file with existing contributions)
|
|
175
|
-
if [[ ! -f "$cw_state" ]]; then
|
|
176
|
-
print_info "Discovering external contributions for contribution watch..."
|
|
177
|
-
if bash "$cw_script" seed >/dev/null 2>&1; then
|
|
178
|
-
print_info "Contribution watch seeded (external issues/PRs discovered)"
|
|
179
|
-
else
|
|
180
|
-
print_warning "Contribution watch seed failed (non-fatal, will retry on next run)"
|
|
181
|
-
fi
|
|
182
|
-
fi
|
|
183
|
-
|
|
184
|
-
# Install/update scheduled scanner
|
|
185
|
-
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
186
|
-
_install_cw_launchd "$cw_label" "$cw_script" "$_cw_log_dir"
|
|
187
|
-
else
|
|
188
|
-
_install_cw_linux "$cw_script" "$_cw_log_dir"
|
|
189
|
-
fi
|
|
190
|
-
return 0
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
# Install complexity scan via launchd (macOS).
|
|
194
|
-
# Args: $1=label, $2=script path, $3=log dir
|
|
195
|
-
# (t2903) Extracted from pulse dispatch preflight — independent schedule so
|
|
196
|
-
# the 200-470s scan never starves dispatch or downstream scanners.
|
|
197
|
-
_install_complexity_scan_launchd() {
|
|
198
|
-
local cs_label="$1"
|
|
199
|
-
local cs_script="$2"
|
|
200
|
-
local _cs_log_dir="$3"
|
|
201
|
-
local cs_plist="$HOME/Library/LaunchAgents/${cs_label}.plist"
|
|
202
|
-
|
|
203
|
-
local _xml_cs_script _xml_cs_home _xml_cs_log_dir
|
|
204
|
-
_xml_cs_script=$(_xml_escape "$cs_script")
|
|
205
|
-
_xml_cs_home=$(_xml_escape "$HOME")
|
|
206
|
-
_xml_cs_log_dir=$(_xml_escape "$_cs_log_dir")
|
|
207
|
-
|
|
208
|
-
local cs_plist_content
|
|
209
|
-
cs_plist_content=$(
|
|
210
|
-
cat <<CS_PLIST
|
|
211
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
212
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
213
|
-
<plist version="1.0">
|
|
214
|
-
<dict>
|
|
215
|
-
<key>Label</key>
|
|
216
|
-
<string>${cs_label}</string>
|
|
217
|
-
<key>ProgramArguments</key>
|
|
218
|
-
<array>
|
|
219
|
-
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
220
|
-
<string>${_xml_cs_script}</string>
|
|
221
|
-
<string>run</string>
|
|
222
|
-
</array>
|
|
223
|
-
<key>StartInterval</key>
|
|
224
|
-
<integer>3600</integer>
|
|
225
|
-
<key>StandardOutPath</key>
|
|
226
|
-
<string>${_xml_cs_log_dir}/complexity-scan-runner.log</string>
|
|
227
|
-
<key>StandardErrorPath</key>
|
|
228
|
-
<string>${_xml_cs_log_dir}/complexity-scan-runner.log</string>
|
|
229
|
-
<key>EnvironmentVariables</key>
|
|
230
|
-
<dict>
|
|
231
|
-
<key>PATH</key>
|
|
232
|
-
<string>$(aidevops_launchd_sanitized_path)</string>
|
|
233
|
-
<key>HOME</key>
|
|
234
|
-
<string>${_xml_cs_home}</string>
|
|
235
|
-
</dict>
|
|
236
|
-
<key>RunAtLoad</key>
|
|
237
|
-
<true/>
|
|
238
|
-
<key>KeepAlive</key>
|
|
239
|
-
<false/>
|
|
240
|
-
<key>ProcessType</key>
|
|
241
|
-
<string>Background</string>
|
|
242
|
-
<key>LowPriorityBackgroundIO</key>
|
|
243
|
-
<true/>
|
|
244
|
-
<key>Nice</key>
|
|
245
|
-
<integer>10</integer>
|
|
246
|
-
</dict>
|
|
247
|
-
</plist>
|
|
248
|
-
CS_PLIST
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
if _launchd_install_if_changed "$cs_label" "$cs_plist" "$cs_plist_content"; then
|
|
252
|
-
print_info "Complexity scan enabled (launchd, hourly run)"
|
|
253
|
-
else
|
|
254
|
-
print_warning "Failed to load complexity scan LaunchAgent"
|
|
255
|
-
fi
|
|
256
|
-
return 0
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
# Install complexity scan via systemd or cron (Linux).
|
|
260
|
-
# Args: $1=script path, $2=log dir
|
|
261
|
-
_install_complexity_scan_linux() {
|
|
262
|
-
local cs_script="$1"
|
|
263
|
-
local _cs_log_dir="$2"
|
|
264
|
-
local cs_systemd="aidevops-complexity-scan"
|
|
265
|
-
_install_scheduler_linux \
|
|
266
|
-
"$cs_systemd" \
|
|
267
|
-
"aidevops: complexity-scan" \
|
|
268
|
-
"$CRON_HOURLY" \
|
|
269
|
-
"\"${cs_script}\" run" \
|
|
270
|
-
"3600" \
|
|
271
|
-
"${_cs_log_dir}/complexity-scan-runner.log" \
|
|
272
|
-
"" \
|
|
273
|
-
"Complexity scan enabled (hourly run)" \
|
|
274
|
-
"Failed to install complexity scan scheduler" \
|
|
275
|
-
"true" \
|
|
276
|
-
"true"
|
|
277
|
-
return 0
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
# Setup complexity scan (t2903) — extracts the weekly complexity scan from
|
|
281
|
-
# pulse dispatch preflight into its own launchd/cron schedule. The scan was
|
|
282
|
-
# observed consuming 200-470s per pulse cycle (26%+ of the 1800s pulse stale
|
|
283
|
-
# ceiling), starving downstream scanners. Promoting it to its own schedule
|
|
284
|
-
# decouples it from dispatch entirely. The runner reuses run_weekly_complexity_scan
|
|
285
|
-
# from pulse-simplification.sh, which has internal 15-min cadence gating
|
|
286
|
-
# (COMPLEXITY_SCAN_INTERVAL=900) so hourly launchd ticks are always safe.
|
|
287
|
-
setup_complexity_scan() {
|
|
288
|
-
local cs_script="$HOME/.aidevops/agents/scripts/complexity-scan-runner.sh"
|
|
289
|
-
local cs_label="sh.aidevops.complexity-scan"
|
|
290
|
-
if ! [[ -x "$cs_script" ]]; then
|
|
291
|
-
return 0
|
|
292
|
-
fi
|
|
293
|
-
|
|
294
|
-
# Reuse contribution-watch's log-dir resolver (same logic, same config key).
|
|
295
|
-
local _cs_log_dir
|
|
296
|
-
_cs_log_dir=$(_resolve_cw_log_dir) || return 1
|
|
297
|
-
mkdir -p "$_cs_log_dir"
|
|
298
|
-
|
|
299
|
-
# Install/update scheduled runner
|
|
300
|
-
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
301
|
-
_install_complexity_scan_launchd "$cs_label" "$cs_script" "$_cs_log_dir"
|
|
302
|
-
else
|
|
303
|
-
_install_complexity_scan_linux "$cs_script" "$_cs_log_dir"
|
|
304
|
-
fi
|
|
305
|
-
return 0
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
# Install the dedicated merge-pass launchd plist (macOS).
|
|
309
|
-
# Uses the timeout-protected pulse-merge-routine.sh path (t3378). The older
|
|
310
|
-
# t21247 pulse-wrapper.sh --merge-only path had no hard ceiling; a hung merge
|
|
311
|
-
# pass could leave green PRs unmerged until manual intervention.
|
|
312
|
-
# Supersedes the t2862 sh.aidevops.pulse-merge-routine label while keeping the
|
|
313
|
-
# timeout-protected helper implementation:
|
|
314
|
-
# - Label: com.aidevops.aidevops-supervisor-merge
|
|
315
|
-
# - Script: pulse-merge-routine.sh run
|
|
316
|
-
# - Interval: 60s (down from 120s) — targets <=90s PR-green-to-merged latency
|
|
317
|
-
# - Log: pulse-merge.log (isolated from main pulse log)
|
|
318
|
-
#
|
|
319
|
-
# Args: $1=label $2=wrapper_script $3=log_dir
|
|
320
|
-
_install_pulse_merge_routine_launchd() {
|
|
321
|
-
local pmr_label="$1"
|
|
322
|
-
local pmr_script="$2"
|
|
323
|
-
local _pmr_log_dir="$3"
|
|
324
|
-
local pmr_plist="$HOME/Library/LaunchAgents/${pmr_label}.plist"
|
|
325
|
-
|
|
326
|
-
# One-time migration: unload and remove the legacy sh.aidevops.pulse-merge-routine
|
|
327
|
-
# plist (installed by t2862) to avoid running two merge passes simultaneously.
|
|
328
|
-
local _legacy_pmr_label="sh.aidevops.pulse-merge-routine"
|
|
329
|
-
local _legacy_pmr_plist="$HOME/Library/LaunchAgents/${_legacy_pmr_label}.plist"
|
|
330
|
-
if [[ -f "$_legacy_pmr_plist" ]]; then
|
|
331
|
-
launchctl unload "$_legacy_pmr_plist" 2>/dev/null || true
|
|
332
|
-
rm -f "$_legacy_pmr_plist"
|
|
333
|
-
print_info "Removed legacy pulse-merge-routine plist (superseded by ${pmr_label})"
|
|
334
|
-
fi
|
|
335
|
-
|
|
336
|
-
local _xml_pmr_script _xml_pmr_home _xml_pmr_log_dir _xml_bash_bin
|
|
337
|
-
_xml_pmr_script=$(_xml_escape "$pmr_script")
|
|
338
|
-
_xml_pmr_home=$(_xml_escape "$HOME")
|
|
339
|
-
_xml_pmr_log_dir=$(_xml_escape "$_pmr_log_dir")
|
|
340
|
-
_xml_bash_bin=$(_xml_escape "$(_resolve_modern_bash)")
|
|
341
|
-
|
|
342
|
-
local pmr_plist_content
|
|
343
|
-
pmr_plist_content=$(
|
|
344
|
-
cat <<PMR_PLIST
|
|
345
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
346
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
347
|
-
<plist version="1.0">
|
|
348
|
-
<dict>
|
|
349
|
-
<key>Label</key>
|
|
350
|
-
<string>${pmr_label}</string>
|
|
351
|
-
<key>ProgramArguments</key>
|
|
352
|
-
<array>
|
|
353
|
-
<string>${_xml_bash_bin}</string>
|
|
354
|
-
<string>${_xml_pmr_script}</string>
|
|
355
|
-
<string>run</string>
|
|
356
|
-
</array>
|
|
357
|
-
<key>StartInterval</key>
|
|
358
|
-
<integer>60</integer>
|
|
359
|
-
<key>StandardOutPath</key>
|
|
360
|
-
<string>${_xml_pmr_log_dir}/pulse-merge.log</string>
|
|
361
|
-
<key>StandardErrorPath</key>
|
|
362
|
-
<string>${_xml_pmr_log_dir}/pulse-merge.log</string>
|
|
363
|
-
<key>EnvironmentVariables</key>
|
|
364
|
-
<dict>
|
|
365
|
-
<key>PATH</key>
|
|
366
|
-
<string>$(aidevops_launchd_sanitized_path)</string>
|
|
367
|
-
<key>HOME</key>
|
|
368
|
-
<string>${_xml_pmr_home}</string>
|
|
369
|
-
</dict>
|
|
370
|
-
<key>RunAtLoad</key>
|
|
371
|
-
<false/>
|
|
372
|
-
<key>KeepAlive</key>
|
|
373
|
-
<false/>
|
|
374
|
-
<key>SoftResourceLimits</key>
|
|
375
|
-
<dict>
|
|
376
|
-
<key>NumberOfFiles</key>
|
|
377
|
-
<integer>4096</integer>
|
|
378
|
-
</dict>
|
|
379
|
-
</dict>
|
|
380
|
-
</plist>
|
|
381
|
-
PMR_PLIST
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
if _launchd_install_if_changed "$pmr_label" "$pmr_plist" "$pmr_plist_content"; then
|
|
385
|
-
print_info "Pulse merge pass enabled (launchd, every 60s via pulse-merge-routine.sh)"
|
|
386
|
-
else
|
|
387
|
-
print_warning "Failed to load pulse merge pass LaunchAgent"
|
|
388
|
-
fi
|
|
389
|
-
return 0
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
# Install the dedicated merge-pass scheduler via systemd or cron (Linux).
|
|
393
|
-
# Uses timeout-protected pulse-merge-routine.sh (t3378).
|
|
394
|
-
# - Command: pulse-merge-routine.sh run
|
|
395
|
-
# - Interval: every 60s (cron * * * * *, systemd OnUnitActiveSec=60)
|
|
396
|
-
# - Log: pulse-merge.log
|
|
397
|
-
# Args: $1=wrapper_script $2=log_dir
|
|
398
|
-
_install_pulse_merge_routine_linux() {
|
|
399
|
-
local pmr_script="$1"
|
|
400
|
-
local _pmr_log_dir="$2"
|
|
401
|
-
local pmr_systemd="aidevops-pulse-merge"
|
|
402
|
-
|
|
403
|
-
# One-time migration: remove legacy systemd/cron entry for aidevops-pulse-merge-routine.
|
|
404
|
-
if _systemd_user_available 2>/dev/null; then
|
|
405
|
-
systemctl --user disable --now "aidevops-pulse-merge-routine.timer" 2>/dev/null || true
|
|
406
|
-
rm -f "$HOME/.config/systemd/user/aidevops-pulse-merge-routine.service" 2>/dev/null || true
|
|
407
|
-
rm -f "$HOME/.config/systemd/user/aidevops-pulse-merge-routine.timer" 2>/dev/null || true
|
|
408
|
-
systemctl --user daemon-reload 2>/dev/null || true
|
|
409
|
-
fi
|
|
410
|
-
if crontab -l 2>/dev/null | grep -qF "pulse-merge-routine"; then
|
|
411
|
-
crontab -l 2>/dev/null | grep -v 'aidevops: pulse-merge-routine' | crontab - 2>/dev/null || true
|
|
412
|
-
fi
|
|
413
|
-
|
|
414
|
-
_install_scheduler_linux \
|
|
415
|
-
"$pmr_systemd" \
|
|
416
|
-
"aidevops: pulse-merge-routine" \
|
|
417
|
-
"* * * * *" \
|
|
418
|
-
"\"${pmr_script}\" run" \
|
|
419
|
-
"60" \
|
|
420
|
-
"${_pmr_log_dir}/pulse-merge.log" \
|
|
421
|
-
"" \
|
|
422
|
-
"Pulse merge pass enabled (every 60s via pulse-merge-routine.sh)" \
|
|
423
|
-
"Failed to install pulse merge pass scheduler" \
|
|
424
|
-
"true" \
|
|
425
|
-
"true"
|
|
426
|
-
return 0
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
# Setup the dedicated merge-pass scheduler (t21247, GH#21247).
|
|
430
|
-
#
|
|
431
|
-
# Supersedes t2862/GH#20919's legacy label/120s interval. The current
|
|
432
|
-
# approach runs pulse-merge-routine.sh every 60s so green PRs merge
|
|
433
|
-
# within <=90s of CI completion regardless of dispatch-cycle length (~23 min
|
|
434
|
-
# average). The routine has its own hard timeout and lockdir so a hung merge
|
|
435
|
-
# pass cannot starve the backlog.
|
|
436
|
-
#
|
|
437
|
-
# Requires: pulse-merge-routine.sh must be installed and executable.
|
|
438
|
-
# The in-cycle merge pass in pulse-wrapper.sh is kept as defense-in-depth —
|
|
439
|
-
# it short-circuits when a merge pass ran within the last 60s.
|
|
440
|
-
setup_pulse_merge_routine() {
|
|
441
|
-
local pmr_wrapper="$HOME/.aidevops/agents/scripts/pulse-merge-routine.sh"
|
|
442
|
-
local pmr_label="com.aidevops.aidevops-supervisor-merge"
|
|
443
|
-
if ! [[ -x "$pmr_wrapper" ]]; then
|
|
444
|
-
return 0
|
|
445
|
-
fi
|
|
446
|
-
|
|
447
|
-
# Reuse contribution-watch's log-dir resolver (same logic, same config key).
|
|
448
|
-
local _pmr_log_dir
|
|
449
|
-
_pmr_log_dir=$(_resolve_cw_log_dir) || return 1
|
|
450
|
-
mkdir -p "$_pmr_log_dir"
|
|
451
|
-
|
|
452
|
-
# Install/update scheduled runner
|
|
453
|
-
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
454
|
-
_install_pulse_merge_routine_launchd "$pmr_label" "$pmr_wrapper" "$_pmr_log_dir"
|
|
455
|
-
else
|
|
456
|
-
_install_pulse_merge_routine_linux "$pmr_wrapper" "$_pmr_log_dir"
|
|
457
|
-
fi
|
|
458
|
-
return 0
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
# Setup draft responses — private repo + local draft storage for reviewing
|
|
462
|
-
# AI-drafted replies to external contributions (t1555).
|
|
463
|
-
# Respects config: aidevops config set orchestration.draft_responses false
|
|
464
|
-
setup_draft_responses() {
|
|
465
|
-
local dr_script="$HOME/.aidevops/agents/scripts/draft-response-helper.sh"
|
|
466
|
-
if [[ -x "$dr_script" ]] && is_feature_enabled orchestration.draft_responses 2>/dev/null && is_feature_enabled orchestration.contribution_watch 2>/dev/null && command -v gh &>/dev/null && gh auth status &>/dev/null 2>&1; then
|
|
467
|
-
mkdir -p "$HOME/.aidevops/.agent-workspace/draft-responses"
|
|
468
|
-
if bash "$dr_script" init >/dev/null 2>&1; then
|
|
469
|
-
print_info "Draft responses ready (private repo + local drafts)"
|
|
470
|
-
else
|
|
471
|
-
print_warning "Draft responses repo setup failed (non-fatal, local drafts still work)"
|
|
472
|
-
fi
|
|
473
|
-
fi
|
|
474
|
-
return 0
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
# Setup profile README — auto-create repo and seed README if not already set up.
|
|
478
|
-
# Requires gh CLI authenticated. Creates username/username repo, seeds README
|
|
479
|
-
# with stat markers, registers in repos.json with priority: "profile".
|
|
480
|
-
_profile_readme_ready() {
|
|
481
|
-
local pr_script="$1"
|
|
482
|
-
if ! [[ -x "$pr_script" ]]; then
|
|
483
|
-
return 1
|
|
484
|
-
fi
|
|
485
|
-
if ! command -v gh &>/dev/null; then
|
|
486
|
-
return 1
|
|
487
|
-
fi
|
|
488
|
-
if ! gh auth status &>/dev/null; then
|
|
489
|
-
return 1
|
|
490
|
-
fi
|
|
491
|
-
return 0
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
_run_profile_readme_init() {
|
|
495
|
-
local pr_script="$1"
|
|
496
|
-
print_info "Checking GitHub profile README..."
|
|
497
|
-
if bash "$pr_script" init; then
|
|
498
|
-
print_info "Profile README ready."
|
|
499
|
-
else
|
|
500
|
-
print_warning "Profile README setup failed (non-fatal, skipping)"
|
|
501
|
-
fi
|
|
502
|
-
return 0
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
_install_profile_readme_launchd() {
|
|
506
|
-
local pr_label="$1"
|
|
507
|
-
local pr_script="$2"
|
|
508
|
-
local pr_plist="$HOME/Library/LaunchAgents/${pr_label}.plist"
|
|
509
|
-
local _xml_pr_script _xml_pr_home
|
|
510
|
-
_xml_pr_script=$(_xml_escape "$pr_script")
|
|
511
|
-
_xml_pr_home=$(_xml_escape "$HOME")
|
|
512
|
-
|
|
513
|
-
local pr_plist_content
|
|
514
|
-
pr_plist_content=$(
|
|
515
|
-
cat <<PR_PLIST
|
|
516
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
517
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
518
|
-
<plist version="1.0">
|
|
519
|
-
<dict>
|
|
520
|
-
<key>Label</key>
|
|
521
|
-
<string>${pr_label}</string>
|
|
522
|
-
<key>ProgramArguments</key>
|
|
523
|
-
<array>
|
|
524
|
-
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
525
|
-
<string>${_xml_pr_script}</string>
|
|
526
|
-
<string>update</string>
|
|
527
|
-
</array>
|
|
528
|
-
<key>StartInterval</key>
|
|
529
|
-
<integer>3600</integer>
|
|
530
|
-
<key>StandardOutPath</key>
|
|
531
|
-
<string>${_xml_pr_home}/.aidevops/.agent-workspace/logs/profile-readme-update.log</string>
|
|
532
|
-
<key>StandardErrorPath</key>
|
|
533
|
-
<string>${_xml_pr_home}/.aidevops/.agent-workspace/logs/profile-readme-update.log</string>
|
|
534
|
-
<key>EnvironmentVariables</key>
|
|
535
|
-
<dict>
|
|
536
|
-
<key>PATH</key>
|
|
537
|
-
<string>$(aidevops_launchd_sanitized_path)</string>
|
|
538
|
-
<key>HOME</key>
|
|
539
|
-
<string>${_xml_pr_home}</string>
|
|
540
|
-
</dict>
|
|
541
|
-
<key>RunAtLoad</key>
|
|
542
|
-
<false/>
|
|
543
|
-
<key>KeepAlive</key>
|
|
544
|
-
<false/>
|
|
545
|
-
<key>ProcessType</key>
|
|
546
|
-
<string>Background</string>
|
|
547
|
-
<key>LowPriorityBackgroundIO</key>
|
|
548
|
-
<true/>
|
|
549
|
-
<key>Nice</key>
|
|
550
|
-
<integer>10</integer>
|
|
551
|
-
</dict>
|
|
552
|
-
</plist>
|
|
553
|
-
PR_PLIST
|
|
554
|
-
)
|
|
555
|
-
|
|
556
|
-
if _launchd_install_if_changed "$pr_label" "$pr_plist" "$pr_plist_content"; then
|
|
557
|
-
if _launchd_kickstart_and_recover "$pr_label" "$pr_plist"; then
|
|
558
|
-
print_info "Profile README update kickstart verified"
|
|
559
|
-
else
|
|
560
|
-
print_warning "Profile README update LaunchAgent loaded but kickstart verification failed"
|
|
561
|
-
fi
|
|
562
|
-
print_info "Profile README update enabled (launchd, hourly)"
|
|
563
|
-
else
|
|
564
|
-
print_warning "Failed to load profile README update LaunchAgent"
|
|
565
|
-
fi
|
|
566
|
-
return 0
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
_install_profile_readme_scheduler() {
|
|
570
|
-
local pr_label="$1"
|
|
571
|
-
local pr_systemd="$2"
|
|
572
|
-
local pr_script="$3"
|
|
573
|
-
local pr_log="$4"
|
|
574
|
-
|
|
575
|
-
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
576
|
-
_install_profile_readme_launchd "$pr_label" "$pr_script"
|
|
577
|
-
return 0
|
|
578
|
-
fi
|
|
579
|
-
|
|
580
|
-
_install_scheduler_linux \
|
|
581
|
-
"$pr_systemd" \
|
|
582
|
-
"aidevops: profile-readme-update" \
|
|
583
|
-
"$CRON_HOURLY" \
|
|
584
|
-
"\"${pr_script}\" update" \
|
|
585
|
-
"3600" \
|
|
586
|
-
"$pr_log" \
|
|
587
|
-
"" \
|
|
588
|
-
"Profile README update enabled (hourly)" \
|
|
589
|
-
"Failed to install profile README update scheduler" \
|
|
590
|
-
"false" \
|
|
591
|
-
"true"
|
|
592
|
-
return 0
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
setup_profile_readme() {
|
|
596
|
-
local pr_script="$HOME/.aidevops/agents/scripts/profile-readme-helper.sh"
|
|
597
|
-
local pr_label="sh.aidevops.profile-readme-update"
|
|
598
|
-
if ! _profile_readme_ready "$pr_script"; then
|
|
599
|
-
return 0
|
|
600
|
-
fi
|
|
601
|
-
|
|
602
|
-
# Initialize profile repo if not already set up.
|
|
603
|
-
# Always run init — it's idempotent and handles:
|
|
604
|
-
# - Fresh installs (no profile repo)
|
|
605
|
-
# - Missing markers (injects them into existing README)
|
|
606
|
-
# - Diverged history (repo deleted and recreated on GitHub)
|
|
607
|
-
# - Already-initialized repos (returns early with no changes)
|
|
608
|
-
_run_profile_readme_init "$pr_script"
|
|
609
|
-
|
|
610
|
-
# Profile README auto-update scheduled job.
|
|
611
|
-
# Installed whenever gh CLI is available — the update script self-heals
|
|
612
|
-
# (discovers/creates the profile repo on first run via _resolve_profile_repo).
|
|
613
|
-
# macOS: launchd plist (hourly) | Linux: systemd timer or cron (hourly)
|
|
614
|
-
local pr_systemd="aidevops-profile-readme-update"
|
|
615
|
-
local pr_log="$HOME/.aidevops/.agent-workspace/logs/profile-readme-update.log"
|
|
616
|
-
mkdir -p "$HOME/.aidevops/.agent-workspace/logs"
|
|
617
|
-
|
|
618
|
-
_install_profile_readme_scheduler "$pr_label" "$pr_systemd" "$pr_script" "$pr_log"
|
|
619
|
-
return 0
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
# Detect Windows Git Bash / MINGW64 / MSYS2 environment.
|
|
623
|
-
# WSL reports "Linux" from uname -s and uses the cron path — correct behaviour.
|
|
624
|
-
# Returns 0 (true) on Windows Git Bash/MINGW/MSYS/Cygwin, 1 otherwise.
|
|
625
|
-
_is_windows() {
|
|
626
|
-
case "$(uname -s)" in
|
|
627
|
-
MINGW* | MSYS* | CYGWIN*)
|
|
628
|
-
return 0
|
|
629
|
-
;;
|
|
630
|
-
*)
|
|
631
|
-
return 1
|
|
632
|
-
;;
|
|
633
|
-
esac
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
# Install OAuth token refresh via Windows Task Scheduler (schtasks).
|
|
637
|
-
# Args: $1=tr_script (Unix path), $2=log_dir (Unix path)
|
|
638
|
-
# Runs every 30 minutes, matching macOS launchd and Linux cron behaviour.
|
|
639
|
-
# Uses bash.exe from Git for Windows to execute the shell script.
|
|
640
|
-
_install_token_refresh_schtasks() {
|
|
641
|
-
local tr_script="$1"
|
|
642
|
-
local log_dir="$2"
|
|
643
|
-
local task_name="aidevops-token-refresh"
|
|
644
|
-
|
|
645
|
-
# Resolve bash.exe — Git for Windows ships it alongside git.exe
|
|
646
|
-
local bash_exe
|
|
647
|
-
bash_exe=$(command -v bash.exe 2>/dev/null || command -v bash 2>/dev/null || echo "bash")
|
|
648
|
-
|
|
649
|
-
# Convert Unix paths to Windows paths for schtasks (requires cygpath from Git Bash)
|
|
650
|
-
local tr_script_win log_dir_win bash_exe_win
|
|
651
|
-
if command -v cygpath &>/dev/null; then
|
|
652
|
-
tr_script_win=$(cygpath -w "$tr_script")
|
|
653
|
-
log_dir_win=$(cygpath -w "$log_dir")
|
|
654
|
-
bash_exe_win=$(cygpath -w "$bash_exe")
|
|
655
|
-
else
|
|
656
|
-
# Fallback: manual conversion (replace /c/ with C:\, forward to backslash)
|
|
657
|
-
tr_script_win=$(echo "$tr_script" | sed 's|^/\([a-zA-Z]\)/|\1:\\|; s|/|\\|g')
|
|
658
|
-
log_dir_win=$(echo "$log_dir" | sed 's|^/\([a-zA-Z]\)/|\1:\\|; s|/|\\|g')
|
|
659
|
-
bash_exe_win="bash.exe"
|
|
660
|
-
fi
|
|
661
|
-
|
|
662
|
-
# Remove existing task (idempotent — ignore error if not present)
|
|
663
|
-
schtasks /Delete /TN "$task_name" /F >/dev/null 2>&1 || true
|
|
664
|
-
|
|
665
|
-
# Create scheduled task: every 30 minutes, run at logon, run whether logged on or not
|
|
666
|
-
# /SC MINUTE /MO 30 = every 30 minutes
|
|
667
|
-
# /RL HIGHEST = run with highest available privileges (needed for token writes)
|
|
668
|
-
# /F = force creation (overwrite if exists)
|
|
669
|
-
# The action runs bash.exe with -c to chain both refresh calls
|
|
670
|
-
local action_cmd
|
|
671
|
-
action_cmd="\"${bash_exe_win}\" -c \"'${tr_script_win}' refresh anthropic >> '${log_dir_win}\\token-refresh.log' 2>&1; '${tr_script_win}' refresh openai >> '${log_dir_win}\\token-refresh.log' 2>&1\""
|
|
672
|
-
|
|
673
|
-
if schtasks /Create \
|
|
674
|
-
/TN "$task_name" \
|
|
675
|
-
/TR "$action_cmd" \
|
|
676
|
-
/SC MINUTE \
|
|
677
|
-
/MO 30 \
|
|
678
|
-
/RL HIGHEST \
|
|
679
|
-
/F \
|
|
680
|
-
>/dev/null 2>&1; then
|
|
681
|
-
print_info "OAuth token refresh enabled (schtasks, every 30 min)"
|
|
682
|
-
# Run immediately to refresh any expired tokens
|
|
683
|
-
schtasks /Run /TN "$task_name" >/dev/null 2>&1 || true
|
|
684
|
-
else
|
|
685
|
-
print_warning "Failed to create token refresh scheduled task. Run manually: schtasks /Create /TN aidevops-token-refresh /TR \"bash '${tr_script_win}' refresh anthropic\" /SC MINUTE /MO 30"
|
|
686
|
-
fi
|
|
687
|
-
return 0
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
# Remove OAuth token refresh Windows scheduled task (uninstall path).
|
|
691
|
-
_uninstall_token_refresh_schtasks() {
|
|
692
|
-
local task_name="aidevops-token-refresh"
|
|
693
|
-
if schtasks /Query /TN "$task_name" >/dev/null 2>&1; then
|
|
694
|
-
schtasks /Delete /TN "$task_name" /F >/dev/null 2>&1 || true
|
|
695
|
-
print_info "OAuth token refresh disabled (schtasks task removed)"
|
|
696
|
-
fi
|
|
697
|
-
return 0
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
# Setup OAuth token refresh scheduled job.
|
|
701
|
-
# Refreshes expired/expiring tokens every 30 min so sessions never hit
|
|
702
|
-
# "invalid x-api-key". Also runs at load to catch tokens that expired
|
|
703
|
-
# while the machine was off.
|
|
704
|
-
# macOS: launchd plist | Linux/WSL: systemd timer or cron | Windows Git Bash: schtasks
|
|
705
|
-
_oauth_token_refresh_ready() {
|
|
706
|
-
local tr_script="$1"
|
|
707
|
-
if ! [[ -x "$tr_script" ]]; then
|
|
708
|
-
return 1
|
|
709
|
-
fi
|
|
710
|
-
if ! [[ -f "$HOME/.aidevops/oauth-pool.json" ]]; then
|
|
711
|
-
return 1
|
|
712
|
-
fi
|
|
713
|
-
return 0
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
_install_token_refresh_launchd() {
|
|
717
|
-
local tr_label="$1"
|
|
718
|
-
local tr_script="$2"
|
|
719
|
-
local tr_plist="$HOME/Library/LaunchAgents/${tr_label}.plist"
|
|
720
|
-
local _xml_tr_script _xml_tr_home
|
|
721
|
-
_xml_tr_script=$(_xml_escape "$tr_script")
|
|
722
|
-
_xml_tr_home=$(_xml_escape "$HOME")
|
|
723
|
-
|
|
724
|
-
local tr_plist_content
|
|
725
|
-
tr_plist_content=$(
|
|
726
|
-
cat <<TR_PLIST
|
|
727
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
728
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
729
|
-
<plist version="1.0">
|
|
730
|
-
<dict>
|
|
731
|
-
<key>Label</key>
|
|
732
|
-
<string>${tr_label}</string>
|
|
733
|
-
<key>ProgramArguments</key>
|
|
734
|
-
<array>
|
|
735
|
-
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
736
|
-
<string>-c</string>
|
|
737
|
-
<string>"${_xml_tr_script}" refresh anthropic; "${_xml_tr_script}" refresh openai</string>
|
|
738
|
-
</array>
|
|
739
|
-
<key>StartInterval</key>
|
|
740
|
-
<integer>1800</integer>
|
|
741
|
-
<key>StandardOutPath</key>
|
|
742
|
-
<string>${_xml_tr_home}/.aidevops/.agent-workspace/logs/token-refresh.log</string>
|
|
743
|
-
<key>StandardErrorPath</key>
|
|
744
|
-
<string>${_xml_tr_home}/.aidevops/.agent-workspace/logs/token-refresh.log</string>
|
|
745
|
-
<key>EnvironmentVariables</key>
|
|
746
|
-
<dict>
|
|
747
|
-
<key>PATH</key>
|
|
748
|
-
<string>$(aidevops_launchd_sanitized_path)</string>
|
|
749
|
-
<key>HOME</key>
|
|
750
|
-
<string>${_xml_tr_home}</string>
|
|
751
|
-
</dict>
|
|
752
|
-
<key>RunAtLoad</key>
|
|
753
|
-
<true/>
|
|
754
|
-
<key>KeepAlive</key>
|
|
755
|
-
<false/>
|
|
756
|
-
<key>ProcessType</key>
|
|
757
|
-
<string>Background</string>
|
|
758
|
-
<key>LowPriorityBackgroundIO</key>
|
|
759
|
-
<true/>
|
|
760
|
-
<key>Nice</key>
|
|
761
|
-
<integer>10</integer>
|
|
762
|
-
</dict>
|
|
763
|
-
</plist>
|
|
764
|
-
TR_PLIST
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
if _launchd_install_if_changed "$tr_label" "$tr_plist" "$tr_plist_content"; then
|
|
768
|
-
print_info "OAuth token refresh enabled (launchd, every 30 min)"
|
|
769
|
-
else
|
|
770
|
-
print_warning "Failed to load token refresh LaunchAgent"
|
|
771
|
-
fi
|
|
772
|
-
return 0
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
setup_oauth_token_refresh() {
|
|
776
|
-
local tr_script="$HOME/.aidevops/agents/scripts/oauth-pool-helper.sh"
|
|
777
|
-
local tr_label="sh.aidevops.token-refresh"
|
|
778
|
-
if ! _oauth_token_refresh_ready "$tr_script"; then
|
|
779
|
-
return 0
|
|
780
|
-
fi
|
|
781
|
-
|
|
782
|
-
local tr_log_dir="$HOME/.aidevops/.agent-workspace/logs"
|
|
783
|
-
mkdir -p "$tr_log_dir"
|
|
784
|
-
|
|
785
|
-
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
786
|
-
_install_token_refresh_launchd "$tr_label" "$tr_script"
|
|
787
|
-
elif _is_windows; then
|
|
788
|
-
# Windows Git Bash / MINGW64 / MSYS2: use Task Scheduler (schtasks)
|
|
789
|
-
_install_token_refresh_schtasks "$tr_script" "$tr_log_dir"
|
|
790
|
-
else
|
|
791
|
-
# Linux / WSL without systemd: systemd timer or cron fallback
|
|
792
|
-
_install_scheduler_linux \
|
|
793
|
-
"aidevops-token-refresh" \
|
|
794
|
-
"aidevops: token-refresh" \
|
|
795
|
-
"*/30 * * * *" \
|
|
796
|
-
"\"${tr_script}\" refresh anthropic; \"${tr_script}\" refresh openai" \
|
|
797
|
-
"1800" \
|
|
798
|
-
"${tr_log_dir}/token-refresh.log" \
|
|
799
|
-
"" \
|
|
800
|
-
"OAuth token refresh enabled (every 30 min)" \
|
|
801
|
-
"Failed to install token refresh scheduler" \
|
|
802
|
-
"true" \
|
|
803
|
-
"true"
|
|
804
|
-
fi
|
|
805
|
-
return 0
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
# Setup opencode DB maintenance scheduler (r913, t2183).
|
|
809
|
-
# Runs weekly (Sun 04:00 local by default) to checkpoint/optimize/vacuum opencode.db.
|
|
810
|
-
# The helper self-noops on missing DB, so installing unconditionally is safe —
|
|
811
|
-
# a non-opencode machine wakes up weekly, sees no DB, exits 0 silently.
|
|
812
|
-
#
|
|
813
|
-
# Platform split (mirrors the pattern for token-refresh):
|
|
814
|
-
# macOS — helper owns its plist generation via cmd_install (Approach B).
|
|
815
|
-
# Linux — _install_scheduler_linux with cron `0 4 * * 0` + systemd
|
|
816
|
-
# OnCalendar `Sun *-*-* 04:00:00` for accurate wall-clock firing.
|
|
817
|
-
# Windows — TODO(t2183-followup): opencode on Windows is rare and the
|
|
818
|
-
# helper self-noops on missing DB, so leaving unscheduled is
|
|
819
|
-
# low-risk for this iteration.
|
|
820
|
-
setup_opencode_db_maintenance() {
|
|
821
|
-
local ocdbm_script="$HOME/.aidevops/agents/scripts/opencode-db-maintenance-helper.sh"
|
|
822
|
-
if ! [[ -x "$ocdbm_script" ]]; then
|
|
823
|
-
return 0
|
|
824
|
-
fi
|
|
825
|
-
|
|
826
|
-
local ocdbm_log_dir="$HOME/.aidevops/.agent-workspace/logs"
|
|
827
|
-
local ocdbm_hour="${OPENCODE_DB_MAINTENANCE_HOUR:-4}"
|
|
828
|
-
local ocdbm_minute="${OPENCODE_DB_MAINTENANCE_MINUTE:-0}"
|
|
829
|
-
local ocdbm_mode="${OPENCODE_DB_MAINTENANCE_MODE:-auto}"
|
|
830
|
-
local ocdbm_command="\"${ocdbm_script}\" auto"
|
|
831
|
-
if [[ "$ocdbm_mode" == "maintenance-window" ]]; then
|
|
832
|
-
ocdbm_command="\"${ocdbm_script}\" maintenance-window --force-opencode"
|
|
833
|
-
fi
|
|
834
|
-
mkdir -p "$ocdbm_log_dir"
|
|
835
|
-
|
|
836
|
-
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
837
|
-
# Helper owns its own plist generation (Approach B, like repo-sync).
|
|
838
|
-
# Quiet the helper's multi-line output and emit one consolidated line
|
|
839
|
-
# to match the style of setup_profile_readme / setup_oauth_token_refresh.
|
|
840
|
-
if OPENCODE_DB_MAINTENANCE_HOUR="$ocdbm_hour" OPENCODE_DB_MAINTENANCE_MINUTE="$ocdbm_minute" OPENCODE_DB_MAINTENANCE_MODE="$ocdbm_mode" bash "$ocdbm_script" install >/dev/null 2>&1; then
|
|
841
|
-
print_info "OpenCode DB maintenance enabled (launchd, weekly Sun ${ocdbm_hour}:${ocdbm_minute})"
|
|
842
|
-
else
|
|
843
|
-
print_warning "Failed to install opencode DB maintenance LaunchAgent"
|
|
844
|
-
fi
|
|
845
|
-
elif _is_windows; then
|
|
846
|
-
# Windows scheduling deferred — helper self-noops on missing DB so
|
|
847
|
-
# the cost of leaving unscheduled is ~0 until opencode lands on
|
|
848
|
-
# Windows in quantity.
|
|
849
|
-
return 0
|
|
850
|
-
else
|
|
851
|
-
# Linux / WSL: prefer systemd user timer, fall back to cron.
|
|
852
|
-
# Weekly Sunday local time — cron: `${minute} ${hour} * * 0`; systemd OnCalendar
|
|
853
|
-
# ensures wall-clock firing even across suspends/reboots.
|
|
854
|
-
_install_scheduler_linux \
|
|
855
|
-
"aidevops-opencode-db-maintenance" \
|
|
856
|
-
"aidevops: opencode-db-maintenance" \
|
|
857
|
-
"${ocdbm_minute} ${ocdbm_hour} * * 0" \
|
|
858
|
-
"${ocdbm_command}" \
|
|
859
|
-
"604800" \
|
|
860
|
-
"${ocdbm_log_dir}/opencode-db-maintenance.log" \
|
|
861
|
-
"" \
|
|
862
|
-
"OpenCode DB maintenance enabled (weekly Sun ${ocdbm_hour}:${ocdbm_minute})" \
|
|
863
|
-
"Failed to install opencode DB maintenance scheduler" \
|
|
864
|
-
"false" \
|
|
865
|
-
"true" \
|
|
866
|
-
"Sun *-*-* ${ocdbm_hour}:${ocdbm_minute}:00"
|
|
867
|
-
fi
|
|
868
|
-
return 0
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
# Setup repo-sync scheduler if not already installed.
|
|
872
|
-
# Keeps local git repos up to date with daily ff-only pulls.
|
|
873
|
-
# Respects config: aidevops config set orchestration.repo_sync false
|
|
874
|
-
setup_repo_sync() {
|
|
875
|
-
local repo_sync_script="$HOME/.aidevops/agents/scripts/repo-sync-helper.sh"
|
|
876
|
-
if ! [[ -x "$repo_sync_script" ]] || ! is_feature_enabled repo_sync 2>/dev/null; then
|
|
877
|
-
return 0
|
|
878
|
-
fi
|
|
879
|
-
|
|
880
|
-
local _repo_sync_installed=false
|
|
881
|
-
if _launchd_has_agent "com.aidevops.aidevops-repo-sync"; then
|
|
882
|
-
_repo_sync_installed=true
|
|
883
|
-
elif _launchd_has_agent "sh.aidevops.repo-sync"; then
|
|
884
|
-
_repo_sync_installed=true
|
|
885
|
-
elif crontab -l 2>/dev/null | grep -qF "aidevops-repo-sync"; then
|
|
886
|
-
_repo_sync_installed=true
|
|
887
|
-
elif command -v systemctl >/dev/null 2>&1 &&
|
|
888
|
-
systemctl --user is-enabled "aidevops-repo-sync.timer" >/dev/null 2>&1; then
|
|
889
|
-
_repo_sync_installed=true
|
|
890
|
-
fi
|
|
891
|
-
if [[ "$_repo_sync_installed" == "false" ]]; then
|
|
892
|
-
if [[ "$NON_INTERACTIVE" == "true" ]]; then
|
|
893
|
-
bash "$repo_sync_script" enable >/dev/null 2>&1 || true
|
|
894
|
-
print_info "Repo sync enabled (daily). Disable: aidevops repo-sync disable"
|
|
895
|
-
else
|
|
896
|
-
echo ""
|
|
897
|
-
echo "Repo sync keeps your local git repos up to date by running"
|
|
898
|
-
echo "git pull --ff-only daily on clean repos on their default branch."
|
|
899
|
-
echo ""
|
|
900
|
-
setup_prompt enable_repo_sync "Enable daily repo sync? [Y/n]: " "Y"
|
|
901
|
-
if [[ "$enable_repo_sync" =~ ^[Yy]?$ || -z "$enable_repo_sync" ]]; then
|
|
902
|
-
bash "$repo_sync_script" enable
|
|
903
|
-
else
|
|
904
|
-
print_info "Skipped. Enable later: aidevops repo-sync enable"
|
|
905
|
-
fi
|
|
906
|
-
fi
|
|
907
|
-
fi
|
|
908
|
-
return 0
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
# Setup r914 repo-aidevops-health scheduler if not already installed.
|
|
912
|
-
# Daily drift keeper for repos.json: bumps stale .aidevops.json versions
|
|
913
|
-
# and surfaces missing-folder / no-init drift for human triage.
|
|
914
|
-
# Respects config: aidevops config set orchestration.repo_aidevops_health false
|
|
915
|
-
setup_repo_aidevops_health() {
|
|
916
|
-
local repo_health_script="$HOME/.aidevops/agents/scripts/repo-aidevops-health-helper.sh"
|
|
917
|
-
if ! [[ -x "$repo_health_script" ]] || ! is_feature_enabled repo_aidevops_health 2>/dev/null; then
|
|
918
|
-
return 0
|
|
919
|
-
fi
|
|
920
|
-
|
|
921
|
-
local _repo_health_installed=false
|
|
922
|
-
if _launchd_has_agent "sh.aidevops.repo-aidevops-health"; then
|
|
923
|
-
_repo_health_installed=true
|
|
924
|
-
elif crontab -l 2>/dev/null | grep -qF "aidevops-repo-aidevops-health"; then
|
|
925
|
-
_repo_health_installed=true
|
|
926
|
-
elif command -v systemctl >/dev/null 2>&1 &&
|
|
927
|
-
systemctl --user is-enabled "aidevops-repo-aidevops-health.timer" >/dev/null 2>&1; then
|
|
928
|
-
_repo_health_installed=true
|
|
929
|
-
fi
|
|
930
|
-
if [[ "$_repo_health_installed" == "false" ]]; then
|
|
931
|
-
if [[ "$NON_INTERACTIVE" == "true" ]]; then
|
|
932
|
-
bash "$repo_health_script" enable >/dev/null 2>&1 || true
|
|
933
|
-
print_info "r914 repo-aidevops-health enabled (daily @03:30). Disable: aidevops repo-aidevops-health disable"
|
|
934
|
-
else
|
|
935
|
-
echo ""
|
|
936
|
-
echo "r914 keeps \`.aidevops.json\` versions current across all registered"
|
|
937
|
-
echo "repos and surfaces registry drift (missing folders, unregistered git"
|
|
938
|
-
echo "repos) for human triage. Runs daily at 03:30."
|
|
939
|
-
echo ""
|
|
940
|
-
setup_prompt enable_repo_health "Enable daily r914 repo-aidevops-health? [Y/n]: " "Y"
|
|
941
|
-
if [[ "$enable_repo_health" =~ ^[Yy]?$ || -z "$enable_repo_health" ]]; then
|
|
942
|
-
bash "$repo_health_script" enable
|
|
943
|
-
else
|
|
944
|
-
print_info "Skipped. Enable later: aidevops repo-aidevops-health enable"
|
|
945
|
-
fi
|
|
946
|
-
fi
|
|
947
|
-
fi
|
|
948
|
-
return 0
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
# ============================================================================
|
|
952
|
-
# Peer productivity monitor (t2932)
|
|
953
|
-
# ============================================================================
|
|
954
|
-
#
|
|
955
|
-
# Adaptive cross-runner dispatch coordination: observes peer GitHub activity
|
|
956
|
-
# every 30 min and updates ~/.config/aidevops/dispatch-override.conf to
|
|
957
|
-
# `ignore` peers whose pulse is broken (claims issues but never PRs) and
|
|
958
|
-
# back to `honour` when they recover. Self-healing across the ecosystem —
|
|
959
|
-
# each runner observes peers independently, no central coordinator needed.
|
|
960
|
-
# Manual entries in dispatch-override.conf above the auto-managed marker
|
|
961
|
-
# always take precedence.
|
|
962
|
-
|
|
963
|
-
# Install peer-productivity-monitor launchd plist (macOS).
|
|
964
|
-
# Args: $1=label $2=script $3=log_dir
|
|
965
|
-
_install_peer_productivity_monitor_launchd() {
|
|
966
|
-
local ppm_label="$1"
|
|
967
|
-
local ppm_script="$2"
|
|
968
|
-
local _ppm_log_dir="$3"
|
|
969
|
-
local ppm_plist="$HOME/Library/LaunchAgents/${ppm_label}.plist"
|
|
970
|
-
|
|
971
|
-
local _xml_ppm_script _xml_ppm_home _xml_ppm_log_dir
|
|
972
|
-
_xml_ppm_script=$(_xml_escape "$ppm_script")
|
|
973
|
-
_xml_ppm_home=$(_xml_escape "$HOME")
|
|
974
|
-
_xml_ppm_log_dir=$(_xml_escape "$_ppm_log_dir")
|
|
975
|
-
|
|
976
|
-
local ppm_plist_content
|
|
977
|
-
ppm_plist_content=$(
|
|
978
|
-
cat <<PPM_PLIST
|
|
979
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
980
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
981
|
-
<plist version="1.0">
|
|
982
|
-
<dict>
|
|
983
|
-
<key>Label</key>
|
|
984
|
-
<string>${ppm_label}</string>
|
|
985
|
-
<key>ProgramArguments</key>
|
|
986
|
-
<array>
|
|
987
|
-
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
988
|
-
<string>${_xml_ppm_script}</string>
|
|
989
|
-
<string>observe</string>
|
|
990
|
-
</array>
|
|
991
|
-
<key>StartInterval</key>
|
|
992
|
-
<integer>1800</integer>
|
|
993
|
-
<key>StandardOutPath</key>
|
|
994
|
-
<string>${_xml_ppm_log_dir}/peer-productivity-launchd.log</string>
|
|
995
|
-
<key>StandardErrorPath</key>
|
|
996
|
-
<string>${_xml_ppm_log_dir}/peer-productivity-launchd.log</string>
|
|
997
|
-
<key>EnvironmentVariables</key>
|
|
998
|
-
<dict>
|
|
999
|
-
<key>PATH</key>
|
|
1000
|
-
<string>$(aidevops_launchd_sanitized_path)</string>
|
|
1001
|
-
<key>HOME</key>
|
|
1002
|
-
<string>${_xml_ppm_home}</string>
|
|
1003
|
-
</dict>
|
|
1004
|
-
<key>RunAtLoad</key>
|
|
1005
|
-
<true/>
|
|
1006
|
-
<key>KeepAlive</key>
|
|
1007
|
-
<false/>
|
|
1008
|
-
<key>ProcessType</key>
|
|
1009
|
-
<string>Background</string>
|
|
1010
|
-
<key>LowPriorityBackgroundIO</key>
|
|
1011
|
-
<true/>
|
|
1012
|
-
<key>Nice</key>
|
|
1013
|
-
<integer>10</integer>
|
|
1014
|
-
</dict>
|
|
1015
|
-
</plist>
|
|
1016
|
-
PPM_PLIST
|
|
1017
|
-
)
|
|
1018
|
-
|
|
1019
|
-
if _launchd_install_if_changed "$ppm_label" "$ppm_plist" "$ppm_plist_content"; then
|
|
1020
|
-
print_info "Peer productivity monitor enabled (launchd, every 30 min)"
|
|
1021
|
-
else
|
|
1022
|
-
print_warning "Failed to load peer-productivity-monitor LaunchAgent"
|
|
1023
|
-
fi
|
|
1024
|
-
return 0
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
# Install peer-productivity-monitor via systemd or cron (Linux).
|
|
1028
|
-
# Args: $1=script path, $2=log dir
|
|
1029
|
-
_install_peer_productivity_monitor_linux() {
|
|
1030
|
-
local ppm_script="$1"
|
|
1031
|
-
local _ppm_log_dir="$2"
|
|
1032
|
-
local ppm_systemd="aidevops-peer-productivity-monitor"
|
|
1033
|
-
_install_scheduler_linux \
|
|
1034
|
-
"$ppm_systemd" \
|
|
1035
|
-
"aidevops: peer-productivity-monitor" \
|
|
1036
|
-
"*/30 * * * *" \
|
|
1037
|
-
"\"${ppm_script}\" observe" \
|
|
1038
|
-
"1800" \
|
|
1039
|
-
"${_ppm_log_dir}/peer-productivity-launchd.log" \
|
|
1040
|
-
"" \
|
|
1041
|
-
"Peer productivity monitor enabled (every 30 min)" \
|
|
1042
|
-
"Failed to install peer-productivity-monitor scheduler" \
|
|
1043
|
-
"true" \
|
|
1044
|
-
"true"
|
|
1045
|
-
return 0
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
# Setup peer-productivity-monitor (t2932) — observes peer GitHub activity
|
|
1049
|
-
# every 30 min and updates ~/.config/aidevops/dispatch-override.conf so the
|
|
1050
|
-
# local pulse competes with broken peers and collaborates with healthy ones.
|
|
1051
|
-
# Manual entries in dispatch-override.conf above the auto-managed marker
|
|
1052
|
-
# always take precedence.
|
|
1053
|
-
setup_peer_productivity_monitor() {
|
|
1054
|
-
local ppm_script="$HOME/.aidevops/agents/scripts/peer-productivity-monitor.sh"
|
|
1055
|
-
local ppm_label="sh.aidevops.peer-productivity-monitor"
|
|
1056
|
-
if ! [[ -x "$ppm_script" ]]; then
|
|
1057
|
-
return 0
|
|
1058
|
-
fi
|
|
1059
|
-
|
|
1060
|
-
# Reuse contribution-watch's log-dir resolver (same logic, same config key).
|
|
1061
|
-
local _ppm_log_dir
|
|
1062
|
-
_ppm_log_dir=$(_resolve_cw_log_dir) || return 1
|
|
1063
|
-
mkdir -p "$_ppm_log_dir"
|
|
1064
|
-
|
|
1065
|
-
# Install/update scheduled runner
|
|
1066
|
-
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
1067
|
-
_install_peer_productivity_monitor_launchd "$ppm_label" "$ppm_script" "$_ppm_log_dir"
|
|
1068
|
-
else
|
|
1069
|
-
_install_peer_productivity_monitor_linux "$ppm_script" "$_ppm_log_dir"
|
|
1070
|
-
fi
|
|
1071
|
-
return 0
|
|
1072
|
-
}
|