aidevops 3.12.0 → 3.13.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 CHANGED
@@ -1 +1 @@
1
- 3.12.0
1
+ 3.13.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.12.0
8
+ # Version: 3.13.0
9
9
 
10
10
  set -euo pipefail
11
11
 
@@ -1825,9 +1825,24 @@ _cmd_email() {
1825
1825
  ;;
1826
1826
  poll)
1827
1827
  # Direct poll commands forwarded to email-poll-helper.sh
1828
- _dispatch_helper "$_EPH" "$_EPH" "$action" "$@" ;;
1828
+ local poll_action="${1:-tick}"
1829
+ shift || true
1830
+ _dispatch_helper "$_EPH" "$_EPH" "$poll_action" "$@" ;;
1831
+ thread)
1832
+ # Thread lookup: email thread <message-id> [knowledge-root]
1833
+ local _ETH="email-thread-helper.sh"
1834
+ _dispatch_helper "$_ETH" "$_ETH" thread "$@" ;;
1835
+ build)
1836
+ # Thread rebuild: email build [knowledge-root] [--force]
1837
+ local _ETH2="email-thread-helper.sh"
1838
+ _dispatch_helper "$_ETH2" "$_ETH2" build "$@" ;;
1839
+ filter)
1840
+ # Filter rules: email filter tick|add|test|list [knowledge-root]
1841
+ local _EFH="email-filter-helper.sh"
1842
+ [[ $# -eq 0 ]] && set -- list
1843
+ _dispatch_helper "$_EFH" "$_EFH" "$@" ;;
1829
1844
  *)
1830
- echo "Usage: aidevops email <mailbox|poll> [subcommand]"
1845
+ echo "Usage: aidevops email <mailbox|poll|thread|build|filter> [subcommand]"
1831
1846
  echo ""
1832
1847
  echo "Email subcommands:"
1833
1848
  echo " mailbox add Register a new IMAP mailbox (interactive)"
@@ -1836,6 +1851,12 @@ _cmd_email() {
1836
1851
  echo " mailbox remove <id> Un-register a mailbox"
1837
1852
  echo " poll tick Poll all mailboxes now (same as routine r044)"
1838
1853
  echo " poll backfill <id> Backfill a mailbox from a given date"
1854
+ echo " thread <message-id> Look up thread by message-id"
1855
+ echo " build [--force] Rebuild thread index from email sources"
1856
+ echo " filter list List filter rules"
1857
+ echo " filter add Add a new filter rule (interactive)"
1858
+ echo " filter test <rule> Dry-run rule against last 50 sources"
1859
+ echo " filter tick Run filter pass (routine r045)"
1839
1860
  ;;
1840
1861
  esac
1841
1862
  return 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidevops",
3
- "version": "3.12.0",
3
+ "version": "3.13.0",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,6 +14,11 @@ PULSE_STALE_THRESHOLD_SECONDS=1800
14
14
  # future cadence shift only touches one place.
15
15
  CRON_HOURLY="0 * * * *"
16
16
 
17
+ # Cron expression: every minute. Shared by process-guard, memory-pressure
18
+ # monitor, and pulse-watchdog schedulers (cron's minimum granularity).
19
+ # Kept DRY for the same reason as CRON_HOURLY.
20
+ CRON_EVERY_MINUTE="* * * * *"
21
+
17
22
  # Resolve the modern bash binary path for use in launchd ProgramArguments.
18
23
  # Launchd bypasses the shebang when ProgramArguments specifies an explicit
19
24
  # interpreter, so we must resolve the path at plist generation time.
@@ -668,7 +673,12 @@ ${_env_overrides_xml} </dict>
668
673
  <key>RunAtLoad</key>
669
674
  <true/>
670
675
  <key>KeepAlive</key>
671
- <false/>
676
+ <dict>
677
+ <key>SuccessfulExit</key>
678
+ <false/>
679
+ </dict>
680
+ <key>ThrottleInterval</key>
681
+ <integer>30</integer>
672
682
  </dict>
673
683
  </plist>
674
684
  PLIST
@@ -731,6 +741,161 @@ _install_pulse_launchd() {
731
741
  return 0
732
742
  }
733
743
 
744
+ # Generate the pulse-watchdog launchd plist XML content.
745
+ # Args: $1=label, $2=tick_script, $3=bash_bin
746
+ # Prints the complete plist XML to stdout.
747
+ #
748
+ # The watchdog is an independent launchd job that runs every 60s and revives
749
+ # pulse if it has been dead longer than (StartInterval + grace). Layered
750
+ # defense alongside the pulse plist's KeepAlive=<dict><SuccessfulExit=false>
751
+ # (auto-restart on crash) and StartInterval (scheduled cadence). Catches the
752
+ # "clean exit + lost launchd schedule" failure mode that no other layer covers.
753
+ # (t2939)
754
+ _generate_pulse_watchdog_plist_content() {
755
+ local watchdog_label="$1"
756
+ local tick_script="$2"
757
+ local bash_bin="$3"
758
+
759
+ local _xml_label _xml_tick _xml_bash _xml_home _xml_path
760
+ _xml_label=$(_xml_escape "$watchdog_label")
761
+ _xml_tick=$(_xml_escape "$tick_script")
762
+ _xml_bash=$(_xml_escape "$bash_bin")
763
+ _xml_home=$(_xml_escape "$HOME")
764
+ _xml_path=$(_xml_escape "$PATH")
765
+
766
+ cat <<PLIST
767
+ <?xml version="1.0" encoding="UTF-8"?>
768
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
769
+ <plist version="1.0">
770
+ <dict>
771
+ <key>Label</key>
772
+ <string>${_xml_label}</string>
773
+ <key>ProgramArguments</key>
774
+ <array>
775
+ <string>${_xml_bash}</string>
776
+ <string>${_xml_tick}</string>
777
+ </array>
778
+ <key>StartInterval</key>
779
+ <integer>60</integer>
780
+ <key>StandardOutPath</key>
781
+ <string>${_xml_home}/.aidevops/logs/pulse-watchdog-launchd.log</string>
782
+ <key>StandardErrorPath</key>
783
+ <string>${_xml_home}/.aidevops/logs/pulse-watchdog-launchd.log</string>
784
+ <key>EnvironmentVariables</key>
785
+ <dict>
786
+ <key>PATH</key>
787
+ <string>${_xml_path}</string>
788
+ <key>HOME</key>
789
+ <string>${_xml_home}</string>
790
+ </dict>
791
+ <key>RunAtLoad</key>
792
+ <true/>
793
+ <key>KeepAlive</key>
794
+ <false/>
795
+ <key>ThrottleInterval</key>
796
+ <integer>30</integer>
797
+ </dict>
798
+ </plist>
799
+ PLIST
800
+ return 0
801
+ }
802
+
803
+ # Install the pulse-watchdog via launchd (macOS).
804
+ # t2939: independent revival mechanism — see _generate_pulse_watchdog_plist_content
805
+ # header for the layering rationale.
806
+ _install_pulse_watchdog_launchd() {
807
+ local watchdog_label="sh.aidevops.pulse-watchdog"
808
+ local tick_script="$HOME/.aidevops/agents/scripts/pulse-watchdog-tick.sh"
809
+ local watchdog_plist="$HOME/Library/LaunchAgents/${watchdog_label}.plist"
810
+
811
+ # Refuse to install if the tick script is missing — the watchdog would
812
+ # fire-and-fail every 60s, polluting logs without doing useful work.
813
+ if [[ ! -x "$tick_script" ]]; then
814
+ print_warning "Pulse watchdog tick script missing or non-executable: $tick_script"
815
+ return 1
816
+ fi
817
+
818
+ local _xml_bash_bin
819
+ _xml_bash_bin=$(_resolve_modern_bash)
820
+
821
+ local watchdog_plist_content
822
+ watchdog_plist_content=$(_generate_pulse_watchdog_plist_content "$watchdog_label" "$tick_script" "$_xml_bash_bin")
823
+
824
+ if [[ -z "$watchdog_plist_content" ]]; then
825
+ print_warning "Pulse watchdog plist generation produced empty content — skipping"
826
+ return 1
827
+ fi
828
+
829
+ # shell-portability: ignore next — _install_pulse_watchdog_launchd is macOS-only
830
+ if _launchd_install_if_changed "$watchdog_label" "$watchdog_plist" "$watchdog_plist_content"; then
831
+ print_info "Pulse watchdog enabled (launchd, every 60s)"
832
+ else
833
+ print_warning "Failed to load pulse watchdog LaunchAgent"
834
+ fi
835
+ return 0
836
+ }
837
+
838
+ # Install the pulse-watchdog via systemd (Linux).
839
+ # t2939: parallels _install_pulse_watchdog_launchd for systems with systemd --user.
840
+ _install_pulse_watchdog_systemd() {
841
+ local tick_script="$HOME/.aidevops/agents/scripts/pulse-watchdog-tick.sh"
842
+ local watchdog_systemd="aidevops-pulse-watchdog"
843
+ local watchdog_log="$HOME/.aidevops/logs/pulse-watchdog-launchd.log"
844
+
845
+ if [[ ! -x "$tick_script" ]]; then
846
+ print_warning "Pulse watchdog tick script missing or non-executable: $tick_script"
847
+ return 1
848
+ fi
849
+
850
+ # Reuse the standard scheduler installer (cron-fallback aware).
851
+ # StartInterval=60 maps to every-minute cron schedule.
852
+ # shell-portability: ignore next — _install_scheduler_linux is Linux-only
853
+ _install_scheduler_linux \
854
+ "$watchdog_systemd" \
855
+ "aidevops: pulse-watchdog" \
856
+ "$CRON_EVERY_MINUTE" \
857
+ "\"${tick_script}\"" \
858
+ "60" \
859
+ "$watchdog_log" \
860
+ "" \
861
+ "Pulse watchdog enabled (every 60s)" \
862
+ "Failed to install pulse watchdog scheduler" \
863
+ "true" \
864
+ "false"
865
+ return 0
866
+ }
867
+
868
+ # Setup the pulse-watchdog scheduler (parallels setup_supervisor_pulse).
869
+ # t2939: layered defense — only installs when supervisor pulse is enabled,
870
+ # since a watchdog without a pulse to watch is a no-op every 60s.
871
+ #
872
+ # Args: $1 = pulse effective state ("true"/"false")
873
+ setup_pulse_watchdog() {
874
+ local _pulse_effective="$1"
875
+ local watchdog_label="sh.aidevops.pulse-watchdog"
876
+ local watchdog_systemd="aidevops-pulse-watchdog"
877
+
878
+ if [[ "$_pulse_effective" != "true" ]]; then
879
+ # Pulse disabled — uninstall the watchdog if present.
880
+ _uninstall_scheduler \
881
+ "$(uname -s)" \
882
+ "$watchdog_label" \
883
+ "$watchdog_systemd" \
884
+ "aidevops: pulse-watchdog" \
885
+ "Pulse watchdog disabled (pulse is off)"
886
+ return 0
887
+ fi
888
+
889
+ mkdir -p "$HOME/.aidevops/logs"
890
+
891
+ if [[ "$(uname -s)" == "Darwin" ]]; then
892
+ _install_pulse_watchdog_launchd
893
+ else
894
+ _install_pulse_watchdog_systemd
895
+ fi
896
+ return 0
897
+ }
898
+
734
899
  # Check if systemd user services are available on this Linux system.
735
900
  # Returns 0 if systemd --user is functional, 1 otherwise.
736
901
  _systemd_user_available() {
@@ -1352,7 +1517,7 @@ GUARD_PLIST
1352
1517
  _install_scheduler_linux \
1353
1518
  "$guard_systemd" \
1354
1519
  "aidevops: process-guard" \
1355
- "* * * * *" \
1520
+ "$CRON_EVERY_MINUTE" \
1356
1521
  "\"${guard_script}\" kill-runaways" \
1357
1522
  "30" \
1358
1523
  "$guard_log" \
@@ -1444,7 +1609,7 @@ MONITOR_PLIST
1444
1609
  _install_scheduler_linux \
1445
1610
  "$monitor_systemd" \
1446
1611
  "aidevops: memory-pressure-monitor" \
1447
- "* * * * *" \
1612
+ "$CRON_EVERY_MINUTE" \
1448
1613
  "\"${monitor_script}\"" \
1449
1614
  "60" \
1450
1615
  "$monitor_log" \
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.12.0
15
+ # Version: 3.13.0
16
16
  #
17
17
  # Quick Install:
18
18
  # npm install -g aidevops && aidevops update (recommended)
@@ -1246,6 +1246,12 @@ _setup_noninteractive_schedulers() {
1246
1246
  if _should_setup_noninteractive_supervisor_pulse; then
1247
1247
  setup_supervisor_pulse "$os"
1248
1248
  fi
1249
+ # t2939: pulse-watchdog (independent revival mechanism). Always installed
1250
+ # alongside the pulse — it is a no-op when pulse is disabled. Skipping the
1251
+ # `_should_setup_noninteractive_*` guard intentionally: this is layered
1252
+ # defense, the cost of installing it is one plist file, and the user opts
1253
+ # in by enabling the pulse itself.
1254
+ setup_pulse_watchdog "${PULSE_ENABLED:-}"
1249
1255
  # Regenerate other schedulers if already installed (GH#17695 Finding B).
1250
1256
  # Stats wrapper is a pulse dependency — also install on first run when
1251
1257
  # the supervisor pulse is consented (t2418, GH#20016).
@@ -1331,6 +1337,8 @@ _setup_post_setup_steps() {
1331
1337
  # Post-setup: auto-update, schedulers, final instructions (GH#5793)
1332
1338
  setup_auto_update
1333
1339
  setup_supervisor_pulse "$os"
1340
+ # t2939: pulse-watchdog — independent revival mechanism, layered defense.
1341
+ setup_pulse_watchdog "${PULSE_ENABLED:-}"
1334
1342
  setup_stats_wrapper "${PULSE_ENABLED:-}"
1335
1343
  setup_failure_miner "${PULSE_ENABLED:-}"
1336
1344
  setup_repo_sync