aidevops 3.11.16 → 3.12.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/VERSION +1 -1
- package/aidevops.sh +109 -1
- package/package.json +1 -1
- package/setup-modules/schedulers.sh +361 -0
- package/setup-modules/tool-install.sh +45 -15
- package/setup.sh +146 -45
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.12.0
|
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
|
+
# Version: 3.12.0
|
|
9
9
|
|
|
10
10
|
set -euo pipefail
|
|
11
11
|
|
|
@@ -480,6 +480,46 @@ _update_check_homebrew() {
|
|
|
480
480
|
return 0
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
+
# t2926 / GH#21102: Re-check setsid on every 'aidevops update' run.
|
|
484
|
+
# setsid (from util-linux) is required to detach pulse workers into their own
|
|
485
|
+
# process group — without it, every pulse restart sends SIGHUP to its PGID,
|
|
486
|
+
# killing in-flight workers. This check runs even when setup.sh is skipped
|
|
487
|
+
# (already up-to-date path), so Homebrew drift doesn't silently break workers.
|
|
488
|
+
_update_check_setsid() {
|
|
489
|
+
command -v setsid >/dev/null 2>&1 && return 0
|
|
490
|
+
|
|
491
|
+
# setsid is missing. On macOS with Homebrew, auto-install util-linux.
|
|
492
|
+
# Use a boolean flag to avoid repeating the OS literal string.
|
|
493
|
+
local _on_mac=false
|
|
494
|
+
[[ "$(uname -s)" == Darwin* ]] && _on_mac=true
|
|
495
|
+
if $_on_mac && command -v brew >/dev/null 2>&1; then
|
|
496
|
+
print_info "setsid not found — installing util-linux for worker PGID isolation (GH#21102)"
|
|
497
|
+
if brew install util-linux 2>&1 | tail -3; then
|
|
498
|
+
local brew_prefix=""
|
|
499
|
+
brew_prefix="$(brew --prefix 2>/dev/null || true)"
|
|
500
|
+
local keg_setsid="${brew_prefix}/opt/util-linux/bin/setsid"
|
|
501
|
+
local link_target="${brew_prefix}/bin/setsid"
|
|
502
|
+
if [[ -x "$keg_setsid" && ! -e "$link_target" ]]; then
|
|
503
|
+
ln -s "$keg_setsid" "$link_target" && \
|
|
504
|
+
print_success "Symlinked setsid: $keg_setsid → $link_target"
|
|
505
|
+
fi
|
|
506
|
+
if command -v setsid >/dev/null 2>&1; then
|
|
507
|
+
print_success "setsid installed at $(command -v setsid) (worker PGID isolation enabled)"
|
|
508
|
+
else
|
|
509
|
+
print_error "util-linux installed but setsid still not in PATH — check brew --prefix"
|
|
510
|
+
fi
|
|
511
|
+
else
|
|
512
|
+
print_error "brew install util-linux failed — workers will share pulse PGID until resolved"
|
|
513
|
+
fi
|
|
514
|
+
elif $_on_mac; then
|
|
515
|
+
print_error "setsid not found — worker isolation broken; install Homebrew then run: brew install util-linux"
|
|
516
|
+
else
|
|
517
|
+
print_error "setsid not found — worker isolation broken; install util-linux via your distro package manager"
|
|
518
|
+
fi
|
|
519
|
+
|
|
520
|
+
return 0
|
|
521
|
+
}
|
|
522
|
+
|
|
483
523
|
# Verify supply chain signature after pulling framework updates.
|
|
484
524
|
# Checks that the HEAD commit is signed by the trusted maintainer key.
|
|
485
525
|
# Non-blocking: warns on failure, does not abort the update.
|
|
@@ -638,6 +678,8 @@ cmd_update() {
|
|
|
638
678
|
_update_check_planning
|
|
639
679
|
_update_check_tools
|
|
640
680
|
_update_sweep_opencode_symlinks
|
|
681
|
+
# t2926: Re-check setsid on every update (runs even when setup.sh is skipped).
|
|
682
|
+
_update_check_setsid
|
|
641
683
|
|
|
642
684
|
# t2898: When invoked interactively (terminal stdin AND not from the
|
|
643
685
|
# auto-update daemon itself, which sets AIDEVOPS_AUTO_UPDATE=1 in its
|
|
@@ -1456,10 +1498,13 @@ _help_commands() {
|
|
|
1456
1498
|
echo " approve <cmd> Cryptographic issue/PR approval (setup/issue/pr/verify/status)"
|
|
1457
1499
|
echo " security [cmd] Full security assessment (posture + hygiene + supply chain)"
|
|
1458
1500
|
echo " contributions External contributions inbox (bare: status | seed/scan/stop/restart/install/uninstall)"
|
|
1501
|
+
echo " inbox [cmd] Capture transit zone (bare: status | provision/add/find/digest/help)"
|
|
1502
|
+
echo " email [cmd] Email mailbox management (mailbox add/list/test/remove)"
|
|
1459
1503
|
echo " ip-check <cmd> IP reputation checks (check/batch/report/providers)"
|
|
1460
1504
|
echo " review-gate <cmd> Configure review_gate.rate_limit_behavior (list/set/unset)"
|
|
1461
1505
|
echo " secret <cmd> Manage secrets (set/list/run/init/import/status)"
|
|
1462
1506
|
echo " config <cmd> Feature toggles (list/get/set/reset/path/help)"
|
|
1507
|
+
echo " knowledge <cmd> Knowledge plane management (init/status/provision)"
|
|
1463
1508
|
echo " stats <cmd> LLM usage analytics (summary/models/projects/costs/trend)"
|
|
1464
1509
|
echo " tabby <cmd> Manage Tabby terminal profiles (sync/status/zshrc/help)"
|
|
1465
1510
|
echo " parent-status <N> Show decomposition state of parent-task issue #N (alias: ps)"
|
|
@@ -1539,6 +1584,13 @@ _help_detailed_sections() {
|
|
|
1539
1584
|
echo " aidevops config reset [key] # Reset toggle(s) to defaults"
|
|
1540
1585
|
echo " aidevops config path # Show config file path"
|
|
1541
1586
|
echo ""
|
|
1587
|
+
echo "Knowledge Plane:"
|
|
1588
|
+
echo " aidevops knowledge init repo # Provision _knowledge/ in current repo"
|
|
1589
|
+
echo " aidevops knowledge init personal # Provision at ~/.aidevops/.agent-workspace/knowledge/"
|
|
1590
|
+
echo " aidevops knowledge init off # Disable knowledge plane"
|
|
1591
|
+
echo " aidevops knowledge status # Show provisioning state"
|
|
1592
|
+
echo " aidevops knowledge provision [path] # Re-provision (idempotent)"
|
|
1593
|
+
echo ""
|
|
1542
1594
|
echo "LLM Stats:"
|
|
1543
1595
|
echo " aidevops stats # Show usage summary (last 30 days)"
|
|
1544
1596
|
echo " aidevops stats summary # Overall usage summary"
|
|
@@ -1745,6 +1797,50 @@ _cmd_security() {
|
|
|
1745
1797
|
return 0
|
|
1746
1798
|
}
|
|
1747
1799
|
|
|
1800
|
+
# Route 'aidevops email [subcommand]' to email helpers
|
|
1801
|
+
_cmd_email() {
|
|
1802
|
+
local sub="${1:-help}"
|
|
1803
|
+
local _EPH="email-poll-helper.sh"
|
|
1804
|
+
shift || true
|
|
1805
|
+
case "$sub" in
|
|
1806
|
+
mailbox)
|
|
1807
|
+
local action="${1:-list}"
|
|
1808
|
+
shift || true
|
|
1809
|
+
local _EMR_HELPER="email-mailbox-register-helper.sh"
|
|
1810
|
+
case "$action" in
|
|
1811
|
+
add) _dispatch_helper "$_EMR_HELPER" "$_EMR_HELPER" add "$@" ;;
|
|
1812
|
+
list) _dispatch_helper "$_EPH" "$_EPH" list "$@" ;;
|
|
1813
|
+
test) _dispatch_helper "$_EPH" "$_EPH" test "$@" ;;
|
|
1814
|
+
remove) _dispatch_helper "$_EMR_HELPER" "$_EMR_HELPER" remove "$@" ;;
|
|
1815
|
+
*)
|
|
1816
|
+
echo "Usage: aidevops email mailbox <add|list|test|remove>"
|
|
1817
|
+
echo ""
|
|
1818
|
+
echo "Mailbox subcommands:"
|
|
1819
|
+
echo " add Interactive: prompt for provider, user, gopass path; test connection"
|
|
1820
|
+
echo " list Table of mailboxes with last-polled-at and last-error"
|
|
1821
|
+
echo " test <id> Dry-run fetch (1 message); does not commit state"
|
|
1822
|
+
echo " remove <id> Un-register a mailbox"
|
|
1823
|
+
;;
|
|
1824
|
+
esac
|
|
1825
|
+
;;
|
|
1826
|
+
poll)
|
|
1827
|
+
# Direct poll commands forwarded to email-poll-helper.sh
|
|
1828
|
+
_dispatch_helper "$_EPH" "$_EPH" "$action" "$@" ;;
|
|
1829
|
+
*)
|
|
1830
|
+
echo "Usage: aidevops email <mailbox|poll> [subcommand]"
|
|
1831
|
+
echo ""
|
|
1832
|
+
echo "Email subcommands:"
|
|
1833
|
+
echo " mailbox add Register a new IMAP mailbox (interactive)"
|
|
1834
|
+
echo " mailbox list Show all mailboxes + polling status"
|
|
1835
|
+
echo " mailbox test <id> Dry-run connection test"
|
|
1836
|
+
echo " mailbox remove <id> Un-register a mailbox"
|
|
1837
|
+
echo " poll tick Poll all mailboxes now (same as routine r044)"
|
|
1838
|
+
echo " poll backfill <id> Backfill a mailbox from a given date"
|
|
1839
|
+
;;
|
|
1840
|
+
esac
|
|
1841
|
+
return 0
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1748
1844
|
# Route 'aidevops client-format [subcommand]' to appropriate helpers
|
|
1749
1845
|
_cmd_client_format() {
|
|
1750
1846
|
case "${1:-status}" in
|
|
@@ -1835,10 +1931,22 @@ main() {
|
|
|
1835
1931
|
[[ $# -eq 0 ]] && set -- status
|
|
1836
1932
|
_dispatch_helper "contribution-watch-helper.sh" "contribution-watch-helper.sh" "$@"
|
|
1837
1933
|
;;
|
|
1934
|
+
inbox)
|
|
1935
|
+
# Bare `aidevops inbox` defaults to status (most common use).
|
|
1936
|
+
[[ $# -eq 0 ]] && set -- status
|
|
1937
|
+
_dispatch_helper "inbox-helper.sh" "inbox-helper.sh" "$@"
|
|
1938
|
+
;;
|
|
1939
|
+
case | cases)
|
|
1940
|
+
# Bare `aidevops case` defaults to list (most common use).
|
|
1941
|
+
[[ $# -eq 0 ]] && set -- list
|
|
1942
|
+
_dispatch_helper "case-helper.sh" "case-helper.sh" "$@"
|
|
1943
|
+
;;
|
|
1944
|
+
email) _cmd_email "$@" ;;
|
|
1838
1945
|
stats | observability) _dispatch_helper "observability-helper.sh" "observability-helper.sh" "$@" ;;
|
|
1839
1946
|
tabby) _dispatch_helper "tabby-helper.sh" "tabby-helper.sh" "$@" ;;
|
|
1840
1947
|
init-routines) _dispatch_helper "init-routines-helper.sh" "init-routines-helper.sh" "$@" ;;
|
|
1841
1948
|
parent-status | ps) _dispatch_helper "parent-status-helper.sh" "parent-status-helper.sh" "$@" ;;
|
|
1949
|
+
knowledge) _dispatch_helper "knowledge-helper.sh" "knowledge-helper.sh" "$@" ;;
|
|
1842
1950
|
config | configure) _dispatch_config "$@" ;;
|
|
1843
1951
|
uninstall | remove) cmd_uninstall ;;
|
|
1844
1952
|
version | v | -v | --version) cmd_version ;;
|
package/package.json
CHANGED
|
@@ -703,6 +703,17 @@ _install_pulse_launchd() {
|
|
|
703
703
|
_interval_label="${_interval_sec}s"
|
|
704
704
|
fi
|
|
705
705
|
|
|
706
|
+
# One-time legacy cleanup: unload and remove the old-label plist if present.
|
|
707
|
+
# Users on stale installs may have com.aidevops.supervisor-pulse (legacy) and
|
|
708
|
+
# com.aidevops.aidevops-supervisor-pulse (current) both loaded, causing 2x
|
|
709
|
+
# dispatch. Only targets the hardcoded legacy path; idempotent — no-op when
|
|
710
|
+
# the legacy file is absent.
|
|
711
|
+
local _legacy_plist="$HOME/Library/LaunchAgents/com.aidevops.supervisor-pulse.plist"
|
|
712
|
+
if [[ -f "$_legacy_plist" ]]; then
|
|
713
|
+
launchctl unload "$_legacy_plist" 2>/dev/null || true
|
|
714
|
+
rm -f "$_legacy_plist"
|
|
715
|
+
fi
|
|
716
|
+
|
|
706
717
|
# _launchd_install_if_changed handles unload-before-replace only when content
|
|
707
718
|
# has changed, and writes atomically via tmp+rename (see setup.sh).
|
|
708
719
|
# shell-portability: ignore next — _install_pulse_launchd is macOS-only (launchd)
|
|
@@ -1684,6 +1695,233 @@ setup_contribution_watch() {
|
|
|
1684
1695
|
return 0
|
|
1685
1696
|
}
|
|
1686
1697
|
|
|
1698
|
+
# Install complexity scan via launchd (macOS).
|
|
1699
|
+
# Args: $1=label, $2=script path, $3=log dir
|
|
1700
|
+
# (t2903) Extracted from pulse dispatch preflight — independent schedule so
|
|
1701
|
+
# the 200-470s scan never starves dispatch or downstream scanners.
|
|
1702
|
+
_install_complexity_scan_launchd() {
|
|
1703
|
+
local cs_label="$1"
|
|
1704
|
+
local cs_script="$2"
|
|
1705
|
+
local _cs_log_dir="$3"
|
|
1706
|
+
local cs_plist="$HOME/Library/LaunchAgents/${cs_label}.plist"
|
|
1707
|
+
|
|
1708
|
+
local _xml_cs_script _xml_cs_home _xml_cs_log_dir
|
|
1709
|
+
_xml_cs_script=$(_xml_escape "$cs_script")
|
|
1710
|
+
_xml_cs_home=$(_xml_escape "$HOME")
|
|
1711
|
+
_xml_cs_log_dir=$(_xml_escape "$_cs_log_dir")
|
|
1712
|
+
|
|
1713
|
+
local cs_plist_content
|
|
1714
|
+
cs_plist_content=$(
|
|
1715
|
+
cat <<CS_PLIST
|
|
1716
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
1717
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1718
|
+
<plist version="1.0">
|
|
1719
|
+
<dict>
|
|
1720
|
+
<key>Label</key>
|
|
1721
|
+
<string>${cs_label}</string>
|
|
1722
|
+
<key>ProgramArguments</key>
|
|
1723
|
+
<array>
|
|
1724
|
+
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
1725
|
+
<string>${_xml_cs_script}</string>
|
|
1726
|
+
<string>run</string>
|
|
1727
|
+
</array>
|
|
1728
|
+
<key>StartInterval</key>
|
|
1729
|
+
<integer>3600</integer>
|
|
1730
|
+
<key>StandardOutPath</key>
|
|
1731
|
+
<string>${_xml_cs_log_dir}/complexity-scan-runner.log</string>
|
|
1732
|
+
<key>StandardErrorPath</key>
|
|
1733
|
+
<string>${_xml_cs_log_dir}/complexity-scan-runner.log</string>
|
|
1734
|
+
<key>EnvironmentVariables</key>
|
|
1735
|
+
<dict>
|
|
1736
|
+
<key>PATH</key>
|
|
1737
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
1738
|
+
<key>HOME</key>
|
|
1739
|
+
<string>${_xml_cs_home}</string>
|
|
1740
|
+
</dict>
|
|
1741
|
+
<key>RunAtLoad</key>
|
|
1742
|
+
<true/>
|
|
1743
|
+
<key>KeepAlive</key>
|
|
1744
|
+
<false/>
|
|
1745
|
+
<key>ProcessType</key>
|
|
1746
|
+
<string>Background</string>
|
|
1747
|
+
<key>LowPriorityBackgroundIO</key>
|
|
1748
|
+
<true/>
|
|
1749
|
+
<key>Nice</key>
|
|
1750
|
+
<integer>10</integer>
|
|
1751
|
+
</dict>
|
|
1752
|
+
</plist>
|
|
1753
|
+
CS_PLIST
|
|
1754
|
+
)
|
|
1755
|
+
|
|
1756
|
+
if _launchd_install_if_changed "$cs_label" "$cs_plist" "$cs_plist_content"; then
|
|
1757
|
+
print_info "Complexity scan enabled (launchd, hourly run)"
|
|
1758
|
+
else
|
|
1759
|
+
print_warning "Failed to load complexity scan LaunchAgent"
|
|
1760
|
+
fi
|
|
1761
|
+
return 0
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
# Install complexity scan via systemd or cron (Linux).
|
|
1765
|
+
# Args: $1=script path, $2=log dir
|
|
1766
|
+
_install_complexity_scan_linux() {
|
|
1767
|
+
local cs_script="$1"
|
|
1768
|
+
local _cs_log_dir="$2"
|
|
1769
|
+
local cs_systemd="aidevops-complexity-scan"
|
|
1770
|
+
_install_scheduler_linux \
|
|
1771
|
+
"$cs_systemd" \
|
|
1772
|
+
"aidevops: complexity-scan" \
|
|
1773
|
+
"$CRON_HOURLY" \
|
|
1774
|
+
"\"${cs_script}\" run" \
|
|
1775
|
+
"3600" \
|
|
1776
|
+
"${_cs_log_dir}/complexity-scan-runner.log" \
|
|
1777
|
+
"" \
|
|
1778
|
+
"Complexity scan enabled (hourly run)" \
|
|
1779
|
+
"Failed to install complexity scan scheduler" \
|
|
1780
|
+
"true" \
|
|
1781
|
+
"true"
|
|
1782
|
+
return 0
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
# Setup complexity scan (t2903) — extracts the weekly complexity scan from
|
|
1786
|
+
# pulse dispatch preflight into its own launchd/cron schedule. The scan was
|
|
1787
|
+
# observed consuming 200-470s per pulse cycle (26%+ of the 1800s pulse stale
|
|
1788
|
+
# ceiling), starving downstream scanners. Promoting it to its own schedule
|
|
1789
|
+
# decouples it from dispatch entirely. The runner reuses run_weekly_complexity_scan
|
|
1790
|
+
# from pulse-simplification.sh, which has internal 15-min cadence gating
|
|
1791
|
+
# (COMPLEXITY_SCAN_INTERVAL=900) so hourly launchd ticks are always safe.
|
|
1792
|
+
setup_complexity_scan() {
|
|
1793
|
+
local cs_script="$HOME/.aidevops/agents/scripts/complexity-scan-runner.sh"
|
|
1794
|
+
local cs_label="sh.aidevops.complexity-scan"
|
|
1795
|
+
if ! [[ -x "$cs_script" ]]; then
|
|
1796
|
+
return 0
|
|
1797
|
+
fi
|
|
1798
|
+
|
|
1799
|
+
# Reuse contribution-watch's log-dir resolver (same logic, same config key).
|
|
1800
|
+
local _cs_log_dir
|
|
1801
|
+
_cs_log_dir=$(_resolve_cw_log_dir) || return 1
|
|
1802
|
+
mkdir -p "$_cs_log_dir"
|
|
1803
|
+
|
|
1804
|
+
# Install/update scheduled runner
|
|
1805
|
+
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
1806
|
+
_install_complexity_scan_launchd "$cs_label" "$cs_script" "$_cs_log_dir"
|
|
1807
|
+
else
|
|
1808
|
+
_install_complexity_scan_linux "$cs_script" "$_cs_log_dir"
|
|
1809
|
+
fi
|
|
1810
|
+
return 0
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
# Install pulse-merge-routine launchd plist (macOS).
|
|
1814
|
+
# Args: $1=label $2=script $3=log_dir
|
|
1815
|
+
_install_pulse_merge_routine_launchd() {
|
|
1816
|
+
local pmr_label="$1"
|
|
1817
|
+
local pmr_script="$2"
|
|
1818
|
+
local _pmr_log_dir="$3"
|
|
1819
|
+
local pmr_plist="$HOME/Library/LaunchAgents/${pmr_label}.plist"
|
|
1820
|
+
|
|
1821
|
+
local _xml_pmr_script _xml_pmr_home _xml_pmr_log_dir
|
|
1822
|
+
_xml_pmr_script=$(_xml_escape "$pmr_script")
|
|
1823
|
+
_xml_pmr_home=$(_xml_escape "$HOME")
|
|
1824
|
+
_xml_pmr_log_dir=$(_xml_escape "$_pmr_log_dir")
|
|
1825
|
+
|
|
1826
|
+
local pmr_plist_content
|
|
1827
|
+
pmr_plist_content=$(
|
|
1828
|
+
cat <<PMR_PLIST
|
|
1829
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
1830
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1831
|
+
<plist version="1.0">
|
|
1832
|
+
<dict>
|
|
1833
|
+
<key>Label</key>
|
|
1834
|
+
<string>${pmr_label}</string>
|
|
1835
|
+
<key>ProgramArguments</key>
|
|
1836
|
+
<array>
|
|
1837
|
+
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
1838
|
+
<string>${_xml_pmr_script}</string>
|
|
1839
|
+
<string>run</string>
|
|
1840
|
+
</array>
|
|
1841
|
+
<key>StartInterval</key>
|
|
1842
|
+
<integer>120</integer>
|
|
1843
|
+
<key>StandardOutPath</key>
|
|
1844
|
+
<string>${_xml_pmr_log_dir}/pulse-merge-routine.log</string>
|
|
1845
|
+
<key>StandardErrorPath</key>
|
|
1846
|
+
<string>${_xml_pmr_log_dir}/pulse-merge-routine.log</string>
|
|
1847
|
+
<key>EnvironmentVariables</key>
|
|
1848
|
+
<dict>
|
|
1849
|
+
<key>PATH</key>
|
|
1850
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
1851
|
+
<key>HOME</key>
|
|
1852
|
+
<string>${_xml_pmr_home}</string>
|
|
1853
|
+
</dict>
|
|
1854
|
+
<key>RunAtLoad</key>
|
|
1855
|
+
<true/>
|
|
1856
|
+
<key>KeepAlive</key>
|
|
1857
|
+
<false/>
|
|
1858
|
+
<key>ProcessType</key>
|
|
1859
|
+
<string>Background</string>
|
|
1860
|
+
<key>LowPriorityBackgroundIO</key>
|
|
1861
|
+
<true/>
|
|
1862
|
+
<key>Nice</key>
|
|
1863
|
+
<integer>10</integer>
|
|
1864
|
+
</dict>
|
|
1865
|
+
</plist>
|
|
1866
|
+
PMR_PLIST
|
|
1867
|
+
)
|
|
1868
|
+
|
|
1869
|
+
if _launchd_install_if_changed "$pmr_label" "$pmr_plist" "$pmr_plist_content"; then
|
|
1870
|
+
print_info "Pulse merge routine enabled (launchd, every 2 min)"
|
|
1871
|
+
else
|
|
1872
|
+
print_warning "Failed to load pulse merge routine LaunchAgent"
|
|
1873
|
+
fi
|
|
1874
|
+
return 0
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
# Install pulse-merge-routine via systemd or cron (Linux).
|
|
1878
|
+
# Args: $1=script path, $2=log dir
|
|
1879
|
+
_install_pulse_merge_routine_linux() {
|
|
1880
|
+
local pmr_script="$1"
|
|
1881
|
+
local _pmr_log_dir="$2"
|
|
1882
|
+
local pmr_systemd="aidevops-pulse-merge-routine"
|
|
1883
|
+
_install_scheduler_linux \
|
|
1884
|
+
"$pmr_systemd" \
|
|
1885
|
+
"aidevops: pulse-merge-routine" \
|
|
1886
|
+
"*/2 * * * *" \
|
|
1887
|
+
"\"${pmr_script}\" run" \
|
|
1888
|
+
"120" \
|
|
1889
|
+
"${_pmr_log_dir}/pulse-merge-routine.log" \
|
|
1890
|
+
"" \
|
|
1891
|
+
"Pulse merge routine enabled (every 2 min)" \
|
|
1892
|
+
"Failed to install pulse merge routine scheduler" \
|
|
1893
|
+
"true" \
|
|
1894
|
+
"true"
|
|
1895
|
+
return 0
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
# Setup pulse merge routine (t2862, GH#20919) — runs merge_ready_prs_all_repos()
|
|
1899
|
+
# as a fast 120s standalone routine, decoupled from the monolithic pulse cycle.
|
|
1900
|
+
# The pulse cycle's preflight stack (60-470s) meant the merge pass ran only ~7
|
|
1901
|
+
# times/24h despite ~40+ cycles. This routine ensures green PRs merge within ~3
|
|
1902
|
+
# min of CI completion. The in-cycle merge call in pulse-wrapper.sh is kept as
|
|
1903
|
+
# defense-in-depth but short-circuits when this routine ran within the last 60s.
|
|
1904
|
+
setup_pulse_merge_routine() {
|
|
1905
|
+
local pmr_script="$HOME/.aidevops/agents/scripts/pulse-merge-routine.sh"
|
|
1906
|
+
local pmr_label="sh.aidevops.pulse-merge-routine"
|
|
1907
|
+
if ! [[ -x "$pmr_script" ]]; then
|
|
1908
|
+
return 0
|
|
1909
|
+
fi
|
|
1910
|
+
|
|
1911
|
+
# Reuse contribution-watch's log-dir resolver (same logic, same config key).
|
|
1912
|
+
local _pmr_log_dir
|
|
1913
|
+
_pmr_log_dir=$(_resolve_cw_log_dir) || return 1
|
|
1914
|
+
mkdir -p "$_pmr_log_dir"
|
|
1915
|
+
|
|
1916
|
+
# Install/update scheduled runner
|
|
1917
|
+
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
1918
|
+
_install_pulse_merge_routine_launchd "$pmr_label" "$pmr_script" "$_pmr_log_dir"
|
|
1919
|
+
else
|
|
1920
|
+
_install_pulse_merge_routine_linux "$pmr_script" "$_pmr_log_dir"
|
|
1921
|
+
fi
|
|
1922
|
+
return 0
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1687
1925
|
# Setup draft responses — private repo + local draft storage for reviewing
|
|
1688
1926
|
# AI-drafted replies to external contributions (t1555).
|
|
1689
1927
|
# Respects config: aidevops config set orchestration.draft_responses false
|
|
@@ -2161,3 +2399,126 @@ setup_repo_aidevops_health() {
|
|
|
2161
2399
|
fi
|
|
2162
2400
|
return 0
|
|
2163
2401
|
}
|
|
2402
|
+
|
|
2403
|
+
# ============================================================================
|
|
2404
|
+
# Peer productivity monitor (t2932)
|
|
2405
|
+
# ============================================================================
|
|
2406
|
+
#
|
|
2407
|
+
# Adaptive cross-runner dispatch coordination: observes peer GitHub activity
|
|
2408
|
+
# every 30 min and updates ~/.config/aidevops/dispatch-override.conf to
|
|
2409
|
+
# `ignore` peers whose pulse is broken (claims issues but never PRs) and
|
|
2410
|
+
# back to `honour` when they recover. Self-healing across the ecosystem —
|
|
2411
|
+
# each runner observes peers independently, no central coordinator needed.
|
|
2412
|
+
# Manual entries in dispatch-override.conf above the auto-managed marker
|
|
2413
|
+
# always take precedence.
|
|
2414
|
+
|
|
2415
|
+
# Install peer-productivity-monitor launchd plist (macOS).
|
|
2416
|
+
# Args: $1=label $2=script $3=log_dir
|
|
2417
|
+
_install_peer_productivity_monitor_launchd() {
|
|
2418
|
+
local ppm_label="$1"
|
|
2419
|
+
local ppm_script="$2"
|
|
2420
|
+
local _ppm_log_dir="$3"
|
|
2421
|
+
local ppm_plist="$HOME/Library/LaunchAgents/${ppm_label}.plist"
|
|
2422
|
+
|
|
2423
|
+
local _xml_ppm_script _xml_ppm_home _xml_ppm_log_dir
|
|
2424
|
+
_xml_ppm_script=$(_xml_escape "$ppm_script")
|
|
2425
|
+
_xml_ppm_home=$(_xml_escape "$HOME")
|
|
2426
|
+
_xml_ppm_log_dir=$(_xml_escape "$_ppm_log_dir")
|
|
2427
|
+
|
|
2428
|
+
local ppm_plist_content
|
|
2429
|
+
ppm_plist_content=$(
|
|
2430
|
+
cat <<PPM_PLIST
|
|
2431
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2432
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
2433
|
+
<plist version="1.0">
|
|
2434
|
+
<dict>
|
|
2435
|
+
<key>Label</key>
|
|
2436
|
+
<string>${ppm_label}</string>
|
|
2437
|
+
<key>ProgramArguments</key>
|
|
2438
|
+
<array>
|
|
2439
|
+
<string>$(_xml_escape "$(_resolve_modern_bash)")</string>
|
|
2440
|
+
<string>${_xml_ppm_script}</string>
|
|
2441
|
+
<string>observe</string>
|
|
2442
|
+
</array>
|
|
2443
|
+
<key>StartInterval</key>
|
|
2444
|
+
<integer>1800</integer>
|
|
2445
|
+
<key>StandardOutPath</key>
|
|
2446
|
+
<string>${_xml_ppm_log_dir}/peer-productivity-launchd.log</string>
|
|
2447
|
+
<key>StandardErrorPath</key>
|
|
2448
|
+
<string>${_xml_ppm_log_dir}/peer-productivity-launchd.log</string>
|
|
2449
|
+
<key>EnvironmentVariables</key>
|
|
2450
|
+
<dict>
|
|
2451
|
+
<key>PATH</key>
|
|
2452
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
2453
|
+
<key>HOME</key>
|
|
2454
|
+
<string>${_xml_ppm_home}</string>
|
|
2455
|
+
</dict>
|
|
2456
|
+
<key>RunAtLoad</key>
|
|
2457
|
+
<true/>
|
|
2458
|
+
<key>KeepAlive</key>
|
|
2459
|
+
<false/>
|
|
2460
|
+
<key>ProcessType</key>
|
|
2461
|
+
<string>Background</string>
|
|
2462
|
+
<key>LowPriorityBackgroundIO</key>
|
|
2463
|
+
<true/>
|
|
2464
|
+
<key>Nice</key>
|
|
2465
|
+
<integer>10</integer>
|
|
2466
|
+
</dict>
|
|
2467
|
+
</plist>
|
|
2468
|
+
PPM_PLIST
|
|
2469
|
+
)
|
|
2470
|
+
|
|
2471
|
+
if _launchd_install_if_changed "$ppm_label" "$ppm_plist" "$ppm_plist_content"; then
|
|
2472
|
+
print_info "Peer productivity monitor enabled (launchd, every 30 min)"
|
|
2473
|
+
else
|
|
2474
|
+
print_warning "Failed to load peer-productivity-monitor LaunchAgent"
|
|
2475
|
+
fi
|
|
2476
|
+
return 0
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
# Install peer-productivity-monitor via systemd or cron (Linux).
|
|
2480
|
+
# Args: $1=script path, $2=log dir
|
|
2481
|
+
_install_peer_productivity_monitor_linux() {
|
|
2482
|
+
local ppm_script="$1"
|
|
2483
|
+
local _ppm_log_dir="$2"
|
|
2484
|
+
local ppm_systemd="aidevops-peer-productivity-monitor"
|
|
2485
|
+
_install_scheduler_linux \
|
|
2486
|
+
"$ppm_systemd" \
|
|
2487
|
+
"aidevops: peer-productivity-monitor" \
|
|
2488
|
+
"*/30 * * * *" \
|
|
2489
|
+
"\"${ppm_script}\" observe" \
|
|
2490
|
+
"1800" \
|
|
2491
|
+
"${_ppm_log_dir}/peer-productivity-launchd.log" \
|
|
2492
|
+
"" \
|
|
2493
|
+
"Peer productivity monitor enabled (every 30 min)" \
|
|
2494
|
+
"Failed to install peer-productivity-monitor scheduler" \
|
|
2495
|
+
"true" \
|
|
2496
|
+
"true"
|
|
2497
|
+
return 0
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
# Setup peer-productivity-monitor (t2932) — observes peer GitHub activity
|
|
2501
|
+
# every 30 min and updates ~/.config/aidevops/dispatch-override.conf so the
|
|
2502
|
+
# local pulse competes with broken peers and collaborates with healthy ones.
|
|
2503
|
+
# Manual entries in dispatch-override.conf above the auto-managed marker
|
|
2504
|
+
# always take precedence.
|
|
2505
|
+
setup_peer_productivity_monitor() {
|
|
2506
|
+
local ppm_script="$HOME/.aidevops/agents/scripts/peer-productivity-monitor.sh"
|
|
2507
|
+
local ppm_label="sh.aidevops.peer-productivity-monitor"
|
|
2508
|
+
if ! [[ -x "$ppm_script" ]]; then
|
|
2509
|
+
return 0
|
|
2510
|
+
fi
|
|
2511
|
+
|
|
2512
|
+
# Reuse contribution-watch's log-dir resolver (same logic, same config key).
|
|
2513
|
+
local _ppm_log_dir
|
|
2514
|
+
_ppm_log_dir=$(_resolve_cw_log_dir) || return 1
|
|
2515
|
+
mkdir -p "$_ppm_log_dir"
|
|
2516
|
+
|
|
2517
|
+
# Install/update scheduled runner
|
|
2518
|
+
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
2519
|
+
_install_peer_productivity_monitor_launchd "$ppm_label" "$ppm_script" "$_ppm_log_dir"
|
|
2520
|
+
else
|
|
2521
|
+
_install_peer_productivity_monitor_linux "$ppm_script" "$_ppm_log_dir"
|
|
2522
|
+
fi
|
|
2523
|
+
return 0
|
|
2524
|
+
}
|
|
@@ -390,12 +390,14 @@ setup_shell_linting_tools() {
|
|
|
390
390
|
|
|
391
391
|
setup_setsid_advisory() {
|
|
392
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
|
|
394
|
-
# killed by any PG-scoped signal (launchd unload, restart chain).
|
|
393
|
+
# (t2757, GH#20561, GH#21102). Without it, workers inherit pulse's PGID and
|
|
394
|
+
# are killed by any PG-scoped signal (launchd unload, restart chain).
|
|
395
395
|
#
|
|
396
396
|
# Linux: setsid ships with util-linux (present on all mainstream distros).
|
|
397
|
-
# macOS: available from macOS 12+ at /usr/bin/setsid. Older
|
|
398
|
-
#
|
|
397
|
+
# macOS: available from macOS 12+ at /usr/bin/setsid. Older macOS or systems
|
|
398
|
+
# where /usr/bin/setsid is absent need util-linux via Homebrew.
|
|
399
|
+
# util-linux is keg-only on Homebrew — binary is not linked into PATH
|
|
400
|
+
# automatically, so we create a symlink after install.
|
|
399
401
|
if command -v setsid >/dev/null 2>&1; then
|
|
400
402
|
local setsid_path
|
|
401
403
|
setsid_path="$(command -v setsid)"
|
|
@@ -403,23 +405,51 @@ setup_setsid_advisory() {
|
|
|
403
405
|
return 0
|
|
404
406
|
fi
|
|
405
407
|
|
|
406
|
-
# setsid missing —
|
|
407
|
-
#
|
|
408
|
-
|
|
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 ""
|
|
408
|
+
# setsid missing — on macOS with Homebrew, auto-install util-linux and
|
|
409
|
+
# symlink setsid into PATH (GH#21102 / t2926). On Linux and macOS without
|
|
410
|
+
# Homebrew, emit an actionable error with install instructions.
|
|
412
411
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
413
412
|
if command -v brew >/dev/null 2>&1; then
|
|
414
|
-
|
|
413
|
+
print_info "setsid not found — installing util-linux for worker PGID isolation (GH#21102)"
|
|
414
|
+
if brew install util-linux 2>&1 | tail -3; then
|
|
415
|
+
# util-linux is keg-only: binary lives under the keg, not in /opt/homebrew/bin.
|
|
416
|
+
# Symlink setsid into a standard PATH directory so 'command -v setsid' works.
|
|
417
|
+
local brew_prefix
|
|
418
|
+
brew_prefix="$(brew --prefix 2>/dev/null || echo "")"
|
|
419
|
+
local keg_setsid="${brew_prefix}/opt/util-linux/bin/setsid"
|
|
420
|
+
local link_target="${brew_prefix}/bin/setsid"
|
|
421
|
+
if [[ -x "$keg_setsid" && ! -e "$link_target" ]]; then
|
|
422
|
+
ln -s "$keg_setsid" "$link_target" && \
|
|
423
|
+
print_success "Symlinked setsid: $keg_setsid → $link_target"
|
|
424
|
+
fi
|
|
425
|
+
# Verify setsid is now reachable
|
|
426
|
+
if command -v setsid >/dev/null 2>&1; then
|
|
427
|
+
print_success "setsid installed at $(command -v setsid) (worker PGID isolation enabled)"
|
|
428
|
+
else
|
|
429
|
+
print_error "util-linux installed but setsid still not in PATH — check brew --prefix"
|
|
430
|
+
fi
|
|
431
|
+
else
|
|
432
|
+
print_error "brew install util-linux failed — workers will share pulse PGID until resolved"
|
|
433
|
+
echo " Manual fix: brew install util-linux"
|
|
434
|
+
fi
|
|
415
435
|
else
|
|
416
|
-
|
|
436
|
+
print_error "setsid not found — worker isolation broken; install util-linux"
|
|
437
|
+
echo " Impact: every pulse restart sends SIGHUP to workers in its PGID,"
|
|
438
|
+
echo " killing in-flight workers before they can finish (GH#21102)"
|
|
439
|
+
echo " Fix: install Homebrew, then run: brew install util-linux"
|
|
417
440
|
echo " Or upgrade to macOS 12+ where /usr/bin/setsid ships by default"
|
|
418
441
|
fi
|
|
419
442
|
else
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
443
|
+
# Linux: setsid should be present on all mainstream distros via util-linux.
|
|
444
|
+
# If it is missing, emit an error rather than a warning — workers will be
|
|
445
|
+
# killed on every pulse cycle restart without it.
|
|
446
|
+
print_error "setsid not found — worker isolation broken; install util-linux"
|
|
447
|
+
echo " Impact: every pulse restart sends SIGHUP to workers in its PGID,"
|
|
448
|
+
echo " killing in-flight workers before they can finish (GH#21102)"
|
|
449
|
+
echo " Fix: sudo apt install util-linux # Debian/Ubuntu"
|
|
450
|
+
echo " sudo dnf install util-linux # Fedora/RHEL"
|
|
451
|
+
echo " sudo pacman -S util-linux # Arch"
|
|
452
|
+
echo " sudo apk add util-linux # Alpine"
|
|
423
453
|
fi
|
|
424
454
|
echo ""
|
|
425
455
|
|
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.
|
|
15
|
+
# Version: 3.12.0
|
|
16
16
|
#
|
|
17
17
|
# Quick Install:
|
|
18
18
|
# npm install -g aidevops && aidevops update (recommended)
|
|
@@ -243,7 +243,16 @@ _launchd_install_if_changed() {
|
|
|
243
243
|
|
|
244
244
|
# Atomic write: build at sibling tmp path, then rename into place.
|
|
245
245
|
# If printf is killed mid-write, the destination is untouched.
|
|
246
|
-
|
|
246
|
+
# mktemp avoids predictable tmp names (defense-in-depth against symlink attacks).
|
|
247
|
+
local tmp_plist
|
|
248
|
+
tmp_plist=$(mktemp "${plist_path}.XXXXXX") || return 1
|
|
249
|
+
# Guard: refuse to write empty content — catching this before the write avoids
|
|
250
|
+
# creating a tmp file that the file-size check would also catch, but the
|
|
251
|
+
# content check is more direct and gives a clearer failure point.
|
|
252
|
+
if [[ -z "$new_content" ]]; then
|
|
253
|
+
rm -f "$tmp_plist"
|
|
254
|
+
return 1
|
|
255
|
+
fi
|
|
247
256
|
if ! printf '%s\n' "$new_content" >"$tmp_plist"; then
|
|
248
257
|
rm -f "$tmp_plist"
|
|
249
258
|
return 1
|
|
@@ -254,7 +263,10 @@ _launchd_install_if_changed() {
|
|
|
254
263
|
rm -f "$tmp_plist"
|
|
255
264
|
return 1
|
|
256
265
|
fi
|
|
257
|
-
mv -f "$tmp_plist" "$plist_path"
|
|
266
|
+
if ! mv -f "$tmp_plist" "$plist_path"; then
|
|
267
|
+
rm -f "$tmp_plist"
|
|
268
|
+
return 1
|
|
269
|
+
fi
|
|
258
270
|
launchctl load "$plist_path" 2>/dev/null || return 1
|
|
259
271
|
return 0
|
|
260
272
|
}
|
|
@@ -959,6 +971,62 @@ _deploy_hotfix_config() {
|
|
|
959
971
|
return 0
|
|
960
972
|
}
|
|
961
973
|
|
|
974
|
+
# t2919: Early pulse plist install. The pulse launchd agent is critical
|
|
975
|
+
# infrastructure — without it, every other pulse-driven feature (worker
|
|
976
|
+
# dispatch, issue routing, cross-repo coordination) is dead. Previously,
|
|
977
|
+
# setup_supervisor_pulse only ran inside _setup_post_setup_steps which
|
|
978
|
+
# executes AFTER ~25 other migration/setup steps. When `aidevops update`
|
|
979
|
+
# runs unattended and any earlier step times out (e.g. brew taps, MCP
|
|
980
|
+
# installs, slow repo scans), the pulse plist never gets installed/refreshed
|
|
981
|
+
# and the runner falls behind.
|
|
982
|
+
#
|
|
983
|
+
# Install immediately after deploy_aidevops_agents (so the scripts the plist
|
|
984
|
+
# references already exist on disk). The late install in _setup_post_setup_steps
|
|
985
|
+
# remains as the canonical regenerate-on-change path — _launchd_install_if_changed
|
|
986
|
+
# compares content and skips reload when identical, so the second call is a
|
|
987
|
+
# no-op when nothing changed. Failure here is non-fatal: the late path retries.
|
|
988
|
+
_setup_install_pulse_plist_early() {
|
|
989
|
+
local _early_os
|
|
990
|
+
_early_os="$(uname -s)"
|
|
991
|
+
if _should_setup_noninteractive_supervisor_pulse; then
|
|
992
|
+
setup_supervisor_pulse "$_early_os" || print_warning "Early pulse plist install failed (will retry late)"
|
|
993
|
+
fi
|
|
994
|
+
return 0
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
# Provision knowledge planes for all repos in repos.json where knowledge != "off".
|
|
998
|
+
# Idempotent: already-provisioned directories are not modified.
|
|
999
|
+
# Called from the non-interactive setup path (update) and after interactive init.
|
|
1000
|
+
setup_knowledge_planes() {
|
|
1001
|
+
local repos_file="$HOME/.config/aidevops/repos.json"
|
|
1002
|
+
local helper
|
|
1003
|
+
helper="${BASH_SOURCE[0]%/*}/.agents/scripts/knowledge-helper.sh"
|
|
1004
|
+
if [[ ! -f "$helper" ]]; then
|
|
1005
|
+
helper="$HOME/.aidevops/agents/scripts/knowledge-helper.sh"
|
|
1006
|
+
fi
|
|
1007
|
+
if [[ ! -f "$helper" ]]; then
|
|
1008
|
+
print_warning "knowledge-helper.sh not found — skipping knowledge plane provisioning"
|
|
1009
|
+
return 0
|
|
1010
|
+
fi
|
|
1011
|
+
if [[ ! -f "$repos_file" ]]; then
|
|
1012
|
+
return 0
|
|
1013
|
+
fi
|
|
1014
|
+
if ! command -v jq &>/dev/null; then
|
|
1015
|
+
print_warning "jq not installed — skipping knowledge plane provisioning"
|
|
1016
|
+
return 0
|
|
1017
|
+
fi
|
|
1018
|
+
local repo_path mode
|
|
1019
|
+
while IFS=$'\t' read -r repo_path mode; do
|
|
1020
|
+
[[ -z "$repo_path" || "$mode" == "off" ]] && continue
|
|
1021
|
+
if [[ ! -d "$repo_path" ]]; then
|
|
1022
|
+
print_warning "knowledge-plane: repo path not found: $repo_path"
|
|
1023
|
+
continue
|
|
1024
|
+
fi
|
|
1025
|
+
bash "$helper" provision "$repo_path" || print_warning "knowledge-plane: provision failed for $repo_path"
|
|
1026
|
+
done < <(jq -r '.initialized_repos[] | select(.knowledge != null and .knowledge != "off") | [.path, .knowledge] | @tsv' "$repos_file" 2>/dev/null || true)
|
|
1027
|
+
return 0
|
|
1028
|
+
}
|
|
1029
|
+
|
|
962
1030
|
# Non-interactive path: deploy agents and run safe migrations only (no prompts).
|
|
963
1031
|
_setup_run_non_interactive() {
|
|
964
1032
|
print_info "Non-interactive mode: deploying agents and running safe migrations only"
|
|
@@ -966,6 +1034,9 @@ _setup_run_non_interactive() {
|
|
|
966
1034
|
check_requirements
|
|
967
1035
|
# Run quality tool detection in non-interactive mode too (warn-only path).
|
|
968
1036
|
check_quality_tools
|
|
1037
|
+
# Check setsid availability; auto-install util-linux on macOS if missing
|
|
1038
|
+
# (GH#21102 / t2926: missing setsid kills workers on every pulse restart).
|
|
1039
|
+
setup_setsid_advisory
|
|
969
1040
|
check_python_upgrade_available
|
|
970
1041
|
set_permissions
|
|
971
1042
|
migrate_old_backups
|
|
@@ -990,6 +1061,7 @@ _setup_run_non_interactive() {
|
|
|
990
1061
|
setup_opencode_cli
|
|
991
1062
|
validate_opencode_config
|
|
992
1063
|
deploy_aidevops_agents
|
|
1064
|
+
_setup_install_pulse_plist_early
|
|
993
1065
|
_deploy_hotfix_config
|
|
994
1066
|
sync_agent_sources
|
|
995
1067
|
install_aidevops_cli
|
|
@@ -1052,6 +1124,8 @@ _setup_run_non_interactive() {
|
|
|
1052
1124
|
# copies doesn't burn CPU (t2885). Idempotent. macOS only — Linux
|
|
1053
1125
|
# indexers tracked separately.
|
|
1054
1126
|
setup_worktree_exclusions
|
|
1127
|
+
# Provision knowledge planes for repos where knowledge != "off" (idempotent).
|
|
1128
|
+
setup_knowledge_planes
|
|
1055
1129
|
return 0
|
|
1056
1130
|
}
|
|
1057
1131
|
|
|
@@ -1159,6 +1233,72 @@ _setup_run_interactive() {
|
|
|
1159
1233
|
}
|
|
1160
1234
|
|
|
1161
1235
|
# Post-setup steps: schedulers, final instructions, optional tool update check.
|
|
1236
|
+
# Non-interactive scheduler installation. Extracted from
|
|
1237
|
+
# `_setup_post_setup_steps` (t2903) to keep the parent under the
|
|
1238
|
+
# function-complexity gate threshold. Each `_should_setup_noninteractive_*`
|
|
1239
|
+
# guard returns 0 when the corresponding scheduler is already installed
|
|
1240
|
+
# (regenerate on update) or first-time install is consented via config.
|
|
1241
|
+
_setup_noninteractive_schedulers() {
|
|
1242
|
+
local os="$1"
|
|
1243
|
+
|
|
1244
|
+
# Auto-update handles non-interactive internally (systemd detection fixed in GH#17861)
|
|
1245
|
+
setup_auto_update
|
|
1246
|
+
if _should_setup_noninteractive_supervisor_pulse; then
|
|
1247
|
+
setup_supervisor_pulse "$os"
|
|
1248
|
+
fi
|
|
1249
|
+
# Regenerate other schedulers if already installed (GH#17695 Finding B).
|
|
1250
|
+
# Stats wrapper is a pulse dependency — also install on first run when
|
|
1251
|
+
# the supervisor pulse is consented (t2418, GH#20016).
|
|
1252
|
+
if _should_setup_noninteractive_stats_wrapper; then
|
|
1253
|
+
setup_stats_wrapper "${PULSE_ENABLED:-}"
|
|
1254
|
+
fi
|
|
1255
|
+
if _should_setup_noninteractive_scheduler "Failure miner" "sh.aidevops.routine-gh-failure-miner" "aidevops: gh-failure-miner" "aidevops-gh-failure-miner"; then
|
|
1256
|
+
setup_failure_miner "${PULSE_ENABLED:-}"
|
|
1257
|
+
fi
|
|
1258
|
+
if _should_setup_noninteractive_scheduler "Process guard" "sh.aidevops.process-guard" "aidevops: process-guard" "aidevops-process-guard"; then
|
|
1259
|
+
setup_process_guard
|
|
1260
|
+
fi
|
|
1261
|
+
if _should_setup_noninteractive_scheduler "Memory pressure" "sh.aidevops.memory-pressure-monitor" "aidevops: memory-pressure-monitor" "aidevops-memory-pressure-monitor"; then
|
|
1262
|
+
setup_memory_pressure_monitor
|
|
1263
|
+
fi
|
|
1264
|
+
if _should_setup_noninteractive_scheduler "Screen time" "sh.aidevops.screen-time-snapshot" "aidevops: screen-time-snapshot" "aidevops-screen-time-snapshot"; then
|
|
1265
|
+
setup_screen_time_snapshot
|
|
1266
|
+
fi
|
|
1267
|
+
if _should_setup_noninteractive_scheduler "Contribution watch" "sh.aidevops.contribution-watch" "aidevops: contribution-watch" "aidevops-contribution-watch"; then
|
|
1268
|
+
setup_contribution_watch
|
|
1269
|
+
fi
|
|
1270
|
+
# t2903 (#21049): complexity scan — extracted from pulse dispatch preflight
|
|
1271
|
+
if _should_setup_noninteractive_scheduler "Complexity scan" "sh.aidevops.complexity-scan" "aidevops: complexity-scan" "aidevops-complexity-scan"; then
|
|
1272
|
+
setup_complexity_scan
|
|
1273
|
+
fi
|
|
1274
|
+
# t2862 (GH#20919): pulse merge routine — fast 120s standalone merge pass
|
|
1275
|
+
if _should_setup_noninteractive_scheduler "Pulse merge routine" "sh.aidevops.pulse-merge-routine" "aidevops: pulse-merge-routine" "aidevops-pulse-merge-routine"; then
|
|
1276
|
+
setup_pulse_merge_routine
|
|
1277
|
+
fi
|
|
1278
|
+
# t2932 (GH#21125): peer productivity monitor — adaptive cross-runner
|
|
1279
|
+
# dispatch coordination, runs every 30 min.
|
|
1280
|
+
if _should_setup_noninteractive_scheduler "Peer productivity monitor" "sh.aidevops.peer-productivity-monitor" "aidevops: peer-productivity-monitor" "aidevops-peer-productivity-monitor"; then
|
|
1281
|
+
setup_peer_productivity_monitor
|
|
1282
|
+
fi
|
|
1283
|
+
# Repo sync handles non-interactive mode internally (systemd detection fixed in GH#17861)
|
|
1284
|
+
setup_repo_sync
|
|
1285
|
+
# r914 repo-aidevops-health — daily drift keeper (t2366)
|
|
1286
|
+
setup_repo_aidevops_health
|
|
1287
|
+
if _should_setup_noninteractive_scheduler "Profile README" "sh.aidevops.profile-readme-update" "aidevops: profile-readme-update" "aidevops-profile-readme-update"; then
|
|
1288
|
+
setup_profile_readme
|
|
1289
|
+
fi
|
|
1290
|
+
if _should_setup_noninteractive_scheduler "OAuth token refresh" "sh.aidevops.token-refresh" "aidevops: token-refresh" "aidevops-token-refresh"; then
|
|
1291
|
+
setup_oauth_token_refresh
|
|
1292
|
+
fi
|
|
1293
|
+
# opencode DB maintenance (r913, t2183). Helper self-noops on missing
|
|
1294
|
+
# DB — safe to install unconditionally in non-interactive mode too.
|
|
1295
|
+
setup_opencode_db_maintenance
|
|
1296
|
+
# Migrate cron entries to systemd after schedulers are installed (GH#17695 Finding D)
|
|
1297
|
+
migrate_cron_to_systemd
|
|
1298
|
+
setup_tabby
|
|
1299
|
+
return 0
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1162
1302
|
_setup_post_setup_steps() {
|
|
1163
1303
|
local os="$1"
|
|
1164
1304
|
|
|
@@ -1184,48 +1324,7 @@ _setup_post_setup_steps() {
|
|
|
1184
1324
|
# Exceptions: regenerate existing schedulers (GH#17381, GH#17695 Finding B)
|
|
1185
1325
|
# and allow first-time install when config consent is explicitly true (GH#17403).
|
|
1186
1326
|
if [[ "$NON_INTERACTIVE" == "true" ]]; then
|
|
1187
|
-
|
|
1188
|
-
setup_auto_update
|
|
1189
|
-
if _should_setup_noninteractive_supervisor_pulse; then
|
|
1190
|
-
setup_supervisor_pulse "$os"
|
|
1191
|
-
fi
|
|
1192
|
-
# Regenerate other schedulers if already installed (GH#17695 Finding B).
|
|
1193
|
-
# Stats wrapper is a pulse dependency — also install on first run when
|
|
1194
|
-
# the supervisor pulse is consented (t2418, GH#20016).
|
|
1195
|
-
if _should_setup_noninteractive_stats_wrapper; then
|
|
1196
|
-
setup_stats_wrapper "${PULSE_ENABLED:-}"
|
|
1197
|
-
fi
|
|
1198
|
-
if _should_setup_noninteractive_scheduler "Failure miner" "sh.aidevops.routine-gh-failure-miner" "aidevops: gh-failure-miner" "aidevops-gh-failure-miner"; then
|
|
1199
|
-
setup_failure_miner "${PULSE_ENABLED:-}"
|
|
1200
|
-
fi
|
|
1201
|
-
if _should_setup_noninteractive_scheduler "Process guard" "sh.aidevops.process-guard" "aidevops: process-guard" "aidevops-process-guard"; then
|
|
1202
|
-
setup_process_guard
|
|
1203
|
-
fi
|
|
1204
|
-
if _should_setup_noninteractive_scheduler "Memory pressure" "sh.aidevops.memory-pressure-monitor" "aidevops: memory-pressure-monitor" "aidevops-memory-pressure-monitor"; then
|
|
1205
|
-
setup_memory_pressure_monitor
|
|
1206
|
-
fi
|
|
1207
|
-
if _should_setup_noninteractive_scheduler "Screen time" "sh.aidevops.screen-time-snapshot" "aidevops: screen-time-snapshot" "aidevops-screen-time-snapshot"; then
|
|
1208
|
-
setup_screen_time_snapshot
|
|
1209
|
-
fi
|
|
1210
|
-
if _should_setup_noninteractive_scheduler "Contribution watch" "sh.aidevops.contribution-watch" "aidevops: contribution-watch" "aidevops-contribution-watch"; then
|
|
1211
|
-
setup_contribution_watch
|
|
1212
|
-
fi
|
|
1213
|
-
# Repo sync handles non-interactive mode internally (systemd detection fixed in GH#17861)
|
|
1214
|
-
setup_repo_sync
|
|
1215
|
-
# r914 repo-aidevops-health — daily drift keeper (t2366)
|
|
1216
|
-
setup_repo_aidevops_health
|
|
1217
|
-
if _should_setup_noninteractive_scheduler "Profile README" "sh.aidevops.profile-readme-update" "aidevops: profile-readme-update" "aidevops-profile-readme-update"; then
|
|
1218
|
-
setup_profile_readme
|
|
1219
|
-
fi
|
|
1220
|
-
if _should_setup_noninteractive_scheduler "OAuth token refresh" "sh.aidevops.token-refresh" "aidevops: token-refresh" "aidevops-token-refresh"; then
|
|
1221
|
-
setup_oauth_token_refresh
|
|
1222
|
-
fi
|
|
1223
|
-
# opencode DB maintenance (r913, t2183). Helper self-noops on missing
|
|
1224
|
-
# DB — safe to install unconditionally in non-interactive mode too.
|
|
1225
|
-
setup_opencode_db_maintenance
|
|
1226
|
-
# Migrate cron entries to systemd after schedulers are installed (GH#17695 Finding D)
|
|
1227
|
-
migrate_cron_to_systemd
|
|
1228
|
-
setup_tabby
|
|
1327
|
+
_setup_noninteractive_schedulers "$os"
|
|
1229
1328
|
return 0
|
|
1230
1329
|
fi
|
|
1231
1330
|
|
|
@@ -1241,6 +1340,8 @@ _setup_post_setup_steps() {
|
|
|
1241
1340
|
setup_memory_pressure_monitor
|
|
1242
1341
|
setup_screen_time_snapshot
|
|
1243
1342
|
setup_contribution_watch
|
|
1343
|
+
setup_complexity_scan
|
|
1344
|
+
setup_pulse_merge_routine
|
|
1244
1345
|
setup_draft_responses
|
|
1245
1346
|
setup_profile_readme
|
|
1246
1347
|
setup_oauth_token_refresh
|