agex 0.2.4 → 0.2.7

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.
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ # Closes browser (video recording disabled)
3
+ agent-browser close {{SESSION_ARG}}
4
+ rm -f "{{OUTPUT_DIR}}/.session"
5
+ echo "Browser closed"
@@ -0,0 +1,130 @@
1
+ #!/bin/bash
2
+ # Merges video segments with crossfade, removes duplicate frames, and closes browser
3
+ # Usage: ab-close [segment_numbers...]
4
+ # Examples:
5
+ # ab-close # Merge all segments
6
+ # ab-close 1 3 5 # Merge only segments 1, 3, 5
7
+
8
+ # Remove duplicate/similar frames and create shortened video
9
+ deduplicate_video() {
10
+ local input="$1"
11
+ local output="$2"
12
+ local fps="$3"
13
+
14
+ echo "Removing duplicate frames (output fps: $fps)..."
15
+ # Suppress ffmpeg/ffprobe progress output (extremely verbose, confuses agent)
16
+ if ffmpeg -y -i "$input" -vf "mpdecimate,setpts=N/FRAME_RATE/TB" -r "$fps" -c:v libx264 "$output" 2>/dev/null; then
17
+ local orig_frames=$(ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=noprint_wrappers=1:nokey=1 "$input" 2>/dev/null)
18
+ local new_frames=$(ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=noprint_wrappers=1:nokey=1 "$output" 2>/dev/null)
19
+ echo "Deduplicated: $orig_frames -> $new_frames frames"
20
+ rm -f "$input"
21
+ return 0
22
+ else
23
+ echo "Warning: deduplication failed, keeping original"
24
+ mv "$input" "$output"
25
+ return 1
26
+ fi
27
+ }
28
+
29
+ # Wait to capture final frame before stopping recording
30
+ agent-browser wait 500 {{SESSION_ARG}} 2>&1 || true
31
+
32
+ # Stop any active recording first (ignore errors if not recording)
33
+ agent-browser record stop {{SESSION_ARG}} 2>&1 || true
34
+
35
+ # If segment numbers provided, use only those; otherwise use all
36
+ if [ $# -gt 0 ]; then
37
+ SEGMENTS=()
38
+ for num in "$@"; do
39
+ SEGMENT_FILE="{{SEGMENTS_DIR}}/segment-$(printf '%03d' $num).webm"
40
+ if [ -f "$SEGMENT_FILE" ]; then
41
+ SEGMENTS+=("$SEGMENT_FILE")
42
+ else
43
+ echo "Warning: segment $num not found ($SEGMENT_FILE)"
44
+ fi
45
+ done
46
+ echo "Using specified segments: $@"
47
+ else
48
+ SEGMENTS=($(ls -1 "{{SEGMENTS_DIR}}"/segment-*.webm 2>/dev/null | sort))
49
+ fi
50
+ SEGMENT_COUNT=${#SEGMENTS[@]}
51
+
52
+ if [ "$SEGMENT_COUNT" -eq "0" ]; then
53
+ echo "No video segments to merge"
54
+ elif [ "$SEGMENT_COUNT" -eq "1" ]; then
55
+ echo "Single segment, processing..."
56
+ cp "${SEGMENTS[0]}" "{{RAW_VIDEO_PATH}}"
57
+ deduplicate_video "{{RAW_VIDEO_PATH}}" "{{FINAL_VIDEO_PATH}}" {{DEDUP_FPS}}
58
+ echo "Video saved: {{FINAL_VIDEO_PATH}}"
59
+ rm -rf "{{SEGMENTS_DIR}}"
60
+ else
61
+ echo "Merging $SEGMENT_COUNT video segments with crossfade..."
62
+
63
+ # Build ffmpeg filter for crossfade between all segments
64
+ # Crossfade duration in seconds
65
+ XFADE_DURATION=0.3
66
+
67
+ # Build input args and filter
68
+ INPUTS=""
69
+ FILTER=""
70
+
71
+ for i in "${!SEGMENTS[@]}"; do
72
+ INPUTS="$INPUTS -i \"${SEGMENTS[$i]}\""
73
+ done
74
+
75
+ # For 2 segments: simple crossfade
76
+ # For N segments: chain crossfades
77
+ if [ "$SEGMENT_COUNT" -eq "2" ]; then
78
+ FILTER="[0:v][1:v]xfade=transition=fade:duration=$XFADE_DURATION:offset=0[outv];[0:a][1:a]acrossfade=d=$XFADE_DURATION[outa]"
79
+ OUTPUT_MAP="-map [outv] -map [outa]"
80
+ else
81
+ # Build chain: xfade 0+1 -> tmp1, xfade tmp1+2 -> tmp2, etc.
82
+ PREV="0:v"
83
+ PREV_A="0:a"
84
+ for ((i=1; i<SEGMENT_COUNT; i++)); do
85
+ if [ $i -eq $((SEGMENT_COUNT-1)) ]; then
86
+ # Last crossfade outputs to final
87
+ FILTER="$FILTER[$PREV][$i:v]xfade=transition=fade:duration=$XFADE_DURATION:offset=0[outv];"
88
+ FILTER="$FILTER[$PREV_A][$i:a]acrossfade=d=$XFADE_DURATION[outa]"
89
+ else
90
+ FILTER="$FILTER[$PREV][$i:v]xfade=transition=fade:duration=$XFADE_DURATION:offset=0[tmp$i];"
91
+ FILTER="$FILTER[$PREV_A][$i:a]acrossfade=d=$XFADE_DURATION[tmpa$i];"
92
+ PREV="tmp$i"
93
+ PREV_A="tmpa$i"
94
+ fi
95
+ done
96
+ OUTPUT_MAP="-map [outv] -map [outa]"
97
+ fi
98
+
99
+ # Run ffmpeg with crossfade
100
+ FFMPEG_CMD="ffmpeg $INPUTS -filter_complex \"$FILTER\" $OUTPUT_MAP -y \"{{RAW_VIDEO_PATH}}\""
101
+
102
+ # Suppress ffmpeg progress output (extremely verbose, confuses agent)
103
+ if eval "$FFMPEG_CMD" 2>/dev/null; then
104
+ echo "Video merged with crossfade"
105
+ deduplicate_video "{{RAW_VIDEO_PATH}}" "{{FINAL_VIDEO_PATH}}" {{DEDUP_FPS}}
106
+ echo "Video saved: {{FINAL_VIDEO_PATH}}"
107
+ rm -rf "{{SEGMENTS_DIR}}"
108
+ else
109
+ # Fallback to simple concat if crossfade fails
110
+ echo "Crossfade failed, trying simple concat..."
111
+ CONCAT_LIST="{{SEGMENTS_DIR}}/concat.txt"
112
+ for f in "${SEGMENTS[@]}"; do
113
+ echo "file '$f'"
114
+ done > "$CONCAT_LIST"
115
+
116
+ # Suppress ffmpeg progress output
117
+ if ffmpeg -f concat -safe 0 -i "$CONCAT_LIST" -c copy "{{RAW_VIDEO_PATH}}" -y 2>/dev/null; then
118
+ echo "Video merged (simple concat)"
119
+ deduplicate_video "{{RAW_VIDEO_PATH}}" "{{FINAL_VIDEO_PATH}}" {{DEDUP_FPS}}
120
+ echo "Video saved: {{FINAL_VIDEO_PATH}}"
121
+ rm -rf "{{SEGMENTS_DIR}}"
122
+ else
123
+ echo "Warning: ffmpeg merge failed, keeping segments in {{SEGMENTS_DIR}}"
124
+ fi
125
+ fi
126
+ fi
127
+
128
+ agent-browser close {{SESSION_ARG}}
129
+ rm -f "{{OUTPUT_DIR}}/.session"
130
+ echo "Browser closed"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # agex effects injection wrapper
3
+ agent-browser eval --stdin {{SESSION_ARG}} < "{{INIT_SCRIPT_PATH}}"
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # Moves the visual cursor to element center
3
+ # Supports @ref (e.g., @e5) or CSS selector
4
+ SELECTOR="$1"
5
+ if [ -z "$SELECTOR" ]; then
6
+ echo "Usage: ab-move <selector|@ref>"
7
+ exit 1
8
+ fi
9
+
10
+ # Use agent-browser hover to move to element (supports @ref via getLocator!)
11
+ HOVER_RESULT=$(agent-browser hover "$SELECTOR" 2>&1)
12
+ if [[ "$HOVER_RESULT" == *"not found"* ]] || [[ "$HOVER_RESULT" == *"Error"* ]]; then
13
+ echo "element not found: $SELECTOR"
14
+ exit 1
15
+ fi
16
+
17
+ # Sync our visual cursor to the mouse position (hover moved mouse to element center)
18
+ agent-browser eval "window.__agexCursor && window.__agexCursor.sync()" >/dev/null 2>&1
19
+ echo "moved to element"
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ # Shows a visual "NOT FOUND" indicator and captures screenshot
3
+ # Usage: ab-not-found "search term" <screenshot.png> [color]
4
+ # Examples:
5
+ # ab-not-found "Login button" 001-no-login.png
6
+ # ab-not-found "word 'test'" 002-no-test.png "#ff0000"
7
+
8
+ SEARCH_TERM="$1"
9
+ SCREENSHOT_NAME="$2"
10
+ COLOR="${3:-#ff4444}"
11
+
12
+ if [ -z "$SEARCH_TERM" ] || [ -z "$SCREENSHOT_NAME" ]; then
13
+ echo "Usage: ab-not-found \"search term\" <screenshot.png> [color]"
14
+ exit 1
15
+ fi
16
+
17
+ SCREENSHOT_PATH="{{OUTPUT_DIR}}/$SCREENSHOT_NAME"
18
+
19
+ # Escape search term for JS
20
+ ESCAPED_TERM=$(printf '%s' "$SEARCH_TERM" | sed "s/'/\\\\'/g")
21
+
22
+ # Create "NOT FOUND" overlay with search term
23
+ agent-browser eval "window.__agexEffects.proof.notFound('${ESCAPED_TERM}','$COLOR')" 2>&1 || true
24
+
25
+ # Wait for overlay to render
26
+ agent-browser wait 200
27
+
28
+ # Take screenshot
29
+ agent-browser screenshot "$SCREENSHOT_PATH"
30
+ echo "Screenshot saved: $SCREENSHOT_PATH"
31
+
32
+ # Clean up overlay after screenshot
33
+ agent-browser eval "window.__agexEffects.proof.clearNotFound()" 2>&1 || true
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ # Opens URL with agex setup (viewport, effects)
3
+ URL="$1"
4
+ if [ -z "$URL" ]; then
5
+ echo "Usage: ab-open <url>"
6
+ exit 1
7
+ fi
8
+ # Write session ID to output directory for other scripts to use
9
+ echo "{{SESSION_ID}}" > "{{OUTPUT_DIR}}/.session"
10
+ agent-browser open "$URL" {{SESSION_ARG}} {{HEADED_FLAG}}
11
+ agent-browser set viewport {{VIEWPORT_WIDTH}} {{VIEWPORT_HEIGHT}} {{SESSION_ARG}}
12
+ {{VIDEO_MKDIR}}
13
+ {{VIDEO_INIT_COUNTER}}
14
+ # stdout suppressed to avoid echoing JS source back to agent
15
+ agent-browser eval --stdin {{SESSION_ARG}} < "{{INIT_SCRIPT_PATH}}" >/dev/null
16
+ echo "Browser ready at $URL (session: {{SESSION_ID}})"
17
+ {{VIDEO_READY_MSG}}
@@ -0,0 +1,126 @@
1
+ #!/bin/bash
2
+ # Highlights an element and captures a screenshot that includes the full element
3
+ # Usage: ab-proof <selector|@ref> "label" <screenshot.png> [color]
4
+ # Examples:
5
+ # ab-proof @e5 "Table of Contents" 001-toc.png
6
+ # ab-proof "#main-content" "Content verified" 002-content.png "#00ff00"
7
+
8
+ SELECTOR="$1"
9
+ LABEL="$2"
10
+ SCREENSHOT_NAME="$3"
11
+ COLOR="${4:-#00ff00}"
12
+
13
+ if [ -z "$SELECTOR" ] || [ -z "$LABEL" ] || [ -z "$SCREENSHOT_NAME" ]; then
14
+ echo "Usage: ab-proof <selector|@ref> \"label\" <screenshot.png> [color]"
15
+ exit 1
16
+ fi
17
+
18
+ SCREENSHOT_PATH="{{OUTPUT_DIR}}/$SCREENSHOT_NAME"
19
+ PADDING=20
20
+
21
+ # Get viewport dimensions
22
+ VIEWPORT_WIDTH={{VIEWPORT_WIDTH}}
23
+ VIEWPORT_HEIGHT={{VIEWPORT_HEIGHT}}
24
+
25
+ # Escape label for JS
26
+ ESCAPED_LABEL=$(printf '%s' "$LABEL" | sed "s/'/\\\\'/g")
27
+
28
+ # Get element bounding box
29
+ if [[ "$SELECTOR" == @* ]]; then
30
+ BOX=$(agent-browser get box "$SELECTOR" 2>&1) || true
31
+ else
32
+ ESCAPED_SELECTOR=$(printf '%s' "$SELECTOR" | sed "s/'/\\\\'/g")
33
+ BOX=$(agent-browser eval "JSON.stringify(window.__agexEffects.proof.getBoundingBox('${ESCAPED_SELECTOR}'))" 2>&1) || true
34
+ fi
35
+
36
+ if [ -z "$BOX" ] || [[ "$BOX" == "null" ]] || [[ "$BOX" == *"error"* ]]; then
37
+ echo "Element not found: $SELECTOR"
38
+ exit 1
39
+ fi
40
+
41
+ # Parse bounding box (support negative values)
42
+ EL_X=$(echo "$BOX" | grep -o '"x":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
43
+ EL_Y=$(echo "$BOX" | grep -o '"y":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
44
+ EL_WIDTH=$(echo "$BOX" | grep -o '"width":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
45
+ EL_HEIGHT=$(echo "$BOX" | grep -o '"height":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
46
+
47
+ # Account for label height (approx 50px below element)
48
+ LABEL_HEIGHT=50
49
+ TOTAL_HEIGHT=$((EL_HEIGHT + LABEL_HEIGHT + PADDING * 2))
50
+ TOTAL_WIDTH=$((EL_WIDTH + PADDING * 2))
51
+
52
+ # Scroll element into view (centered)
53
+ if [[ "$SELECTOR" == @* ]]; then
54
+ agent-browser eval "window.__agexEffects.proof.scrollToCoords($EL_Y,$VIEWPORT_HEIGHT,$EL_HEIGHT)" 2>&1 || true
55
+ else
56
+ agent-browser eval "window.__agexEffects.proof.scrollToSelector('${ESCAPED_SELECTOR}')" 2>&1 || true
57
+ fi
58
+
59
+ # Wait for scroll
60
+ agent-browser wait 200
61
+
62
+ # Re-get bounding box after scroll (position changed)
63
+ if [[ "$SELECTOR" == @* ]]; then
64
+ NEW_BOX=$(agent-browser get box "$SELECTOR" 2>&1) || true
65
+ if [ -n "$NEW_BOX" ] && [[ "$NEW_BOX" != "null" ]] && [[ "$NEW_BOX" != *"error"* ]]; then
66
+ NEW_X=$(echo "$NEW_BOX" | grep -o '"x":[0-9.-]*' | cut -d: -f2)
67
+ NEW_Y=$(echo "$NEW_BOX" | grep -o '"y":[0-9.-]*' | cut -d: -f2)
68
+ if [ -n "$NEW_X" ] && [ -n "$NEW_Y" ]; then
69
+ BOX="$NEW_BOX"
70
+ fi
71
+ fi
72
+ else
73
+ BOX=$(agent-browser eval "JSON.stringify(window.__agexEffects.proof.getBoundingBox('${ESCAPED_SELECTOR}'))" 2>&1) || true
74
+ fi
75
+
76
+ # Parse updated bounding box
77
+ EL_X=$(echo "$BOX" | grep -o '"x":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
78
+ EL_Y=$(echo "$BOX" | grep -o '"y":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
79
+ EL_WIDTH=$(echo "$BOX" | grep -o '"width":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
80
+ EL_HEIGHT=$(echo "$BOX" | grep -o '"height":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
81
+
82
+ # Apply highlight border + label
83
+ agent-browser eval "window.__agexEffects.proof.highlightProof($BOX,'${ESCAPED_LABEL}','$COLOR')" 2>&1 || true
84
+
85
+ # Wait for highlight to render
86
+ agent-browser wait 100
87
+
88
+ # Decide screenshot strategy based on element size vs viewport
89
+ if [ "$TOTAL_HEIGHT" -gt "$VIEWPORT_HEIGHT" ] || [ "$TOTAL_WIDTH" -gt "$VIEWPORT_WIDTH" ]; then
90
+ # Element larger than viewport - use full page screenshot + crop
91
+ TEMP_FULL="{{OUTPUT_DIR}}/.temp-full-$$.png"
92
+
93
+ agent-browser screenshot "$TEMP_FULL" --full
94
+
95
+ # Get scroll position to calculate absolute coords
96
+ SCROLL_POS=$(agent-browser eval "JSON.stringify(window.__agexEffects.proof.getScrollPosition())" 2>&1) || true
97
+ SCROLL_X=$(echo "$SCROLL_POS" | grep -o '"x":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
98
+ SCROLL_Y=$(echo "$SCROLL_POS" | grep -o '"y":[0-9.-]*' | cut -d: -f2 | cut -d. -f1)
99
+
100
+ # Calculate crop region (element position + padding + label)
101
+ CROP_X=$((SCROLL_X + EL_X - PADDING))
102
+ CROP_Y=$((SCROLL_Y + EL_Y - PADDING))
103
+ CROP_WIDTH=$((EL_WIDTH + PADDING * 2))
104
+ CROP_HEIGHT=$((EL_HEIGHT + PADDING * 2 + LABEL_HEIGHT))
105
+
106
+ # Ensure non-negative
107
+ [ "$CROP_X" -lt 0 ] && CROP_X=0
108
+ [ "$CROP_Y" -lt 0 ] && CROP_Y=0
109
+
110
+ # Try to crop with ImageMagick
111
+ if command -v convert &> /dev/null; then
112
+ convert "$TEMP_FULL" -crop "${CROP_WIDTH}x${CROP_HEIGHT}+${CROP_X}+${CROP_Y}" +repage "$SCREENSHOT_PATH"
113
+ rm -f "$TEMP_FULL"
114
+ echo "Screenshot saved (cropped): $SCREENSHOT_PATH"
115
+ else
116
+ mv "$TEMP_FULL" "$SCREENSHOT_PATH"
117
+ echo "Screenshot saved (full page, ImageMagick not available for crop): $SCREENSHOT_PATH"
118
+ fi
119
+ else
120
+ # Element fits in viewport - take regular screenshot
121
+ agent-browser screenshot "$SCREENSHOT_PATH"
122
+ echo "Screenshot saved: $SCREENSHOT_PATH"
123
+ fi
124
+
125
+ # Clean up highlight after screenshot
126
+ agent-browser eval "window.__agexEffects.proof.clearProof()" 2>&1 || true
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Video recording disabled - just run the commands
3
+ eval "$2"
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ echo "Video recording disabled"
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+ # Starts video recording
3
+ COUNTER=$(cat "{{SEGMENT_COUNTER_PATH}}" 2>/dev/null || echo "0")
4
+ COUNTER=$((COUNTER + 1))
5
+ echo "$COUNTER" > "{{SEGMENT_COUNTER_PATH}}"
6
+ SEGMENT_NAME=$(printf "segment-%03d.webm" "$COUNTER")
7
+ SEGMENT_PATH="{{SEGMENTS_DIR}}/$SEGMENT_NAME"
8
+
9
+ agent-browser record start "$SEGMENT_PATH" {{SESSION_ARG}}
10
+
11
+ # RE-INJECT init script after recording starts (recording causes page context change)
12
+ # stdout suppressed to avoid echoing JS source back to agent
13
+ agent-browser eval --stdin {{SESSION_ARG}} < "{{INIT_SCRIPT_PATH}}" >/dev/null
14
+
15
+ # Wait to capture initial frame
16
+ agent-browser wait 200 {{SESSION_ARG}}
17
+
18
+ echo "Recording started: $SEGMENT_NAME"
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ echo "Video recording disabled"
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ # Stops video recording
3
+ agent-browser wait 200 {{SESSION_ARG}}
4
+ agent-browser record stop {{SESSION_ARG}}
5
+ echo "Recording stopped"
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Records a video segment with title overlay
3
+ # Usage: ab-record "Title Text" "command1 && command2 && ..."
4
+
5
+ TITLE="$1"
6
+ COMMANDS="$2"
7
+
8
+ if [ -z "$TITLE" ] || [ -z "$COMMANDS" ]; then
9
+ echo "Usage: ab-record \"Title Text\" \"command1 && command2 && ...\""
10
+ exit 1
11
+ fi
12
+
13
+ COUNTER=$(cat "{{SEGMENT_COUNTER_PATH}}" 2>/dev/null || echo "0")
14
+ COUNTER=$((COUNTER + 1))
15
+ echo "$COUNTER" > "{{SEGMENT_COUNTER_PATH}}"
16
+ SEGMENT_NAME=$(printf "segment-%03d.webm" "$COUNTER")
17
+ SEGMENT_PATH="{{SEGMENTS_DIR}}/$SEGMENT_NAME"
18
+
19
+ agent-browser record start "$SEGMENT_PATH" {{SESSION_ARG}}
20
+ echo "Recording segment $SEGMENT_NAME: $TITLE"
21
+
22
+ # RE-INJECT init script after recording starts (recording causes page context change)
23
+ # stdout suppressed to avoid echoing JS source back to agent
24
+ agent-browser eval --stdin {{SESSION_ARG}} < "{{INIT_SCRIPT_PATH}}" >/dev/null
25
+
26
+ # Inject title overlay
27
+ ESCAPED_TITLE=$(printf '%s' "$TITLE" | sed "s/'/\\\\'/g")
28
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.banner.show('${ESCAPED_TITLE}')"
29
+
30
+ # Wait for effects to be ready
31
+ agent-browser wait 200 {{SESSION_ARG}}
32
+
33
+ # Execute the commands
34
+ eval "$COMMANDS"
35
+ EXIT_CODE=$?
36
+
37
+ # Wait before stopping to capture final frame
38
+ agent-browser wait 200 {{SESSION_ARG}}
39
+
40
+ agent-browser record stop {{SESSION_ARG}}
41
+ echo "Recording stopped: $SEGMENT_NAME"
42
+
43
+ exit $EXIT_CODE
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+ # Takes screenshot with proper path
3
+ NAME="$1"
4
+ if [ -z "$NAME" ]; then
5
+ echo "Usage: ab-screenshot <name>"
6
+ exit 1
7
+ fi
8
+ agent-browser screenshot "{{OUTPUT_DIR}}/$NAME" {{SESSION_ARG}}
9
+ echo "Screenshot saved: {{OUTPUT_DIR}}/$NAME"
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+ # Auto-inject --session for all agent-browser commands
3
+ for arg in "$@"; do
4
+ [ "$arg" = "--session" ] && exec "{{REAL_AGENT_BROWSER}}" "$@"
5
+ done
6
+ exec "{{REAL_AGENT_BROWSER}}" "$@" --session {{SESSION_ID}}
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ SELECTOR="$1"
3
+ TEXT="$2"
4
+ POSITION="${3:-bottom}"
5
+ COLOR="${4:-#333}"
6
+ if [ -z "$SELECTOR" ] || [ -z "$TEXT" ]; then
7
+ echo "Usage: fx-annotation <css-selector> <text> [position:top|bottom|left|right] [color]"
8
+ exit 1
9
+ fi
10
+ {{FX_INJECT_HELPER}}
11
+ ESCAPED_SELECTOR=$(printf '%s' "$SELECTOR" | sed "s/'/\\\\'/g")
12
+ ESCAPED_TEXT=$(printf '%s' "$TEXT" | sed "s/'/\\\\'/g")
13
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.annotation.add('${ESCAPED_SELECTOR}',{text:'${ESCAPED_TEXT}',position:'$POSITION',color:'$COLOR'})"
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+ FROM="$1"
3
+ TO="$2"
4
+ COLOR="${3:-#ff4444}"
5
+ if [ -z "$FROM" ] || [ -z "$TO" ]; then
6
+ echo "Usage: fx-arrow <from-css-selector> <to-css-selector> [color]"
7
+ exit 1
8
+ fi
9
+ {{FX_INJECT_HELPER}}
10
+ ESCAPED_FROM=$(printf '%s' "$FROM" | sed "s/'/\\\\'/g")
11
+ ESCAPED_TO=$(printf '%s' "$TO" | sed "s/'/\\\\'/g")
12
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.drawing.arrowBetween('${ESCAPED_FROM}','${ESCAPED_TO}','$COLOR')"
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ SELECTOR="$1"
3
+ LABEL="$2"
4
+ COLOR="${3:-#ff4444}"
5
+ if [ -z "$SELECTOR" ] || [ -z "$LABEL" ]; then
6
+ echo "Usage: fx-circle <css-selector> \"label\" [color]"
7
+ echo " Example: fx-circle \"#logo\" \"Logo element\" '#ff0000'"
8
+ exit 1
9
+ fi
10
+ {{FX_INJECT_HELPER}}
11
+ ESCAPED_SELECTOR=$(printf '%s' "$SELECTOR" | sed "s/'/\\\\'/g")
12
+ ESCAPED_LABEL=$(printf '%s' "$LABEL" | sed "s/'/\\\\'/g")
13
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.drawing.circle('${ESCAPED_SELECTOR}','$COLOR','${ESCAPED_LABEL}')"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ {{FX_INJECT_HELPER}}
3
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.clearAll()"
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ SELECTOR="$1"
3
+ LABEL="$2"
4
+ COLOR="${3:-#2e7d32}"
5
+ if [ -z "$SELECTOR" ] || [ -z "$LABEL" ]; then
6
+ echo "Usage: fx-highlight <selector> \"label\" [color]"
7
+ echo " Examples:"
8
+ echo " fx-highlight @e5 \"Logo found\" '#00ff00'"
9
+ echo " fx-highlight \"img.logo\" \"Logo found\" '#00ff00'"
10
+ exit 1
11
+ fi
12
+
13
+ # Escape label for JS (handle quotes and backslashes)
14
+ ESCAPED_LABEL=$(printf '%s' "$LABEL" | sed "s/\\\\/\\\\\\\\/g; s/'/\\\\'/g")
15
+
16
+ if [[ "$SELECTOR" == @* ]]; then
17
+ # @ref format - use agent-browser get box to get element position
18
+ BOX=$(agent-browser get box "$SELECTOR" {{SESSION_ARG}} 2>&1) || true
19
+ if [ -z "$BOX" ] || [[ "$BOX" == *"error"* ]]; then
20
+ echo "Element not found: $SELECTOR"
21
+ exit 1
22
+ fi
23
+
24
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.proof.highlightBox($BOX,'${ESCAPED_LABEL}','$COLOR')" 2>&1 || true
25
+ else
26
+ # CSS selector - use effects.js
27
+ ESCAPED_SELECTOR=$(printf '%s' "$SELECTOR" | sed "s/\\\\/\\\\\\\\/g; s/'/\\\\'/g")
28
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.proof.highlightSelector('${ESCAPED_SELECTOR}','${ESCAPED_LABEL}','$COLOR')" 2>&1 || true
29
+ fi
30
+
31
+ agent-browser wait 100 {{SESSION_ARG}}
32
+ echo "Highlighted $SELECTOR with label"
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ SELECTOR="$1"
3
+ LABEL="$2"
4
+ OPACITY="${3:-0.7}"
5
+ if [ -z "$SELECTOR" ] || [ -z "$LABEL" ]; then
6
+ echo "Usage: fx-spotlight <css-selector> \"label\" [opacity]"
7
+ echo " Example: fx-spotlight \"#main-content\" \"Main content area\" 0.7"
8
+ exit 1
9
+ fi
10
+ {{FX_INJECT_HELPER}}
11
+ ESCAPED_SELECTOR=$(printf '%s' "$SELECTOR" | sed "s/'/\\\\'/g")
12
+ ESCAPED_LABEL=$(printf '%s' "$LABEL" | sed "s/'/\\\\'/g")
13
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.spotlight.show('${ESCAPED_SELECTOR}',{opacity:$OPACITY,label:'${ESCAPED_LABEL}'})"
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+ TITLE="$1"
3
+ SUBTITLE="$2"
4
+ if [ -z "$TITLE" ]; then
5
+ echo "Usage: fx-title <title> [subtitle]"
6
+ exit 1
7
+ fi
8
+ {{FX_INJECT_HELPER}}
9
+ ESCAPED_TITLE=$(printf '%s' "$TITLE" | sed "s/'/\\\\'/g")
10
+ ESCAPED_SUBTITLE=$(printf '%s' "$SUBTITLE" | sed "s/'/\\\\'/g")
11
+ agent-browser eval {{SESSION_ARG}} "window.__agexEffects.banner.show('${ESCAPED_TITLE}','${ESCAPED_SUBTITLE}')"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ MS="${1:-500}"
3
+ agent-browser wait "$MS" {{SESSION_ARG}}
@@ -5348,6 +5348,18 @@ import * as path2 from "path";
5348
5348
  import { fileURLToPath } from "url";
5349
5349
  function getAssetsDir() {
5350
5350
  const __dirname2 = path2.dirname(fileURLToPath(import.meta.url));
5351
+ let dir = __dirname2;
5352
+ for (let i = 0; i < 5; i++) {
5353
+ const candidate = path2.join(dir, "assets");
5354
+ if (fs.existsSync(path2.join(candidate, "cursor.js"))) {
5355
+ return candidate;
5356
+ }
5357
+ const nestedCandidate = path2.join(dir, "assets", "assets");
5358
+ if (fs.existsSync(path2.join(nestedCandidate, "cursor.js"))) {
5359
+ return nestedCandidate;
5360
+ }
5361
+ dir = path2.dirname(dir);
5362
+ }
5351
5363
  return path2.resolve(__dirname2, "..", "assets");
5352
5364
  }
5353
5365
  function getCursorScriptPath() {
@@ -5409,6 +5421,18 @@ function renderScript(template, vars) {
5409
5421
  // ../browse/dist/scripts/index.js
5410
5422
  var __dirname = path3.dirname(fileURLToPath2(import.meta.url));
5411
5423
  function getScriptsDir() {
5424
+ let dir = __dirname;
5425
+ for (let i = 0; i < 5; i++) {
5426
+ const candidate = path3.join(dir, "scripts");
5427
+ if (fs2.existsSync(path3.join(candidate, "ab-fx.sh"))) {
5428
+ return candidate;
5429
+ }
5430
+ const assetsCandidate = path3.join(dir, "assets", "scripts");
5431
+ if (fs2.existsSync(path3.join(assetsCandidate, "ab-fx.sh"))) {
5432
+ return assetsCandidate;
5433
+ }
5434
+ dir = path3.dirname(dir);
5435
+ }
5412
5436
  return path3.resolve(__dirname, "..", "..", "scripts");
5413
5437
  }
5414
5438
  function loadRawScript(name) {
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  runReview,
13
13
  validate,
14
14
  viewportStringSchema
15
- } from "./chunk-CY6NF2GO.js";
15
+ } from "./chunk-FCCNIKUD.js";
16
16
 
17
17
  // src/cli.ts
18
18
  import { defineCommand as defineCommand6, runMain } from "citty";
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  provePr,
6
6
  runAgent,
7
7
  runReview
8
- } from "./chunk-CY6NF2GO.js";
8
+ } from "./chunk-FCCNIKUD.js";
9
9
  export {
10
10
  AgexError,
11
11
  isAgexError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agex",
3
- "version": "0.2.4",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,12 @@
8
8
  "agex": "dist/cli.js"
9
9
  },
10
10
  "files": [
11
- "dist",
11
+ "dist/cli.js",
12
+ "dist/cli.js.map",
13
+ "dist/index.js",
14
+ "dist/index.js.map",
15
+ "dist/index.d.ts",
16
+ "dist/chunk-*.js",
12
17
  "assets"
13
18
  ],
14
19
  "dependencies": {
@@ -21,15 +26,15 @@
21
26
  "typescript": "^5.9.3",
22
27
  "vitest": "^2.1.9",
23
28
  "agex-agent": "0.1.0",
24
- "agex-core": "0.1.0",
25
29
  "agex-browse": "0.1.0",
26
- "agex-prove-pr": "0.1.0",
30
+ "agex-core": "0.1.0",
27
31
  "agex-review": "0.1.0",
28
- "agex-prove": "0.1.0"
32
+ "agex-prove": "0.1.0",
33
+ "agex-prove-pr": "0.1.0"
29
34
  },
30
35
  "scripts": {
31
36
  "build": "tsc -p tsconfig.json",
32
- "bundle": "tsup && cp -r ../browse/assets ./assets",
37
+ "bundle": "rm -rf dist assets && tsup && mkdir -p ./assets/assets ./assets/scripts && cp -r ../browse/assets/ ./assets/assets/ && cp -r ../browse/scripts/ ./assets/scripts/",
33
38
  "test": "vitest run"
34
39
  }
35
40
  }
package/dist/cli.d.ts DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- import 'agex-prove';
3
- import 'agex-prove-pr';
4
- import 'agex-agent';
5
- import 'agex-review';
6
- import 'agex-core';