Skip to content

Commit afaca0b

Browse files
committed
feat: Enhanced CLI Watcher with automatic terminal process monitoring
CLI Watcher now automatically detects and tracks terminal-based development activity, distinguishing between interactive CLIs (vim, REPLs) and work processes (builds, deploys) without manual configuration. Key Features: - Fully automatic: Only requires 'enabled: true' - auto-detects ALL terminal processes - Smart defaults: Adapts activityWindow and gracePeriod to workspace idle timeout - Automatic classification: Interactive (vim, python REPL) vs work (builds, deploys) - Backward compatible: Old configs continue working with deprecation warnings - Safety mechanisms: Grace period (5min), max age limit (6h), activity monitoring - Flexible duration parsing: Accepts 6h, 25m, 60 (seconds), or duration strings Configuration: - Minimal: enabled: true (that's it!) - Smart defaults: Auto-calculated from workspace idle timeout - Time settings: checkPeriod, activityWindow, gracePeriod, maxProcessAge - Override options: watchedCommands (fix misclassification), ignoredCommands (skip entirely) Technical Implementation: - User process filtering: Parent TTY chain analysis (filters shells, system processes) - Interactive detection: Foreground process group + TTY read analysis - Activity tracking: TTY access time (Atime) monitoring for user input - Multicall binary support: Uses /proc/comm for proper command detection - Self-skip: che-machine-exec doesn't watch itself - Config validation: Warns about misconfigurations without breaking - Platform portability: Detects clock ticks from /proc/self/auxv with platform-specific fallbacks - Performance: Boot time and clock ticks cached at startup (not per process) - Robustness: Infinite loop protection, cycle detection, error checking on all parsing Code Quality Improvements: - Added comprehensive unit tests (79 test cases covering pure functions and concurrency) - Tests cover: duration parsing, YAML unmarshaling, defaults, validation, platform detection, applyPolicy, race conditions - Merged duplicate policy logic into single unified function - Added error checking to all fmt.Sscanf calls for consistency - Fixed comment-code mismatches for clarity - Warning on breaking changes (globally-excluded commands) Test Coverage: - Added concurrent Start/Stop tests (catches race conditions) - Added clock skew tests (documents negative age handling) - Enhanced /proc parsing integration tests - All 79 tests pass with -race flag - No data races detected All issues verified with test-driven approach: wrote tests first to detect bugs, then fixed implementation. Thread-safety verified with Go race detector (-race flag). Documentation: - timeout/CLI-WATCHER.md - Comprehensive guide with testing scenarios - timeout/.noidle.minimal - Minimal config example - timeout/.noidle.example - Full config with all options - Added auto-detection test scenario for manual testing - Added comprehensive upgrade guide for breaking behavioral changes - Developer testing section added Issue: https://redhat.atlassian.net/browse/CRW-11505 Assisted-By: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
1 parent d54935d commit afaca0b

8 files changed

Lines changed: 2853 additions & 65 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ _testmain.go
2323
*.test
2424
*.prof
2525
.idea/
26+
.claude/
2627

2728
che-machine-exec

devfile.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ components:
3232
type: terminal-dev
3333
protocol: ws
3434
secure: false
35-
image: registry.access.redhat.com/ubi9/go-toolset:1.25.5-1770596585
35+
image: registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778675823
3636
args:
3737
- tail
3838
- '-f'

timeout/.noidle.example

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Example .noidle configuration for CLI Watcher
2+
#
3+
# This file shows all available configuration options.
4+
# For minimal config, see .noidle.minimal (just 'enabled: true' is enough!)
5+
#
6+
# Place this file in your project directory, or in $HOME/.noidle
7+
# You can also set CLI_WATCHER_CONFIG environment variable to specify a custom path.
8+
#
9+
# NOTE: Comments (lines starting with #) are part of YAML syntax and work fine.
10+
# Copy any section below directly into your .noidle file.
11+
12+
enabled: true
13+
checkPeriod: 30 # How often to check for active processes (accepts: 30, 30s, etc.)
14+
activityWindow: 25m # How long to wait for activity from interactive processes
15+
gracePeriod: 5m # All processes prevent idling when this young
16+
maxProcessAge: 6h # Safety limit to prevent indefinite idling prevention
17+
18+
watchedCommands:
19+
# Simple string format (backward compatible)
20+
# These commands will be auto-detected (interactive vs work process)
21+
- claude # AI assistant, will be auto-detected as interactive (checks for user input activities)
22+
- gemini # AI assistant, will be auto-detected as interactive (checks for user input activities)
23+
- kubectl # Kubernetes deployment, will be auto-detected as non-interactive (always prevents idling)
24+
25+
# Object format with explicit interactive mode control
26+
- name: vim
27+
interactive: auto # Auto-detect based on foreground + TTY read behavior
28+
29+
# Force interactive mode (always check for user input activity)
30+
- name: claude
31+
interactive: true # Force interactive even if auto-detection thinks otherwise
32+
33+
# Long-running build/deploy commands - force non-interactive (always prevent idling)
34+
- name: npm
35+
interactive: false # Force non-interactive (always prevent idling)
36+
37+
- name: docker
38+
interactive: no # Same as 'false'
39+
40+
- name: gradle
41+
interactive: false # Force non-interactive (always prevent idling during builds)
42+
43+
# Override always-ignored commands (USE WITH CAUTION)
44+
- name: watch
45+
interactive: false
46+
forceWatch: true # Override always-ignored list to watch 'watch' command
47+
# Normally 'watch', 'top', 'htop', 'tail' are always ignored
48+
49+
- name: top
50+
forceWatch: yes # Also accepts: true, yes, false, no (default: false)
51+
52+
# Interactive mode options:
53+
# - auto: Auto-detect (foreground + has read from TTY → interactive, otherwise → work process)
54+
# - true/yes: Force interactive (always check for user input activity)
55+
# - false/no: Force non-interactive work process (always prevent idling, default)
56+
#
57+
# ForceWatch option (optional, defaults to false):
58+
# - true/yes: Override always-ignored list (tail, watch, top, htop) to monitor this command
59+
# - false/no: Respect always-ignored list (default behavior)
60+
# WARNING: Use forceWatch with caution. Always-ignored commands are typically passive
61+
# monitoring tools that shouldn't prevent workspace idling.
62+
#
63+
# Global settings:
64+
# - activityWindowSeconds: How long to wait for input from interactive processes (default: 1500 = 25 min)
65+
# - gracePeriodSeconds: All processes prevent idling when younger than this (default: 300 = 5 min)
66+
# - checkPeriodSeconds: How often to scan for processes (default: 60 = 1 min)
67+
#
68+
# Unconfigured commands:
69+
# - Auto-detected as interactive or work process during grace period
70+
# - After grace period: interactive processes checked for activity, work processes always prevent idling
71+
#
72+
# Always-ignored commands (never prevent idling):
73+
# - tail, watch, top, htop (passive monitoring tools)
74+
# These are always ignored even if explicitly listed in watchedCommands.

timeout/.noidle.minimal

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Minimal .noidle configuration
2+
#
3+
# This is the absolute minimum needed to enable CLI watcher.
4+
# All user processes will be watched with smart defaults.
5+
#
6+
# NOTE: Comments (lines starting with #) are supported in YAML.
7+
# You can copy these examples directly into your .noidle file.
8+
9+
enabled: true
10+
11+
# That's it! With just this, the CLI watcher will:
12+
#
13+
# ✅ Watch ALL user-initiated processes (processes with TTY from user terminals)
14+
# ✅ Auto-detect interactive processes (vim, python REPL, etc.) vs work processes (builds, deploys)
15+
# ✅ Interactive processes: check for user input activity within 25 minutes
16+
# ✅ Work processes: always prevent idling while running
17+
# ✅ All processes: prevent idling for first 5 minutes (grace period)
18+
# ✅ Safety limit: stop preventing idling after 6 hours (catches hung/forgotten processes)
19+
# ✅ Ignore passive monitoring tools: tail, watch, top, htop
20+
#
21+
# You can override any defaults:
22+
#
23+
# checkPeriod: 45 # Check every 45 seconds instead of default 60
24+
# activityWindow: 30m # 30 minutes instead of default 25m
25+
# gracePeriod: 10m # 10 minutes instead of default 5m
26+
# maxProcessAge: 8h # 8 hours instead of default 6h
27+
#
28+
# Explicitly configure specific commands only if you need to override auto-detection:
29+
#
30+
# watchedCommands:
31+
# - name: myTool
32+
# interactive: false # Force non-interactive (always prevent idling)

0 commit comments

Comments
 (0)