aidevops 3.8.94 → 3.8.96
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/VERSION +1 -1
- package/aidevops.sh +5 -1
- package/package.json +1 -1
- package/setup-modules/schedulers.sh +137 -14
- package/setup-modules/tool-install.sh +38 -0
- package/setup.sh +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.8.
|
|
1
|
+
3.8.96
|
package/aidevops.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# AI DevOps Framework CLI
|
|
6
6
|
# Usage: aidevops <command> [options]
|
|
7
7
|
#
|
|
8
|
-
# Version: 3.8.
|
|
8
|
+
# Version: 3.8.96
|
|
9
9
|
|
|
10
10
|
set -euo pipefail
|
|
11
11
|
|
|
@@ -1384,10 +1384,12 @@ _help_commands() {
|
|
|
1384
1384
|
echo " security [cmd] Full security assessment (posture + hygiene + supply chain)"
|
|
1385
1385
|
echo " contributions External contributions inbox (bare: status | seed/scan/stop/restart/install/uninstall)"
|
|
1386
1386
|
echo " ip-check <cmd> IP reputation checks (check/batch/report/providers)"
|
|
1387
|
+
echo " review-gate <cmd> Configure review_gate.rate_limit_behavior (list/set/unset)"
|
|
1387
1388
|
echo " secret <cmd> Manage secrets (set/list/run/init/import/status)"
|
|
1388
1389
|
echo " config <cmd> Feature toggles (list/get/set/reset/path/help)"
|
|
1389
1390
|
echo " stats <cmd> LLM usage analytics (summary/models/projects/costs/trend)"
|
|
1390
1391
|
echo " tabby <cmd> Manage Tabby terminal profiles (sync/status/zshrc/help)"
|
|
1392
|
+
echo " parent-status <N> Show decomposition state of parent-task issue #N (alias: ps)"
|
|
1391
1393
|
echo " detect Find and register aidevops projects"
|
|
1392
1394
|
echo " uninstall Remove aidevops from your system"
|
|
1393
1395
|
echo " version Show version information"
|
|
@@ -1748,6 +1750,7 @@ main() {
|
|
|
1748
1750
|
model-accounts-pool | map) _dispatch_helper "oauth-pool-helper.sh" "oauth-pool-helper.sh" "$@" ;;
|
|
1749
1751
|
client-format) _cmd_client_format "$@" ;;
|
|
1750
1752
|
opencode-sandbox | oc-sandbox) _dispatch_helper "opencode-sandbox-helper.sh" "opencode-sandbox-helper.sh" "$@" ;;
|
|
1753
|
+
review-gate | review_gate) _dispatch_helper "review-gate-config-helper.sh" "review-gate-config-helper.sh" "$@" ;;
|
|
1751
1754
|
secret | secrets) _dispatch_helper "secret-helper.sh" "secret-helper.sh" "$@" ;;
|
|
1752
1755
|
approve) _dispatch_helper "approval-helper.sh" "approval-helper.sh" "$@" ;;
|
|
1753
1756
|
signing) _dispatch_helper "signing-setup.sh" "signing-setup.sh" "$@" ;;
|
|
@@ -1760,6 +1763,7 @@ main() {
|
|
|
1760
1763
|
stats | observability) _dispatch_helper "observability-helper.sh" "observability-helper.sh" "$@" ;;
|
|
1761
1764
|
tabby) _dispatch_helper "tabby-helper.sh" "tabby-helper.sh" "$@" ;;
|
|
1762
1765
|
init-routines) _dispatch_helper "init-routines-helper.sh" "init-routines-helper.sh" "$@" ;;
|
|
1766
|
+
parent-status | ps) _dispatch_helper "parent-status-helper.sh" "parent-status-helper.sh" "$@" ;;
|
|
1763
1767
|
config | configure) _dispatch_config "$@" ;;
|
|
1764
1768
|
uninstall | remove) cmd_uninstall ;;
|
|
1765
1769
|
version | v | -v | --version) cmd_version ;;
|
package/package.json
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
# Keep pulse workers alive long enough for opus-tier dispatches.
|
|
10
10
|
PULSE_STALE_THRESHOLD_SECONDS=1800
|
|
11
11
|
|
|
12
|
+
# Cron expression: top of every hour. Shared by stats-wrapper,
|
|
13
|
+
# contribution-watch, and profile-readme schedulers — keep DRY so a
|
|
14
|
+
# future cadence shift only touches one place.
|
|
15
|
+
CRON_HOURLY="0 * * * *"
|
|
16
|
+
|
|
12
17
|
# Resolve the modern bash binary path for use in launchd ProgramArguments.
|
|
13
18
|
# Launchd bypasses the shebang when ProgramArguments specifies an explicit
|
|
14
19
|
# interpreter, so we must resolve the path at plist generation time.
|
|
@@ -263,12 +268,14 @@ PULSE_STALE_THRESHOLD=${PULSE_STALE_THRESHOLD_SECONDS}"
|
|
|
263
268
|
}
|
|
264
269
|
|
|
265
270
|
# Read supervisor.pulse_interval_seconds from settings.json.
|
|
266
|
-
# Falls back to
|
|
271
|
+
# Falls back to 180 if the file is missing, the key is absent, or jq is unavailable.
|
|
267
272
|
# Clamps to the validated range [30, 3600].
|
|
268
273
|
# GH#18018: previously this was hardcoded as "120" in _install_supervisor_pulse.
|
|
274
|
+
# t2744: default raised 120 → 180 to reduce GraphQL pressure (33% fewer cycles)
|
|
275
|
+
# on multi-repo setups where per-cycle cost chronically exceeds 5000/hr.
|
|
269
276
|
_read_pulse_interval_seconds() {
|
|
270
277
|
local _settings_file="$HOME/.config/aidevops/settings.json"
|
|
271
|
-
local _interval=
|
|
278
|
+
local _interval=180
|
|
272
279
|
|
|
273
280
|
if command -v jq >/dev/null 2>&1 && [[ -f "$_settings_file" ]]; then
|
|
274
281
|
local _raw
|
|
@@ -492,9 +499,100 @@ _build_pulse_headless_env_xml() {
|
|
|
492
499
|
return 0
|
|
493
500
|
}
|
|
494
501
|
|
|
502
|
+
# Read user-owned plist env override file and emit XML key/string pairs
|
|
503
|
+
# for the matching label's env vars. Keys prefixed with _ are skipped
|
|
504
|
+
# (used as comments in the JSON template).
|
|
505
|
+
#
|
|
506
|
+
# Args: $1=plist_label (e.g. "com.aidevops.aidevops-supervisor-pulse")
|
|
507
|
+
# $2=override_file (absolute path; default ~/.agents/configs/plist-env-overrides.json)
|
|
508
|
+
# $3=indent (string to prepend each line; default "\t\t")
|
|
509
|
+
#
|
|
510
|
+
# Returns 0 on success (including empty result when label not found).
|
|
511
|
+
# Prints WARN to stderr and returns 0 when file is present but malformed.
|
|
512
|
+
# Emits nothing when file is absent.
|
|
513
|
+
_build_plist_env_overrides_xml() {
|
|
514
|
+
local _label="$1"
|
|
515
|
+
local _override_file="${2:-$HOME/.aidevops/agents/configs/plist-env-overrides.json}"
|
|
516
|
+
local _indent="${3:- }"
|
|
517
|
+
|
|
518
|
+
# Missing file is the normal case (user has not created the override file yet)
|
|
519
|
+
[[ -f "$_override_file" ]] || return 0
|
|
520
|
+
|
|
521
|
+
# Require jq — without it we cannot parse JSON safely
|
|
522
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
523
|
+
echo "[schedulers] WARN: jq not found; skipping plist-env-overrides.json injection" >&2
|
|
524
|
+
return 0
|
|
525
|
+
fi
|
|
526
|
+
|
|
527
|
+
# Validate JSON
|
|
528
|
+
if ! jq empty "$_override_file" 2>/dev/null; then
|
|
529
|
+
echo "[schedulers] WARN: plist-env-overrides.json is malformed; skipping injection (file: $_override_file)" >&2
|
|
530
|
+
return 0
|
|
531
|
+
fi
|
|
532
|
+
|
|
533
|
+
# Extract key=value pairs for the matching label; skip _ prefixed keys
|
|
534
|
+
local _pairs
|
|
535
|
+
_pairs=$(jq -r --arg label "$_label" '
|
|
536
|
+
.[$label] // {} |
|
|
537
|
+
to_entries[] |
|
|
538
|
+
select(.key | startswith("_") | not) |
|
|
539
|
+
"\(.key)=\(.value)"
|
|
540
|
+
' "$_override_file" 2>/dev/null) || return 0
|
|
541
|
+
|
|
542
|
+
[[ -z "$_pairs" ]] && return 0
|
|
543
|
+
|
|
544
|
+
local _line _key _val _xml_key _xml_val
|
|
545
|
+
while IFS= read -r _line; do
|
|
546
|
+
[[ -z "$_line" ]] && continue
|
|
547
|
+
_key="${_line%%=*}"
|
|
548
|
+
_val="${_line#*=}"
|
|
549
|
+
_xml_key=$(_xml_escape "$_key")
|
|
550
|
+
_xml_val=$(_xml_escape "$_val")
|
|
551
|
+
printf '%s<key>%s</key>\n%s<string>%s</string>\n' \
|
|
552
|
+
"$_indent" "$_xml_key" "$_indent" "$_xml_val"
|
|
553
|
+
done <<<"$_pairs"
|
|
554
|
+
|
|
555
|
+
return 0
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
# Log which env var overrides were injected from plist-env-overrides.json for a label.
|
|
559
|
+
# Prints to stdout (setup.sh output). No-op when file absent or label not found.
|
|
560
|
+
# Args: $1=plist_label, $2=override_file (optional)
|
|
561
|
+
_log_plist_env_overrides() {
|
|
562
|
+
local _label="$1"
|
|
563
|
+
local _override_file="${2:-$HOME/.aidevops/agents/configs/plist-env-overrides.json}"
|
|
564
|
+
|
|
565
|
+
[[ -f "$_override_file" ]] || return 0
|
|
566
|
+
command -v jq >/dev/null 2>&1 || return 0
|
|
567
|
+
jq empty "$_override_file" 2>/dev/null || return 0
|
|
568
|
+
|
|
569
|
+
local _keys
|
|
570
|
+
_keys=$(jq -r --arg label "$_label" '
|
|
571
|
+
.[$label] // {} |
|
|
572
|
+
keys[] |
|
|
573
|
+
select(startswith("_") | not)
|
|
574
|
+
' "$_override_file" 2>/dev/null) || return 0
|
|
575
|
+
|
|
576
|
+
[[ -z "$_keys" ]] && return 0
|
|
577
|
+
|
|
578
|
+
local _count
|
|
579
|
+
_count=$(echo "$_keys" | wc -l | tr -d ' ')
|
|
580
|
+
local _keys_inline
|
|
581
|
+
_keys_inline=$(echo "$_keys" | tr '\n' ' ' | sed 's/ $//')
|
|
582
|
+
print_info " plist-env-overrides: injected ${_count} var(s) into ${_label}: ${_keys_inline}"
|
|
583
|
+
return 0
|
|
584
|
+
}
|
|
585
|
+
|
|
495
586
|
# Generate the full pulse launchd plist XML content.
|
|
496
587
|
# Args: $1=pulse_label, $2=wrapper_script, $3=opencode_bin
|
|
497
588
|
# Prints the complete plist XML to stdout.
|
|
589
|
+
#
|
|
590
|
+
# StartInterval is read from supervisor.pulse_interval_seconds in
|
|
591
|
+
# settings.json via _read_pulse_interval_seconds (default 180 — t2744).
|
|
592
|
+
# Previously this was hardcoded as 120, meaning macOS users could not
|
|
593
|
+
# tune the pulse cadence via settings (Linux/cron path always honoured
|
|
594
|
+
# the setting). The hardcoding is now removed; the macOS path matches
|
|
595
|
+
# the Linux path's behaviour.
|
|
498
596
|
_generate_pulse_plist_content() {
|
|
499
597
|
local pulse_label="$1"
|
|
500
598
|
local wrapper_script="$2"
|
|
@@ -519,6 +617,17 @@ _generate_pulse_plist_content() {
|
|
|
519
617
|
local _xml_bash_bin
|
|
520
618
|
_xml_bash_bin=$(_xml_escape "$(_resolve_modern_bash)")
|
|
521
619
|
|
|
620
|
+
# Resolve the configured pulse interval (settings.json, with default).
|
|
621
|
+
# Already validated to [30, 3600] inside _read_pulse_interval_seconds.
|
|
622
|
+
local _pulse_interval_sec
|
|
623
|
+
_pulse_interval_sec=$(_read_pulse_interval_seconds)
|
|
624
|
+
|
|
625
|
+
# Inject user-owned plist env overrides (GH#20563 / t2759).
|
|
626
|
+
# Reads ~/.aidevops/agents/configs/plist-env-overrides.json when present.
|
|
627
|
+
# Missing file or label not found → emits nothing (no-op, safe default).
|
|
628
|
+
local _env_overrides_xml
|
|
629
|
+
_env_overrides_xml=$(_build_plist_env_overrides_xml "$pulse_label")
|
|
630
|
+
|
|
522
631
|
cat <<PLIST
|
|
523
632
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
524
633
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -532,7 +641,7 @@ _generate_pulse_plist_content() {
|
|
|
532
641
|
<string>${_xml_wrapper_script}</string>
|
|
533
642
|
</array>
|
|
534
643
|
<key>StartInterval</key>
|
|
535
|
-
<integer
|
|
644
|
+
<integer>${_pulse_interval_sec}</integer>
|
|
536
645
|
<key>StandardOutPath</key>
|
|
537
646
|
<string>${_xml_home}/.aidevops/logs/pulse-wrapper.log</string>
|
|
538
647
|
<key>StandardErrorPath</key>
|
|
@@ -550,7 +659,7 @@ _generate_pulse_plist_content() {
|
|
|
550
659
|
<key>PULSE_STALE_THRESHOLD</key>
|
|
551
660
|
<string>${PULSE_STALE_THRESHOLD_SECONDS}</string>
|
|
552
661
|
${_headless_xml_env}
|
|
553
|
-
</dict>
|
|
662
|
+
${_env_overrides_xml} </dict>
|
|
554
663
|
<key>SoftResourceLimits</key>
|
|
555
664
|
<dict>
|
|
556
665
|
<key>NumberOfFiles</key>
|
|
@@ -579,13 +688,24 @@ _install_pulse_launchd() {
|
|
|
579
688
|
# Write the plist (always regenerated to pick up config changes)
|
|
580
689
|
_generate_pulse_plist_content "$pulse_label" "$wrapper_script" "$opencode_bin" >"$pulse_plist"
|
|
581
690
|
|
|
691
|
+
# Resolve interval for the user-facing message (matches what the plist contains).
|
|
692
|
+
local _interval_sec _interval_label
|
|
693
|
+
_interval_sec=$(_read_pulse_interval_seconds)
|
|
694
|
+
if (( _interval_sec % 60 == 0 )); then
|
|
695
|
+
_interval_label="$((_interval_sec / 60)) min"
|
|
696
|
+
else
|
|
697
|
+
_interval_label="${_interval_sec}s"
|
|
698
|
+
fi
|
|
699
|
+
|
|
582
700
|
# shell-portability: ignore next — _install_pulse_launchd is macOS-only (launchd)
|
|
583
701
|
if launchctl load "$pulse_plist"; then
|
|
584
702
|
if [[ "$_pulse_installed" == "true" ]]; then
|
|
585
|
-
print_info "Supervisor pulse updated (launchd config regenerated)"
|
|
703
|
+
print_info "Supervisor pulse updated (launchd config regenerated, every ${_interval_label})"
|
|
586
704
|
else
|
|
587
|
-
print_info "Supervisor pulse enabled (launchd, every
|
|
705
|
+
print_info "Supervisor pulse enabled (launchd, every ${_interval_label})"
|
|
588
706
|
fi
|
|
707
|
+
# Log any user-provided env var overrides that were injected (GH#20563 / t2759)
|
|
708
|
+
_log_plist_env_overrides "$pulse_label"
|
|
589
709
|
else
|
|
590
710
|
print_warning "Failed to load supervisor pulse LaunchAgent"
|
|
591
711
|
fi
|
|
@@ -940,7 +1060,10 @@ _uninstall_pulse() {
|
|
|
940
1060
|
# Setup stats-wrapper scheduler — runs quality sweep and health issue updates
|
|
941
1061
|
# separately from the pulse (t1429). Only installed when the supervisor
|
|
942
1062
|
# pulse is enabled (stats are useless without it).
|
|
943
|
-
# macOS: launchd plist (
|
|
1063
|
+
# macOS: launchd plist (hourly) | Linux: systemd timer or cron (hourly)
|
|
1064
|
+
# t2744: interval raised from 15 min → hourly. Stats UI is not realtime,
|
|
1065
|
+
# the four-times-an-hour cadence drove ~200-400 GraphQL points/hr of pure
|
|
1066
|
+
# overhead on multi-repo setups.
|
|
944
1067
|
setup_stats_wrapper() {
|
|
945
1068
|
local _pulse_lower="$1"
|
|
946
1069
|
# Use effective pulse state (PULSE_ENABLED) if available; fall back to consent string.
|
|
@@ -974,7 +1097,7 @@ setup_stats_wrapper() {
|
|
|
974
1097
|
<string>${_xml_stats_script}</string>
|
|
975
1098
|
</array>
|
|
976
1099
|
<key>StartInterval</key>
|
|
977
|
-
<integer>
|
|
1100
|
+
<integer>3600</integer>
|
|
978
1101
|
<key>StandardOutPath</key>
|
|
979
1102
|
<string>${_xml_stats_home}/.aidevops/logs/stats.log</string>
|
|
980
1103
|
<key>StandardErrorPath</key>
|
|
@@ -995,7 +1118,7 @@ setup_stats_wrapper() {
|
|
|
995
1118
|
PLIST
|
|
996
1119
|
)
|
|
997
1120
|
if _launchd_install_if_changed "$stats_label" "$stats_plist" "$stats_plist_content"; then
|
|
998
|
-
print_info "Stats wrapper enabled (launchd, every
|
|
1121
|
+
print_info "Stats wrapper enabled (launchd, every hour)"
|
|
999
1122
|
else
|
|
1000
1123
|
print_warning "Failed to load stats wrapper LaunchAgent"
|
|
1001
1124
|
fi
|
|
@@ -1003,12 +1126,12 @@ PLIST
|
|
|
1003
1126
|
_install_scheduler_linux \
|
|
1004
1127
|
"$stats_systemd" \
|
|
1005
1128
|
"aidevops: stats-wrapper" \
|
|
1006
|
-
"
|
|
1129
|
+
"$CRON_HOURLY" \
|
|
1007
1130
|
"\"${stats_script}\"" \
|
|
1008
|
-
"
|
|
1131
|
+
"3600" \
|
|
1009
1132
|
"$stats_log" \
|
|
1010
1133
|
"" \
|
|
1011
|
-
"Stats wrapper enabled (every
|
|
1134
|
+
"Stats wrapper enabled (every hour)" \
|
|
1012
1135
|
"Failed to install stats wrapper scheduler" \
|
|
1013
1136
|
"true" \
|
|
1014
1137
|
"false"
|
|
@@ -1503,7 +1626,7 @@ _install_cw_linux() {
|
|
|
1503
1626
|
_install_scheduler_linux \
|
|
1504
1627
|
"$cw_systemd" \
|
|
1505
1628
|
"aidevops: contribution-watch" \
|
|
1506
|
-
"
|
|
1629
|
+
"$CRON_HOURLY" \
|
|
1507
1630
|
"\"${cw_script}\" scan" \
|
|
1508
1631
|
"3600" \
|
|
1509
1632
|
"${_cw_log_dir}/contribution-watch.log" \
|
|
@@ -1670,7 +1793,7 @@ _install_profile_readme_scheduler() {
|
|
|
1670
1793
|
_install_scheduler_linux \
|
|
1671
1794
|
"$pr_systemd" \
|
|
1672
1795
|
"aidevops: profile-readme-update" \
|
|
1673
|
-
"
|
|
1796
|
+
"$CRON_HOURLY" \
|
|
1674
1797
|
"\"${pr_script}\" update" \
|
|
1675
1798
|
"3600" \
|
|
1676
1799
|
"$pr_log" \
|
|
@@ -388,6 +388,44 @@ setup_shell_linting_tools() {
|
|
|
388
388
|
return 0
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
setup_setsid_advisory() {
|
|
392
|
+
# setsid is required to detach pulse workers into their own process group
|
|
393
|
+
# (t2757, GH#20561). Without it, workers inherit pulse's PGID and are
|
|
394
|
+
# killed by any PG-scoped signal (launchd unload, restart chain).
|
|
395
|
+
#
|
|
396
|
+
# Linux: setsid ships with util-linux (present on all mainstream distros).
|
|
397
|
+
# macOS: available from macOS 12+ at /usr/bin/setsid. Older versions need
|
|
398
|
+
# util-linux via Homebrew (brew install util-linux).
|
|
399
|
+
if command -v setsid >/dev/null 2>&1; then
|
|
400
|
+
local setsid_path
|
|
401
|
+
setsid_path="$(command -v setsid)"
|
|
402
|
+
print_success "setsid found at $setsid_path (worker process-group isolation enabled)"
|
|
403
|
+
return 0
|
|
404
|
+
fi
|
|
405
|
+
|
|
406
|
+
# setsid missing — emit an advisory; it's a quality-of-life improvement,
|
|
407
|
+
# not a hard requirement (pulse falls back to nohup-only).
|
|
408
|
+
print_warning "setsid not found — pulse workers will share the pulse process group"
|
|
409
|
+
echo " Impact: a pulse restart or launchd unload may kill in-flight workers"
|
|
410
|
+
echo " (GH#20561 / t2757: worker survived 3/4 dispatches without setsid isolation)"
|
|
411
|
+
echo ""
|
|
412
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
413
|
+
if command -v brew >/dev/null 2>&1; then
|
|
414
|
+
echo " Install: brew install util-linux"
|
|
415
|
+
else
|
|
416
|
+
echo " Install Homebrew first, then: brew install util-linux"
|
|
417
|
+
echo " Or upgrade to macOS 12+ where /usr/bin/setsid ships by default"
|
|
418
|
+
fi
|
|
419
|
+
else
|
|
420
|
+
echo " Install: sudo apt install util-linux # Debian/Ubuntu"
|
|
421
|
+
echo " sudo dnf install util-linux # Fedora/RHEL"
|
|
422
|
+
echo " sudo pacman -S util-linux # Arch"
|
|
423
|
+
fi
|
|
424
|
+
echo ""
|
|
425
|
+
|
|
426
|
+
return 0
|
|
427
|
+
}
|
|
428
|
+
|
|
391
429
|
setup_shellcheck_wrapper() {
|
|
392
430
|
# Replace the real shellcheck binary with our wrapper script to prevent
|
|
393
431
|
# --external-sources from causing exponential memory growth (GH#2915).
|
package/setup.sh
CHANGED
|
@@ -12,7 +12,7 @@ shopt -s inherit_errexit 2>/dev/null || true
|
|
|
12
12
|
# AI Assistant Server Access Framework Setup Script
|
|
13
13
|
# Helps developers set up the framework for their infrastructure
|
|
14
14
|
#
|
|
15
|
-
# Version: 3.8.
|
|
15
|
+
# Version: 3.8.96
|
|
16
16
|
#
|
|
17
17
|
# Quick Install:
|
|
18
18
|
# npm install -g aidevops && aidevops update (recommended)
|