|
31481
|
635
|
14
|
2026-04-15T15:49:13.408506+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776268153408_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScripts|ProfilesWindowHe iTerm2ShellEditViewSessionScripts|ProfilesWindowHelp• 0-zshDOCKER-rw-r--r--₴81DEV (-zsh)O $82APP (-zsh)• 83ec2-user@ip-10-.. 884-zsh• ₴5-zsh86lukasstaff3952462215Apr17:56compact_monitor_2_1776264952043.mp4lukasstaff1241334415Apr17:56-rw-r--r---rw-r--r---rw-r--r---rw-r--r---rw-r--r---rw-r--r---W-r=-r---rw-r--r---rw-r--r--lukasstaff3753062215compact_monitor_2_1776264966984.mp4Apr18:01compact_monitor_2_1776265280164.mp4lukasstaff1639225715 Apr18:01compact_monitor_2_1776265294548.mp4lukasstaff4355233415Apr18:07lukasstaff633585615compact_monitor_2_1776265608065.mp4Apr18:07compact_monitor_2_1776265624847.mp4lukasstaff4010273215Apr18:12compact_monitor_2_1776265936487.mp4lukasstaff1085207515Apr18:12lukasstafflukasstaff330107721518:17compact_monitor_2_1776265950996.mp4Aprcompact_monitor_2_1776266266304.mp41230209015Apr18:18compact_monitor_2_1776266279297.mp4lukasstaff3799713315 Apr18:23compact_monitor_2_1776266594590.mp4lukasstaff13660744staff1518:23-rw-r-1lukas3848351115Aprcompact_monitor_2_1776266608276.mp4Apr18:40compact_monitor_2_1776267597529.mp4-rw-r-1 lukasstaff4413025015 Apr18:40compact_monitor_2_1776267613110.mp4-rw-r1lukasstaff36116652 15 Apr18:40compact_monitor_2_1776267629232.mp4Lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/.screenpipe/data/data/2026-04-15 $ for d in ~/.screenpipe/data/data/*/; doecho"$(basename Sd): JPGs=S(find$d -name•*jpg'1 wc -1) MP4s=$(find $d -name"*.mp4'I wc -l)"done# And check MP4 naming on old vs new dayls ~/.screenpipe/data/data/2026-04-09/*.mp4 | head -5ls ~/.screenpipe/data/data/2026-04-15/*.mp4 | head -5# Check what version you're oncat ~/.screenpipe/config.jsonquote>2026-04-09: JPGs=0 MP4S=2026-04-11: JPGs=1 MP4s=2026-04-12: JPGs=0 MP45=2026-04-13: JPGS=0 MP4S=2026-04-14: JPGS=0 MP4S=2026-04-15: JPGs=82 MP4S=12274469245234zsh: command not found: #/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775754248190.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775754549504.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775754928127.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775755253065.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775755561408.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776236979346.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237284692.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237595554.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237899635.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776238207393.mp4zsh: command not found: #lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data/2026-04-15 $lalol-zshO ₴7.* Unable to a...O 88100% C8Wed 15 Apr 18:49:13T81-zsh• *9...
|
NULL
|
8503922493626987318
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScripts|ProfilesWindowHe iTerm2ShellEditViewSessionScripts|ProfilesWindowHelp• 0-zshDOCKER-rw-r--r--₴81DEV (-zsh)O $82APP (-zsh)• 83ec2-user@ip-10-.. 884-zsh• ₴5-zsh86lukasstaff3952462215Apr17:56compact_monitor_2_1776264952043.mp4lukasstaff1241334415Apr17:56-rw-r--r---rw-r--r---rw-r--r---rw-r--r---rw-r--r---rw-r--r---W-r=-r---rw-r--r---rw-r--r--lukasstaff3753062215compact_monitor_2_1776264966984.mp4Apr18:01compact_monitor_2_1776265280164.mp4lukasstaff1639225715 Apr18:01compact_monitor_2_1776265294548.mp4lukasstaff4355233415Apr18:07lukasstaff633585615compact_monitor_2_1776265608065.mp4Apr18:07compact_monitor_2_1776265624847.mp4lukasstaff4010273215Apr18:12compact_monitor_2_1776265936487.mp4lukasstaff1085207515Apr18:12lukasstafflukasstaff330107721518:17compact_monitor_2_1776265950996.mp4Aprcompact_monitor_2_1776266266304.mp41230209015Apr18:18compact_monitor_2_1776266279297.mp4lukasstaff3799713315 Apr18:23compact_monitor_2_1776266594590.mp4lukasstaff13660744staff1518:23-rw-r-1lukas3848351115Aprcompact_monitor_2_1776266608276.mp4Apr18:40compact_monitor_2_1776267597529.mp4-rw-r-1 lukasstaff4413025015 Apr18:40compact_monitor_2_1776267613110.mp4-rw-r1lukasstaff36116652 15 Apr18:40compact_monitor_2_1776267629232.mp4Lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/.screenpipe/data/data/2026-04-15 $ for d in ~/.screenpipe/data/data/*/; doecho"$(basename Sd): JPGs=S(find$d -name•*jpg'1 wc -1) MP4s=$(find $d -name"*.mp4'I wc -l)"done# And check MP4 naming on old vs new dayls ~/.screenpipe/data/data/2026-04-09/*.mp4 | head -5ls ~/.screenpipe/data/data/2026-04-15/*.mp4 | head -5# Check what version you're oncat ~/.screenpipe/config.jsonquote>2026-04-09: JPGs=0 MP4S=2026-04-11: JPGs=1 MP4s=2026-04-12: JPGs=0 MP45=2026-04-13: JPGS=0 MP4S=2026-04-14: JPGS=0 MP4S=2026-04-15: JPGs=82 MP4S=12274469245234zsh: command not found: #/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775754248190.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775754549504.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775754928127.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775755253065.mp4/Users/lukas/.screenpipe/data/data/2026-04-09/compact_monitor_1_1775755561408.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776236979346.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237284692.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237595554.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776237899635.mp4/Users/lukas/.screenpipe/data/data/2026-04-15/compact_monitor_1_1776238207393.mp4zsh: command not found: #lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data/2026-04-15 $lalol-zshO ₴7.* Unable to a...O 88100% C8Wed 15 Apr 18:49:13T81-zsh• *9...
|
NULL
|
|
50642
|
1087
|
16
|
2026-04-17T15:10:12.812429+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776438612812_m1.jpg...
|
iTerm2
|
sqlite3
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
elements|465966705
ui_events|9804622
ocr_text|1443 elements|465966705
ui_events|9804622
ocr_text|1443084543
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
-- estimate yesterday's rows
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'
UNION ALL
SELECT 'elements',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))
FROM dbstat d, elements e
WHERE d.name = 'elements'
UNION ALL
SELECT 'ui_events',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')
FROM dbstat d, ui_events e
WHERE d.name = 'ui_events';"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT name, SUM(payload) as bytes, COUNT(*) as pages
FROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')
GROUP BY name;"
elements|466530643|121280
frames|1006977358|254673
ocr_text|1443956212|359424
ui_events|9826328|2576
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
UW PICO 5.09 New Buffer
[ Read 25 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
SELECT 'frames', COUNT(*) FROM frames
UNION ALL SELECT 'elements', COUNT(*) FROM elements
UNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events
UNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;"
frames|50235
elements|3279563
ui_events|60293
ocr_text|39114
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)
SPEED=$(
dd if=/dev/urandom of="$NAS_MOUNT/.speed_test" bs=1m count=10 2>&1 | \
grep -o '[0-9.]* bytes/sec' | awk '{print $1}'
rm -f "$NAS_MOUNT/.speed_test"
)
echo "NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s"
# 2. Get bytes/row for each table from dbstat
BYTES_FRAMES=$(sqlite3 "$DB_SRC" "SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';")
zsh: unknown file attribute: i
NAS write speed: 0 MB/s
zsh: command not found: #
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll
total 7525816
drwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .
drwx------+ 91 lukas staff 2912 17 Apr 17:23 ..
-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store
-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json
drwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data
-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite
-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm
-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal
drwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes
-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log
-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log
-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log
-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log
-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log
-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log
-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log
-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log
-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE="$HOME/.screenpipe/sync.log"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo "$msg" | tee -a "$LOG_FILE"
[2026-04-17 17:45:23]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh
zsh: command not found: screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
OK: archive.db exists
199M /Volumes/Test/screenpipe/archive.db
6 tables
[2026-04-17 17:58:51] ========================================
[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:58:51] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 17:59:49] ========================================
[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:59:49] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-15
frames: 12874
elements: 886876
ui_events: 14453
ocr_text: 11412
meetings: 0
[+00m01s] ▶ Initialising tables, indexes, FTS
[2026-04-17 17:59:50] Sync complete for 2026-04-15
[2026-04-17 17:59:50] ========================================
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT date(MIN(timestamp)) FROM frames;"
2026-04-09
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 18:09:41] ========================================
[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14
[2026-04-17 18:09:41] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-14
frames: 10733
elements: 695969
ui_events: 10542
ocr_text: 8206
meetings: 0
[+00m00s] ▶ Initialising tables, indexes, FTS
creating tables ✓ 0m02s
creating indexes ✓ 0m03s
creating FTS tables ✓ 0m01s
[+00m06s] ▶ Syncing data for 2026-04-14
video_chunks ✓ 0m01s
frames (10733 rows) ⠦
DOCKER
Close Tab
DEV (docker)
Close Tab
APP (-zsh)
Close Tab
sqlite3
Close Tab
-zsh
Close Tab
✳ Review screenpipe usage and Boosteroid integration (claude)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
ec2-user@ip-10-20-6-111:~ (nc)
Close Tab
⌥⌘1
sqlite3...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"elements|465966705\nui_events|9804622\nocr_text|1443084543\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n -- estimate yesterday's rows\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows\nFROM dbstat d, frames f\nWHERE d.name = 'frames'\nUNION ALL\nSELECT 'elements',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))\nFROM dbstat d, elements e\nWHERE d.name = 'elements'\nUNION ALL\nSELECT 'ui_events',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')\nFROM dbstat d, ui_events e\nWHERE d.name = 'ui_events';\"\n\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows \nFROM dbstat d, frames f\nWHERE d.name = 'frames'\"\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT name, SUM(payload) as bytes, COUNT(*) as pages\nFROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')\nGROUP BY name;\"\nelements|466530643|121280\nframes|1006977358|254673\nocr_text|1443956212|359424\nui_events|9826328|2576\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 25 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \nSELECT 'frames', COUNT(*) FROM frames\nUNION ALL SELECT 'elements', COUNT(*) FROM elements\nUNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events\nUNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;\"\nframes|50235\nelements|3279563\nui_events|60293\nocr_text|39114\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)\nSPEED=$(\n dd if=/dev/urandom of=\"$NAS_MOUNT/.speed_test\" bs=1m count=10 2>&1 | \\\n grep -o '[0-9.]* bytes/sec' | awk '{print $1}'\n rm -f \"$NAS_MOUNT/.speed_test\"\n)\necho \"NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s\"\n\n# 2. Get bytes/row for each table from dbstat\nBYTES_FRAMES=$(sqlite3 \"$DB_SRC\" \"SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';\")\nzsh: unknown file attribute: i\nNAS write speed: 0 MB/s\nzsh: command not found: #\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll \ntotal 7525816\ndrwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .\ndrwx------+ 91 lukas staff 2912 17 Apr 17:23 ..\n-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store\n-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json\ndrwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data\n-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite\n-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm\n-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal\ndrwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes\n-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log\n-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log\n-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log\n-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log\n-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log\n-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log\n-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log\n-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log\n-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE=\"$HOME/.screenpipe/sync.log\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg=\"[$(date '+%Y-%m-%d %H:%M:%S')] $*\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo \"$msg\" | tee -a \"$LOG_FILE\"\n[2026-04-17 17:45:23] \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh \nzsh: command not found: screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nOK: archive.db exists\n199M\u0000\u0000\u0000\t/Volumes/Test/screenpipe/archive.db\n6 tables\n[2026-04-17 17:58:51] ========================================\n[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:58:51] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 17:59:49] ========================================\n[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:59:49] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-15\n frames: 12874\n elements: 886876\n ui_events: 14453\n ocr_text: 11412\n meetings: 0\n\n[+00m01s] ▶ Initialising tables, indexes, FTS\n\n[2026-04-17 17:59:50] Sync complete for 2026-04-15\n[2026-04-17 17:59:50] ========================================\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT date(MIN(timestamp)) FROM frames;\"\n2026-04-09\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14 \nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 18:09:41] ========================================\n[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14\n[2026-04-17 18:09:41] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-14\n frames: 10733\n elements: 695969\n ui_events: 10542\n ocr_text: 8206\n meetings: 0\n\n[+00m00s] ▶ Initialising tables, indexes, FTS\n creating tables ✓ 0m02s\n creating indexes ✓ 0m03s\n creating FTS tables ✓ 0m01s\n\n[+00m06s] ▶ Syncing data for 2026-04-14\n video_chunks ✓ 0m01s\n frames (10733 rows) ⠦","depth":4,"value":"elements|465966705\nui_events|9804622\nocr_text|1443084543\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n -- estimate yesterday's rows\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows\nFROM dbstat d, frames f\nWHERE d.name = 'frames'\nUNION ALL\nSELECT 'elements',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))\nFROM dbstat d, elements e\nWHERE d.name = 'elements'\nUNION ALL\nSELECT 'ui_events',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')\nFROM dbstat d, ui_events e\nWHERE d.name = 'ui_events';\"\n\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows \nFROM dbstat d, frames f\nWHERE d.name = 'frames'\"\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT name, SUM(payload) as bytes, COUNT(*) as pages\nFROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')\nGROUP BY name;\"\nelements|466530643|121280\nframes|1006977358|254673\nocr_text|1443956212|359424\nui_events|9826328|2576\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 25 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \nSELECT 'frames', COUNT(*) FROM frames\nUNION ALL SELECT 'elements', COUNT(*) FROM elements\nUNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events\nUNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;\"\nframes|50235\nelements|3279563\nui_events|60293\nocr_text|39114\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)\nSPEED=$(\n dd if=/dev/urandom of=\"$NAS_MOUNT/.speed_test\" bs=1m count=10 2>&1 | \\\n grep -o '[0-9.]* bytes/sec' | awk '{print $1}'\n rm -f \"$NAS_MOUNT/.speed_test\"\n)\necho \"NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s\"\n\n# 2. Get bytes/row for each table from dbstat\nBYTES_FRAMES=$(sqlite3 \"$DB_SRC\" \"SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';\")\nzsh: unknown file attribute: i\nNAS write speed: 0 MB/s\nzsh: command not found: #\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll \ntotal 7525816\ndrwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .\ndrwx------+ 91 lukas staff 2912 17 Apr 17:23 ..\n-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store\n-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json\ndrwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data\n-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite\n-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm\n-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal\ndrwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes\n-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log\n-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log\n-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log\n-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log\n-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log\n-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log\n-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log\n-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log\n-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE=\"$HOME/.screenpipe/sync.log\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg=\"[$(date '+%Y-%m-%d %H:%M:%S')] $*\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo \"$msg\" | tee -a \"$LOG_FILE\"\n[2026-04-17 17:45:23] \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh \nzsh: command not found: screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nOK: archive.db exists\n199M\u0000\u0000\u0000\t/Volumes/Test/screenpipe/archive.db\n6 tables\n[2026-04-17 17:58:51] ========================================\n[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:58:51] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 17:59:49] ========================================\n[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:59:49] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-15\n frames: 12874\n elements: 886876\n ui_events: 14453\n ocr_text: 11412\n meetings: 0\n\n[+00m01s] ▶ Initialising tables, indexes, FTS\n\n[2026-04-17 17:59:50] Sync complete for 2026-04-15\n[2026-04-17 17:59:50] ========================================\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT date(MIN(timestamp)) FROM frames;\"\n2026-04-09\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14 \nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 18:09:41] ========================================\n[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14\n[2026-04-17 18:09:41] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-14\n frames: 10733\n elements: 695969\n ui_events: 10542\n ocr_text: 8206\n meetings: 0\n\n[+00m00s] ▶ Initialising tables, indexes, FTS\n creating tables ✓ 0m02s\n creating indexes ✓ 0m03s\n creating FTS tables ✓ 0m01s\n\n[+00m06s] ▶ Syncing data for 2026-04-14\n video_chunks ✓ 0m01s\n frames (10733 rows) ⠦","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.00069444446,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.0048611113,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (docker)","depth":2,"bounds":{"left":0.12361111,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.12777779,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.24652778,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.25069445,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"sqlite3","depth":2,"bounds":{"left":0.36944443,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.37361112,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.4923611,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.4965278,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Review screenpipe usage and Boosteroid integration (claude)","depth":2,"bounds":{"left":0.61527777,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.61944443,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"ec2-user@ip-10-30-159-186:~ (nc)","depth":2,"bounds":{"left":0.73819447,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.7423611,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"ec2-user@ip-10-20-6-111:~ (nc)","depth":2,"bounds":{"left":0.8611111,"top":0.06,"width":0.12291667,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.86527777,"top":0.064444445,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95555556,"top":0.033333335,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"sqlite3","depth":1,"bounds":{"left":0.48333332,"top":0.034444444,"width":0.034027778,"height":0.017777778},"role_description":"text"}]...
|
8503612538388933471
|
-1629251819081372013
|
idle
|
accessibility
|
NULL
|
elements|465966705
ui_events|9804622
ocr_text|1443 elements|465966705
ui_events|9804622
ocr_text|1443084543
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
-- estimate yesterday's rows
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'
UNION ALL
SELECT 'elements',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))
FROM dbstat d, elements e
WHERE d.name = 'elements'
UNION ALL
SELECT 'ui_events',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')
FROM dbstat d, ui_events e
WHERE d.name = 'ui_events';"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT name, SUM(payload) as bytes, COUNT(*) as pages
FROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')
GROUP BY name;"
elements|466530643|121280
frames|1006977358|254673
ocr_text|1443956212|359424
ui_events|9826328|2576
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
UW PICO 5.09 New Buffer
[ Read 25 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
SELECT 'frames', COUNT(*) FROM frames
UNION ALL SELECT 'elements', COUNT(*) FROM elements
UNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events
UNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;"
frames|50235
elements|3279563
ui_events|60293
ocr_text|39114
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)
SPEED=$(
dd if=/dev/urandom of="$NAS_MOUNT/.speed_test" bs=1m count=10 2>&1 | \
grep -o '[0-9.]* bytes/sec' | awk '{print $1}'
rm -f "$NAS_MOUNT/.speed_test"
)
echo "NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s"
# 2. Get bytes/row for each table from dbstat
BYTES_FRAMES=$(sqlite3 "$DB_SRC" "SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';")
zsh: unknown file attribute: i
NAS write speed: 0 MB/s
zsh: command not found: #
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll
total 7525816
drwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .
drwx------+ 91 lukas staff 2912 17 Apr 17:23 ..
-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store
-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json
drwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data
-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite
-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm
-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal
drwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes
-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log
-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log
-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log
-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log
-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log
-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log
-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log
-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log
-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE="$HOME/.screenpipe/sync.log"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo "$msg" | tee -a "$LOG_FILE"
[2026-04-17 17:45:23]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh
zsh: command not found: screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
OK: archive.db exists
199M /Volumes/Test/screenpipe/archive.db
6 tables
[2026-04-17 17:58:51] ========================================
[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:58:51] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 17:59:49] ========================================
[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:59:49] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-15
frames: 12874
elements: 886876
ui_events: 14453
ocr_text: 11412
meetings: 0
[+00m01s] ▶ Initialising tables, indexes, FTS
[2026-04-17 17:59:50] Sync complete for 2026-04-15
[2026-04-17 17:59:50] ========================================
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT date(MIN(timestamp)) FROM frames;"
2026-04-09
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 18:09:41] ========================================
[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14
[2026-04-17 18:09:41] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-14
frames: 10733
elements: 695969
ui_events: 10542
ocr_text: 8206
meetings: 0
[+00m00s] ▶ Initialising tables, indexes, FTS
creating tables ✓ 0m02s
creating indexes ✓ 0m03s
creating FTS tables ✓ 0m01s
[+00m06s] ▶ Syncing data for 2026-04-14
video_chunks ✓ 0m01s
frames (10733 rows) ⠦
DOCKER
Close Tab
DEV (docker)
Close Tab
APP (-zsh)
Close Tab
sqlite3
Close Tab
-zsh
Close Tab
✳ Review screenpipe usage and Boosteroid integration (claude)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
ec2-user@ip-10-20-6-111:~ (nc)
Close Tab
⌥⌘1
sqlite3...
|
50639
|
|
50644
|
1088
|
20
|
2026-04-17T15:10:35.868668+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776438635868_m2.jpg...
|
iTerm2
|
sqlite3
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
elements|465966705
ui_events|9804622
ocr_text|1443 elements|465966705
ui_events|9804622
ocr_text|1443084543
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
-- estimate yesterday's rows
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'
UNION ALL
SELECT 'elements',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))
FROM dbstat d, elements e
WHERE d.name = 'elements'
UNION ALL
SELECT 'ui_events',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')
FROM dbstat d, ui_events e
WHERE d.name = 'ui_events';"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT name, SUM(payload) as bytes, COUNT(*) as pages
FROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')
GROUP BY name;"
elements|466530643|121280
frames|1006977358|254673
ocr_text|1443956212|359424
ui_events|9826328|2576
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
UW PICO 5.09 New Buffer
[ Read 25 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
SELECT 'frames', COUNT(*) FROM frames
UNION ALL SELECT 'elements', COUNT(*) FROM elements
UNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events
UNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;"
frames|50235
elements|3279563
ui_events|60293
ocr_text|39114
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)
SPEED=$(
dd if=/dev/urandom of="$NAS_MOUNT/.speed_test" bs=1m count=10 2>&1 | \
grep -o '[0-9.]* bytes/sec' | awk '{print $1}'
rm -f "$NAS_MOUNT/.speed_test"
)
echo "NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s"
# 2. Get bytes/row for each table from dbstat
BYTES_FRAMES=$(sqlite3 "$DB_SRC" "SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';")
zsh: unknown file attribute: i
NAS write speed: 0 MB/s
zsh: command not found: #
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll
total 7525816
drwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .
drwx------+ 91 lukas staff 2912 17 Apr 17:23 ..
-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store
-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json
drwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data
-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite
-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm
-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal
drwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes
-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log
-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log
-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log
-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log
-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log
-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log
-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log
-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log
-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE="$HOME/.screenpipe/sync.log"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo "$msg" | tee -a "$LOG_FILE"
[2026-04-17 17:45:23]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh
zsh: command not found: screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
OK: archive.db exists
199M /Volumes/Test/screenpipe/archive.db
6 tables
[2026-04-17 17:58:51] ========================================
[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:58:51] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 17:59:49] ========================================
[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:59:49] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-15
frames: 12874
elements: 886876
ui_events: 14453
ocr_text: 11412
meetings: 0
[+00m01s] ▶ Initialising tables, indexes, FTS
[2026-04-17 17:59:50] Sync complete for 2026-04-15
[2026-04-17 17:59:50] ========================================
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT date(MIN(timestamp)) FROM frames;"
2026-04-09
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 18:09:41] ========================================
[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14
[2026-04-17 18:09:41] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-14
frames: 10733
elements: 695969
ui_events: 10542
ocr_text: 8206
meetings: 0
[+00m00s] ▶ Initialising tables, indexes, FTS
creating tables ✓ 0m02s
creating indexes ✓ 0m03s
creating FTS tables ✓ 0m01s
[+00m06s] ▶ Syncing data for 2026-04-14
video_chunks ✓ 0m01s
frames (10733 rows) ⠦
DOCKER
Close Tab
DEV (docker)
Close Tab
APP (-zsh)
Close Tab
sqlite3
Close Tab
-zsh
Close Tab
✳ Review screenpipe usage and Boosteroid integration (claude)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
ec2-user@ip-10-20-6-111:~ (nc)
Close Tab
⌥⌘1
sqlite3...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"elements|465966705\nui_events|9804622\nocr_text|1443084543\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n -- estimate yesterday's rows\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows\nFROM dbstat d, frames f\nWHERE d.name = 'frames'\nUNION ALL\nSELECT 'elements',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))\nFROM dbstat d, elements e\nWHERE d.name = 'elements'\nUNION ALL\nSELECT 'ui_events',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')\nFROM dbstat d, ui_events e\nWHERE d.name = 'ui_events';\"\n\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows \nFROM dbstat d, frames f\nWHERE d.name = 'frames'\"\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT name, SUM(payload) as bytes, COUNT(*) as pages\nFROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')\nGROUP BY name;\"\nelements|466530643|121280\nframes|1006977358|254673\nocr_text|1443956212|359424\nui_events|9826328|2576\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 25 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \nSELECT 'frames', COUNT(*) FROM frames\nUNION ALL SELECT 'elements', COUNT(*) FROM elements\nUNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events\nUNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;\"\nframes|50235\nelements|3279563\nui_events|60293\nocr_text|39114\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)\nSPEED=$(\n dd if=/dev/urandom of=\"$NAS_MOUNT/.speed_test\" bs=1m count=10 2>&1 | \\\n grep -o '[0-9.]* bytes/sec' | awk '{print $1}'\n rm -f \"$NAS_MOUNT/.speed_test\"\n)\necho \"NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s\"\n\n# 2. Get bytes/row for each table from dbstat\nBYTES_FRAMES=$(sqlite3 \"$DB_SRC\" \"SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';\")\nzsh: unknown file attribute: i\nNAS write speed: 0 MB/s\nzsh: command not found: #\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll \ntotal 7525816\ndrwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .\ndrwx------+ 91 lukas staff 2912 17 Apr 17:23 ..\n-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store\n-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json\ndrwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data\n-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite\n-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm\n-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal\ndrwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes\n-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log\n-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log\n-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log\n-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log\n-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log\n-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log\n-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log\n-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log\n-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE=\"$HOME/.screenpipe/sync.log\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg=\"[$(date '+%Y-%m-%d %H:%M:%S')] $*\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo \"$msg\" | tee -a \"$LOG_FILE\"\n[2026-04-17 17:45:23] \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh \nzsh: command not found: screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nOK: archive.db exists\n199M\u0000\u0000\u0000\t/Volumes/Test/screenpipe/archive.db\n6 tables\n[2026-04-17 17:58:51] ========================================\n[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:58:51] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 17:59:49] ========================================\n[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:59:49] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-15\n frames: 12874\n elements: 886876\n ui_events: 14453\n ocr_text: 11412\n meetings: 0\n\n[+00m01s] ▶ Initialising tables, indexes, FTS\n\n[2026-04-17 17:59:50] Sync complete for 2026-04-15\n[2026-04-17 17:59:50] ========================================\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT date(MIN(timestamp)) FROM frames;\"\n2026-04-09\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14 \nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 18:09:41] ========================================\n[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14\n[2026-04-17 18:09:41] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-14\n frames: 10733\n elements: 695969\n ui_events: 10542\n ocr_text: 8206\n meetings: 0\n\n[+00m00s] ▶ Initialising tables, indexes, FTS\n creating tables ✓ 0m02s\n creating indexes ✓ 0m03s\n creating FTS tables ✓ 0m01s\n\n[+00m06s] ▶ Syncing data for 2026-04-14\n video_chunks ✓ 0m01s\n frames (10733 rows) ⠦","depth":4,"value":"elements|465966705\nui_events|9804622\nocr_text|1443084543\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n -- estimate yesterday's rows\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows\nFROM dbstat d, frames f\nWHERE d.name = 'frames'\nUNION ALL\nSELECT 'elements',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))\nFROM dbstat d, elements e\nWHERE d.name = 'elements'\nUNION ALL\nSELECT 'ui_events',\n COUNT(*),\n SUM(payload),\n SUM(payload)/COUNT(*),\n (SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')\nFROM dbstat d, ui_events e\nWHERE d.name = 'ui_events';\"\n\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT\n 'frames' as tbl,\n COUNT(*) as total_rows,\n SUM(payload) as total_bytes,\n SUM(payload)/COUNT(*) as bytes_per_row,\n (SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows \nFROM dbstat d, frames f\nWHERE d.name = 'frames'\"\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\nSELECT name, SUM(payload) as bytes, COUNT(*) as pages\nFROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')\nGROUP BY name;\"\nelements|466530643|121280\nframes|1006977358|254673\nocr_text|1443956212|359424\nui_events|9826328|2576\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"\n UW PICO 5.09 New Buffer \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [ Read 25 lines ] \n^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos \n^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell \nSELECT 'frames', COUNT(*) FROM frames\nUNION ALL SELECT 'elements', COUNT(*) FROM elements\nUNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events\nUNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;\"\nframes|50235\nelements|3279563\nui_events|60293\nocr_text|39114\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)\nSPEED=$(\n dd if=/dev/urandom of=\"$NAS_MOUNT/.speed_test\" bs=1m count=10 2>&1 | \\\n grep -o '[0-9.]* bytes/sec' | awk '{print $1}'\n rm -f \"$NAS_MOUNT/.speed_test\"\n)\necho \"NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s\"\n\n# 2. Get bytes/row for each table from dbstat\nBYTES_FRAMES=$(sqlite3 \"$DB_SRC\" \"SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';\")\nzsh: unknown file attribute: i\nNAS write speed: 0 MB/s\nzsh: command not found: #\n^CError: stepping, interrupted (9)\nProgram interrupted.\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll \ntotal 7525816\ndrwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .\ndrwx------+ 91 lukas staff 2912 17 Apr 17:23 ..\n-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store\n-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json\ndrwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data\n-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite\n-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm\n-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal\ndrwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes\n-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log\n-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log\n-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log\n-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log\n-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log\n-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log\n-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log\n-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log\n-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE=\"$HOME/.screenpipe/sync.log\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg=\"[$(date '+%Y-%m-%d %H:%M:%S')] $*\"\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo \"$msg\" | tee -a \"$LOG_FILE\"\n[2026-04-17 17:45:23] \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh \nzsh: command not found: screenpipe_sync.sh\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nOK: archive.db exists\n199M\u0000\u0000\u0000\t/Volumes/Test/screenpipe/archive.db\n6 tables\n[2026-04-17 17:58:51] ========================================\n[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:58:51] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15\nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 17:59:49] ========================================\n[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15\n[2026-04-17 17:59:49] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-15\n frames: 12874\n elements: 886876\n ui_events: 14453\n ocr_text: 11412\n meetings: 0\n\n[+00m01s] ▶ Initialising tables, indexes, FTS\n\n[2026-04-17 17:59:50] Sync complete for 2026-04-15\n[2026-04-17 17:59:50] ========================================\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite \"SELECT date(MIN(timestamp)) FROM frames;\"\n2026-04-09\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14 \nOK: NAS mounted\nOK: Source DB exists\n3.6G\u0000\u0000\u0000\t/Users/lukas/.screenpipe/db.sqlite\nINFO: archive.db does not exist yet - will be created on first sync\n[2026-04-17 18:09:41] ========================================\n[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14\n[2026-04-17 18:09:41] ========================================\n\n[+00m00s] ▶ Preflight checks\n Source DB: OK (3.6G)\n NAS mount: OK /Volumes/Test/screenpipe\n Archive DB: will be created\n\n[+00m00s] ▶ Counting source rows for 2026-04-14\n frames: 10733\n elements: 695969\n ui_events: 10542\n ocr_text: 8206\n meetings: 0\n\n[+00m00s] ▶ Initialising tables, indexes, FTS\n creating tables ✓ 0m02s\n creating indexes ✓ 0m03s\n creating FTS tables ✓ 0m01s\n\n[+00m06s] ▶ Syncing data for 2026-04-14\n video_chunks ✓ 0m01s\n frames (10733 rows) ⠦","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.23359375,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.2359375,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (docker)","depth":2,"bounds":{"left":0.30273438,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.30507812,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.371875,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.37421876,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"sqlite3","depth":2,"bounds":{"left":0.44101563,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.44335938,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5101563,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.5125,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Review screenpipe usage and Boosteroid integration (claude)","depth":2,"bounds":{"left":0.5792969,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.5816406,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"ec2-user@ip-10-30-159-186:~ (nc)","depth":2,"bounds":{"left":0.6484375,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.6507813,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"ec2-user@ip-10-20-6-111:~ (nc)","depth":2,"bounds":{"left":0.7175781,"top":1.0,"width":0.06914063,"height":-0.037500024},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.7199219,"top":1.0,"width":0.00625,"height":-0.04027772},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.77070314,"top":1.0,"width":0.021875,"height":-0.020833373},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"sqlite3","depth":1,"bounds":{"left":0.50507814,"top":1.0,"width":0.019140625,"height":-0.021527767},"role_description":"text"}]...
|
8503612538388933471
|
-1629251819081372013
|
visual_change
|
accessibility
|
NULL
|
elements|465966705
ui_events|9804622
ocr_text|1443 elements|465966705
ui_events|9804622
ocr_text|1443084543
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
-- estimate yesterday's rows
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'
UNION ALL
SELECT 'elements',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM elements WHERE frame_id IN (SELECT id FROM frames WHERE date(timestamp) = '2026-04-15'))
FROM dbstat d, elements e
WHERE d.name = 'elements'
UNION ALL
SELECT 'ui_events',
COUNT(*),
SUM(payload),
SUM(payload)/COUNT(*),
(SELECT COUNT(*) FROM ui_events WHERE date(timestamp) = '2026-04-15')
FROM dbstat d, ui_events e
WHERE d.name = 'ui_events';"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT
'frames' as tbl,
COUNT(*) as total_rows,
SUM(payload) as total_bytes,
SUM(payload)/COUNT(*) as bytes_per_row,
(SELECT COUNT(*) FROM frames WHERE date(timestamp) = '2026-04-15') as day_rows
FROM dbstat d, frames f
WHERE d.name = 'frames'"
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
SELECT name, SUM(payload) as bytes, COUNT(*) as pages
FROM dbstat WHERE name IN ('frames','elements','ui_events','ocr_text')
GROUP BY name;"
elements|466530643|121280
frames|1006977358|254673
ocr_text|1443956212|359424
ui_events|9826328|2576
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "
UW PICO 5.09 New Buffer
[ Read 25 lines ]
^G Get Help ^O WriteOut ^R Read File ^Y Prev Pg ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where is ^V Next Pg ^U UnCut Text ^T To Spell
SELECT 'frames', COUNT(*) FROM frames
UNION ALL SELECT 'elements', COUNT(*) FROM elements
UNION ALL SELECT 'ui_events', COUNT(*) FROM ui_events
UNION ALL SELECT 'ocr_text', COUNT(*) FROM ocr_text;"
frames|50235
elements|3279563
ui_events|60293
ocr_text|39114
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ # 1. Measure NAS write speed (write 10MB test file, measure seconds)
SPEED=$(
dd if=/dev/urandom of="$NAS_MOUNT/.speed_test" bs=1m count=10 2>&1 | \
grep -o '[0-9.]* bytes/sec' | awk '{print $1}'
rm -f "$NAS_MOUNT/.speed_test"
)
echo "NAS write speed: $(( SPEED / 1024 / 1024 )) MB/s"
# 2. Get bytes/row for each table from dbstat
BYTES_FRAMES=$(sqlite3 "$DB_SRC" "SELECT SUM(payload)/COUNT(*) FROM dbstat,frames WHERE dbstat.name='frames';")
zsh: unknown file attribute: i
NAS write speed: 0 MB/s
zsh: command not found: #
^CError: stepping, interrupted (9)
Program interrupted.
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ll
total 7525816
drwxr-xr-x 18 lukas staff 576 17 Apr 08:56 .
drwx------+ 91 lukas staff 2912 17 Apr 17:23 ..
-rw-r--r--@ 1 lukas staff 8196 16 Apr 17:07 .DS_Store
-rw-r--r-- 1 lukas staff 358 16 Apr 16:49 config.json
drwxr-xr-x 6 lukas staff 192 15 Apr 14:53 data
-rw-r--r-- 1 lukas staff 3841454080 17 Apr 17:36 db.sqlite
-rw-r--r-- 1 lukas staff 98304 17 Apr 16:11 db.sqlite-shm
-rw-r--r-- 1 lukas staff 9698512 17 Apr 17:38 db.sqlite-wal
drwxr-xr-x 9 lukas staff 288 15 Apr 14:53 pipes
-rw-r--r-- 1 lukas staff 132736 9 Apr 21:27 screenpipe.2026-04-09.0.log
-rw-r--r-- 1 lukas staff 95425 11 Apr 23:14 screenpipe.2026-04-11.0.log
-rw-r--r-- 1 lukas staff 72332 12 Apr 23:55 screenpipe.2026-04-12.0.log
-rw-r--r-- 1 lukas staff 71555 13 Apr 19:50 screenpipe.2026-04-13.0.log
-rw-r--r-- 1 lukas staff 162389 14 Apr 19:31 screenpipe.2026-04-14.0.log
-rw-r--r-- 1 lukas staff 175763 15 Apr 18:55 screenpipe.2026-04-15.0.log
-rw-r--r-- 1 lukas staff 196994 16 Apr 20:33 screenpipe.2026-04-16.0.log
-rw-r--r-- 1 lukas staff 172446 17 Apr 17:38 screenpipe.2026-04-17.0.log
-rwxr-xr-x 1 lukas staff 666 16 Apr 19:43 screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ nano screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ code screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ LOG_FILE="$HOME/.screenpipe/sync.log"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ echo "$msg" | tee -a "$LOG_FILE"
[2026-04-17 17:45:23]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ screenpipe_sync.sh
zsh: command not found: screenpipe_sync.sh
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
OK: archive.db exists
199M /Volumes/Test/screenpipe/archive.db
6 tables
[2026-04-17 17:58:51] ========================================
[2026-04-17 17:58:51] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:58:51] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
[2026-04-17 17:58:52] Date 2026-04-15 already has 12874 frames in archive — skipping
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-15
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 17:59:49] ========================================
[2026-04-17 17:59:49] Screenpipe sync starting for: 2026-04-15
[2026-04-17 17:59:49] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-15
frames: 12874
elements: 886876
ui_events: 14453
ocr_text: 11412
meetings: 0
[+00m01s] ▶ Initialising tables, indexes, FTS
[2026-04-17 17:59:50] Sync complete for 2026-04-15
[2026-04-17 17:59:50] ========================================
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ sqlite3 ~/.screenpipe/db.sqlite "SELECT date(MIN(timestamp)) FROM frames;"
2026-04-09
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe $ ~/.screenpipe/screenpipe_sync.sh 2026-04-14
OK: NAS mounted
OK: Source DB exists
3.6G /Users/lukas/.screenpipe/db.sqlite
INFO: archive.db does not exist yet - will be created on first sync
[2026-04-17 18:09:41] ========================================
[2026-04-17 18:09:41] Screenpipe sync starting for: 2026-04-14
[2026-04-17 18:09:41] ========================================
[+00m00s] ▶ Preflight checks
Source DB: OK (3.6G)
NAS mount: OK /Volumes/Test/screenpipe
Archive DB: will be created
[+00m00s] ▶ Counting source rows for 2026-04-14
frames: 10733
elements: 695969
ui_events: 10542
ocr_text: 8206
meetings: 0
[+00m00s] ▶ Initialising tables, indexes, FTS
creating tables ✓ 0m02s
creating indexes ✓ 0m03s
creating FTS tables ✓ 0m01s
[+00m06s] ▶ Syncing data for 2026-04-14
video_chunks ✓ 0m01s
frames (10733 rows) ⠦
DOCKER
Close Tab
DEV (docker)
Close Tab
APP (-zsh)
Close Tab
sqlite3
Close Tab
-zsh
Close Tab
✳ Review screenpipe usage and Boosteroid integration (claude)
Close Tab
ec2-user@ip-10-30-159-186:~ (nc)
Close Tab
ec2-user@ip-10-20-6-111:~ (nc)
Close Tab
⌥⌘1
sqlite3...
|
NULL
|
|
71633
|
1715
|
0
|
2026-04-22T13:11:26.681818+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776863486681_m2.jpg...
|
PhpStorm
|
faVsco.js – RequestGenerateAskJiminnyReportJob.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25797874,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29654256,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.35305852,"top":0.22905028,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.36569148,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"LOG_PREFIX","depth":4,"bounds":{"left":0.37666222,"top":0.22825219,"width":0.043882977,"height":0.015961692},"value":"LOG_PREFIX","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.42952126,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.43949467,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.4481383,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.45678192,"top":0.22825219,"width":0.00731383,"height":0.017557861},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3/16","depth":4,"bounds":{"left":0.47041222,"top":0.22745411,"width":0.025598405,"height":0.017557861},"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.49601063,"top":0.22665602,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.48470744,"top":0.25778133,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.49401596,"top":0.25778133,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5036569,"top":0.25618514,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5109708,"top":0.25618514,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\AutomatedReports;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Bus\\Queueable;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Contracts\\Routing\\UrlGenerator;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\ProphetAi\\Exceptions\\ProphetException;\nuse Jiminny\\Component\\ProphetAi\\ProphetClient;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AskJiminnyReportActivityService;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Psr\\Log\\LoggerInterface;\nuse Throwable;\n\nclass RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use Queueable;\n\n private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';\n\n private const int MIN_ACTIVITIES_COUNT = 1;\n\n public int $tries = 2;\n\n private ?AutomatedReportResult $reportResult = null;\n\n public function __construct(private readonly string $reportUuid)\n {\n $this->onQueue(Constants::QUEUE_ANALYTICS);\n }\n\n public function uniqueId(): string\n {\n return $this->reportUuid;\n }\n\n public function handle(\n AutomatedReportsService $reportService,\n AskJiminnyReportActivityService $activityService,\n ProphetClient $prophetClient,\n LoggerInterface $logger,\n UrlGenerator $urlGenerator,\n JobDispatcherInterface $jobDispatcher,\n ): void {\n $logger->info(self::LOG_PREFIX . ' Started', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n try {\n $automatedReport = $reportService->getReport($this->reportUuid);\n\n if (! $this->validateReport($automatedReport, $logger)) {\n return;\n }\n\n $creator = $automatedReport->getCreator();\n if ($creator === null) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $savedSearch = $automatedReport->getSavedSearch();\n if ($savedSearch === null) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $prompt = $automatedReport->getAskAnythingPrompt();\n if ($prompt === null) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $this->reportResult = $reportService->getOrCreateReportResult(\n automatedReport: $automatedReport,\n data: [\n 'status' => AutomatedReportResult::STATUS_DEFAULT,\n 'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,\n ]\n );\n\n $activityIds = $activityService->getActivityIdsForSavedSearch(\n savedSearch: $savedSearch,\n user: $creator,\n frequency: $automatedReport->getFrequency(),\n );\n\n $logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [\n 'automatedReportUuid' => $this->reportUuid,\n 'activityCount' => count($activityIds),\n ]);\n\n if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {\n $this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);\n\n $logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [\n 'automatedReportUuid' => $this->reportUuid,\n 'activityCount' => count($activityIds),\n ]);\n\n $this->dispatchNotGeneratedNotifications(\n $automatedReport,\n $reportService,\n $urlGenerator,\n $jobDispatcher,\n $logger,\n );\n\n return;\n }\n\n $payload = $reportService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $this->reportResult,\n activityIds: $activityIds,\n );\n\n $this->reportResult->update([\n 'name' => $reportService->getReportFileName($this->reportResult),\n 'payload' => $payload,\n 'status' => AutomatedReportResult::STATUS_REQUESTED,\n 'requested_at' => Carbon::now()->toDateTimeString(),\n ]);\n\n $logger->info(self::LOG_PREFIX . ' Request sent', [\n 'automatedReportUuid' => $this->reportUuid,\n 'reportUuid' => $this->reportResult->getUuid(),\n 'payload' => $payload,\n ]);\n\n $response = $prophetClient->sendRequest(\n endpoint: ProphetClient::ASK_JIMINNY_REPORT,\n requestArray: $payload,\n );\n\n $logger->info(self::LOG_PREFIX . ' Response received', [\n 'response' => $response->getContent(),\n ]);\n } catch (Throwable $exception) {\n $reason = $exception instanceof ProphetException\n ? AutomatedReportResult::REASON_PROPHET_API_ERROR\n : AutomatedReportResult::REASON_DEFAULT;\n\n $this->failReport($reason);\n\n $logger->error(self::LOG_PREFIX . ' Error', [\n 'automatedReportUuid' => $this->reportUuid,\n 'reportUuid' => $this->reportResult?->getUuid(),\n 'code' => $exception->getCode(),\n 'message' => $exception->getMessage(),\n ]);\n\n if ($this->attempts() < $this->tries) {\n $logger->info(self::LOG_PREFIX . ' Retry scheduled', [\n 'attempts' => $this->attempts(),\n ]);\n\n $this->release(30);\n } else {\n $this->fail($exception);\n }\n }\n }\n\n private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool\n {\n if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [\n 'automatedReportUuid' => $this->reportUuid,\n 'type' => $automatedReport->getType(),\n ]);\n\n return false;\n }\n\n if (! $automatedReport->getStatus()) {\n $logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return false;\n }\n\n if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {\n $logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return false;\n }\n\n return true;\n }\n\n private function failReport(int $reason): void\n {\n $this->reportResult?->update([\n 'status' => AutomatedReportResult::STATUS_FAILED,\n 'reason' => $reason,\n ]);\n }\n\n private function dispatchNotGeneratedNotifications(\n AutomatedReport $automatedReport,\n AutomatedReportsService $reportService,\n UrlGenerator $urlGenerator,\n JobDispatcherInterface $jobDispatcher,\n LoggerInterface $logger,\n ): void {\n if ($this->reportResult === null) {\n return;\n }\n\n $recipients = $reportService->getValidRecipientUsers($automatedReport);\n if (empty($recipients)) {\n $logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $reportName = $automatedReport->getCustomName()\n ?: $reportService->getReportTypeName($this->reportResult);\n $periodName = $reportService->getReportPeriodName($this->reportResult);\n $reportsPageUrl = $urlGenerator->route('ai.reports.show');\n\n foreach ($recipients as $recipient) {\n $jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(\n reportUuid: $this->reportResult->getUuid(),\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n reportName: $reportName,\n periodName: $periodName,\n reportsPageUrl: $reportsPageUrl,\n ));\n }\n\n $logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [\n 'automatedReportUuid' => $this->reportUuid,\n 'recipientsCount' => count($recipients),\n ]);\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Jobs\\AutomatedReports;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Bus\\Queueable;\nuse Illuminate\\Contracts\\Queue\\ShouldBeUnique;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Contracts\\Routing\\UrlGenerator;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\ProphetAi\\Exceptions\\ProphetException;\nuse Jiminny\\Component\\ProphetAi\\ProphetClient;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AskJiminnyReportActivityService;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Psr\\Log\\LoggerInterface;\nuse Throwable;\n\nclass RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique\n{\n use InteractsWithQueue;\n use Queueable;\n\n private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';\n\n private const int MIN_ACTIVITIES_COUNT = 1;\n\n public int $tries = 2;\n\n private ?AutomatedReportResult $reportResult = null;\n\n public function __construct(private readonly string $reportUuid)\n {\n $this->onQueue(Constants::QUEUE_ANALYTICS);\n }\n\n public function uniqueId(): string\n {\n return $this->reportUuid;\n }\n\n public function handle(\n AutomatedReportsService $reportService,\n AskJiminnyReportActivityService $activityService,\n ProphetClient $prophetClient,\n LoggerInterface $logger,\n UrlGenerator $urlGenerator,\n JobDispatcherInterface $jobDispatcher,\n ): void {\n $logger->info(self::LOG_PREFIX . ' Started', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n try {\n $automatedReport = $reportService->getReport($this->reportUuid);\n\n if (! $this->validateReport($automatedReport, $logger)) {\n return;\n }\n\n $creator = $automatedReport->getCreator();\n if ($creator === null) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $savedSearch = $automatedReport->getSavedSearch();\n if ($savedSearch === null) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $prompt = $automatedReport->getAskAnythingPrompt();\n if ($prompt === null) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $this->reportResult = $reportService->getOrCreateReportResult(\n automatedReport: $automatedReport,\n data: [\n 'status' => AutomatedReportResult::STATUS_DEFAULT,\n 'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,\n ]\n );\n\n $activityIds = $activityService->getActivityIdsForSavedSearch(\n savedSearch: $savedSearch,\n user: $creator,\n frequency: $automatedReport->getFrequency(),\n );\n\n $logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [\n 'automatedReportUuid' => $this->reportUuid,\n 'activityCount' => count($activityIds),\n ]);\n\n if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {\n $this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);\n\n $logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [\n 'automatedReportUuid' => $this->reportUuid,\n 'activityCount' => count($activityIds),\n ]);\n\n $this->dispatchNotGeneratedNotifications(\n $automatedReport,\n $reportService,\n $urlGenerator,\n $jobDispatcher,\n $logger,\n );\n\n return;\n }\n\n $payload = $reportService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $this->reportResult,\n activityIds: $activityIds,\n );\n\n $this->reportResult->update([\n 'name' => $reportService->getReportFileName($this->reportResult),\n 'payload' => $payload,\n 'status' => AutomatedReportResult::STATUS_REQUESTED,\n 'requested_at' => Carbon::now()->toDateTimeString(),\n ]);\n\n $logger->info(self::LOG_PREFIX . ' Request sent', [\n 'automatedReportUuid' => $this->reportUuid,\n 'reportUuid' => $this->reportResult->getUuid(),\n 'payload' => $payload,\n ]);\n\n $response = $prophetClient->sendRequest(\n endpoint: ProphetClient::ASK_JIMINNY_REPORT,\n requestArray: $payload,\n );\n\n $logger->info(self::LOG_PREFIX . ' Response received', [\n 'response' => $response->getContent(),\n ]);\n } catch (Throwable $exception) {\n $reason = $exception instanceof ProphetException\n ? AutomatedReportResult::REASON_PROPHET_API_ERROR\n : AutomatedReportResult::REASON_DEFAULT;\n\n $this->failReport($reason);\n\n $logger->error(self::LOG_PREFIX . ' Error', [\n 'automatedReportUuid' => $this->reportUuid,\n 'reportUuid' => $this->reportResult?->getUuid(),\n 'code' => $exception->getCode(),\n 'message' => $exception->getMessage(),\n ]);\n\n if ($this->attempts() < $this->tries) {\n $logger->info(self::LOG_PREFIX . ' Retry scheduled', [\n 'attempts' => $this->attempts(),\n ]);\n\n $this->release(30);\n } else {\n $this->fail($exception);\n }\n }\n }\n\n private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool\n {\n if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {\n $logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [\n 'automatedReportUuid' => $this->reportUuid,\n 'type' => $automatedReport->getType(),\n ]);\n\n return false;\n }\n\n if (! $automatedReport->getStatus()) {\n $logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return false;\n }\n\n if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {\n $logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return false;\n }\n\n return true;\n }\n\n private function failReport(int $reason): void\n {\n $this->reportResult?->update([\n 'status' => AutomatedReportResult::STATUS_FAILED,\n 'reason' => $reason,\n ]);\n }\n\n private function dispatchNotGeneratedNotifications(\n AutomatedReport $automatedReport,\n AutomatedReportsService $reportService,\n UrlGenerator $urlGenerator,\n JobDispatcherInterface $jobDispatcher,\n LoggerInterface $logger,\n ): void {\n if ($this->reportResult === null) {\n return;\n }\n\n $recipients = $reportService->getValidRecipientUsers($automatedReport);\n if (empty($recipients)) {\n $logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [\n 'automatedReportUuid' => $this->reportUuid,\n ]);\n\n return;\n }\n\n $reportName = $automatedReport->getCustomName()\n ?: $reportService->getReportTypeName($this->reportResult);\n $periodName = $reportService->getReportPeriodName($this->reportResult);\n $reportsPageUrl = $urlGenerator->route('ai.reports.show');\n\n foreach ($recipients as $recipient) {\n $jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(\n reportUuid: $this->reportResult->getUuid(),\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n reportName: $reportName,\n periodName: $periodName,\n reportsPageUrl: $reportsPageUrl,\n ));\n }\n\n $logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [\n 'automatedReportUuid' => $this->reportUuid,\n 'recipientsCount' => count($recipients),\n ]);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8503495010777809356
|
-787814688868807964
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide...
|
NULL
|
|
31211
|
630
|
6
|
2026-04-15T15:21:53.811950+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776266513811_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
17631177155689192/200Imperial Age-Halberdier Creat 17631177155689192/200Imperial Age-Halberdier Created---Warning: You are being attacked byPlayer 8 Almish Yiltawar!!!---Capped Ram Created----Light Cavalry Created--1 kovaliklukas: 56213/56213E Magnus Olafsson: 40814/40814 38 Almish Yiltawar: 39457/39457Rajyapala: 23275/232756 LÁezl6 I: 12440/22440 2 IVL Maximilian of Habsbung: 6531/6531 V IV3 HuaseÁm: 5931/50314 Lowig VI: 5792/5702...
|
NULL
|
8502229287239860081
|
NULL
|
click
|
ocr
|
NULL
|
17631177155689192/200Imperial Age-Halberdier Creat 17631177155689192/200Imperial Age-Halberdier Created---Warning: You are being attacked byPlayer 8 Almish Yiltawar!!!---Capped Ram Created----Light Cavalry Created--1 kovaliklukas: 56213/56213E Magnus Olafsson: 40814/40814 38 Almish Yiltawar: 39457/39457Rajyapala: 23275/232756 LÁezl6 I: 12440/22440 2 IVL Maximilian of Habsbung: 6531/6531 V IV3 HuaseÁm: 5931/50314 Lowig VI: 5792/5702...
|
NULL
|
|
37182
|
765
|
12
|
2026-04-16T12:17:38.272142+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776341858272_m2.jpg...
|
PhpStorm
|
faVsco.js – console [EU]
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
26...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.03046875,"top":0.017361112,"width":0.0453125,"height":0.022222223},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"bounds":{"left":0.07578125,"top":0.017361112,"width":0.14960937,"height":0.022222223},"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny, but local branch is out of sync with remote","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.78515625,"top":0.017361112,"width":0.01328125,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AutomatedReportsCommandTest","depth":6,"bounds":{"left":0.803125,"top":0.017361112,"width":0.09765625,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AutomatedReportsCommandTest'","depth":6,"bounds":{"left":0.9007813,"top":0.017361112,"width":0.01328125,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AutomatedReportsCommandTest'","depth":6,"bounds":{"left":0.9140625,"top":0.017361112,"width":0.01328125,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9273437,"top":0.017361112,"width":0.01328125,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96015626,"top":0.017361112,"width":0.01328125,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9734375,"top":0.017361112,"width":0.01328125,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9867188,"top":0.017361112,"width":0.013281226,"height":0.022222223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.01015625,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.01015625,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.049609374,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.01015625,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4","depth":4,"bounds":{"left":0.49570313,"top":0.15208334,"width":0.009375,"height":0.013194445},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.50742185,"top":0.15208334,"width":0.009375,"height":0.013194445},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.51875,"top":0.15069444,"width":0.00859375,"height":0.015972223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.52734375,"top":0.15069444,"width":0.008203125,"height":0.015972223},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Http\\Controllers;\n\nuse Illuminate\\Http\\JsonResponse;\nuse Jiminny\\Component\\FeatureFlags\\FeatureRepository;\nuse Jiminny\\Contracts\\Crm\\Providers;\nuse Jiminny\\Events\\EventDispatcher;\nuse Jiminny\\Events\\Users\\SocialAccountConnected;\nuse Jiminny\\Integrations\\RouteProviderList;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Repositories\\SocialAccountRepository;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\Api\\TokenBuilder;\nuse Psr\\Log\\LoggerInterface;\n\n/**\n * Provision important Team Setup option, that are in essence configurable.\n */\nclass TeamSetupController extends Controller\n{\n public function __construct(\n private readonly EventDispatcher $eventDispatcher,\n private readonly TokenBuilder $tokenBuilder,\n private readonly SocialAccountRepository $socialAccountRepository,\n private readonly LoggerInterface $logger,\n private readonly FeatureRepository $featureRepository,\n ) {\n parent::__construct();\n }\n public function features(): JsonResponse\n {\n $availableFeatures = $this->featureRepository->getFeatures();\n $availableFeaturesSerialised = [];\n foreach ($availableFeatures as $feature) {\n // getSlug() returns null for unknown enum values during deployment race condition\n $slug = $feature->getSlug();\n if ($slug === null) {\n continue;\n }\n $availableFeaturesSerialised[] = [\n 'id' => $slug->name,\n 'label' => $feature->getTitle(),\n 'description' => $feature->getDescription(),\n 'type' => $feature->getType()->name,\n 'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,\n ];\n }\n\n return response()->json($availableFeaturesSerialised);\n }\n\n public function tiers(): JsonResponse\n {\n $tiers = $this->featureRepository->getTiersOrderedByWeight();\n\n return response()->json(\n array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)\n );\n }\n\n /**\n * Get all enabled / available CRM providers\n */\n public function crmServices(): JsonResponse\n {\n return response()->json(\n Providers::getAllEnabledCrmProviders()\n );\n }\n\n public function calendars(): JsonResponse\n {\n return response()->json([\n ['id' => 'office', 'label' => 'Office'],\n ['id' => 'google', 'label' => 'Google'],\n ]);\n }\n\n public function connectProviders(): JsonResponse\n {\n $user = $this->getUser();\n $team = $user->getTeam();\n\n $providerSlug = $team->getCrmConfiguration()->getProviderName();\n\n $providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();\n if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {\n $providers[$providerSlug]['viaIntegrationApp'] = false;\n }\n\n return response()->json(array_values($providers));\n }\n\n /**\n * Prepare a token for integration app\n */\n public function integrationAppToken(): JsonResponse\n {\n $user = $this->getUser();\n $team = $user->getTeam();\n\n $realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());\n /** If the provider is not via Integration APP, do nothing */\n if (! Providers::isIntegrationAppProvider($realProviderKey)) {\n return response()->json(['token' => '']);\n }\n\n /** No need to generate a token if the user des not require CRM */\n if (! $user->isCrmRequired()) {\n return response()->json(['token' => '']);\n }\n\n /** We keep all IntegrationApp providers as \"integration-app\" in the SocialAccount */\n $crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);\n\n $socialAccount = $user->getSocialAccount($crmProviderKey);\n\n /**\n * We need a valid token to communicate with IntegrationApp.\n *\n * Either we need to create a new valid token and save it in a social account\n */\n if ($socialAccount === null) {\n $crmTokenCandidate = $this->tokenBuilder->create($team);\n $socialAccount = $this->socialAccountRepository->create($user, [\n 'provider' => $crmProviderKey,\n 'provider_user_id' => $team->getUuid(),\n 'provider_user_token' => $crmTokenCandidate,\n 'expires' => $this->tokenBuilder->getExpireTime(),\n 'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,\n ]);\n\n $this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [\n 'team_id' => $team->getId(),\n 'provider' => $crmProviderKey,\n 'provider_user_id' => $team->getUuid(),\n 'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,\n ]);\n }\n\n /**\n * Or if a social account exists, but the token has expired, we need to regenerate it\n * and update the social account\n */\n if ($socialAccount->isExpired()) {\n $crmTokenCandidate = $this->tokenBuilder->create($team);\n $socialAccount = $this->socialAccountRepository->update($socialAccount, [\n 'provider_user_token' => $crmTokenCandidate,\n 'expires' => $this->tokenBuilder->getExpireTime(),\n ]);\n\n $this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [\n 'team_id' => $team->getId(),\n 'provider' => $crmProviderKey,\n 'provider_user_id' => $team->getUuid(),\n 'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,\n ]);\n }\n\n return response()->json([\n 'token' => $socialAccount->getProviderUserToken(),\n ]);\n }\n\n public function integrationAppConnect(): JsonResponse\n {\n $user = $this->getUser();\n $team = $user->getTeam();\n\n $realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());\n /** If the provider is not via Integration APP, do nothing */\n if (! Providers::isIntegrationAppProvider($realProviderKey)) {\n $this->logger->error('[IntegrationApp] Unexpected provider for connection.', [\n 'team_id' => $team->getId(),\n 'provider' => $realProviderKey,\n ]);\n\n return response()\n ->json([\n 'success' => false,\n 'message' => 'Action is not supported by the current CRM Provider',\n ])\n ->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);\n }\n\n /** We keep all IntegrationApp providers as \"integration-app\" in the SocialAccount */\n $crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);\n\n /** @var ?SocialAccount $socialAccount */\n $socialAccount = $user->getSocialAccount($crmProviderKey);\n if ($socialAccount === null) {\n $this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [\n 'team_id' => $team->getId(),\n 'iapp_provider' => $realProviderKey,\n 'provider' => $crmProviderKey,\n ]);\n\n return response()\n ->json([\n 'success' => false,\n 'message' => 'Something went wrong. Social account is cannot be found.',\n ])\n ->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);\n }\n\n $socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);\n $socialAccount->save();\n\n $this->logger->info('[IntegrationApp] Social account is connected.', [\n 'team_id' => $team->getId(),\n 'iapp_provider' => $realProviderKey,\n 'provider' => $crmProviderKey,\n 'state' => SocialAccount::STATE_CONNECTED,\n ]);\n\n $this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));\n\n return response()\n ->json([\n 'success' => true,\n 'message' => sprintf(\n '%s is successfully connected',\n Providers::getIntegrationAppProviderLabel($realProviderKey)\n ),\n ])\n ->setStatusCode(JsonResponse::HTTP_ACCEPTED);\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Http\\Controllers;\n\nuse Illuminate\\Http\\JsonResponse;\nuse Jiminny\\Component\\FeatureFlags\\FeatureRepository;\nuse Jiminny\\Contracts\\Crm\\Providers;\nuse Jiminny\\Events\\EventDispatcher;\nuse Jiminny\\Events\\Users\\SocialAccountConnected;\nuse Jiminny\\Integrations\\RouteProviderList;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Repositories\\SocialAccountRepository;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\Api\\TokenBuilder;\nuse Psr\\Log\\LoggerInterface;\n\n/**\n * Provision important Team Setup option, that are in essence configurable.\n */\nclass TeamSetupController extends Controller\n{\n public function __construct(\n private readonly EventDispatcher $eventDispatcher,\n private readonly TokenBuilder $tokenBuilder,\n private readonly SocialAccountRepository $socialAccountRepository,\n private readonly LoggerInterface $logger,\n private readonly FeatureRepository $featureRepository,\n ) {\n parent::__construct();\n }\n public function features(): JsonResponse\n {\n $availableFeatures = $this->featureRepository->getFeatures();\n $availableFeaturesSerialised = [];\n foreach ($availableFeatures as $feature) {\n // getSlug() returns null for unknown enum values during deployment race condition\n $slug = $feature->getSlug();\n if ($slug === null) {\n continue;\n }\n $availableFeaturesSerialised[] = [\n 'id' => $slug->name,\n 'label' => $feature->getTitle(),\n 'description' => $feature->getDescription(),\n 'type' => $feature->getType()->name,\n 'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,\n ];\n }\n\n return response()->json($availableFeaturesSerialised);\n }\n\n public function tiers(): JsonResponse\n {\n $tiers = $this->featureRepository->getTiersOrderedByWeight();\n\n return response()->json(\n array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)\n );\n }\n\n /**\n * Get all enabled / available CRM providers\n */\n public function crmServices(): JsonResponse\n {\n return response()->json(\n Providers::getAllEnabledCrmProviders()\n );\n }\n\n public function calendars(): JsonResponse\n {\n return response()->json([\n ['id' => 'office', 'label' => 'Office'],\n ['id' => 'google', 'label' => 'Google'],\n ]);\n }\n\n public function connectProviders(): JsonResponse\n {\n $user = $this->getUser();\n $team = $user->getTeam();\n\n $providerSlug = $team->getCrmConfiguration()->getProviderName();\n\n $providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();\n if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {\n $providers[$providerSlug]['viaIntegrationApp'] = false;\n }\n\n return response()->json(array_values($providers));\n }\n\n /**\n * Prepare a token for integration app\n */\n public function integrationAppToken(): JsonResponse\n {\n $user = $this->getUser();\n $team = $user->getTeam();\n\n $realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());\n /** If the provider is not via Integration APP, do nothing */\n if (! Providers::isIntegrationAppProvider($realProviderKey)) {\n return response()->json(['token' => '']);\n }\n\n /** No need to generate a token if the user des not require CRM */\n if (! $user->isCrmRequired()) {\n return response()->json(['token' => '']);\n }\n\n /** We keep all IntegrationApp providers as \"integration-app\" in the SocialAccount */\n $crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);\n\n $socialAccount = $user->getSocialAccount($crmProviderKey);\n\n /**\n * We need a valid token to communicate with IntegrationApp.\n *\n * Either we need to create a new valid token and save it in a social account\n */\n if ($socialAccount === null) {\n $crmTokenCandidate = $this->tokenBuilder->create($team);\n $socialAccount = $this->socialAccountRepository->create($user, [\n 'provider' => $crmProviderKey,\n 'provider_user_id' => $team->getUuid(),\n 'provider_user_token' => $crmTokenCandidate,\n 'expires' => $this->tokenBuilder->getExpireTime(),\n 'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,\n ]);\n\n $this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [\n 'team_id' => $team->getId(),\n 'provider' => $crmProviderKey,\n 'provider_user_id' => $team->getUuid(),\n 'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,\n ]);\n }\n\n /**\n * Or if a social account exists, but the token has expired, we need to regenerate it\n * and update the social account\n */\n if ($socialAccount->isExpired()) {\n $crmTokenCandidate = $this->tokenBuilder->create($team);\n $socialAccount = $this->socialAccountRepository->update($socialAccount, [\n 'provider_user_token' => $crmTokenCandidate,\n 'expires' => $this->tokenBuilder->getExpireTime(),\n ]);\n\n $this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [\n 'team_id' => $team->getId(),\n 'provider' => $crmProviderKey,\n 'provider_user_id' => $team->getUuid(),\n 'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,\n ]);\n }\n\n return response()->json([\n 'token' => $socialAccount->getProviderUserToken(),\n ]);\n }\n\n public function integrationAppConnect(): JsonResponse\n {\n $user = $this->getUser();\n $team = $user->getTeam();\n\n $realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());\n /** If the provider is not via Integration APP, do nothing */\n if (! Providers::isIntegrationAppProvider($realProviderKey)) {\n $this->logger->error('[IntegrationApp] Unexpected provider for connection.', [\n 'team_id' => $team->getId(),\n 'provider' => $realProviderKey,\n ]);\n\n return response()\n ->json([\n 'success' => false,\n 'message' => 'Action is not supported by the current CRM Provider',\n ])\n ->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);\n }\n\n /** We keep all IntegrationApp providers as \"integration-app\" in the SocialAccount */\n $crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);\n\n /** @var ?SocialAccount $socialAccount */\n $socialAccount = $user->getSocialAccount($crmProviderKey);\n if ($socialAccount === null) {\n $this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [\n 'team_id' => $team->getId(),\n 'iapp_provider' => $realProviderKey,\n 'provider' => $crmProviderKey,\n ]);\n\n return response()\n ->json([\n 'success' => false,\n 'message' => 'Something went wrong. Social account is cannot be found.',\n ])\n ->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);\n }\n\n $socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);\n $socialAccount->save();\n\n $this->logger->info('[IntegrationApp] Social account is connected.', [\n 'team_id' => $team->getId(),\n 'iapp_provider' => $realProviderKey,\n 'provider' => $crmProviderKey,\n 'state' => SocialAccount::STATE_CONNECTED,\n ]);\n\n $this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));\n\n return response()\n ->json([\n 'success' => true,\n 'message' => sprintf(\n '%s is successfully connected',\n Providers::getIntegrationAppProviderLabel($realProviderKey)\n ),\n ])\n ->setStatusCode(JsonResponse::HTTP_ACCEPTED);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.5375,"top":0.08611111,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.54765624,"top":0.08611111,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.5605469,"top":0.08611111,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.57070315,"top":0.08611111,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.58085936,"top":0.08611111,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.59375,"top":0.08611111,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.60664064,"top":0.08611111,"width":0.028515626,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.63789064,"top":0.08611111,"width":0.01015625,"height":0.016666668},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.6507813,"top":0.08611111,"width":0.034765624,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.9515625,"top":0.08611111,"width":0.033203125,"height":0.016666668},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.01015625,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.01015625,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.049609374,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.23320313,"top":1.0,"width":0.01015625,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"26","depth":4,"bounds":{"left":0.9019531,"top":0.10763889,"width":0.012109375,"height":0.013194445},"role_description":"text"}]...
|
8501882996499084550
|
-4108250601104916590
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Jiminny\Component\FeatureFlags\FeatureRepository;
use Jiminny\Contracts\Crm\Providers;
use Jiminny\Events\EventDispatcher;
use Jiminny\Events\Users\SocialAccountConnected;
use Jiminny\Integrations\RouteProviderList;
use Jiminny\Models\SocialAccount;
use Jiminny\Repositories\SocialAccountRepository;
use Jiminny\Services\Crm\IntegrationApp\Api\TokenBuilder;
use Psr\Log\LoggerInterface;
/**
* Provision important Team Setup option, that are in essence configurable.
*/
class TeamSetupController extends Controller
{
public function __construct(
private readonly EventDispatcher $eventDispatcher,
private readonly TokenBuilder $tokenBuilder,
private readonly SocialAccountRepository $socialAccountRepository,
private readonly LoggerInterface $logger,
private readonly FeatureRepository $featureRepository,
) {
parent::__construct();
}
public function features(): JsonResponse
{
$availableFeatures = $this->featureRepository->getFeatures();
$availableFeaturesSerialised = [];
foreach ($availableFeatures as $feature) {
// getSlug() returns null for unknown enum values during deployment race condition
$slug = $feature->getSlug();
if ($slug === null) {
continue;
}
$availableFeaturesSerialised[] = [
'id' => $slug->name,
'label' => $feature->getTitle(),
'description' => $feature->getDescription(),
'type' => $feature->getType()->name,
'tier_id' => $feature->getTier() ? $feature->getTier()->getUuid() : null,
];
}
return response()->json($availableFeaturesSerialised);
}
public function tiers(): JsonResponse
{
$tiers = $this->featureRepository->getTiersOrderedByWeight();
return response()->json(
array_map(static function ($tier) { return ['id' => $tier->getUuid(), 'title' => $tier->getTitle()]; }, $tiers)
);
}
/**
* Get all enabled / available CRM providers
*/
public function crmServices(): JsonResponse
{
return response()->json(
Providers::getAllEnabledCrmProviders()
);
}
public function calendars(): JsonResponse
{
return response()->json([
['id' => 'office', 'label' => 'Office'],
['id' => 'google', 'label' => 'Google'],
]);
}
public function connectProviders(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$providerSlug = $team->getCrmConfiguration()->getProviderName();
$providers = RouteProviderList::getFrontendDefinitionsForConnectProviders();
if (Providers::isSalesforceTestableViaIntegrationApp($providerSlug)) {
$providers[$providerSlug]['viaIntegrationApp'] = false;
}
return response()->json(array_values($providers));
}
/**
* Prepare a token for integration app
*/
public function integrationAppToken(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
return response()->json(['token' => '']);
}
/** No need to generate a token if the user des not require CRM */
if (! $user->isCrmRequired()) {
return response()->json(['token' => '']);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
$socialAccount = $user->getSocialAccount($crmProviderKey);
/**
* We need a valid token to communicate with IntegrationApp.
*
* Either we need to create a new valid token and save it in a social account
*/
if ($socialAccount === null) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->create($user, [
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
$this->logger->info('[IntegrationApp] Connect step - New social account created with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
/**
* Or if a social account exists, but the token has expired, we need to regenerate it
* and update the social account
*/
if ($socialAccount->isExpired()) {
$crmTokenCandidate = $this->tokenBuilder->create($team);
$socialAccount = $this->socialAccountRepository->update($socialAccount, [
'provider_user_token' => $crmTokenCandidate,
'expires' => $this->tokenBuilder->getExpireTime(),
]);
$this->logger->info('[IntegrationApp] Connect step - Social account updated with new token.', [
'team_id' => $team->getId(),
'provider' => $crmProviderKey,
'provider_user_id' => $team->getUuid(),
'state' => SocialAccount::STATE_FULL_REFRESH_REQUIRED,
]);
}
return response()->json([
'token' => $socialAccount->getProviderUserToken(),
]);
}
public function integrationAppConnect(): JsonResponse
{
$user = $this->getUser();
$team = $user->getTeam();
$realProviderKey = Providers::getCrmIntegrationSlug($team->getCrmConfiguration());
/** If the provider is not via Integration APP, do nothing */
if (! Providers::isIntegrationAppProvider($realProviderKey)) {
$this->logger->error('[IntegrationApp] Unexpected provider for connection.', [
'team_id' => $team->getId(),
'provider' => $realProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Action is not supported by the current CRM Provider',
])
->setStatusCode(JsonResponse::HTTP_BAD_REQUEST);
}
/** We keep all IntegrationApp providers as "integration-app" in the SocialAccount */
$crmProviderKey = Providers::getTranslatedCrmProviderKey($realProviderKey);
/** @var ?SocialAccount $socialAccount */
$socialAccount = $user->getSocialAccount($crmProviderKey);
if ($socialAccount === null) {
$this->logger->error('[IntegrationApp] Unexpected error. Social account is missing.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
]);
return response()
->json([
'success' => false,
'message' => 'Something went wrong. Social account is cannot be found.',
])
->setStatusCode(JsonResponse::HTTP_FAILED_DEPENDENCY);
}
$socialAccount->setAttribute('state', SocialAccount::STATE_CONNECTED);
$socialAccount->save();
$this->logger->info('[IntegrationApp] Social account is connected.', [
'team_id' => $team->getId(),
'iapp_provider' => $realProviderKey,
'provider' => $crmProviderKey,
'state' => SocialAccount::STATE_CONNECTED,
]);
$this->eventDispatcher->dispatch(new SocialAccountConnected($socialAccount));
return response()
->json([
'success' => true,
'message' => sprintf(
'%s is successfully connected',
Providers::getIntegrationAppProviderLabel($realProviderKey)
),
])
->setStatusCode(JsonResponse::HTTP_ACCEPTED);
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:
Hide
26...
|
37181
|
|
33066
|
668
|
35
|
2026-04-16T07:32:45.497705+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776324765497_m2.jpg...
|
Firefox
|
[SRD-6787] Issue with reconnecting Zoho - Jira — W [SRD-6787] Issue with reconnecting Zoho - Jira — Work...
|
True
|
jiminny.atlassian.net/jira/servicedesk/projects/SR jiminny.atlassian.net/jira/servicedesk/projects/SRD/queues/custom/37/SRD-6787...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
[SRD-6787] Issue with reconnecting Zoho - Jira
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Sandbox: Verify your identity in Salesforce - [EMAIL] - Jiminny Mail
Jiminny x Shiji - Reconnecting the platform
Jiminny x Shiji - Reconnecting the platform
For you - Confluence
For you - Confluence
Lukas Kovalik - Time Off
Lukas Kovalik - Time Off
Product Growth Platform | Userpilot
Product Growth Platform | Userpilot
Userpilot
Userpilot
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Jiminny
Jiminny
New Tab
New Tab
Verify it’s you To help keep your account safe, Google wants to make sure it’s really you
Verify it’s you To help keep your account safe, Google wants to make sure it’s really you
Google
Google
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.00234375,"top":0.045138888,"width":0.017578125,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6787] Issue with reconnecting Zoho - Jira","depth":4,"bounds":{"left":0.019921875,"top":0.045138888,"width":0.01796875,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXRadioButton","text":"JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app","depth":4,"bounds":{"left":0.037890624,"top":0.045138888,"width":0.01796875,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.055859376,"top":0.045138888,"width":0.017578125,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0734375,"top":0.045138888,"width":0.01796875,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Sandbox: Verify your identity in Salesforce - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.00234375,"top":0.07361111,"width":0.017578125,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Jiminny x Shiji - Reconnecting the platform","depth":4,"bounds":{"left":0.0,"top":0.11111111,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny x Shiji - Reconnecting the platform","depth":5,"bounds":{"left":0.015625,"top":0.12083333,"width":0.087890625,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"For you - Confluence","depth":4,"bounds":{"left":0.0,"top":0.13958333,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"For you - Confluence","depth":5,"bounds":{"left":0.015625,"top":0.14930555,"width":0.04296875,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Lukas Kovalik - Time Off","depth":4,"bounds":{"left":0.0,"top":0.16805555,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Lukas Kovalik - Time Off","depth":5,"bounds":{"left":0.015625,"top":0.17777778,"width":0.049609374,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Product Growth Platform | Userpilot","depth":4,"bounds":{"left":0.0,"top":0.19652778,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product Growth Platform | Userpilot","depth":5,"bounds":{"left":0.015625,"top":0.20625,"width":0.07304688,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Userpilot","depth":4,"bounds":{"left":0.0,"top":0.225,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Userpilot","depth":5,"bounds":{"left":0.015625,"top":0.23472223,"width":0.01875,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2534722,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":5,"bounds":{"left":0.015625,"top":0.26319444,"width":0.24101563,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.28194445,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.015625,"top":0.29166666,"width":0.015625,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"bounds":{"left":0.0,"top":0.31041667,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"bounds":{"left":0.015625,"top":0.3201389,"width":0.017578125,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Verify it’s you To help keep your account safe, Google wants to make sure it’s really you","depth":4,"bounds":{"left":0.0,"top":0.33888888,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Verify it’s you To help keep your account safe, Google wants to make sure it’s really you","depth":5,"bounds":{"left":0.015625,"top":0.34861112,"width":0.17851563,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Google","depth":4,"bounds":{"left":0.0,"top":0.3673611,"width":0.09375,"height":0.028472222},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Google","depth":5,"bounds":{"left":0.015625,"top":0.37708333,"width":0.014453125,"height":0.009722223},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.003125,"top":0.39722222,"width":0.08710937,"height":0.022222223},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.003125,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.01640625,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.029296875,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0421875,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.05546875,"top":0.97430557,"width":0.0125,"height":0.022222223},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
8501426571974890342
|
-4696332161447378839
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
[SRD-6787] Issue with reconnecting Zoho - Jira
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Sandbox: Verify your identity in Salesforce - [EMAIL] - Jiminny Mail
Jiminny x Shiji - Reconnecting the platform
Jiminny x Shiji - Reconnecting the platform
For you - Confluence
For you - Confluence
Lukas Kovalik - Time Off
Lukas Kovalik - Time Off
Product Growth Platform | Userpilot
Product Growth Platform | Userpilot
Userpilot
Userpilot
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Jiminny
Jiminny
New Tab
New Tab
Verify it’s you To help keep your account safe, Google wants to make sure it’s really you
Verify it’s you To help keep your account safe, Google wants to make sure it’s really you
Google
Google
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)...
|
NULL
|
|
33250
|
672
|
21
|
2026-04-16T07:41:05.005246+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776325265005_m2.jpg...
|
Slack
|
Slack
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
DMsAchivityFilesLaterMoreJiminny ...= Unreads@ Thr DMsAchivityFilesLaterMoreJiminny ...= Unreads@ Threads6 HuddlesDrafts & sent:8 DirectoriesEh External connections# Starred& platform-inner-team# Channels# ai-chapter# alerts# backend#: confusion-clinic# curiosity_labiengineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# randomit releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messazes. Nikolay Ivanov0. A..602e. Vasil Vasilev3 Aneliya Angelova, ...R. Stoyan Taneve. VesP. Galya Dimitrova Ele Steliyan Georgiev3Adelina Petrova. Il..(Q. Adelina Petrova LD. Nikolay Nikolov**:AppsoastJira Cloud6d Huddle with Aneliya Angelova$ 0lobl6d Huddle with Aneliya AngelovaSupport Daily • in 4h 19 mA100% [45t• Thu 16 Apr 10:41:04Search Jiminny Inc# 0. Aneliya Angelova D• Messagest Add canvasC Filesвидях този ПР https://github.com/jiminny/app/fThursday, March 26th~Lukas Kovalik 3:24 PMда имаше един бъг, от последния code smellFriday, March 27th~Aneliya Angelova © 9:33 AMох ясно защо не сьм видяла този проблем след като ретествах след като оправи код смелареално нищо не се е деплойвало - в Circle CІ всичко е зелено, а нищо не се е качвало на QAIi realno sym si testwala s промените от преди последния кьмитimace,onsyQPE Al Notes: Off30 for retn* cre-to6d : 73782662 -68eLukas Kovalik 9:34 AMда но то беше и малко edge caseAneliya Angelova 9:34 AMвчера като трьгнах да качвам на ники бранча и уж се деплойва на ОAI - отварям средата и гледам че стои. ХубспотLukas Kovalik 9:35 AMслучва сеиначе май си работидо сега друго не сьм виждалAneliya Angelova 9:35 AMдаже не знам от кога вес е счупил деплоя и колко време все съм тествала едно и сьщо без да се усетя че деплойванетоne e odourehloLukas Kovalik 9:36 AMsd-reports) Found 1 daily reports to process ["correlation_id":"324752bf-4709-428b-b348-Sac226946721Lukas KovalikScreen shareToday ~Aneliya Angelova O 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMаиде след 15 мин че се зарових в зохоLukas Kovalik Q 10:32 AMако искаш ла се чvем сегаAneliva Angelova 10:33 AMYou joined the huddle LIVE)10:33 AMAneliva Angelova is here tooMessage Aneliya AngelovaAaAl Notes: OffLeave&ГА ^Leave...
|
NULL
|
8500966787598128043
|
NULL
|
visual_change
|
ocr
|
NULL
|
DMsAchivityFilesLaterMoreJiminny ...= Unreads@ Thr DMsAchivityFilesLaterMoreJiminny ...= Unreads@ Threads6 HuddlesDrafts & sent:8 DirectoriesEh External connections# Starred& platform-inner-team# Channels# ai-chapter# alerts# backend#: confusion-clinic# curiosity_labiengineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# randomit releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messazes. Nikolay Ivanov0. A..602e. Vasil Vasilev3 Aneliya Angelova, ...R. Stoyan Taneve. VesP. Galya Dimitrova Ele Steliyan Georgiev3Adelina Petrova. Il..(Q. Adelina Petrova LD. Nikolay Nikolov**:AppsoastJira Cloud6d Huddle with Aneliya Angelova$ 0lobl6d Huddle with Aneliya AngelovaSupport Daily • in 4h 19 mA100% [45t• Thu 16 Apr 10:41:04Search Jiminny Inc# 0. Aneliya Angelova D• Messagest Add canvasC Filesвидях този ПР https://github.com/jiminny/app/fThursday, March 26th~Lukas Kovalik 3:24 PMда имаше един бъг, от последния code smellFriday, March 27th~Aneliya Angelova © 9:33 AMох ясно защо не сьм видяла този проблем след като ретествах след като оправи код смелареално нищо не се е деплойвало - в Circle CІ всичко е зелено, а нищо не се е качвало на QAIi realno sym si testwala s промените от преди последния кьмитimace,onsyQPE Al Notes: Off30 for retn* cre-to6d : 73782662 -68eLukas Kovalik 9:34 AMда но то беше и малко edge caseAneliya Angelova 9:34 AMвчера като трьгнах да качвам на ники бранча и уж се деплойва на ОAI - отварям средата и гледам че стои. ХубспотLukas Kovalik 9:35 AMслучва сеиначе май си работидо сега друго не сьм виждалAneliya Angelova 9:35 AMдаже не знам от кога вес е счупил деплоя и колко време все съм тествала едно и сьщо без да се усетя че деплойванетоne e odourehloLukas Kovalik 9:36 AMsd-reports) Found 1 daily reports to process ["correlation_id":"324752bf-4709-428b-b348-Sac226946721Lukas KovalikScreen shareToday ~Aneliya Angelova O 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMаиде след 15 мин че се зарових в зохоLukas Kovalik Q 10:32 AMако искаш ла се чvем сегаAneliva Angelova 10:33 AMYou joined the huddle LIVE)10:33 AMAneliva Angelova is here tooMessage Aneliya AngelovaAaAl Notes: OffLeave&ГА ^Leave...
|
NULL
|
|
75853
|
1893
|
0
|
2026-04-24T06:57:35.644140+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777013855644_m1.jpg...
|
Firefox
|
JY-20489 | Optimize Nudges - Phase 2 by yalokin-ji JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app — Work...
|
True
|
github.com/jiminny/app/pull/11997/changes#diff-29e github.com/jiminny/app/pull/11997/changes#diff-29eae52080a9f4d5860d815ab8e76ac455eef3d7868b8efa7b55b02f3a017121...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
Close tab
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
Userpilot | Nudge-created
Userpilot | Nudge-created
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
Pipelines - jiminny/app
Pipelines - jiminny/app
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…
Type
/
to search
Chat with Copilot
Open Copilot…
Create new...
Issues(g then i)
Pull requests
Repositories
You have unread notifications(g then n)
Open user navigation menu
Repository navigation
Repository navigation
Code
Code
Pull requests (31)
Pull requests
(
31
)
Agents
Agents
Actions
Actions
Wiki
Wiki
Security and quality (32)
Security and quality
(
32
)
Insights
Insights
Settings
Settings
Important update
Important update
On April 24 we'll start using GitHub Copilot interaction data for AI model training unless you opt out.
Review this update
Review this update
and manage your preferences in your
GitHub account settings
GitHub account settings
.
Dismiss banner
JY-20489 | Optimize Nudges - Phase 2 #11997 Edit title
JY-20489 | Optimize Nudges - Phase 2
#
11997
Edit title
Preview
Preview
Checks pending
Checks pending
Code
Code
Open
yalokin-jiminny
yalokin-jiminny
wants to merge 17 commits into
master
master
from
JY-20489-hudges-phase2
JY-20489-hudges-phase2
Copy head branch name to clipboard
Lines changed: 728 additions & 20 deletions
Conversation (5)
Conversation
(
5
)
Commits (17)
Commits
(
17
)
Checks (2)
Checks
(
2
)
Files changed (13)
Files changed
(
13
)
Pull Request Toolbar
Pull Request Toolbar
Collapse file tree
Open
JY-20489 | Optimize Nudges - Phase 2
JY-20489 | Optimize Nudges - Phase 2
#
11997
All commits
All commits
yalokin-jiminny
yalokin-jiminny
wants to merge 17 commits into
master
master
from
JY-20489-hudges-phase2
JY-20489-hudges-phase2
Copy head branch name to clipboard
10
/
13
viewed
Checks pending
Checks pending
Submit review
Submit
review
Open diff view settings
Open overview panel
Open comments panel
(
0
)
Filter files…
Filter options
File tree
File tree
app
Component
ActivitySearch
FilterDefinition
IndexedAtFrom.php
IndexedAtFrom.php
Service
ActivitySearch.php
ActivitySearch.php
Nudge
Repository
NudgeRepository.php
NudgeRepository.php
NudgeRunFactory.php
NudgeRunFactory.php
Console
Commands
RemoveExpiredNudgesCommand.php
RemoveExpiredNudgesCommand.php
Kernel.php
Kernel.php
Models
Nudge.php
Nudge.php
VO/Repository/OnDemandActivitySearch
Criteria.php
Criteria.php
database/migrations
2026_04_21_170000_add_last_run_at_to_nudges_table.php
2026_04_21_170000_add_last_run_at_to_nudges_table.php
tests/Unit
Component
ActivitySearch/FilterDefinition
IndexedAtFromTest.php
IndexedAtFromTest.php
Nudge
Repository
NudgeRepositoryBoundedDedupTest.php
NudgeRepositoryBoundedDedupTest.php
NudgeRunFactoryTest.php
NudgeRunFactoryTest.php
VO/Repository/OnDemandActivitySearch
CriteriaTest.php
CriteriaTest.php
Expand file
app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php
app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php
app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php
Copy file name to clipboard
Lines changed: 36 additions & 0 deletions
Viewed
Viewed
Comment on this file
More options
Expand file
app/Component/ActivitySearch/Service/ActivitySearch.php
app/Component/ActivitySearch/Service/ActivitySearch.php
app/Component/ActivitySearch/Service/ActivitySearch.php
Copy file name to clipboard
Expand all lines: app/Component/ActivitySearch/Service/ActivitySearch.php
Lines changed: 1 addition & 0 deletions
Viewed
Viewed
Comment on this file
More options
Expand file
app/Component/Nudge/Repository/NudgeRepository.php
app/Component/Nudge/Repository/NudgeRepository.php
app/Component/Nudge/Repository/NudgeRepository.php
Copy file name to clipboard
Expand all lines: app/Component/Nudge/Repository/NudgeRepository.php
Lines changed: 19 additions & 5 deletions
Viewed
Viewed
Comment on this file
More options
Collapse file
app/Component/Nudge/NudgeRunFactory.php
app/Component/Nudge/NudgeRunFactory.php
app/Component/Nudge/NudgeRunFactory.php
Copy file name to clipboard
Expand all lines: app/Component/Nudge/NudgeRunFactory.php
Lines changed: 68 additions & 14 deletions
Not Viewed
Viewed
Comment on this file
More options
Original file line number
Original file line
Diff line number
Diff line change
@@ -4,6 +4,7 @@
4
4
5
namespace
Jiminny
\
Component
\
Nudge
;
5
namespace
Jiminny
\
Component
\
Nudge
;
6
6
7
+
use
Carbon
\
CarbonImmutable
;
7
use
Elastica
\
Document
;
8
use
Elastica
\
Document
;
8
use
Illuminate
\
Support
\
Collection
;
9
use
Illuminate
\
Support
\
Collection
;
9
use
Illuminate
\
Support
\
LazyCollection
;
10
use
Illuminate
\
Support
\
LazyCollection
;
@@ -49,14 +50,23 @@ public function createNudgeRun(Nudge $nudge, array $extraFilters = []): ?NudgeRu
49
);
50
);
50
}
51
}
51
52
53
+
$
searchWindowStart
=
$
this
->
getSearchWindowStart
(
$
nudge
);
54
+
52
$
this
->...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Userpilot | Nudge-created","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Userpilot | Nudge-created","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.016666668,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Skip to content","depth":6,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Skip to content","depth":7,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open menu","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Homepage (g then d)","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"jiminny","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"jiminny","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"app","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search or jump to…","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Type","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to search","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Chat with Copilot","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Open Copilot…","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"Create new...","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Issues(g then i)","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Pull requests","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Repositories","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"You have unread notifications(g then n)","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open user navigation menu","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Repository navigation","depth":9,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Repository navigation","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Code","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Code","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pull requests (31)","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"31","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Agents","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Agents","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Actions","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Actions","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Wiki","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Wiki","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Security and quality (32)","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Security and quality","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"32","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Insights","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Insights","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Settings","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Important update","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Important update","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"On April 24 we'll start using GitHub Copilot interaction data for AI model training unless you opt out.","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Review this update","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Review this update","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"and manage your preferences in your","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"GitHub account settings","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"GitHub account settings","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Dismiss banner","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"JY-20489 | Optimize Nudges - Phase 2 #11997 Edit title","depth":13,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"JY-20489 | Optimize Nudges - Phase 2","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"#","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"11997","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Edit title","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Preview","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Preview","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Checks pending","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Checks pending","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Code","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Code","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Open","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"yalokin-jiminny","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"yalokin-jiminny","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"wants to merge 17 commits into","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"master","depth":15,"role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"master","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"from","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"JY-20489-hudges-phase2","depth":16,"role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20489-hudges-phase2","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy head branch name to clipboard","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Lines changed: 728 additions & 20 deletions","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Conversation (5)","depth":16,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Conversation","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Commits (17)","depth":16,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Commits","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"17","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Checks (2)","depth":16,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Checks","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Files changed (13)","depth":16,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Files changed","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"13","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Pull Request Toolbar","depth":14,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Pull Request Toolbar","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Collapse file tree","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Open","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"JY-20489 | Optimize Nudges - Phase 2","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20489 | Optimize Nudges - Phase 2","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"#","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"11997","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"All commits","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"All commits","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"yalokin-jiminny","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"yalokin-jiminny","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"wants to merge 17 commits into","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"master","depth":15,"role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"master","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"from","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"JY-20489-hudges-phase2","depth":16,"role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20489-hudges-phase2","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy head branch name to clipboard","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"10","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"13","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"viewed","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Checks pending","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Checks pending","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Submit review","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Submit","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"review","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Open diff view settings","depth":14,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open overview panel","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open comments panel","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"(","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"0","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Filter files…","depth":16,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Filter options","depth":16,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"File tree","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"File tree","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Component","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ActivitySearch","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"FilterDefinition","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"IndexedAtFrom.php","depth":27,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"IndexedAtFrom.php","depth":28,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Service","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"ActivitySearch.php","depth":27,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"ActivitySearch.php","depth":28,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nudge","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Repository","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"NudgeRepository.php","depth":27,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"NudgeRepository.php","depth":28,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"NudgeRunFactory.php","depth":25,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"NudgeRunFactory.php","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Console","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Commands","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"RemoveExpiredNudgesCommand.php","depth":25,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"RemoveExpiredNudgesCommand.php","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Kernel.php","depth":23,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Kernel.php","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Models","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Nudge.php","depth":23,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Nudge.php","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"VO/Repository/OnDemandActivitySearch","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Criteria.php","depth":23,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Criteria.php","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"database/migrations","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"2026_04_21_170000_add_last_run_at_to_nudges_table.php","depth":21,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2026_04_21_170000_add_last_run_at_to_nudges_table.php","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"tests/Unit","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Component","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ActivitySearch/FilterDefinition","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"IndexedAtFromTest.php","depth":25,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"IndexedAtFromTest.php","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nudge","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Repository","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"NudgeRepositoryBoundedDedupTest.php","depth":27,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"NudgeRepositoryBoundedDedupTest.php","depth":28,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"NudgeRunFactoryTest.php","depth":25,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"NudgeRunFactoryTest.php","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"VO/Repository/OnDemandActivitySearch","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"CriteriaTest.php","depth":23,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CriteriaTest.php","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy file name to clipboard","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Lines changed: 36 additions & 0 deletions","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Viewed","depth":14,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Viewed","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Comment on this file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"More options","depth":14,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"app/Component/ActivitySearch/Service/ActivitySearch.php","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"app/Component/ActivitySearch/Service/ActivitySearch.php","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app/Component/ActivitySearch/Service/ActivitySearch.php","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy file name to clipboard","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Expand all lines: app/Component/ActivitySearch/Service/ActivitySearch.php","depth":15,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Lines changed: 1 addition & 0 deletions","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Viewed","depth":14,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Viewed","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Comment on this file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"More options","depth":14,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"app/Component/Nudge/Repository/NudgeRepository.php","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"app/Component/Nudge/Repository/NudgeRepository.php","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app/Component/Nudge/Repository/NudgeRepository.php","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy file name to clipboard","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Expand all lines: app/Component/Nudge/Repository/NudgeRepository.php","depth":15,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Lines changed: 19 additions & 5 deletions","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Viewed","depth":14,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Viewed","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Comment on this file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"More options","depth":14,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"app/Component/Nudge/NudgeRunFactory.php","depth":15,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXLink","text":"app/Component/Nudge/NudgeRunFactory.php","depth":16,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app/Component/Nudge/NudgeRunFactory.php","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy file name to clipboard","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Expand all lines: app/Component/Nudge/NudgeRunFactory.php","depth":15,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Lines changed: 68 additions & 14 deletions","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Not Viewed","depth":14,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Viewed","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Comment on this file","depth":14,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"More options","depth":14,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Original file line number","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Original file line","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Diff line number","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Diff line change","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"@@ -4,6 +4,7 @@","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"namespace","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Component","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nudge","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"5","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"namespace","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Component","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nudge","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"6","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"6","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"+","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"use","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Carbon","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"CarbonImmutable","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"use","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Elastica","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Document","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"use","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Elastica","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Document","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"8","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"use","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Illuminate","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Support","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collection","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"9","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"use","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Illuminate","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Support","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collection","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"9","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"use","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Illuminate","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Support","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"LazyCollection","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"use","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Illuminate","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Support","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"\\","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"LazyCollection","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":";","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"@@ -49,14 +50,23 @@ public function createNudgeRun(Nudge $nudge, array $extraFilters = []): ?NudgeRu","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"49","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"50","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"50","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"}","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"51","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"}","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"51","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"52","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"53","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"+","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"searchWindowStart","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"=","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"this","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getSearchWindowStart","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"nudge","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"54","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"+","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"52","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"this","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":18,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8500955883796002594
|
-2938185406980130074
|
idle
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
Close tab
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
Userpilot | Nudge-created
Userpilot | Nudge-created
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
Pipelines - jiminny/app
Pipelines - jiminny/app
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…
Type
/
to search
Chat with Copilot
Open Copilot…
Create new...
Issues(g then i)
Pull requests
Repositories
You have unread notifications(g then n)
Open user navigation menu
Repository navigation
Repository navigation
Code
Code
Pull requests (31)
Pull requests
(
31
)
Agents
Agents
Actions
Actions
Wiki
Wiki
Security and quality (32)
Security and quality
(
32
)
Insights
Insights
Settings
Settings
Important update
Important update
On April 24 we'll start using GitHub Copilot interaction data for AI model training unless you opt out.
Review this update
Review this update
and manage your preferences in your
GitHub account settings
GitHub account settings
.
Dismiss banner
JY-20489 | Optimize Nudges - Phase 2 #11997 Edit title
JY-20489 | Optimize Nudges - Phase 2
#
11997
Edit title
Preview
Preview
Checks pending
Checks pending
Code
Code
Open
yalokin-jiminny
yalokin-jiminny
wants to merge 17 commits into
master
master
from
JY-20489-hudges-phase2
JY-20489-hudges-phase2
Copy head branch name to clipboard
Lines changed: 728 additions & 20 deletions
Conversation (5)
Conversation
(
5
)
Commits (17)
Commits
(
17
)
Checks (2)
Checks
(
2
)
Files changed (13)
Files changed
(
13
)
Pull Request Toolbar
Pull Request Toolbar
Collapse file tree
Open
JY-20489 | Optimize Nudges - Phase 2
JY-20489 | Optimize Nudges - Phase 2
#
11997
All commits
All commits
yalokin-jiminny
yalokin-jiminny
wants to merge 17 commits into
master
master
from
JY-20489-hudges-phase2
JY-20489-hudges-phase2
Copy head branch name to clipboard
10
/
13
viewed
Checks pending
Checks pending
Submit review
Submit
review
Open diff view settings
Open overview panel
Open comments panel
(
0
)
Filter files…
Filter options
File tree
File tree
app
Component
ActivitySearch
FilterDefinition
IndexedAtFrom.php
IndexedAtFrom.php
Service
ActivitySearch.php
ActivitySearch.php
Nudge
Repository
NudgeRepository.php
NudgeRepository.php
NudgeRunFactory.php
NudgeRunFactory.php
Console
Commands
RemoveExpiredNudgesCommand.php
RemoveExpiredNudgesCommand.php
Kernel.php
Kernel.php
Models
Nudge.php
Nudge.php
VO/Repository/OnDemandActivitySearch
Criteria.php
Criteria.php
database/migrations
2026_04_21_170000_add_last_run_at_to_nudges_table.php
2026_04_21_170000_add_last_run_at_to_nudges_table.php
tests/Unit
Component
ActivitySearch/FilterDefinition
IndexedAtFromTest.php
IndexedAtFromTest.php
Nudge
Repository
NudgeRepositoryBoundedDedupTest.php
NudgeRepositoryBoundedDedupTest.php
NudgeRunFactoryTest.php
NudgeRunFactoryTest.php
VO/Repository/OnDemandActivitySearch
CriteriaTest.php
CriteriaTest.php
Expand file
app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php
app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php
app/Component/ActivitySearch/FilterDefinition/IndexedAtFrom.php
Copy file name to clipboard
Lines changed: 36 additions & 0 deletions
Viewed
Viewed
Comment on this file
More options
Expand file
app/Component/ActivitySearch/Service/ActivitySearch.php
app/Component/ActivitySearch/Service/ActivitySearch.php
app/Component/ActivitySearch/Service/ActivitySearch.php
Copy file name to clipboard
Expand all lines: app/Component/ActivitySearch/Service/ActivitySearch.php
Lines changed: 1 addition & 0 deletions
Viewed
Viewed
Comment on this file
More options
Expand file
app/Component/Nudge/Repository/NudgeRepository.php
app/Component/Nudge/Repository/NudgeRepository.php
app/Component/Nudge/Repository/NudgeRepository.php
Copy file name to clipboard
Expand all lines: app/Component/Nudge/Repository/NudgeRepository.php
Lines changed: 19 additions & 5 deletions
Viewed
Viewed
Comment on this file
More options
Collapse file
app/Component/Nudge/NudgeRunFactory.php
app/Component/Nudge/NudgeRunFactory.php
app/Component/Nudge/NudgeRunFactory.php
Copy file name to clipboard
Expand all lines: app/Component/Nudge/NudgeRunFactory.php
Lines changed: 68 additions & 14 deletions
Not Viewed
Viewed
Comment on this file
More options
Original file line number
Original file line
Diff line number
Diff line change
@@ -4,6 +4,7 @@
4
4
5
namespace
Jiminny
\
Component
\
Nudge
;
5
namespace
Jiminny
\
Component
\
Nudge
;
6
6
7
+
use
Carbon
\
CarbonImmutable
;
7
use
Elastica
\
Document
;
8
use
Elastica
\
Document
;
8
use
Illuminate
\
Support
\
Collection
;
9
use
Illuminate
\
Support
\
Collection
;
9
use
Illuminate
\
Support
\
LazyCollection
;
10
use
Illuminate
\
Support
\
LazyCollection
;
@@ -49,14 +50,23 @@ public function createNudgeRun(Nudge $nudge, array $extraFilters = []): ?NudgeRu
49
);
50
);
50
}
51
}
51
52
53
+
$
searchWindowStart
=
$
this
->
getSearchWindowStart
(
$
nudge
);
54
+
52
$
this
->...
|
75851
|
|
63722
|
1391
|
8
|
2026-04-21T09:42:29.068020+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776764549068_m1.jpg...
|
iTerm2
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpDOCKERO $1Sour SlackFileEditViewGoHistoryWindowHelpDOCKERO $1Source DB:NAS mount:Archive DB:Data dir:-zsh[+00m03s] • Counting sourceframes:elements:ul_events:ocr_text:meetings:[+00m03s] • Initialising tccreating tablescreating indexescreating FTS tabl[+00m04s] • Syncing data fcvideo_chunksframes (9093 rowsocr_text (5971 rcui_events (9970 гelements (687142meetings (2 rows)[+03m01s] • Updating FTS irelements_ftsframes_ftsui_events_fts[+06m41s] • Verifying DBframes:elements:ui_events:ocr_text:meetings:[+07m44s] *Copying data forsync 2026-04-20/2026-04-2110:48:54][2026-04-2110:48:54][2026-04-2110:48:54][2026-04-2110:48:54]ArchiTotalSync=====lukas@Lukas-Kovaliks-MacBodOKО $82(4.5G)-zsh• *3JiminnyProfileAll ItemsFavoritesWatchtower<› DeveloperVAULTSEmployeeEngineeringIntegration AccountsJiminnyV TAGS• 2FACSV Import 28.01.22LastPass Import 1.11.21C4udae Vit* Build full da...• *4screenpSearch in Jiminny2 results for "PLanhat"[EMAIL] log in ([EMAIL]> 0# Support Daily - in 2h 18 m100% <78Tue 21 Apr 12:42:28Activity MonitorAll ProcessesCPUMemoryEnergyDiskNetworkProcess NameMem...ThreadsPortsPIDHelp+ New Item2269620752a Jiminny20765Employee vShareEdit40459858Planhat61937617472173623523usernamelukas.kovalik@jiminny.com2619861686password[PASSWORD_DOTS]Good2073761591websitehttps://app-eu3.planhat.com108771086811982> Last edited Wednesday, July 5, 2023 at 3:29:20 PM эrу:Userlukaslukaslukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas5,37 GB2,98 GB4,98 GB...
|
NULL
|
8500879685942901359
|
NULL
|
idle
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpDOCKERO $1Sour SlackFileEditViewGoHistoryWindowHelpDOCKERO $1Source DB:NAS mount:Archive DB:Data dir:-zsh[+00m03s] • Counting sourceframes:elements:ul_events:ocr_text:meetings:[+00m03s] • Initialising tccreating tablescreating indexescreating FTS tabl[+00m04s] • Syncing data fcvideo_chunksframes (9093 rowsocr_text (5971 rcui_events (9970 гelements (687142meetings (2 rows)[+03m01s] • Updating FTS irelements_ftsframes_ftsui_events_fts[+06m41s] • Verifying DBframes:elements:ui_events:ocr_text:meetings:[+07m44s] *Copying data forsync 2026-04-20/2026-04-2110:48:54][2026-04-2110:48:54][2026-04-2110:48:54][2026-04-2110:48:54]ArchiTotalSync=====lukas@Lukas-Kovaliks-MacBodOKО $82(4.5G)-zsh• *3JiminnyProfileAll ItemsFavoritesWatchtower<› DeveloperVAULTSEmployeeEngineeringIntegration AccountsJiminnyV TAGS• 2FACSV Import 28.01.22LastPass Import 1.11.21C4udae Vit* Build full da...• *4screenpSearch in Jiminny2 results for "PLanhat"[EMAIL] log in ([EMAIL]> 0# Support Daily - in 2h 18 m100% <78Tue 21 Apr 12:42:28Activity MonitorAll ProcessesCPUMemoryEnergyDiskNetworkProcess NameMem...ThreadsPortsPIDHelp+ New Item2269620752a Jiminny20765Employee vShareEdit40459858Planhat61937617472173623523usernamelukas.kovalik@jiminny.com2619861686password[PASSWORD_DOTS]Good2073761591websitehttps://app-eu3.planhat.com108771086811982> Last edited Wednesday, July 5, 2023 at 3:29:20 PM эrу:Userlukaslukaslukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas5,37 GB2,98 GB4,98 GB...
|
63720
|
|
10010
|
195
|
1
|
2026-04-14T08:07:32.515921+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-14/1776 /Users/lukas/.screenpipe/data/data/2026-04-14/1776154052515_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxFileEoitViewHistoryBookmarksProfilesToolsWi FirefoxFileEoitViewHistoryBookmarksProfilesToolsWindowHelp= ( meet.google.com/mie-gawc-dsi~ Google GeminiI1 Meet|Platform Sprint 1 Q2 - Platform Tea& Configure SSH access to multiple e@ Console Home | Console Home | usSecurityGroup | EC2 | us-east-2JY-20543 add AJ reports User pilcSRD-6779 |JY-20632 | Unable to |Jy 19/98 evaluation for al activitya Jiminnyg Ask Jiminny test report - 8 Apr 201≤ Service-Desk - Queues - PlatformJY-20543 add AJ reports User pilc(x) Configure SSH access to multipleC CloudWatch I us-east-2Meet - Daily - Platform+ New TabLukas Kovaliky soundcore A..Ask GeminiSummarize page203<40 ll 1 Support Daily • in 3h 53mA100% C• • Tue 14 Apr 11:07:[EMAIL] accountDaily - PlatformEo Scheduled forThu, Apr 16 9:45 AM* Use Gemini to take notesShare notes and transcriptStartO Enhance your appearanceMeet can adjust your image sharpness andlighting so others can see you better. Tocranoe now vou look, use tne roeearancetab.Join anywayOther ways to join vNot nowTry now<D System Defa..N FaceTime HD... +[t Backgrounds….+Gemini is available in Meet as vour personal in-meetina assistant. It can analvze conversation via temborarviaccess to meeting captions. Using Ask Gemini won't create a recording or store meeting data. The meetinghost can turn it oft. Learn more...
|
NULL
|
8500802765626655117
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxFileEoitViewHistoryBookmarksProfilesToolsWi FirefoxFileEoitViewHistoryBookmarksProfilesToolsWindowHelp= ( meet.google.com/mie-gawc-dsi~ Google GeminiI1 Meet|Platform Sprint 1 Q2 - Platform Tea& Configure SSH access to multiple e@ Console Home | Console Home | usSecurityGroup | EC2 | us-east-2JY-20543 add AJ reports User pilcSRD-6779 |JY-20632 | Unable to |Jy 19/98 evaluation for al activitya Jiminnyg Ask Jiminny test report - 8 Apr 201≤ Service-Desk - Queues - PlatformJY-20543 add AJ reports User pilc(x) Configure SSH access to multipleC CloudWatch I us-east-2Meet - Daily - Platform+ New TabLukas Kovaliky soundcore A..Ask GeminiSummarize page203<40 ll 1 Support Daily • in 3h 53mA100% C• • Tue 14 Apr 11:07:[EMAIL] accountDaily - PlatformEo Scheduled forThu, Apr 16 9:45 AM* Use Gemini to take notesShare notes and transcriptStartO Enhance your appearanceMeet can adjust your image sharpness andlighting so others can see you better. Tocranoe now vou look, use tne roeearancetab.Join anywayOther ways to join vNot nowTry now<D System Defa..N FaceTime HD... +[t Backgrounds….+Gemini is available in Meet as vour personal in-meetina assistant. It can analvze conversation via temborarviaccess to meeting captions. Using Ask Gemini won't create a recording or store meeting data. The meetinghost can turn it oft. Learn more...
|
10008
|
|
25510
|
547
|
41
|
2026-04-15T12:54:26.834127+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776257666834_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
1588424337642/50toCastle Age20 BoClick to select t 1588424337642/50toCastle Age20 BoClick to select this military unit.Town Center0/15Đ 5/7kovalfklukas (Britons)Creating 43%Villager2400/24005 Magnus Olafsson: 2010/20108 Almish Yiltawar: 2006/20062 Rajyapala: 1942/19424 Louis VI: 1931/19317 Maximilian of Habsburg: 1912/19123 Huascár: 1836/18361 kovaliklukas: 1800/18006 László I: 1733/1733...
|
NULL
|
8500773507806321208
|
NULL
|
click
|
ocr
|
NULL
|
1588424337642/50toCastle Age20 BoClick to select t 1588424337642/50toCastle Age20 BoClick to select this military unit.Town Center0/15Đ 5/7kovalfklukas (Britons)Creating 43%Villager2400/24005 Magnus Olafsson: 2010/20108 Almish Yiltawar: 2006/20062 Rajyapala: 1942/19424 Louis VI: 1931/19317 Maximilian of Habsburg: 1912/19123 Huascár: 1836/18361 kovaliklukas: 1800/18006 László I: 1733/1733...
|
NULL
|
|
18262
|
388
|
81
|
2026-04-14T16:11:40.387736+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-14/1776 /Users/lukas/.screenpipe/data/data/2026-04-14/1776183100387_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
63280622012528/30Feudal AgeGame Paused (P)Hold dow 63280622012528/30Feudal AgeGame Paused (P)Hold down ALT and right-click to garrisoninside this building for protection andhealing.4 Siddhraj Jaisingh: 1051/10511 kovaliklukas: 1024/1024Anceu Hualloc: 1016/10168 Ashikaga Takauji: 989/9896 Mindaugas: 969/9693 Bird Jaguar: 947/9477 Basil the Macedonian: 919/9195 Honorius: 851/851II...
|
NULL
|
8500602503732217081
|
NULL
|
click
|
ocr
|
NULL
|
63280622012528/30Feudal AgeGame Paused (P)Hold dow 63280622012528/30Feudal AgeGame Paused (P)Hold down ALT and right-click to garrisoninside this building for protection andhealing.4 Siddhraj Jaisingh: 1051/10511 kovaliklukas: 1024/1024Anceu Hualloc: 1016/10168 Ashikaga Takauji: 989/9896 Mindaugas: 969/9693 Bird Jaguar: 947/9477 Basil the Macedonian: 919/9195 Honorius: 851/851II...
|
NULL
|
|
74385
|
1850
|
22
|
2026-04-23T09:44:14.190304+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776937454190_m2.jpg...
|
iTerm2
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Home1889FllesLateMoreSlackcalMistonJiminny... v# p Home1889FllesLateMoreSlackcalMistonJiminny... v# platform-tickets# product launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...6? Direct messagesAdelina Petrova(3) Aneliva Angelova. ...Stoyan Tomov. Nikolay YankovP Petko Kashinskif Aneliva Angelovae Nikolay Nikolov# Mario GeoreievSe: Todor StamatovA Gabriela DurevaeVacil Vaciler.. Galva Dimitrova2 Stefka Stoyanova*% Stoyan Tanev:: Apps• Toast$i Jira CGloudWindowheltAdelina Petrova• Messagest Add canvas@ Filesами ше лобавіToday ~ ли не е нешо посамия event. пуснах го на сляпо нямаше какда го тествамможе ли ла ми кажеш само накой евенткойто може да видим на планетитеAdellina Petrova 12:40 PMлжеймс прели е ползвал userPilotService:Screenshot 2026-04-23 at 12.40.22.pngдали не трябва така да се праща?Lukas Kovallk 12.41 pMlда и аз го ползвам такаще го порверяAdelina Petrova 12:42 PMаз ше направя елин пепорт ла вила лали шепокаже нещоLukas Kovalik 12:43 PNпросто искам ла си соавня и тествам товакоетоо илва вече от планета сьс това кеото.Message Adelina Petrova+ AalFV faVsco.js°9 JY-20157-AJ-report-not-send-notificaticProjectC) Ask.liminnvReportActivityservice.ongC BaseService.phpC) CachedermserviceDecoratoro© CountryCodeResolver.php% CrmActivityProviderintegratedAutomateakeportsserwice.ongC) AutomatedReportsCommana lest.onpy keporcontroller.onp(C) AutomatedReportGenerated.pnpOcrmacuvilyserwice.onp© CrmConfigurationSettingsServ© CrmObjectsResolver.php© DefaultProspectSearchStrateg©) EmailHelper.php© FindsProspectinterface.php© LayoutManager.php• MatchDomainByEmailInterface© OpportunityActivitvMatcher.pr• OpportunitySvncStrateavinteri© OpportunitvSvncStrateayReso© ProspectCache.phr° ProspectSearchScope.php© ProspectSearchStrategyFactol© ProspectSearchStrategyInterfaC) ProviderRedistrv.oho@ RecordSelector.ohnT ResolveComoanvNameßv=mail© TimePeriodlterator.ohn,Mimoor• M Internalv mKiockv M AutomatedRenorts© ActivityTypeService.php©AskJiminnyReportActivitySt(C) AutomatedPenortsCallback©AutomatedReportsService.1© DealStagesService.php© RecipientsService.php€ ReportSort.php€ ReportSortDirection.php© KioskService.php> 0 Maili> C MeetingGenerator> C Notification> C7 OAuth2> M RecallAil> MSecurityreport-not-generated.blade.phgclass Userpilotillent107> M Strateav> MStreamina.> M Team> D Telephonyv M UserPilot(c) SvncllserPilotComnanvllictongC) UcerDilotclient nhnMWehhookC. AbstractService nhnprivate const APT.ENDPOLNT ='https:Wapi.userpilot.1o/v1/';3 usaaesprivate const AnALyIncs.ENDPOINt ='https:/analytex.userpilot.1o/y1/';ousagesprivate function createRequest: PendingRequest(...}public function track(User Suser, string $event, array $payload = []): voidif (Sthis->shouldRequest(Suser->getTeam) === false) {return,Sthis->createRequest->post( url: self::ANALYTICS ENDPOINT . 'track'. ['event name' = Sevent'user id' => Suser-›qetUuido.public function unsertUser(User Suser): voidf...}public function unsertCompany(Team Steam): voidk...}nrivate function detcomnanvMetadatadTeam Steam)• arnav-...?nublie function deletellsen(llsen Suser): void!...}public function deleteCompany(Team $team): void(...}public function shouldRequest(Team $team): boolreturn config( key: 'services.userpilot.key') !== null && $team->getPartnerId === Partner::PARTNER DEFAULT:suppon Dally • In zn 10hБГ100% 152Thu 23 Apr 12:44:13= custom.log= laravel.log4 SF [jiminny@localhost] XA console [PROD]© DatabaseActivities.php© DealsRepository.phpA console [EU]console SlAGiNg Tx: AutoPlayground vDecect a trul gruups y suin pLayuuono paaselect * trom qroups where 1d = 28:dojiminny vОг д1 417 22 2Äжselect * from plavbooks where id = 179:select * from users where 1d = 143:select * from crm_profiles where user_id = 143;select * from activities where erm confiquration id = 39 and tvpe = 'confe'and crm_provider_id IS NOT NULL ORDER by id desc;select * from activities where id = 422003: # 00U0400000086f0MACSELECT ar.id, ar.uuid, ar.media type, ar.status, a.typeFROM automated_report_results arJOIN automated_reports a ON a.id = ar.report idWHERE a.tyRe = 'ask_jiminnx'I TMTT 10÷CSISATautomated_report_results*.* FROMINNER JOINWHERE 1=1AND'automated report results'.'report id' = 'automated_reports'.'idautomated_report_results'.'qenerated_at' IS NOT NULLAND 'automated_ report_results'.'sent_at' IS NOT NULLSELEC * FROM automated reports where 1d = 671SELECT * FROM automated reports where id = 42SELECT * FROM users WHERE id = 143: # aroup 28select * from teams where id = 3143;select * from con confiqurations where id = 500:select * from users where name = 'Integration Account'; # 1695SELECT * EROM social accounts WHFREsociable id = 1695.select * fron activities where erm confiaunation id = 30and recording_state = 'recorded' and duration > 60and status = 'completed' and actual_start_time >= '2025-12-01';|sEiEeT + EPOM antivitioc WHEDE nuid +o hinf:458cf015-h014-4000-h092-5/97h2...
|
NULL
|
8500271729700801959
|
NULL
|
idle
|
ocr
|
NULL
|
Home1889FllesLateMoreSlackcalMistonJiminny... v# p Home1889FllesLateMoreSlackcalMistonJiminny... v# platform-tickets# product launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...6? Direct messagesAdelina Petrova(3) Aneliva Angelova. ...Stoyan Tomov. Nikolay YankovP Petko Kashinskif Aneliva Angelovae Nikolay Nikolov# Mario GeoreievSe: Todor StamatovA Gabriela DurevaeVacil Vaciler.. Galva Dimitrova2 Stefka Stoyanova*% Stoyan Tanev:: Apps• Toast$i Jira CGloudWindowheltAdelina Petrova• Messagest Add canvas@ Filesами ше лобавіToday ~ ли не е нешо посамия event. пуснах го на сляпо нямаше какда го тествамможе ли ла ми кажеш само накой евенткойто може да видим на планетитеAdellina Petrova 12:40 PMлжеймс прели е ползвал userPilotService:Screenshot 2026-04-23 at 12.40.22.pngдали не трябва така да се праща?Lukas Kovallk 12.41 pMlда и аз го ползвам такаще го порверяAdelina Petrova 12:42 PMаз ше направя елин пепорт ла вила лали шепокаже нещоLukas Kovalik 12:43 PNпросто искам ла си соавня и тествам товакоетоо илва вече от планета сьс това кеото.Message Adelina Petrova+ AalFV faVsco.js°9 JY-20157-AJ-report-not-send-notificaticProjectC) Ask.liminnvReportActivityservice.ongC BaseService.phpC) CachedermserviceDecoratoro© CountryCodeResolver.php% CrmActivityProviderintegratedAutomateakeportsserwice.ongC) AutomatedReportsCommana lest.onpy keporcontroller.onp(C) AutomatedReportGenerated.pnpOcrmacuvilyserwice.onp© CrmConfigurationSettingsServ© CrmObjectsResolver.php© DefaultProspectSearchStrateg©) EmailHelper.php© FindsProspectinterface.php© LayoutManager.php• MatchDomainByEmailInterface© OpportunityActivitvMatcher.pr• OpportunitySvncStrateavinteri© OpportunitvSvncStrateayReso© ProspectCache.phr° ProspectSearchScope.php© ProspectSearchStrategyFactol© ProspectSearchStrategyInterfaC) ProviderRedistrv.oho@ RecordSelector.ohnT ResolveComoanvNameßv=mail© TimePeriodlterator.ohn,Mimoor• M Internalv mKiockv M AutomatedRenorts© ActivityTypeService.php©AskJiminnyReportActivitySt(C) AutomatedPenortsCallback©AutomatedReportsService.1© DealStagesService.php© RecipientsService.php€ ReportSort.php€ ReportSortDirection.php© KioskService.php> 0 Maili> C MeetingGenerator> C Notification> C7 OAuth2> M RecallAil> MSecurityreport-not-generated.blade.phgclass Userpilotillent107> M Strateav> MStreamina.> M Team> D Telephonyv M UserPilot(c) SvncllserPilotComnanvllictongC) UcerDilotclient nhnMWehhookC. AbstractService nhnprivate const APT.ENDPOLNT ='https:Wapi.userpilot.1o/v1/';3 usaaesprivate const AnALyIncs.ENDPOINt ='https:/analytex.userpilot.1o/y1/';ousagesprivate function createRequest: PendingRequest(...}public function track(User Suser, string $event, array $payload = []): voidif (Sthis->shouldRequest(Suser->getTeam) === false) {return,Sthis->createRequest->post( url: self::ANALYTICS ENDPOINT . 'track'. ['event name' = Sevent'user id' => Suser-›qetUuido.public function unsertUser(User Suser): voidf...}public function unsertCompany(Team Steam): voidk...}nrivate function detcomnanvMetadatadTeam Steam)• arnav-...?nublie function deletellsen(llsen Suser): void!...}public function deleteCompany(Team $team): void(...}public function shouldRequest(Team $team): boolreturn config( key: 'services.userpilot.key') !== null && $team->getPartnerId === Partner::PARTNER DEFAULT:suppon Dally • In zn 10hБГ100% 152Thu 23 Apr 12:44:13= custom.log= laravel.log4 SF [jiminny@localhost] XA console [PROD]© DatabaseActivities.php© DealsRepository.phpA console [EU]console SlAGiNg Tx: AutoPlayground vDecect a trul gruups y suin pLayuuono paaselect * trom qroups where 1d = 28:dojiminny vОг д1 417 22 2Äжselect * from plavbooks where id = 179:select * from users where 1d = 143:select * from crm_profiles where user_id = 143;select * from activities where erm confiquration id = 39 and tvpe = 'confe'and crm_provider_id IS NOT NULL ORDER by id desc;select * from activities where id = 422003: # 00U0400000086f0MACSELECT ar.id, ar.uuid, ar.media type, ar.status, a.typeFROM automated_report_results arJOIN automated_reports a ON a.id = ar.report idWHERE a.tyRe = 'ask_jiminnx'I TMTT 10÷CSISATautomated_report_results*.* FROMINNER JOINWHERE 1=1AND'automated report results'.'report id' = 'automated_reports'.'idautomated_report_results'.'qenerated_at' IS NOT NULLAND 'automated_ report_results'.'sent_at' IS NOT NULLSELEC * FROM automated reports where 1d = 671SELECT * FROM automated reports where id = 42SELECT * FROM users WHERE id = 143: # aroup 28select * from teams where id = 3143;select * from con confiqurations where id = 500:select * from users where name = 'Integration Account'; # 1695SELECT * EROM social accounts WHFREsociable id = 1695.select * fron activities where erm confiaunation id = 30and recording_state = 'recorded' and duration > 60and status = 'completed' and actual_start_time >= '2025-12-01';|sEiEeT + EPOM antivitioc WHEDE nuid +o hinf:458cf015-h014-4000-h092-5/97h2...
|
74383
|
|
1738
|
36
|
83
|
2026-04-11T17:52:13.779+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775929933779_m1.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShelllEditViewSessionScriptsProfilesWindowHe iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp(ahl100% CSat 11 Apr 20:52:13DOCKER0 ₴12026-04-11T18:41:59.011341Z2026-04-11T18:41:59.165278Z2026-04-11718:41:59116530272026-04-11T18:41:59.165322Z2026-04-11T18:41:59.165338Z2026-04-11T18:41:59.165390Z2026-04-11T18:41:59.953409Z2026-04-11T18:42:00.086573Z2026-04-11T18:42:03.873046Z2026-04-11T18:42:03.873524Z2026-04-11T18:42:58.898798Z2026-04-11T18:43:01.495401Z2026-04-11718:43:05103791642026-04-11T18:43:05.70131372026-04-11T18:43:05.869321Z2026-04-11T18:44:34.370623Z2026-04-11T18:44:37.039894Z2026-04-11T18:44:43.637666Z2026-04-11T18:46:22.830023Z2026-04-11T18:46:24.74642522026-04-11718:46:26.145427Z2026-04-11T18:46:29.154628Z2026-04-11T18:46:45.161217Z2026-04-11T18:46:47.291011Z2026-04-11T18:46:58.867582Z2026-04-11718:47:33.87325622026-04-11T18:48:05.793658ZDEV (-zsh)O 82APP (-zsh)• 83-zsh84-zsh• 86-zsh₴7-zshINFOscreenpipe_engine::hot_frame_cache:INFOhot_frame_cache:warmedwith 838framecoveragefrom 2026-04-10 15:41:58.867994 UTCINFOscreenpipe_engine::vision_manager::manager:entries,Starting vision recordingfor monitor1 (1440x900)screenpipe_engine::vision_manager::manager:Startingevent-driven capture formonitor 1 (device: monitor_1)INFOscreenpipe_engine::vision_manager::manager:Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listINFOINFOscreenpipe_engine::vision_manager::monitor_watcher:Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)INFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)INFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=1362, dur=65msINFOscreenpipe_engine::retention: retention: initialized with 14d retentionINFOscreenpipe:localretention auto-enabled (14 days)INFOscreenpipe_engine::snapshot_compaction: snapshotcompaction: found 108eligibleframesINFOscreenpipe_engine::event_driven_capture: contentdedup:INFOskipping capture for monitor 1 Chash=810635838678462500,trigger=clipboard)screenpipe_engine::snapshot_compaction: snapshotcompaction: 99 frames,18.9MB → 1.8MB (10.7x), 99 JPEGSdeletedINFOscreenpipe_engine::snapshot_compaction: snapshotcompaction: 8 frames,1.5MB → 0.4MB (3.7x), 8 JPEGs deletedINFOscreenpipe_engine::event_driven_capture: contentdedup:skipping capture for monitor 1 (hash=8059017237112085187, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: contentdedup: skipping capture for monitor 1 (hash=1954793808959611333,trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: contentdedup: skipping capture for monitor 1 (hash=1954793808959611333, trigger=clipboard)INFOscreenpipe_engine::event_driven_capture: contentdedup:skipping capture for monitor 1 (hash=-6150475841360271668, trigger=visual_change)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593,trigger=click)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593,trigger=click)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593, trigger=visual_change)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593, trigger=visual_change)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6037013225245471503, trigger=click)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6037013225245471503, trigger=visual_change)INFO screenpipe_engine::server: api_usage_5min: 3 requestsINFOscreenpipe_engine::retention: retention: cleaning up data before 2026-03-28T15:47:33.873226+00:00 (14d retention)INFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 18eligible frames[1] + terminatednpx screenpipe@latest record --disable-audiolukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ sp-statuszsh: command not found: sp-statuslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ source ~/.zprofilePoetry could not find a pyproject.toml file in /Users/lukas/.screenpipe/data/data or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ sp-status{"Sratesst tusa thy""audio_status": "disabled""Last_frame": "2026-04-11T20:43:56+03:00","uptime": 1091.390855333,"fps": 0.19424755023745877,"frames": 212}lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ |*8...
|
NULL
|
8499615266704795815
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShelllEditViewSessionScriptsProfilesWindowHe iTerm2ShelllEditViewSessionScriptsProfilesWindowHelp(ahl100% CSat 11 Apr 20:52:13DOCKER0 ₴12026-04-11T18:41:59.011341Z2026-04-11T18:41:59.165278Z2026-04-11718:41:59116530272026-04-11T18:41:59.165322Z2026-04-11T18:41:59.165338Z2026-04-11T18:41:59.165390Z2026-04-11T18:41:59.953409Z2026-04-11T18:42:00.086573Z2026-04-11T18:42:03.873046Z2026-04-11T18:42:03.873524Z2026-04-11T18:42:58.898798Z2026-04-11T18:43:01.495401Z2026-04-11718:43:05103791642026-04-11T18:43:05.70131372026-04-11T18:43:05.869321Z2026-04-11T18:44:34.370623Z2026-04-11T18:44:37.039894Z2026-04-11T18:44:43.637666Z2026-04-11T18:46:22.830023Z2026-04-11T18:46:24.74642522026-04-11718:46:26.145427Z2026-04-11T18:46:29.154628Z2026-04-11T18:46:45.161217Z2026-04-11T18:46:47.291011Z2026-04-11T18:46:58.867582Z2026-04-11718:47:33.87325622026-04-11T18:48:05.793658ZDEV (-zsh)O 82APP (-zsh)• 83-zsh84-zsh• 86-zsh₴7-zshINFOscreenpipe_engine::hot_frame_cache:INFOhot_frame_cache:warmedwith 838framecoveragefrom 2026-04-10 15:41:58.867994 UTCINFOscreenpipe_engine::vision_manager::manager:entries,Starting vision recordingfor monitor1 (1440x900)screenpipe_engine::vision_manager::manager:Startingevent-driven capture formonitor 1 (device: monitor_1)INFOscreenpipe_engine::vision_manager::manager:Skipping monitor 2 (Display 2_2560x1440_-597,-1440) - not in allowed listINFOINFOscreenpipe_engine::vision_manager::monitor_watcher:Starting monitor watcher (polling every 5 seconds)screenpipe_engine::event_driven_capture: event-driven capture startedfor monitor 1 (device: monitor_1)INFOsck_rs::stream_manager:persistent SCK stream started for display 1 (1440x900, 2fps)INFOscreenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=1362, dur=65msINFOscreenpipe_engine::retention: retention: initialized with 14d retentionINFOscreenpipe:localretention auto-enabled (14 days)INFOscreenpipe_engine::snapshot_compaction: snapshotcompaction: found 108eligibleframesINFOscreenpipe_engine::event_driven_capture: contentdedup:INFOskipping capture for monitor 1 Chash=810635838678462500,trigger=clipboard)screenpipe_engine::snapshot_compaction: snapshotcompaction: 99 frames,18.9MB → 1.8MB (10.7x), 99 JPEGSdeletedINFOscreenpipe_engine::snapshot_compaction: snapshotcompaction: 8 frames,1.5MB → 0.4MB (3.7x), 8 JPEGs deletedINFOscreenpipe_engine::event_driven_capture: contentdedup:skipping capture for monitor 1 (hash=8059017237112085187, trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: contentdedup: skipping capture for monitor 1 (hash=1954793808959611333,trigger=visual_change)INFOscreenpipe_engine::event_driven_capture: contentdedup: skipping capture for monitor 1 (hash=1954793808959611333, trigger=clipboard)INFOscreenpipe_engine::event_driven_capture: contentdedup:skipping capture for monitor 1 (hash=-6150475841360271668, trigger=visual_change)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593,trigger=click)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593,trigger=click)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593, trigger=visual_change)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6692613549932102593, trigger=visual_change)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6037013225245471503, trigger=click)INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6037013225245471503, trigger=visual_change)INFO screenpipe_engine::server: api_usage_5min: 3 requestsINFOscreenpipe_engine::retention: retention: cleaning up data before 2026-03-28T15:47:33.873226+00:00 (14d retention)INFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 18eligible frames[1] + terminatednpx screenpipe@latest record --disable-audiolukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ sp-statuszsh: command not found: sp-statuslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ source ~/.zprofilePoetry could not find a pyproject.toml file in /Users/lukas/.screenpipe/data/data or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ sp-status{"Sratesst tusa thy""audio_status": "disabled""Last_frame": "2026-04-11T20:43:56+03:00","uptime": 1091.390855333,"fps": 0.19424755023745877,"frames": 212}lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data $ |*8...
|
1737
|
|
22127
|
484
|
40
|
2026-04-15T10:31:56.281263+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776249116281_m2.jpg...
|
Slack
|
Stoyan Tanev (DM) - Jiminny Inc - 1 new item - Sla Stoyan Tanev (DM) - Jiminny Inc - 1 new item - Slack...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
platform-inner-team
ai-chapter
alerts
backend
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Stoyan Tanev
Ves
Galya Dimitrova
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Vasil Vasilev
Steliyan Georgiev
Adelina Petrova
,
Ilian Kyuchukov
,
Steliyan Georgiev
Adelina Petrova
Nikolay Nikolov
Galya Dimitrova
,
Nikolay Nikolov
Jira Cloud
Toast
Messages
Messages
Add canvas
Add canvas
Files
Files
Add and Edit Channel Tabs
Canvas
List
Folder
Jump to date
Lukas Kovalik
Mar 26th at 1:49:46 PM
1:49 PM
здрасти, за трите клиента от тикет ги пуснах манулано ако бяха повече или се оправи само или трябва да се пусне
Mar 26th at 1:50:07 PM
1:50
всеки път когато си сменят layout само се оправя
Stoyan Tanev
Mar 26th at 1:50:20 PM
1:50 PM
Понеже често го забелязвам на нови клинети, ама преди ми минаваше опп синка
Mar 26th at 1:50:29 PM
1:50
сега нещо не ми върви, бие ми мемори грешка и това е
Lukas Kovalik
Mar 26th at 1:50:47 PM
1:50 PM
ако искаш мануално трябва да добавиш --strategy lastModified
1 reaction, react with white check mark emoji
1
Add reaction…
Mar 26th at 1:51:29 PM
1:51
че нали сменихме default да е през webhooks за HS
Mar 26th at 1:51:56 PM
1:51
иначе в тикет споменахте за Memory Usage Error
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Mar 26th at 1:52:10 PM
1:52
аз не успах да видя такава грешка
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Mar 26th at 1:52:27 PM
1:52
ако ти се случи пак, снимай ми я моля те
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Stoyan Tanev
Mar 26th at 1:52:50 PM
1:52 PM
Добре, аз ползвам тази команда
php artisan crm:sync-opportunity --teamId --from
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Lukas Kovalik
Mar 26th at 1:53:30 PM
1:53 PM
да и добави стратегия ако искаш на зад
1 reaction, react with raised hands emoji
1
Add reaction…
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Jump to date
New
Stoyan Tanev
Today at 1:24:48 PM
1:24 PM
Здрасти, имаме ли логове от конектвания на интеграция?
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Today at 1:25:08 PM
1:25
понеже сега бях на среща с клиент и тръгнахме да вързваме Зохо, и просто се рефрешва страницата
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Today at 1:25:22 PM
1:25
и пак ни връща в началото
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Today at 1:26:28 PM
1:26
https://app.jiminny.com/export/wmbfq6Ul0HluXIRatejU6t6PHzAhyVUdNiObCr2tOHy6fLwooNJTA
https://app.jiminny.com/export/wmbfq6Ul0HluXIRatejU6t6PHzAhyVUdNiObCr2tOHy6fLwooNJTA
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
здрасти, трябвя да го прегледам, но почти съм сигурен че не епри нас, ако се наложи ще
здрасти, трябвя да го прегледам, но почти съм сигурен че не епри нас, ако се наложи ще
Shift + Return to add a new line
Shift + Return
to add a new line
Channel...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Jiminny Inc","depth":12,"bounds":{"left":0.23867187,"top":1.0,"width":0.0125,"height":-0.05486107},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXRadioButton","text":"Jiminny (Staging)","depth":12,"bounds":{"left":0.23867187,"top":1.0,"width":0.0125,"height":-0.090972185},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Add workspaces","depth":12,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"Home","depth":14,"bounds":{"left":0.26054686,"top":1.0,"width":0.0203125,"height":-0.048611164},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Home","depth":16,"bounds":{"left":0.26484376,"top":1.0,"width":0.01171875,"height":-0.08124995},"role_description":"text"},{"role":"AXRadioButton","text":"DMs","depth":14,"bounds":{"left":0.26054686,"top":1.0,"width":0.0203125,"height":-0.0958333},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"DMs","depth":16,"role_description":"text"},{"role":"AXRadioButton","text":"Activity","depth":14,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Activity","depth":16,"role_description":"text"},{"role":"AXRadioButton","text":"Files","depth":14,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Files","depth":16,"role_description":"text"},{"role":"AXRadioButton","text":"Later","depth":14,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Later","depth":16,"role_description":"text"},{"role":"AXRadioButton","text":"More…","depth":14,"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"Unreads","depth":21,"bounds":{"left":0.3,"top":1.0,"width":0.021484375,"height":-0.079861164},"role_description":"text"},{"role":"AXStaticText","text":"Threads","depth":21,"bounds":{"left":0.3,"top":1.0,"width":0.020703126,"height":-0.079861164},"role_description":"text"},{"role":"AXStaticText","text":"Huddles","depth":21,"bounds":{"left":0.3,"top":1.0,"width":0.021484375,"height":-0.079861164},"role_description":"text"},{"role":"AXStaticText","text":"Drafts & sent","depth":21,"bounds":{"left":0.3,"top":1.0,"width":0.034375,"height":-0.079861164},"role_description":"text"},{"role":"AXStaticText","text":"Directories","depth":21,"bounds":{"left":0.3,"top":1.0,"width":0.028515626,"height":-0.079861164},"role_description":"text"},{"role":"AXStaticText","text":"platform-inner-team","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ai-chapter","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"alerts","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"backend","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"confusion-clinic","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"curiosity_lab","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"engineering","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"frontend","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"general","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"infra-changes","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"jiminny-bg","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"platform-tickets","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"product_launches","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"random","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"releases","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"sofia-office","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"support","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"thank-yous","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"the_people_of_jiminny","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Stoyan Tanev","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Ves","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Aneliya Angelova","depth":23,"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Yankov","depth":23,"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Vasil Vasilev","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Adelina Petrova","depth":23,"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Ilian Kyuchukov","depth":23,"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Steliyan Georgiev","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Adelina Petrova","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Nikolov","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Galya Dimitrova","depth":23,"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Nikolay Nikolov","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Jira Cloud","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Toast","depth":23,"role_description":"text"},{"role":"AXRadioButton","text":"Messages","depth":17,"bounds":{"left":0.3765625,"top":1.0,"width":0.036328126,"height":-0.079861164},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true,"is_expanded":false},{"role":"AXStaticText","text":"Messages","depth":19,"bounds":{"left":0.3875,"top":1.0,"width":0.022265624,"height":-0.087499976},"role_description":"text"},{"role":"AXRadioButton","text":"Add canvas","depth":18,"bounds":{"left":0.4140625,"top":1.0,"width":0.040234376,"height":-0.079861164},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Add canvas","depth":20,"bounds":{"left":0.425,"top":1.0,"width":0.026171874,"height":-0.087499976},"role_description":"text"},{"role":"AXRadioButton","text":"Files","depth":17,"bounds":{"left":0.45546874,"top":1.0,"width":0.024609376,"height":-0.079861164},"role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Files","depth":19,"bounds":{"left":0.46640626,"top":1.0,"width":0.010546875,"height":-0.087499976},"role_description":"text"},{"role":"AXPopUpButton","text":"Add and Edit Channel Tabs","depth":17,"bounds":{"left":0.48164064,"top":1.0,"width":0.0125,"height":-0.079861164},"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Canvas","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"List","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":17,"role_description":"text"},{"role":"AXPopUpButton","text":"Jump to date","depth":22,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Lukas Kovalik","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:49:46 PM","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:49 PM","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"здрасти, за трите клиента от тикет ги пуснах манулано ако бяха повече или се оправи само или трябва да се пусне","depth":24,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:50:07 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:50","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"всеки път когато си сменят layout само се оправя","depth":24,"role_description":"text"},{"role":"AXButton","text":"Stoyan Tanev","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:50:20 PM","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:50 PM","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Понеже често го забелязвам на нови клинети, ама преди ми минаваше опп синка","depth":24,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:50:29 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:50","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"сега нещо не ми върви, бие ми мемори грешка и това е","depth":24,"role_description":"text"},{"role":"AXButton","text":"Lukas Kovalik","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:50:47 PM","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:50 PM","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ако искаш мануално трябва да добавиш --strategy lastModified","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"1 reaction, react with white check mark emoji","depth":24,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":25,"role_description":"text"},{"role":"AXButton","text":"Add reaction…","depth":24,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Mar 26th at 1:51:29 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:51","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"че нали сменихме default да е през webhooks за HS","depth":24,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:51:56 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:51","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"иначе в тикет споменахте за Memory Usage Error","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Mar 26th at 1:52:10 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:52","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"аз не успах да видя такава грешка","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Mar 26th at 1:52:27 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:52","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ако ти се случи пак, снимай ми я моля те","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Stoyan Tanev","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:52:50 PM","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:52 PM","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Добре, аз ползвам тази команда","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"php artisan crm:sync-opportunity --teamId --from","depth":25,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Lukas Kovalik","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Mar 26th at 1:53:30 PM","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:53 PM","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"да и добави стратегия ако искаш на зад","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"1 reaction, react with raised hands emoji","depth":24,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":25,"role_description":"text"},{"role":"AXButton","text":"Add reaction…","depth":24,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Jump to date","depth":22,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"New","depth":22,"role_description":"text"},{"role":"AXButton","text":"Stoyan Tanev","depth":23,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"","depth":23,"role_description":"text"},{"role":"AXLink","text":"Today at 1:24:48 PM","depth":23,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:24 PM","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Здрасти, имаме ли логове от конектвания на интеграция?","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Today at 1:25:08 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:25","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"понеже сега бях на среща с клиент и тръгнахме да вързваме Зохо, и просто се рефрешва страницата","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Today at 1:25:22 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:25","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"и пак ни връща в началото","depth":24,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Today at 1:26:28 PM","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1:26","depth":25,"role_description":"text"},{"role":"AXLink","text":"https://app.jiminny.com/export/wmbfq6Ul0HluXIRatejU6t6PHzAhyVUdNiObCr2tOHy6fLwooNJTA","depth":24,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"https://app.jiminny.com/export/wmbfq6Ul0HluXIRatejU6t6PHzAhyVUdNiObCr2tOHy6fLwooNJTA","depth":25,"role_description":"text"},{"role":"AXCheckBox","text":"React with white_check_mark","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with eyes","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"React with raised_hands","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Add reaction…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reply in thread","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Forward message…","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Save for later","depth":25,"role_description":"toggle button","subrole":"AXToggleButton","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More actions","depth":25,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"здрасти, трябвя да го прегледам, но почти съм сигурен че не епри нас, ако се наложи ще","depth":23,"value":"здрасти, трябвя да го прегледам, но почти съм сигурен че не епри нас, ако се наложи ще","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"здрасти, трябвя да го прегледам, но почти съм сигурен че не епри нас, ако се наложи ще","depth":25,"role_description":"text"},{"role":"AXButton","text":"Shift + Return to add a new line","depth":20,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Shift + Return","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"to add a new line","depth":21,"role_description":"text"},{"role":"AXStaticText","text":"Channel","depth":11,"role_description":"text"}]...
|
8499591127745508567
|
-1569198985826874796
|
idle
|
hybrid
|
NULL
|
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Jiminny Inc
Jiminny (Staging)
Add workspaces
Home
Home
DMs
DMs
Activity
Activity
Files
Files
Later
Later
More…
More
Unreads
Threads
Huddles
Drafts & sent
Directories
platform-inner-team
ai-chapter
alerts
backend
confusion-clinic
curiosity_lab
engineering
frontend
general
infra-changes
jiminny-bg
platform-tickets
product_launches
random
releases
sofia-office
support
thank-yous
the_people_of_jiminny
Stoyan Tanev
Ves
Galya Dimitrova
Aneliya Angelova
,
Nikolay Yankov
,
Steliyan Georgiev
Vasil Vasilev
Steliyan Georgiev
Adelina Petrova
,
Ilian Kyuchukov
,
Steliyan Georgiev
Adelina Petrova
Nikolay Nikolov
Galya Dimitrova
,
Nikolay Nikolov
Jira Cloud
Toast
Messages
Messages
Add canvas
Add canvas
Files
Files
Add and Edit Channel Tabs
Canvas
List
Folder
Jump to date
Lukas Kovalik
Mar 26th at 1:49:46 PM
1:49 PM
здрасти, за трите клиента от тикет ги пуснах манулано ако бяха повече или се оправи само или трябва да се пусне
Mar 26th at 1:50:07 PM
1:50
всеки път когато си сменят layout само се оправя
Stoyan Tanev
Mar 26th at 1:50:20 PM
1:50 PM
Понеже често го забелязвам на нови клинети, ама преди ми минаваше опп синка
Mar 26th at 1:50:29 PM
1:50
сега нещо не ми върви, бие ми мемори грешка и това е
Lukas Kovalik
Mar 26th at 1:50:47 PM
1:50 PM
ако искаш мануално трябва да добавиш --strategy lastModified
1 reaction, react with white check mark emoji
1
Add reaction…
Mar 26th at 1:51:29 PM
1:51
че нали сменихме default да е през webhooks за HS
Mar 26th at 1:51:56 PM
1:51
иначе в тикет споменахте за Memory Usage Error
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Mar 26th at 1:52:10 PM
1:52
аз не успах да видя такава грешка
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Mar 26th at 1:52:27 PM
1:52
ако ти се случи пак, снимай ми я моля те
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Stoyan Tanev
Mar 26th at 1:52:50 PM
1:52 PM
Добре, аз ползвам тази команда
php artisan crm:sync-opportunity --teamId --from
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Lukas Kovalik
Mar 26th at 1:53:30 PM
1:53 PM
да и добави стратегия ако искаш на зад
1 reaction, react with raised hands emoji
1
Add reaction…
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Jump to date
New
Stoyan Tanev
Today at 1:24:48 PM
1:24 PM
Здрасти, имаме ли логове от конектвания на интеграция?
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Today at 1:25:08 PM
1:25
понеже сега бях на среща с клиент и тръгнахме да вързваме Зохо, и просто се рефрешва страницата
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Today at 1:25:22 PM
1:25
и пак ни връща в началото
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
Today at 1:26:28 PM
1:26
https://app.jiminny.com/export/wmbfq6Ul0HluXIRatejU6t6PHzAhyVUdNiObCr2tOHy6fLwooNJTA
https://app.jiminny.com/export/wmbfq6Ul0HluXIRatejU6t6PHzAhyVUdNiObCr2tOHy6fLwooNJTA
React with white_check_mark
React with eyes
React with raised_hands
Add reaction…
Reply in thread
Forward message…
Save for later
More actions
здрасти, трябвя да го прегледам, но почти съм сигурен че не епри нас, ако се наложи ще
здрасти, трябвя да го прегледам, но почти съм сигурен че не епри нас, ако се наложи ще
Shift + Return to add a new line
Shift + Return
to add a new line
Channel
Firefox)FilelEditViewHistoryBookmarks)ProfilesToolsWindowHelp• Not Securehttp:/[IP_ADDRESS]:8766screenpipeActivitysearchAUOIOWork reponp! Western Digital Red Plus 3.5 6TB 5400rpm 256MB SAiS I SeneticТвърд диск, Western Digital Red 6TB Plus ( 3.5", 256N() SQLite Web: db.salitew Screenpipe DashboardV Welcome to Steam— New |aoAl summaryTOTAL SPAN10.1h09:23 → 19:31ACTIVE TIME (WALL CLOCK)10.1hClick a session seament to tilter activity to that time windowlFiltered: Boosteroid xFRAMESAPPS1073020TIME PER APP - CLICK TO FILTER ALL PANELS BY APFPhostormHIretoxBoosteroldlermSackClaudeDiaWindsurfHindenActivity MonitoweansnorxSequel AceNerAutnAcentAlfredallscaleravcastControl CentreCodecoreautnaCoresevcesu A..BKEAKS0 . 0.0hSESSIONS - CLCK TO FILIERS1: 0923-19:31 608 5m1S609mUlEVENIS10530AUDIO198.6ml166,7ml84.8m72.4ml64.1m17.9ml5.2ml1.om1asm0.1m0.1m0.1m0.1mBoosteroidBoosteroidul events: Support Daily • in 1h 29 mБГ100% C2Wed 15 Aor 13:31156Date14 / 04 / 2026 0ACTIVE PERIOD TIMES IN LOCAL TIMEZONE09:23 → 19:31Boosteroid84.70.1...
|
NULL
|
|
55470
|
1196
|
50
|
2026-04-20T09:51:18.906118+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776678678906_m2.jpg...
|
PhpStorm
|
faVsco.js – HS_local [jiminny@localhost]
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11976 on JY-20553-debug- Project: faVsco.js, menu
#11976 on JY-20553-debug-crm-sync-delays, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
33
2
19
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot\ServiceTraits;
use Carbon\Carbon;
use HubSpot\Client\Crm\Deals\Model\CollectionResponseAssociatedId;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Models\Account;
use Exception;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Jobs\Crm\MatchActivitiesToNewOpportunity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\BusinessProcess;
use Jiminny\Exceptions\CrmException;
use Jiminny\Models\Opportunity;
use Illuminate\Support\Collection;
use Jiminny\Models\Stage;
use Jiminny\Repositories\Crm\CrmEntityRepository;
use Jiminny\Services\Crm\Hubspot\DealFieldsService;
use Jiminny\Services\Crm\Hubspot\OpportunitySyncStrategy\HubspotSingleSyncStrategy;
use Jiminny\Services\Crm\Hubspot\WebhookSyncBatchProcessor;
use Jiminny\Services\Crm\OpportunitySyncStrategyResolver;
use Jiminny\Utils\CurrencyFormatter;
/**
* Optimized sync methods for better performance
* These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains
*/
trait OpportunitySyncTrait
{
private const int BATCH_SIZE = 100;
private const int BATCH_PROCESS_SIZE = 800;
protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;
protected CrmEntityRepository $crmEntityRepository;
protected DealFieldsService $dealFieldsService;
private ?array $cachedClosedDealStages = null;
private array $cachedBusinessProcesses = [];
private array $cachedStages = [];
public function syncOpportunities(array $parameters, ?string $strategy = null): int
{
$startTime = microtime(true);
$strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);
$parameters['config'] = $this->config;
$syncCount = 0;
$reportedTotal = 0;
$lastSyncedId = [];
$strategyNames = [];
try {
foreach ($strategies as $strategyName => $syncStrategy) {
$strategyNames[] = $strategyName;
$this->logger->info(
'[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,
['team' => $this->team->getId()]
);
$total = 0;
$lastId = null;
$buffer = [];
// HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies
foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {
$buffer[] = $hsOpportunity;
// process every 800 rows (fits < 1 000 association limit)
if (\count($buffer) >= self::BATCH_PROCESS_SIZE) {
$syncCount += $this->processOpportunityBatch($buffer);
$buffer = [];
}
}
// leftovers
if ($buffer) {
$syncCount += $this->processOpportunityBatch($buffer);
}
$reportedTotal += $total;
$lastSyncedId = $lastId;
}
} catch (\HubSpot\Client\Crm\Deals\ApiException | CrmException $e) {
$this->handleSyncException($e, $parameters);
}
$durationMs = round((microtime(true) - $startTime) * 1000, 2);
$this->logger->info(
'[HubSpot] Synced opportunities',
[
'team' => $this->team->getId(),
'strategies' => implode(',', $strategyNames),
'sync_count' => $syncCount,
'total' => $reportedTotal,
'last_synced_id' => $lastSyncedId,
'duration_ms' => $durationMs,
]
);
return $reportedTotal;
}
private function handleSyncException(\Throwable $e, array $parameters): void
{
if (($parameters['since'] ?? null) instanceof Carbon) {
$parameters['since'] = $parameters['since']->toDateTimeString();
}
$parameters['config'] = $this->config->getId();
$this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [
'teamId' => $this->team->getUuid(),
'parameters' => $parameters,
'reason' => $e->getMessage(),
]);
}
/**
* @inheritdoc
*/
public function syncOpportunity(string $crmId): ?Opportunity
{
$strategy = $this->opportunitySyncStrategyResolver->resolve(
$this->config,
OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,
);
$parameters = [
'config' => $this->config,
'crm_id' => $crmId,
];
try {
if (! $strategy instanceof HubspotSingleSyncStrategy) {
throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');
}
$hsOpportunity = $strategy->fetchOpportunity($parameters);
} catch (\HubSpot\Client\Crm\Deals\ApiException $e) {
$this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [
'teamId' => $this->team->getUuid(),
'crmId' => $crmId,
'reason' => $e->getMessage(),
]);
return null;
}
$hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);
return $this->importOrUpdateOpportunity($hsOpportunity);
}
/**
* Process webhook-collected opportunity batches.
*
* Drains Redis sets containing company CRM IDs collected from webhook events
* and dispatches ImportOpportunityBatch jobs for batch processing.
*
* @return int Number of opportunity IDs dispatched to jobs
*/
public function batchSyncOpportunities(): int
{
$configId = $this->team->getCrmConfiguration()->getId();
return $this->batchProcessor->processBatchesForObjectType(
WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,
$configId
);
}
/**
* Import a batch of opportunities by their CRM IDs.
* Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().
*
* @param array<string> $crmIds HubSpot deal CRM IDs
*
* @return array{success: array, failed_ids: array, errors?: array<string, string>}
*/
public function importOpportunityBatchByIds(array $crmIds): array
{
$fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);
$allDeals = [];
foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {
$deals = $this->client->getOpportunitiesByIds($chunk, $fields);
foreach ($deals as $deal) {
$allDeals[] = $deal;
}
}
// IDs not returned by HubSpot are likely deleted or inaccessible deals.
// These are not failures — retrying won't bring them back.
$fetchedIds = array_map('strval', array_column($allDeals, 'id'));
$notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));
if (! empty($notFoundIds)) {
$this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [
'teamId' => $this->team->getId(),
'notFoundCount' => \count($notFoundIds),
'notFoundIds' => $notFoundIds,
'requestedCount' => \count($crmIds),
'fetchedCount' => \count($allDeals),
]);
}
if (empty($allDeals)) {
return ['success' => [], 'failed_ids' => []];
}
return $this->importOpportunityBatch($allDeals);
}
private function getClosedDealStages(): array
{
if ($this->cachedClosedDealStages !== null) {
return $this->cachedClosedDealStages;
}
$stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);
$data = [
'lost' => [],
'won' => [],
];
foreach ($stages as $stage) {
if ($stage->probability == 0.00) {
$data['lost'][] = $stage->crm_provider_id;
}
if ($stage->probability == 100.00) {
$data['won'][] = $stage->crm_provider_id;
}
}
$this->cachedClosedDealStages = $data;
return $data;
}
/**
* Import deals into the database with pre-fetched associations.
*
* API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT
* caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()
* where Laravel retries the whole job with backoff. After all retries exhausted,
* failed() requeues all IDs to Redis.
*
* The per-deal loop catches exceptions individually. A deal can end up in three states:
* - success: imported/updated successfully
* - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)
* These are permanent issues — retrying won't fix them.
* - skipped (null): missing dependencies (no account, unknown pipeline/stage).
* This is acceptable — the deal cannot be imported until those exist.
*/
private function importOpportunityBatch(array $deals): array
{
$syncedOpportunities = [
'success' => [],
'failed_ids' => [],
];
$dealIds = array_column($deals, 'id');
// Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the
// queue job retries the whole batch and eventually requeues all deal IDs back to Redis.
try {
$companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');
$contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');
$associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);
$existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(
$this->config,
array_map('strval', $dealIds)
);
$existingCrmIdSet = array_flip($existingCrmIds);
} catch (\Throwable $e) {
$this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [
'teamId' => $this->team->getId(),
'dealCount' => count($dealIds),
'error' => $e->getMessage(),
]);
throw $e;
}
foreach ($deals as $deal) {
try {
$deal['associations'] = $this->prepareAssociationsForOpportunity(
$deal['id'],
$companyAssociations,
$contactAssociations,
$associationsData
);
$syncedOpportunity = $this->importOrUpdateOpportunity(
$deal,
isset($existingCrmIdSet[(string) $deal['id']])
);
if ($syncedOpportunity) {
$syncedOpportunities['success'][] = $syncedOpportunity;
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [
'teamId' => $this->team->getId(),
'crmId' => $deal['id'],
'error' => $e->getMessage(),
]);
$syncedOpportunities['failed_ids'][] = $deal['id'];
$syncedOpportunities['errors'][$deal['id']] = $e->getMessage();
}
}
return $syncedOpportunities;
}
/**
* Prepare associated entities for opportunities with optimized batch processing
* Returns structured data with CRM ID to DB ID mappings for each opportunity
*/
private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array
{
// Step 1: Collect all unique company and contact IDs from associations
$allCompanyIds = $this->flattenAssociationIds($companyAssociations);
$allContactIds = $this->flattenAssociationIds($contactAssociations);
// Step 2: Batch sync missing entities and get CRM ID to DB ID mappings
$companyIdMappings = [];
$contactIdMappings = [];
if (! empty($allCompanyIds)) {
$companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);
}
if (! empty($allContactIds)) {
$contactIdMappings = $this->prepareAssociatedContacts($allContactIds);
}
return [
'company_id_mappings' => $companyIdMappings,
'contact_id_mappings' => $contactIdMappings,
];
}
/**
* Flatten association data to get unique IDs
*/
private function flattenAssociationIds(array $associations): array
{
$ids = [];
foreach ($associations as $dealAssociations) {
if (is_array($dealAssociations)) {
foreach ($dealAssociations as $id) {
$ids[$id] = true;
}
}
}
return array_keys($ids);
}
/**
* Batch sync missing accounts
*/
private function prepareAssociatedAccounts(array $companyIds): array
{
// Find which accounts already exist
$existingAccounts = $this->crmEntityRepository
->findAccountsByExternalIds($this->config, $companyIds);
$existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();
$existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {
return [$account->getCrmProviderId() => $account->getId()];
})->toArray();
$missingCompanyIds = array_diff($companyIds, $existingCompanyIds);
if (empty($missingCompanyIds)) {
return $existingAccountsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [
'teamId' => $this->team->getUuid(),
'total_companies' => count($companyIds),
'existing_companies' => count($existingCompanyIds),
'missing_companies' => count($missingCompanyIds),
]);
// we already have limit on opportunity ids count
// Initialize variable before try block
$syncedAccountsData = [];
try {
$syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [
'size' => count($missingCompanyIds),
'error' => $e->getMessage(),
]);
$syncedAccountsData = [];
}
return $existingAccountsData + $syncedAccountsData;
}
/**
* Prepare associated contacts - find existing and sync missing ones
* Returns mapping of CRM ID to DB ID
*/
private function prepareAssociatedContacts(array $contactIds): array
{
// Find which contacts already exist
$existingContacts = $this->crmEntityRepository
->findContactsByExternalIds($this->config, $contactIds);
$existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();
// Create mapping for existing contacts
$existingContactsData = $existingContacts->mapWithKeys(function ($contact) {
return [$contact->getCrmProviderId() => $contact->getId()];
})->toArray();
$missingContactIds = array_diff($contactIds, $existingContactIds);
if (empty($missingContactIds)) {
return $existingContactsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [
'teamId' => $this->team->getUuid(),
'total_contacts' => count($contactIds),
'existing_contacts' => count($existingContactIds),
'missing_contacts' => count($missingContactIds),
]);
// Sync missing contacts using batch API
try {
$syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [
'size' => count($missingContactIds),
'error' => $e->getMessage(),
]);
$syncedContactsData = [];
}
return $existingContactsData + $syncedContactsData;
}
private function batchSyncCrmObjects(string $objectType, array $crmIds): array
{
$syncObjects = [];
$crmObjectIds = array_values($crmIds);
foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {
try {
$objects = $objectType === 'companies' ?
$this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :
$this->client->getContactsByIds($chunk, $this->getContactFields());
foreach ($objects as $objectId => $objectData) {
$this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [
'requested_count' => count($chunk),
'synced_count' => count($objects),
]);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [
'ids' => $chunk,
'error' => $e->getMessage(),
]);
}
}
return $syncObjects;
}
private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void
{
try {
$object = $objectType === 'companies' ?
$this->importAccount($objectData) :
$this->importContact($objectData);
if ($object) {
$syncObjects[$object->getCrmProviderId()] = $object->getId();
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [
'id' => $objectId,
'error' => $e->getMessage(),
]);
}
}
/**
* Prepare associations for a single opportunity
*
* The return value is an array with the following structure:
* [
* 'companies' => [
* $companyCrmId => $companyId,
* ...
* ],
* 'contacts' => [
* $contactCrmId => $contactId,
* ...
* ],
* 'account_id' => $accountId,
* ]
*/
private function prepareAssociationsForOpportunity(
string $oppCrmId,
array $companyAssociations,
array $contactAssociations,
array $associationsData
): array {
$associations = [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
$oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];
foreach ($oppCompanyIds as $companyCrmId) {
if (isset($associationsData['company_id_mappings'][$companyCrmId])) {
$associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];
// Set primary account (first company becomes primary account)
if ($associations['account_id'] === null) {
$associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];
}
}
}
$oppContactIds = $contactAssociations[$oppCrmId] ?? [];
foreach ($oppContactIds as $contactCrmId) {
if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {
$associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];
}
}
return $associations;
}
/**
* Update only associations for an opportunity
*/
private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void
{
// Update contact associations
$this->importOpportunityContacts($opportunity, $associations['contacts']);
// Update company (account) associations
$this->updateOpportunityAccount($opportunity, $associations['account_id']);
}
/**
* Remove all contact associations from an opportunity
*/
private function removeAllOpportunityContacts(Opportunity $opportunity): void
{
$currentCount = (int) $opportunity->contacts()->count();
if ($currentCount > 0) {
$opportunity->contacts()->detach();
$this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_count' => $currentCount,
]);
}
}
private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void
{
if ($accountId === null) {
// No account ID provided - keep current account
return;
}
$currentAccountId = $opportunity->getAccountId();
// Only update if account has changed
if ($currentAccountId !== $accountId) {
$opportunity->account_id = $accountId;
$opportunity->save();
$this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [
'opportunity_id' => $opportunity->getId(),
'old_account_id' => $currentAccountId,
'new_account_id' => $accountId,
]);
}
}
/**
* Find existing opportunities by external IDs (OPTIMIZED VERSION)
* Uses batch query for better performance
*/
private function findExistingOpportunities(array $crmIds): Collection
{
return $this->crmEntityRepository
->findOpportunitiesByExternalIds($this->config, $crmIds);
}
private function processOpportunityBatch(array $opportunities): int
{
$syncedOpportunities = $this->importOpportunityBatch($opportunities);
return count($syncedOpportunities['success'] ?? []);
}
/**
* Convert single deal associations from HubSpot format to internal format
* Handles both HubSpot SDK objects and array formats
*
* @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed
*
* @return array Processed associations with DB IDs
*/
private function convertDealAssociations(array $opportunityAssociations): array
{
$associations = $this->initializeAssociationsStructure();
if (empty($opportunityAssociations)) {
return $associations;
}
$associationIds = $this->extractAssociationIds($opportunityAssociations);
$this->processCompanyAssociations($associationIds, $associations);
$this->processContactAssociations($associationIds, $associations);
return $associations;
}
private function initializeAssociationsStructure(): array
{
return [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
}
private function extractAssociationIds(array $opportunityAssociations): array
{
$associationIds = [];
foreach ($opportunityAssociations as $type => $associationData) {
if (! empty($associationData)) {
$associationIds[$type] = $this->convertSingleDealAssociations($associationData);
}
}
return $associationIds;
}
private function processCompanyAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['companies'])) {
return;
}
$companyId = $associationIds['companies'][0];
$account = $this->findOrSyncAccount($companyId);
if ($account instanceof Account) {
$associations['companies'][$companyId] = $account->getId();
$associations['account_id'] = $account->getId();
}
}
private function processContactAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['contacts'])) {
return;
}
foreach ($associationIds['contacts'] as $contactId) {
$contact = $this->findOrSyncContact($contactId);
if ($contact instanceof Contact) {
$associations['contacts'][$contactId] = $contact->getId();
}
}
}
private function findOrSyncAccount(string $companyId): ?Account
{
$account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);
if (! $account instanceof Account) {
$account = $this->syncAccount($companyId);
}
return $account;
}
private function findOrSyncContact(string $contactId): ?Contact
{
$contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);
if (! $contact instanceof Contact) {
$contact = $this->syncContact($contactId);
}
return $contact;
}
private function convertSingleDealAssociations($opportunityAssociations = null): array
{
$associationData = [];
if ($opportunityAssociations === null) {
return $associationData;
}
// Handle array input (from extractAssociationIds)
if (is_array($opportunityAssociations)) {
return $opportunityAssociations;
}
// Handle CollectionResponseAssociatedId object
if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {
foreach ($opportunityAssociations->getResults() as $association) {
$associationData[] = $association->getId();
}
}
return $associationData;
}
private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity
{
if (empty($crmData['properties'])) {
return null;
}
$crmId = (string) $crmData['id'];
$properties = $crmData['properties'];
$associations = $crmData['associations'] ?? [];
$opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(
$this->config,
$crmId
);
if ($opportunityExists) {
return $this->updateOpportunity($crmId, $properties, $associations);
}
return $this->createOpportunity($crmId, $properties, $associations);
}
/**
* Create new opportunity
*/
private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity
{
$accountId = $this->resolveAccountId($associations);
if (! $accountId) {
return null;
}
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
if (! $businessProcess) {
return null;
}
$stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);
if (! $stage) {
return null;
}
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->importOpportunityContacts($opportunity, $associations['contacts']);
if ($opportunity->wasRecentlyCreated) {
MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());
}
return $opportunity;
}
/**
* Update existing opportunity
*/
private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity
{
$accountId = $this->resolveAccountId($associations);
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
$stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->updateOpportunityAssociations($opportunity, $associations);
return $opportunity;
}
private function resolveAccountId(array $associations): ?int
{
if (! empty($associations['account_id'])) {
return $associations['account_id'];
}
if (empty($associations)) {
return null;
}
// Fallback: use first company as account (currently SDK returns one company)
foreach ($associations['companies'] as $accountId) {
return $accountId;
}
return null;
}
private function buildOpportunityData(
array $properties,
?int $accountId,
?BusinessProcess $businessProcess,
?Stage $stage
): array {
$ownerId = null;
$profile = null;
if (! empty($properties['hubspot_owner_id'])) {
$ownerId = $properties['hubspot_owner_id'];
$profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);
}
$name = 'Unknown';
if (isset($properties['dealname'])) {
$name = mb_strimwidth($properties['dealname'], 0, 128);
}
$amount = $this->resolveAmount($properties);
$currency = $properties['deal_currency_code'] ?? null;
$closeDate = null;
if (! empty($properties['closedate'])) {
$closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');
}
$remotelyCreatedAt = null;
if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {
$date = $this->parseCleanDatetime($properties['createdate']);
$remotelyCreatedAt = $date?->format('Y-m-d H:i:s');
}
$closedStages = $this->getClosedDealStages();
$isWon = in_array($properties['dealstage'], $closedStages['won']);
$isLost = in_array($properties['dealstage'], $closedStages['lost']);
$data = [
'team_id' => $this->team->getId(),
'user_id' => $profile ? $profile->user_id : null,
'owner_id' => $ownerId,
'name' => $name,
'value' => ! empty($amount) ? $amount : null,
'currency_code' => CurrencyFormatter::formatCode($currency),
'close_date' => $closeDate,
'is_closed' => $isWon || $isLost,
'is_won' => $isWon,
'remotely_created_at' => $remotelyCreatedAt,
'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),
'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),
];
if ($accountId) {
$data['account_id'] = $accountId;
}
if ($stage) {
$data['stage_id'] = $stage->id;
}
if ($businessProcess) {
$recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);
if ($recordType) {
$data['record_type_id'] = $recordType->id;
}
}
return $data;
}
private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess
{
if ($pipelineId === null) {
return null;
}
$cacheKey = $this->getBusinessProcessCacheKey($pipelineId);
if (isset($this->cachedBusinessProcesses[$cacheKey])) {
return $this->cachedBusinessProcesses[$cacheKey];
}
$businessProcess = $this->getBusinessProcess($pipelineId);
if (! $businessProcess instanceof BusinessProcess) {
$this->importStages();
$businessProcess = $this->getBusinessProcess($pipelineId);
}
if (! $businessProcess instanceof BusinessProcess) {
$this->logger->info(
'[HubSpot] Deal is not attached to a pipeline',
[
'pipeline' => $pipelineId]
);
}
$this->cachedBusinessProcesses[$cacheKey] = $businessProcess;
return $businessProcess;
}
private function getBusinessProcess(string $pipelineId): ?BusinessProcess
{
return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);
}
private function getBusinessProcessCacheKey(string $pipelineId): string
{
return $this->config->getId() . '_' . $pipelineId;
}
private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage
{
if (empty($stageId)) {
return null;
}
$cacheKey = $businessProcess->getId() . ':' . $stageId;
if (isset($this->cachedStages[$cacheKey])) {
return $this->cachedStages[$cacheKey];
}
$stage = $this->crmEntityRepository->getPipelineStageByConditions(
$businessProcess,
[
'crm_provider_id' => $stageId,
'type' => Stage::TYPE_OPPORTUNITY,
]
);
if ($stage === null) {
$this->importStages(null, $stageId);
}
if ($stage === null) {
$this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);
}
$this->cachedStages[$cacheKey] = $stage;
return $stage;
}
private function resolveAmount(array $properties): ?string
{
$amount = null;
if (! empty($properties['amount'])) {
$amount = str_replace(',', '', $properties['amount']);
}
if ($this->config->hasDefaultCurrencyFieldSet()) {
$valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();
$amount = $properties[$valueFieldName] ?? $amount;
}
return $amount;
}
private function parseCleanDatetime(string $datetime): ?Carbon
{
// Treat pre-1980 values as invalid
$minValidDate = Carbon::parse('1980-01-01 00:00:00');
try {
$date = Carbon::parse($datetime);
if ($minValidDate->gt($date)) {
return null;
}
return $date;
} catch (Exception) {
return null; // On parse error, treat as null
}
}
private function resolveDealProbability(?string $stageProbability): int
{
if ($stageProbability === null) {
return 0;
}
$probability = (float) $stageProbability;
return $probability > 1 ? 0 : (int) ($probability * 100);
}
private function resolveForecastCategory(?string $forecastCategory): string
{
if (! $forecastCategory) {
return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;
}
$forecastCategory = str_replace('_', ' ', $forecastCategory);
return ucwords(strtolower($forecastCategory));
}
private function importExternalFieldData(array $properties, int $opportunityId): void
{
$crmFields = $this->getOpportunitySyncableFields();
$this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);
}
private function importOpportunityContacts(Opportunity $opportunity, array $associations): void
{
// Handle empty or missing contact associations
if (empty($associations)) {
// Remove all existing contact associations if none provided
$this->removeAllOpportunityContacts($opportunity);
return;
}
// Use differential sync approach for better performance and accuracy
$this->syncOpportunityContactsDifferential($opportunity, $associations);
}
/**
* Sync opportunity contacts using differential approach
* This compares current vs new associations and only makes necessary changes
*/
private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void
{
$currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);
$contactAssociationIds = array_keys($contactAssociations);
$contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);
$contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);
if (empty($contactsToAdd) && empty($contactsToRemove)) {
return;
}
$this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);
$this->removeContactAssociations($opportunity, $contactsToRemove);
$this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);
}
private function getCurrentContactCrmIds(Opportunity $opportunity): array
{
return $opportunity->contacts()
->pluck('contacts.crm_provider_id')
->toArray();
}
private function logContactAssociationChanges(
Opportunity $opportunity,
array $currentContactCrmIds,
array $contactAssociations,
array $contactsToAdd,
array $contactsToRemove
): void {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [
'opportunity_id' => $opportunity->getId(),
'current_contacts' => $currentContactCrmIds,
'new_contacts' => $contactAssociations,
'contacts_to_add' => $contactsToAdd,
'contacts_to_remove' => $contactsToRemove,
]);
}
private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void
{
if (empty($contactsToRemove)) {
return;
}
$contactsToDetach = $opportunity->contacts()
->whereIn('contacts.crm_provider_id', $contactsToRemove)
->pluck('contacts.id')
->toArray();
if (! empty($contactsToDetach)) {
$opportunity->contacts()->detach($contactsToDetach);
$this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_contact_crm_ids' => $contactsToRemove,
'removed_contact_count' => count($contactsToDetach),
]);
}
}
private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void
{
if (empty($contactsToAdd)) {
return;
}
$contactsAdded = [];
foreach ($contactsToAdd as $crmId) {
$id = $contactAssociations[$crmId];
if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {
$contactsAdded[] = $crmId;
}
}
$this->logAddedContacts($opportunity, $contactsAdded);
}
private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool
{
try {
$contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);
if (! $contact) {
return false;
}
return $this->performContactAttachment($opportunity, $contact, $crmId);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [
'opportunity_id' => $opportunity->getId(),
'contact_crm_id' => $crmId,
'error' => $e->getMessage(),
]);
return false;
}
}
private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool
{
try {
$opportunity->contacts()->attach($contact->getId(), [
'crm_provider_id' => $crmId,
]);
return true;
} catch (\Illuminate\Database\QueryException $e) {
if (str_contains($e->getMessage(), 'Duplicate entry')) {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [
'contact_id' => $contact->getId(),
'contact_crm_id' => $crmId,
'opportunity_id' => $opportunity->getId(),
]);
return false;
}
throw $e;
}
}
private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void
{
if (! empty($contactsAdded)) {
$this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [
'opportunity_id' => $opportunity->getId(),
'added_contact_crm_ids' => $contactsAdded,
'added_contacts_count' => count($contactsAdded),
]);
}
}
}
Execute
Open Query Execution Settings…
Script
Code changed:
Hide
Sync Changes
Hide This Notification
6
1
6
Previous Highlighted Error
Next Highlighted Error
# [PASSWORD_DOTS] HS [PASSWORD_DOTS]
select * from teams where id = 2; # 2
select * from features; # 2
select * from team_features where team_id = 2; # 2
select * from crm_configurations where id = 2; # 2
select * from users where team_id = 2; #
select * from playbooks where team_id = 2; # event 38
select * from playbook_categories where playbook_id = 38; #
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;
https://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624
https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0
SELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;
# 609126 softphone tr. 11241
SELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;
# 608874 conference tr. 11226 crmId: 103422236596
select * from ai_prompts where transcription_id IN (11241, 11226);
select * from activity_summary_logs where activity_id = 608874;
select * from sidekick_settings;
select * from default_activity_types;
select * from crm_field_data where activity_id = 1223;
select * from crm_layouts where crm_configuration_id = 2;
SELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);
select * from crm_fields where crm_configuration_id = 11 and object_type = 'event';
SELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);
SELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;
SELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u
on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 2 and sa.provider = 'hubspot';
select * from opportunities where team_id = 2
and crm_provider_id IN ('51317301383');
select * from contacts where id = 85;
select * from opportunities where team_id = 2 order by id desc;
select * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112
select * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112
select * from opportunity_contacts where opportunity_id = 5117;
select * from crm_field_data where object_id = 1365;
SELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);
select * from features;
select * from team_features where team_id IN (1);
select * from team_features where feature_id IN (36);
SHOW CREATE TABLE opportunity_contacts;
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';
# $slug = 'HUBSPOT_WEBHOOK_SYNC';
# $team = Jiminny\Models\Team::find(2);
# $feature = Feature::query()->where('slug', $slug)->first();
# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);
# hubspot_webhook_metrics
select * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365
SELECT * FROM opportunity_contacts WHERE opportunity_id = '414';
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';
select * from contacts where id in (414, 464);
select * from activities where crm_configuration_id = 2;
select settings from crm_configurations where id = 11;
select * from teams; # 1, 2
select * from users;
select * from crm_configurations where id = 39;
select * from team_features where team_id = 2;
select * from features;
# SELECT * FROM opportunities WHERE crm_configuration_id = 2
# order by id desc;
# and crm_provider_id = '49908861993';
select * from activity_providers where id IN (443, 202, 203, 227);
select * from activity_imports where id = 795889;
select c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id
where c.provider = 'hubspot';
select * from crm_configurations crm JOIN teams t on crm.team_id = t.id
where provider = 'hubspot';
SELECT * FROM teams WHERE id = 31;
SELECT * FROM users WHERE id = 257;
SELECT * FROM opportunities WHERE team_id = 2;
select * from opportunity_contacts where opportunity_id = 5124;
select * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)
select * from activities where crm_configuration_id = 13;
SELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141
select id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;
SELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;
SELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;
SELECT * FROM contacts WHERE team_id = 2 order by id desc;
select * from opportunity_contacts where contact_id = 6223;
SELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;
select * from crm_profiles where crm_configuration_id = 2;
select * from activities where account_id = 46;
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11976 on JY-20553-debug-crm-sync-delays, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.10837766,"height":0.025538707},"help_text":"Pull request #11976 exists for current branch JY-20553-debug-crm-sync-delays","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.7972075,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"bounds":{"left":0.8125,"top":0.019952115,"width":0.10305851,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"33","depth":4,"bounds":{"left":0.47739363,"top":0.19952115,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.48969415,"top":0.19952115,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"19","depth":4,"bounds":{"left":0.49966756,"top":0.19952115,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5109708,"top":0.19792499,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.51828456,"top":0.19792499,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot\\ServiceTraits;\n\nuse Carbon\\Carbon;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\CollectionResponseAssociatedId;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Models\\Account;\nuse Exception;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Jobs\\Crm\\MatchActivitiesToNewOpportunity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\BusinessProcess;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Models\\Opportunity;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Repositories\\Crm\\CrmEntityRepository;\nuse Jiminny\\Services\\Crm\\Hubspot\\DealFieldsService;\nuse Jiminny\\Services\\Crm\\Hubspot\\OpportunitySyncStrategy\\HubspotSingleSyncStrategy;\nuse Jiminny\\Services\\Crm\\Hubspot\\WebhookSyncBatchProcessor;\nuse Jiminny\\Services\\Crm\\OpportunitySyncStrategyResolver;\nuse Jiminny\\Utils\\CurrencyFormatter;\n\n/**\n * Optimized sync methods for better performance\n * These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains\n */\ntrait OpportunitySyncTrait\n{\n private const int BATCH_SIZE = 100;\n private const int BATCH_PROCESS_SIZE = 800;\n\n protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;\n protected CrmEntityRepository $crmEntityRepository;\n protected DealFieldsService $dealFieldsService;\n\n private ?array $cachedClosedDealStages = null;\n private array $cachedBusinessProcesses = [];\n private array $cachedStages = [];\n\n public function syncOpportunities(array $parameters, ?string $strategy = null): int\n {\n $startTime = microtime(true);\n $strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);\n $parameters['config'] = $this->config;\n $syncCount = 0;\n $reportedTotal = 0;\n $lastSyncedId = [];\n $strategyNames = [];\n\n try {\n foreach ($strategies as $strategyName => $syncStrategy) {\n $strategyNames[] = $strategyName;\n $this->logger->info(\n '[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,\n ['team' => $this->team->getId()]\n );\n\n $total = 0;\n $lastId = null;\n $buffer = [];\n\n // HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies\n foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {\n $buffer[] = $hsOpportunity;\n\n // process every 800 rows (fits < 1 000 association limit)\n if (\\count($buffer) >= self::BATCH_PROCESS_SIZE) {\n $syncCount += $this->processOpportunityBatch($buffer);\n $buffer = [];\n }\n }\n\n // leftovers\n if ($buffer) {\n $syncCount += $this->processOpportunityBatch($buffer);\n }\n\n $reportedTotal += $total;\n $lastSyncedId = $lastId;\n }\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException | CrmException $e) {\n $this->handleSyncException($e, $parameters);\n }\n\n $durationMs = round((microtime(true) - $startTime) * 1000, 2);\n $this->logger->info(\n '[HubSpot] Synced opportunities',\n [\n 'team' => $this->team->getId(),\n 'strategies' => implode(',', $strategyNames),\n 'sync_count' => $syncCount,\n 'total' => $reportedTotal,\n 'last_synced_id' => $lastSyncedId,\n 'duration_ms' => $durationMs,\n ]\n );\n\n return $reportedTotal;\n }\n\n private function handleSyncException(\\Throwable $e, array $parameters): void\n {\n if (($parameters['since'] ?? null) instanceof Carbon) {\n $parameters['since'] = $parameters['since']->toDateTimeString();\n }\n $parameters['config'] = $this->config->getId();\n\n $this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [\n 'teamId' => $this->team->getUuid(),\n 'parameters' => $parameters,\n 'reason' => $e->getMessage(),\n ]);\n }\n\n /**\n * @inheritdoc\n */\n public function syncOpportunity(string $crmId): ?Opportunity\n {\n $strategy = $this->opportunitySyncStrategyResolver->resolve(\n $this->config,\n OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,\n );\n\n $parameters = [\n 'config' => $this->config,\n 'crm_id' => $crmId,\n ];\n\n try {\n if (! $strategy instanceof HubspotSingleSyncStrategy) {\n throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');\n }\n\n $hsOpportunity = $strategy->fetchOpportunity($parameters);\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException $e) {\n $this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [\n 'teamId' => $this->team->getUuid(),\n 'crmId' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n return null;\n }\n\n $hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);\n\n return $this->importOrUpdateOpportunity($hsOpportunity);\n }\n\n /**\n * Process webhook-collected opportunity batches.\n *\n * Drains Redis sets containing company CRM IDs collected from webhook events\n * and dispatches ImportOpportunityBatch jobs for batch processing.\n *\n * @return int Number of opportunity IDs dispatched to jobs\n */\n public function batchSyncOpportunities(): int\n {\n $configId = $this->team->getCrmConfiguration()->getId();\n\n return $this->batchProcessor->processBatchesForObjectType(\n WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,\n $configId\n );\n }\n\n /**\n * Import a batch of opportunities by their CRM IDs.\n * Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().\n *\n * @param array<string> $crmIds HubSpot deal CRM IDs\n *\n * @return array{success: array, failed_ids: array, errors?: array<string, string>}\n */\n public function importOpportunityBatchByIds(array $crmIds): array\n {\n $fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);\n\n $allDeals = [];\n foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {\n $deals = $this->client->getOpportunitiesByIds($chunk, $fields);\n foreach ($deals as $deal) {\n $allDeals[] = $deal;\n }\n }\n\n // IDs not returned by HubSpot are likely deleted or inaccessible deals.\n // These are not failures — retrying won't bring them back.\n $fetchedIds = array_map('strval', array_column($allDeals, 'id'));\n $notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));\n\n if (! empty($notFoundIds)) {\n $this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [\n 'teamId' => $this->team->getId(),\n 'notFoundCount' => \\count($notFoundIds),\n 'notFoundIds' => $notFoundIds,\n 'requestedCount' => \\count($crmIds),\n 'fetchedCount' => \\count($allDeals),\n ]);\n }\n\n if (empty($allDeals)) {\n return ['success' => [], 'failed_ids' => []];\n }\n\n return $this->importOpportunityBatch($allDeals);\n }\n\n private function getClosedDealStages(): array\n {\n if ($this->cachedClosedDealStages !== null) {\n return $this->cachedClosedDealStages;\n }\n\n $stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);\n $data = [\n 'lost' => [],\n 'won' => [],\n ];\n\n foreach ($stages as $stage) {\n if ($stage->probability == 0.00) {\n $data['lost'][] = $stage->crm_provider_id;\n }\n if ($stage->probability == 100.00) {\n $data['won'][] = $stage->crm_provider_id;\n }\n }\n\n $this->cachedClosedDealStages = $data;\n\n return $data;\n }\n\n /**\n * Import deals into the database with pre-fetched associations.\n *\n * API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT\n * caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()\n * where Laravel retries the whole job with backoff. After all retries exhausted,\n * failed() requeues all IDs to Redis.\n *\n * The per-deal loop catches exceptions individually. A deal can end up in three states:\n * - success: imported/updated successfully\n * - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)\n * These are permanent issues — retrying won't fix them.\n * - skipped (null): missing dependencies (no account, unknown pipeline/stage).\n * This is acceptable — the deal cannot be imported until those exist.\n */\n private function importOpportunityBatch(array $deals): array\n {\n $syncedOpportunities = [\n 'success' => [],\n 'failed_ids' => [],\n ];\n $dealIds = array_column($deals, 'id');\n\n // Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the\n // queue job retries the whole batch and eventually requeues all deal IDs back to Redis.\n try {\n $companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');\n $contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');\n\n $associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);\n\n $existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(\n $this->config,\n array_map('strval', $dealIds)\n );\n $existingCrmIdSet = array_flip($existingCrmIds);\n } catch (\\Throwable $e) {\n $this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [\n 'teamId' => $this->team->getId(),\n 'dealCount' => count($dealIds),\n 'error' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n foreach ($deals as $deal) {\n try {\n $deal['associations'] = $this->prepareAssociationsForOpportunity(\n $deal['id'],\n $companyAssociations,\n $contactAssociations,\n $associationsData\n );\n\n $syncedOpportunity = $this->importOrUpdateOpportunity(\n $deal,\n isset($existingCrmIdSet[(string) $deal['id']])\n );\n if ($syncedOpportunity) {\n $syncedOpportunities['success'][] = $syncedOpportunity;\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [\n 'teamId' => $this->team->getId(),\n 'crmId' => $deal['id'],\n 'error' => $e->getMessage(),\n ]);\n $syncedOpportunities['failed_ids'][] = $deal['id'];\n $syncedOpportunities['errors'][$deal['id']] = $e->getMessage();\n }\n }\n\n return $syncedOpportunities;\n }\n\n /**\n * Prepare associated entities for opportunities with optimized batch processing\n * Returns structured data with CRM ID to DB ID mappings for each opportunity\n */\n private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array\n {\n // Step 1: Collect all unique company and contact IDs from associations\n $allCompanyIds = $this->flattenAssociationIds($companyAssociations);\n $allContactIds = $this->flattenAssociationIds($contactAssociations);\n\n // Step 2: Batch sync missing entities and get CRM ID to DB ID mappings\n $companyIdMappings = [];\n $contactIdMappings = [];\n\n if (! empty($allCompanyIds)) {\n $companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);\n }\n\n if (! empty($allContactIds)) {\n $contactIdMappings = $this->prepareAssociatedContacts($allContactIds);\n }\n\n return [\n 'company_id_mappings' => $companyIdMappings,\n 'contact_id_mappings' => $contactIdMappings,\n ];\n }\n\n /**\n * Flatten association data to get unique IDs\n */\n private function flattenAssociationIds(array $associations): array\n {\n $ids = [];\n foreach ($associations as $dealAssociations) {\n if (is_array($dealAssociations)) {\n foreach ($dealAssociations as $id) {\n $ids[$id] = true;\n }\n }\n }\n\n return array_keys($ids);\n }\n\n /**\n * Batch sync missing accounts\n */\n private function prepareAssociatedAccounts(array $companyIds): array\n {\n // Find which accounts already exist\n $existingAccounts = $this->crmEntityRepository\n ->findAccountsByExternalIds($this->config, $companyIds);\n\n $existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();\n\n $existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {\n return [$account->getCrmProviderId() => $account->getId()];\n })->toArray();\n\n $missingCompanyIds = array_diff($companyIds, $existingCompanyIds);\n\n if (empty($missingCompanyIds)) {\n return $existingAccountsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [\n 'teamId' => $this->team->getUuid(),\n 'total_companies' => count($companyIds),\n 'existing_companies' => count($existingCompanyIds),\n 'missing_companies' => count($missingCompanyIds),\n ]);\n\n // we already have limit on opportunity ids count\n // Initialize variable before try block\n $syncedAccountsData = [];\n\n try {\n $syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [\n 'size' => count($missingCompanyIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedAccountsData = [];\n }\n\n return $existingAccountsData + $syncedAccountsData;\n }\n\n /**\n * Prepare associated contacts - find existing and sync missing ones\n * Returns mapping of CRM ID to DB ID\n */\n private function prepareAssociatedContacts(array $contactIds): array\n {\n // Find which contacts already exist\n $existingContacts = $this->crmEntityRepository\n ->findContactsByExternalIds($this->config, $contactIds);\n\n $existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();\n\n // Create mapping for existing contacts\n $existingContactsData = $existingContacts->mapWithKeys(function ($contact) {\n return [$contact->getCrmProviderId() => $contact->getId()];\n })->toArray();\n\n $missingContactIds = array_diff($contactIds, $existingContactIds);\n\n if (empty($missingContactIds)) {\n return $existingContactsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [\n 'teamId' => $this->team->getUuid(),\n 'total_contacts' => count($contactIds),\n 'existing_contacts' => count($existingContactIds),\n 'missing_contacts' => count($missingContactIds),\n ]);\n\n // Sync missing contacts using batch API\n try {\n $syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [\n 'size' => count($missingContactIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedContactsData = [];\n }\n\n return $existingContactsData + $syncedContactsData;\n }\n\n private function batchSyncCrmObjects(string $objectType, array $crmIds): array\n {\n $syncObjects = [];\n $crmObjectIds = array_values($crmIds);\n\n foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {\n try {\n $objects = $objectType === 'companies' ?\n $this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :\n $this->client->getContactsByIds($chunk, $this->getContactFields());\n\n foreach ($objects as $objectId => $objectData) {\n $this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [\n 'requested_count' => count($chunk),\n 'synced_count' => count($objects),\n ]);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [\n 'ids' => $chunk,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n return $syncObjects;\n }\n\n private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void\n {\n try {\n $object = $objectType === 'companies' ?\n $this->importAccount($objectData) :\n $this->importContact($objectData);\n\n if ($object) {\n $syncObjects[$object->getCrmProviderId()] = $object->getId();\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [\n 'id' => $objectId,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n /**\n * Prepare associations for a single opportunity\n *\n * The return value is an array with the following structure:\n * [\n * 'companies' => [\n * $companyCrmId => $companyId,\n * ...\n * ],\n * 'contacts' => [\n * $contactCrmId => $contactId,\n * ...\n * ],\n * 'account_id' => $accountId,\n * ]\n */\n private function prepareAssociationsForOpportunity(\n string $oppCrmId,\n array $companyAssociations,\n array $contactAssociations,\n array $associationsData\n ): array {\n $associations = [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n\n $oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];\n foreach ($oppCompanyIds as $companyCrmId) {\n if (isset($associationsData['company_id_mappings'][$companyCrmId])) {\n $associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];\n\n // Set primary account (first company becomes primary account)\n if ($associations['account_id'] === null) {\n $associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];\n }\n }\n }\n\n $oppContactIds = $contactAssociations[$oppCrmId] ?? [];\n foreach ($oppContactIds as $contactCrmId) {\n if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {\n $associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];\n }\n }\n\n return $associations;\n }\n\n /**\n * Update only associations for an opportunity\n */\n private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void\n {\n // Update contact associations\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n // Update company (account) associations\n $this->updateOpportunityAccount($opportunity, $associations['account_id']);\n }\n\n /**\n * Remove all contact associations from an opportunity\n */\n private function removeAllOpportunityContacts(Opportunity $opportunity): void\n {\n $currentCount = (int) $opportunity->contacts()->count();\n\n if ($currentCount > 0) {\n $opportunity->contacts()->detach();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_count' => $currentCount,\n ]);\n }\n }\n\n private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void\n {\n if ($accountId === null) {\n // No account ID provided - keep current account\n return;\n }\n\n $currentAccountId = $opportunity->getAccountId();\n\n // Only update if account has changed\n if ($currentAccountId !== $accountId) {\n $opportunity->account_id = $accountId;\n $opportunity->save();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [\n 'opportunity_id' => $opportunity->getId(),\n 'old_account_id' => $currentAccountId,\n 'new_account_id' => $accountId,\n ]);\n }\n }\n\n /**\n * Find existing opportunities by external IDs (OPTIMIZED VERSION)\n * Uses batch query for better performance\n */\n private function findExistingOpportunities(array $crmIds): Collection\n {\n return $this->crmEntityRepository\n ->findOpportunitiesByExternalIds($this->config, $crmIds);\n }\n\n private function processOpportunityBatch(array $opportunities): int\n {\n $syncedOpportunities = $this->importOpportunityBatch($opportunities);\n\n return count($syncedOpportunities['success'] ?? []);\n }\n\n /**\n * Convert single deal associations from HubSpot format to internal format\n * Handles both HubSpot SDK objects and array formats\n *\n * @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed\n *\n * @return array Processed associations with DB IDs\n */\n private function convertDealAssociations(array $opportunityAssociations): array\n {\n $associations = $this->initializeAssociationsStructure();\n\n if (empty($opportunityAssociations)) {\n return $associations;\n }\n\n $associationIds = $this->extractAssociationIds($opportunityAssociations);\n\n $this->processCompanyAssociations($associationIds, $associations);\n $this->processContactAssociations($associationIds, $associations);\n\n return $associations;\n }\n\n private function initializeAssociationsStructure(): array\n {\n return [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n }\n\n private function extractAssociationIds(array $opportunityAssociations): array\n {\n $associationIds = [];\n\n foreach ($opportunityAssociations as $type => $associationData) {\n if (! empty($associationData)) {\n $associationIds[$type] = $this->convertSingleDealAssociations($associationData);\n }\n }\n\n return $associationIds;\n }\n\n private function processCompanyAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['companies'])) {\n return;\n }\n\n $companyId = $associationIds['companies'][0];\n $account = $this->findOrSyncAccount($companyId);\n\n if ($account instanceof Account) {\n $associations['companies'][$companyId] = $account->getId();\n $associations['account_id'] = $account->getId();\n }\n }\n\n private function processContactAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['contacts'])) {\n return;\n }\n\n foreach ($associationIds['contacts'] as $contactId) {\n $contact = $this->findOrSyncContact($contactId);\n\n if ($contact instanceof Contact) {\n $associations['contacts'][$contactId] = $contact->getId();\n }\n }\n }\n\n private function findOrSyncAccount(string $companyId): ?Account\n {\n $account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);\n\n if (! $account instanceof Account) {\n $account = $this->syncAccount($companyId);\n }\n\n return $account;\n }\n\n private function findOrSyncContact(string $contactId): ?Contact\n {\n $contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);\n\n if (! $contact instanceof Contact) {\n $contact = $this->syncContact($contactId);\n }\n\n return $contact;\n }\n\n private function convertSingleDealAssociations($opportunityAssociations = null): array\n {\n $associationData = [];\n\n if ($opportunityAssociations === null) {\n return $associationData;\n }\n\n // Handle array input (from extractAssociationIds)\n if (is_array($opportunityAssociations)) {\n return $opportunityAssociations;\n }\n\n // Handle CollectionResponseAssociatedId object\n if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {\n foreach ($opportunityAssociations->getResults() as $association) {\n $associationData[] = $association->getId();\n }\n }\n\n return $associationData;\n }\n\n private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity\n {\n if (empty($crmData['properties'])) {\n return null;\n }\n\n $crmId = (string) $crmData['id'];\n $properties = $crmData['properties'];\n $associations = $crmData['associations'] ?? [];\n\n $opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(\n $this->config,\n $crmId\n );\n\n if ($opportunityExists) {\n return $this->updateOpportunity($crmId, $properties, $associations);\n }\n\n return $this->createOpportunity($crmId, $properties, $associations);\n }\n\n /**\n * Create new opportunity\n */\n private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n if (! $accountId) {\n return null;\n }\n\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n if (! $businessProcess) {\n return null;\n }\n\n $stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);\n if (! $stage) {\n return null;\n }\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n if ($opportunity->wasRecentlyCreated) {\n MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());\n }\n\n return $opportunity;\n }\n\n /**\n * Update existing opportunity\n */\n private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n $stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->updateOpportunityAssociations($opportunity, $associations);\n\n return $opportunity;\n }\n\n private function resolveAccountId(array $associations): ?int\n {\n if (! empty($associations['account_id'])) {\n return $associations['account_id'];\n }\n\n if (empty($associations)) {\n return null;\n }\n\n // Fallback: use first company as account (currently SDK returns one company)\n foreach ($associations['companies'] as $accountId) {\n return $accountId;\n }\n\n return null;\n }\n\n private function buildOpportunityData(\n array $properties,\n ?int $accountId,\n ?BusinessProcess $businessProcess,\n ?Stage $stage\n ): array {\n $ownerId = null;\n $profile = null;\n if (! empty($properties['hubspot_owner_id'])) {\n $ownerId = $properties['hubspot_owner_id'];\n $profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);\n }\n\n $name = 'Unknown';\n if (isset($properties['dealname'])) {\n $name = mb_strimwidth($properties['dealname'], 0, 128);\n }\n\n $amount = $this->resolveAmount($properties);\n $currency = $properties['deal_currency_code'] ?? null;\n\n $closeDate = null;\n if (! empty($properties['closedate'])) {\n $closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');\n }\n\n $remotelyCreatedAt = null;\n if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {\n $date = $this->parseCleanDatetime($properties['createdate']);\n $remotelyCreatedAt = $date?->format('Y-m-d H:i:s');\n }\n\n $closedStages = $this->getClosedDealStages();\n $isWon = in_array($properties['dealstage'], $closedStages['won']);\n $isLost = in_array($properties['dealstage'], $closedStages['lost']);\n\n $data = [\n 'team_id' => $this->team->getId(),\n 'user_id' => $profile ? $profile->user_id : null,\n 'owner_id' => $ownerId,\n 'name' => $name,\n 'value' => ! empty($amount) ? $amount : null,\n 'currency_code' => CurrencyFormatter::formatCode($currency),\n 'close_date' => $closeDate,\n 'is_closed' => $isWon || $isLost,\n 'is_won' => $isWon,\n 'remotely_created_at' => $remotelyCreatedAt,\n 'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),\n 'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),\n ];\n\n if ($accountId) {\n $data['account_id'] = $accountId;\n }\n\n if ($stage) {\n $data['stage_id'] = $stage->id;\n }\n\n if ($businessProcess) {\n $recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);\n if ($recordType) {\n $data['record_type_id'] = $recordType->id;\n }\n }\n\n return $data;\n }\n\n private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess\n {\n if ($pipelineId === null) {\n return null;\n }\n\n $cacheKey = $this->getBusinessProcessCacheKey($pipelineId);\n if (isset($this->cachedBusinessProcesses[$cacheKey])) {\n return $this->cachedBusinessProcesses[$cacheKey];\n }\n\n $businessProcess = $this->getBusinessProcess($pipelineId);\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->importStages();\n $businessProcess = $this->getBusinessProcess($pipelineId);\n }\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->logger->info(\n '[HubSpot] Deal is not attached to a pipeline',\n [\n 'pipeline' => $pipelineId]\n );\n }\n\n $this->cachedBusinessProcesses[$cacheKey] = $businessProcess;\n\n return $businessProcess;\n }\n\n private function getBusinessProcess(string $pipelineId): ?BusinessProcess\n {\n return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);\n }\n\n private function getBusinessProcessCacheKey(string $pipelineId): string\n {\n return $this->config->getId() . '_' . $pipelineId;\n }\n\n private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage\n {\n if (empty($stageId)) {\n return null;\n }\n\n $cacheKey = $businessProcess->getId() . ':' . $stageId;\n if (isset($this->cachedStages[$cacheKey])) {\n return $this->cachedStages[$cacheKey];\n }\n\n $stage = $this->crmEntityRepository->getPipelineStageByConditions(\n $businessProcess,\n [\n 'crm_provider_id' => $stageId,\n 'type' => Stage::TYPE_OPPORTUNITY,\n ]\n );\n\n if ($stage === null) {\n $this->importStages(null, $stageId);\n }\n\n if ($stage === null) {\n $this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);\n }\n\n $this->cachedStages[$cacheKey] = $stage;\n\n return $stage;\n }\n\n private function resolveAmount(array $properties): ?string\n {\n $amount = null;\n if (! empty($properties['amount'])) {\n $amount = str_replace(',', '', $properties['amount']);\n }\n\n if ($this->config->hasDefaultCurrencyFieldSet()) {\n $valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();\n $amount = $properties[$valueFieldName] ?? $amount;\n }\n\n return $amount;\n }\n\n private function parseCleanDatetime(string $datetime): ?Carbon\n {\n // Treat pre-1980 values as invalid\n $minValidDate = Carbon::parse('1980-01-01 00:00:00');\n\n try {\n $date = Carbon::parse($datetime);\n\n if ($minValidDate->gt($date)) {\n return null;\n }\n\n return $date;\n } catch (Exception) {\n return null; // On parse error, treat as null\n }\n }\n\n private function resolveDealProbability(?string $stageProbability): int\n {\n if ($stageProbability === null) {\n return 0;\n }\n\n $probability = (float) $stageProbability;\n\n return $probability > 1 ? 0 : (int) ($probability * 100);\n }\n\n private function resolveForecastCategory(?string $forecastCategory): string\n {\n if (! $forecastCategory) {\n return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;\n }\n\n $forecastCategory = str_replace('_', ' ', $forecastCategory);\n\n return ucwords(strtolower($forecastCategory));\n }\n\n private function importExternalFieldData(array $properties, int $opportunityId): void\n {\n $crmFields = $this->getOpportunitySyncableFields();\n $this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);\n }\n\n private function importOpportunityContacts(Opportunity $opportunity, array $associations): void\n {\n // Handle empty or missing contact associations\n if (empty($associations)) {\n // Remove all existing contact associations if none provided\n $this->removeAllOpportunityContacts($opportunity);\n\n return;\n }\n\n // Use differential sync approach for better performance and accuracy\n $this->syncOpportunityContactsDifferential($opportunity, $associations);\n }\n\n /**\n * Sync opportunity contacts using differential approach\n * This compares current vs new associations and only makes necessary changes\n */\n private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void\n {\n $currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);\n $contactAssociationIds = array_keys($contactAssociations);\n\n $contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);\n $contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);\n\n if (empty($contactsToAdd) && empty($contactsToRemove)) {\n return;\n }\n\n $this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);\n\n $this->removeContactAssociations($opportunity, $contactsToRemove);\n $this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);\n }\n\n private function getCurrentContactCrmIds(Opportunity $opportunity): array\n {\n return $opportunity->contacts()\n ->pluck('contacts.crm_provider_id')\n ->toArray();\n }\n\n private function logContactAssociationChanges(\n Opportunity $opportunity,\n array $currentContactCrmIds,\n array $contactAssociations,\n array $contactsToAdd,\n array $contactsToRemove\n ): void {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [\n 'opportunity_id' => $opportunity->getId(),\n 'current_contacts' => $currentContactCrmIds,\n 'new_contacts' => $contactAssociations,\n 'contacts_to_add' => $contactsToAdd,\n 'contacts_to_remove' => $contactsToRemove,\n ]);\n }\n\n private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void\n {\n if (empty($contactsToRemove)) {\n return;\n }\n\n $contactsToDetach = $opportunity->contacts()\n ->whereIn('contacts.crm_provider_id', $contactsToRemove)\n ->pluck('contacts.id')\n ->toArray();\n\n if (! empty($contactsToDetach)) {\n $opportunity->contacts()->detach($contactsToDetach);\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_contact_crm_ids' => $contactsToRemove,\n 'removed_contact_count' => count($contactsToDetach),\n ]);\n }\n }\n\n private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void\n {\n if (empty($contactsToAdd)) {\n return;\n }\n\n $contactsAdded = [];\n foreach ($contactsToAdd as $crmId) {\n $id = $contactAssociations[$crmId];\n\n if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {\n $contactsAdded[] = $crmId;\n }\n }\n\n $this->logAddedContacts($opportunity, $contactsAdded);\n }\n\n private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool\n {\n try {\n $contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);\n\n if (! $contact) {\n return false;\n }\n\n return $this->performContactAttachment($opportunity, $contact, $crmId);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [\n 'opportunity_id' => $opportunity->getId(),\n 'contact_crm_id' => $crmId,\n 'error' => $e->getMessage(),\n ]);\n\n return false;\n }\n }\n\n private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool\n {\n try {\n $opportunity->contacts()->attach($contact->getId(), [\n 'crm_provider_id' => $crmId,\n ]);\n\n return true;\n } catch (\\Illuminate\\Database\\QueryException $e) {\n if (str_contains($e->getMessage(), 'Duplicate entry')) {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [\n 'contact_id' => $contact->getId(),\n 'contact_crm_id' => $crmId,\n 'opportunity_id' => $opportunity->getId(),\n ]);\n\n return false;\n }\n\n throw $e;\n }\n }\n\n private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void\n {\n if (! empty($contactsAdded)) {\n $this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'added_contact_crm_ids' => $contactsAdded,\n 'added_contacts_count' => count($contactsAdded),\n ]);\n }\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot\\ServiceTraits;\n\nuse Carbon\\Carbon;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\CollectionResponseAssociatedId;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Models\\Account;\nuse Exception;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Jobs\\Crm\\MatchActivitiesToNewOpportunity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\BusinessProcess;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Models\\Opportunity;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Repositories\\Crm\\CrmEntityRepository;\nuse Jiminny\\Services\\Crm\\Hubspot\\DealFieldsService;\nuse Jiminny\\Services\\Crm\\Hubspot\\OpportunitySyncStrategy\\HubspotSingleSyncStrategy;\nuse Jiminny\\Services\\Crm\\Hubspot\\WebhookSyncBatchProcessor;\nuse Jiminny\\Services\\Crm\\OpportunitySyncStrategyResolver;\nuse Jiminny\\Utils\\CurrencyFormatter;\n\n/**\n * Optimized sync methods for better performance\n * These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains\n */\ntrait OpportunitySyncTrait\n{\n private const int BATCH_SIZE = 100;\n private const int BATCH_PROCESS_SIZE = 800;\n\n protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;\n protected CrmEntityRepository $crmEntityRepository;\n protected DealFieldsService $dealFieldsService;\n\n private ?array $cachedClosedDealStages = null;\n private array $cachedBusinessProcesses = [];\n private array $cachedStages = [];\n\n public function syncOpportunities(array $parameters, ?string $strategy = null): int\n {\n $startTime = microtime(true);\n $strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);\n $parameters['config'] = $this->config;\n $syncCount = 0;\n $reportedTotal = 0;\n $lastSyncedId = [];\n $strategyNames = [];\n\n try {\n foreach ($strategies as $strategyName => $syncStrategy) {\n $strategyNames[] = $strategyName;\n $this->logger->info(\n '[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,\n ['team' => $this->team->getId()]\n );\n\n $total = 0;\n $lastId = null;\n $buffer = [];\n\n // HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies\n foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {\n $buffer[] = $hsOpportunity;\n\n // process every 800 rows (fits < 1 000 association limit)\n if (\\count($buffer) >= self::BATCH_PROCESS_SIZE) {\n $syncCount += $this->processOpportunityBatch($buffer);\n $buffer = [];\n }\n }\n\n // leftovers\n if ($buffer) {\n $syncCount += $this->processOpportunityBatch($buffer);\n }\n\n $reportedTotal += $total;\n $lastSyncedId = $lastId;\n }\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException | CrmException $e) {\n $this->handleSyncException($e, $parameters);\n }\n\n $durationMs = round((microtime(true) - $startTime) * 1000, 2);\n $this->logger->info(\n '[HubSpot] Synced opportunities',\n [\n 'team' => $this->team->getId(),\n 'strategies' => implode(',', $strategyNames),\n 'sync_count' => $syncCount,\n 'total' => $reportedTotal,\n 'last_synced_id' => $lastSyncedId,\n 'duration_ms' => $durationMs,\n ]\n );\n\n return $reportedTotal;\n }\n\n private function handleSyncException(\\Throwable $e, array $parameters): void\n {\n if (($parameters['since'] ?? null) instanceof Carbon) {\n $parameters['since'] = $parameters['since']->toDateTimeString();\n }\n $parameters['config'] = $this->config->getId();\n\n $this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [\n 'teamId' => $this->team->getUuid(),\n 'parameters' => $parameters,\n 'reason' => $e->getMessage(),\n ]);\n }\n\n /**\n * @inheritdoc\n */\n public function syncOpportunity(string $crmId): ?Opportunity\n {\n $strategy = $this->opportunitySyncStrategyResolver->resolve(\n $this->config,\n OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,\n );\n\n $parameters = [\n 'config' => $this->config,\n 'crm_id' => $crmId,\n ];\n\n try {\n if (! $strategy instanceof HubspotSingleSyncStrategy) {\n throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');\n }\n\n $hsOpportunity = $strategy->fetchOpportunity($parameters);\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException $e) {\n $this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [\n 'teamId' => $this->team->getUuid(),\n 'crmId' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n return null;\n }\n\n $hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);\n\n return $this->importOrUpdateOpportunity($hsOpportunity);\n }\n\n /**\n * Process webhook-collected opportunity batches.\n *\n * Drains Redis sets containing company CRM IDs collected from webhook events\n * and dispatches ImportOpportunityBatch jobs for batch processing.\n *\n * @return int Number of opportunity IDs dispatched to jobs\n */\n public function batchSyncOpportunities(): int\n {\n $configId = $this->team->getCrmConfiguration()->getId();\n\n return $this->batchProcessor->processBatchesForObjectType(\n WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,\n $configId\n );\n }\n\n /**\n * Import a batch of opportunities by their CRM IDs.\n * Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().\n *\n * @param array<string> $crmIds HubSpot deal CRM IDs\n *\n * @return array{success: array, failed_ids: array, errors?: array<string, string>}\n */\n public function importOpportunityBatchByIds(array $crmIds): array\n {\n $fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);\n\n $allDeals = [];\n foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {\n $deals = $this->client->getOpportunitiesByIds($chunk, $fields);\n foreach ($deals as $deal) {\n $allDeals[] = $deal;\n }\n }\n\n // IDs not returned by HubSpot are likely deleted or inaccessible deals.\n // These are not failures — retrying won't bring them back.\n $fetchedIds = array_map('strval', array_column($allDeals, 'id'));\n $notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));\n\n if (! empty($notFoundIds)) {\n $this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [\n 'teamId' => $this->team->getId(),\n 'notFoundCount' => \\count($notFoundIds),\n 'notFoundIds' => $notFoundIds,\n 'requestedCount' => \\count($crmIds),\n 'fetchedCount' => \\count($allDeals),\n ]);\n }\n\n if (empty($allDeals)) {\n return ['success' => [], 'failed_ids' => []];\n }\n\n return $this->importOpportunityBatch($allDeals);\n }\n\n private function getClosedDealStages(): array\n {\n if ($this->cachedClosedDealStages !== null) {\n return $this->cachedClosedDealStages;\n }\n\n $stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);\n $data = [\n 'lost' => [],\n 'won' => [],\n ];\n\n foreach ($stages as $stage) {\n if ($stage->probability == 0.00) {\n $data['lost'][] = $stage->crm_provider_id;\n }\n if ($stage->probability == 100.00) {\n $data['won'][] = $stage->crm_provider_id;\n }\n }\n\n $this->cachedClosedDealStages = $data;\n\n return $data;\n }\n\n /**\n * Import deals into the database with pre-fetched associations.\n *\n * API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT\n * caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()\n * where Laravel retries the whole job with backoff. After all retries exhausted,\n * failed() requeues all IDs to Redis.\n *\n * The per-deal loop catches exceptions individually. A deal can end up in three states:\n * - success: imported/updated successfully\n * - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)\n * These are permanent issues — retrying won't fix them.\n * - skipped (null): missing dependencies (no account, unknown pipeline/stage).\n * This is acceptable — the deal cannot be imported until those exist.\n */\n private function importOpportunityBatch(array $deals): array\n {\n $syncedOpportunities = [\n 'success' => [],\n 'failed_ids' => [],\n ];\n $dealIds = array_column($deals, 'id');\n\n // Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the\n // queue job retries the whole batch and eventually requeues all deal IDs back to Redis.\n try {\n $companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');\n $contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');\n\n $associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);\n\n $existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(\n $this->config,\n array_map('strval', $dealIds)\n );\n $existingCrmIdSet = array_flip($existingCrmIds);\n } catch (\\Throwable $e) {\n $this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [\n 'teamId' => $this->team->getId(),\n 'dealCount' => count($dealIds),\n 'error' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n foreach ($deals as $deal) {\n try {\n $deal['associations'] = $this->prepareAssociationsForOpportunity(\n $deal['id'],\n $companyAssociations,\n $contactAssociations,\n $associationsData\n );\n\n $syncedOpportunity = $this->importOrUpdateOpportunity(\n $deal,\n isset($existingCrmIdSet[(string) $deal['id']])\n );\n if ($syncedOpportunity) {\n $syncedOpportunities['success'][] = $syncedOpportunity;\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [\n 'teamId' => $this->team->getId(),\n 'crmId' => $deal['id'],\n 'error' => $e->getMessage(),\n ]);\n $syncedOpportunities['failed_ids'][] = $deal['id'];\n $syncedOpportunities['errors'][$deal['id']] = $e->getMessage();\n }\n }\n\n return $syncedOpportunities;\n }\n\n /**\n * Prepare associated entities for opportunities with optimized batch processing\n * Returns structured data with CRM ID to DB ID mappings for each opportunity\n */\n private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array\n {\n // Step 1: Collect all unique company and contact IDs from associations\n $allCompanyIds = $this->flattenAssociationIds($companyAssociations);\n $allContactIds = $this->flattenAssociationIds($contactAssociations);\n\n // Step 2: Batch sync missing entities and get CRM ID to DB ID mappings\n $companyIdMappings = [];\n $contactIdMappings = [];\n\n if (! empty($allCompanyIds)) {\n $companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);\n }\n\n if (! empty($allContactIds)) {\n $contactIdMappings = $this->prepareAssociatedContacts($allContactIds);\n }\n\n return [\n 'company_id_mappings' => $companyIdMappings,\n 'contact_id_mappings' => $contactIdMappings,\n ];\n }\n\n /**\n * Flatten association data to get unique IDs\n */\n private function flattenAssociationIds(array $associations): array\n {\n $ids = [];\n foreach ($associations as $dealAssociations) {\n if (is_array($dealAssociations)) {\n foreach ($dealAssociations as $id) {\n $ids[$id] = true;\n }\n }\n }\n\n return array_keys($ids);\n }\n\n /**\n * Batch sync missing accounts\n */\n private function prepareAssociatedAccounts(array $companyIds): array\n {\n // Find which accounts already exist\n $existingAccounts = $this->crmEntityRepository\n ->findAccountsByExternalIds($this->config, $companyIds);\n\n $existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();\n\n $existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {\n return [$account->getCrmProviderId() => $account->getId()];\n })->toArray();\n\n $missingCompanyIds = array_diff($companyIds, $existingCompanyIds);\n\n if (empty($missingCompanyIds)) {\n return $existingAccountsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [\n 'teamId' => $this->team->getUuid(),\n 'total_companies' => count($companyIds),\n 'existing_companies' => count($existingCompanyIds),\n 'missing_companies' => count($missingCompanyIds),\n ]);\n\n // we already have limit on opportunity ids count\n // Initialize variable before try block\n $syncedAccountsData = [];\n\n try {\n $syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [\n 'size' => count($missingCompanyIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedAccountsData = [];\n }\n\n return $existingAccountsData + $syncedAccountsData;\n }\n\n /**\n * Prepare associated contacts - find existing and sync missing ones\n * Returns mapping of CRM ID to DB ID\n */\n private function prepareAssociatedContacts(array $contactIds): array\n {\n // Find which contacts already exist\n $existingContacts = $this->crmEntityRepository\n ->findContactsByExternalIds($this->config, $contactIds);\n\n $existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();\n\n // Create mapping for existing contacts\n $existingContactsData = $existingContacts->mapWithKeys(function ($contact) {\n return [$contact->getCrmProviderId() => $contact->getId()];\n })->toArray();\n\n $missingContactIds = array_diff($contactIds, $existingContactIds);\n\n if (empty($missingContactIds)) {\n return $existingContactsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [\n 'teamId' => $this->team->getUuid(),\n 'total_contacts' => count($contactIds),\n 'existing_contacts' => count($existingContactIds),\n 'missing_contacts' => count($missingContactIds),\n ]);\n\n // Sync missing contacts using batch API\n try {\n $syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [\n 'size' => count($missingContactIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedContactsData = [];\n }\n\n return $existingContactsData + $syncedContactsData;\n }\n\n private function batchSyncCrmObjects(string $objectType, array $crmIds): array\n {\n $syncObjects = [];\n $crmObjectIds = array_values($crmIds);\n\n foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {\n try {\n $objects = $objectType === 'companies' ?\n $this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :\n $this->client->getContactsByIds($chunk, $this->getContactFields());\n\n foreach ($objects as $objectId => $objectData) {\n $this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [\n 'requested_count' => count($chunk),\n 'synced_count' => count($objects),\n ]);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [\n 'ids' => $chunk,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n return $syncObjects;\n }\n\n private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void\n {\n try {\n $object = $objectType === 'companies' ?\n $this->importAccount($objectData) :\n $this->importContact($objectData);\n\n if ($object) {\n $syncObjects[$object->getCrmProviderId()] = $object->getId();\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [\n 'id' => $objectId,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n /**\n * Prepare associations for a single opportunity\n *\n * The return value is an array with the following structure:\n * [\n * 'companies' => [\n * $companyCrmId => $companyId,\n * ...\n * ],\n * 'contacts' => [\n * $contactCrmId => $contactId,\n * ...\n * ],\n * 'account_id' => $accountId,\n * ]\n */\n private function prepareAssociationsForOpportunity(\n string $oppCrmId,\n array $companyAssociations,\n array $contactAssociations,\n array $associationsData\n ): array {\n $associations = [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n\n $oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];\n foreach ($oppCompanyIds as $companyCrmId) {\n if (isset($associationsData['company_id_mappings'][$companyCrmId])) {\n $associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];\n\n // Set primary account (first company becomes primary account)\n if ($associations['account_id'] === null) {\n $associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];\n }\n }\n }\n\n $oppContactIds = $contactAssociations[$oppCrmId] ?? [];\n foreach ($oppContactIds as $contactCrmId) {\n if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {\n $associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];\n }\n }\n\n return $associations;\n }\n\n /**\n * Update only associations for an opportunity\n */\n private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void\n {\n // Update contact associations\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n // Update company (account) associations\n $this->updateOpportunityAccount($opportunity, $associations['account_id']);\n }\n\n /**\n * Remove all contact associations from an opportunity\n */\n private function removeAllOpportunityContacts(Opportunity $opportunity): void\n {\n $currentCount = (int) $opportunity->contacts()->count();\n\n if ($currentCount > 0) {\n $opportunity->contacts()->detach();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_count' => $currentCount,\n ]);\n }\n }\n\n private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void\n {\n if ($accountId === null) {\n // No account ID provided - keep current account\n return;\n }\n\n $currentAccountId = $opportunity->getAccountId();\n\n // Only update if account has changed\n if ($currentAccountId !== $accountId) {\n $opportunity->account_id = $accountId;\n $opportunity->save();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [\n 'opportunity_id' => $opportunity->getId(),\n 'old_account_id' => $currentAccountId,\n 'new_account_id' => $accountId,\n ]);\n }\n }\n\n /**\n * Find existing opportunities by external IDs (OPTIMIZED VERSION)\n * Uses batch query for better performance\n */\n private function findExistingOpportunities(array $crmIds): Collection\n {\n return $this->crmEntityRepository\n ->findOpportunitiesByExternalIds($this->config, $crmIds);\n }\n\n private function processOpportunityBatch(array $opportunities): int\n {\n $syncedOpportunities = $this->importOpportunityBatch($opportunities);\n\n return count($syncedOpportunities['success'] ?? []);\n }\n\n /**\n * Convert single deal associations from HubSpot format to internal format\n * Handles both HubSpot SDK objects and array formats\n *\n * @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed\n *\n * @return array Processed associations with DB IDs\n */\n private function convertDealAssociations(array $opportunityAssociations): array\n {\n $associations = $this->initializeAssociationsStructure();\n\n if (empty($opportunityAssociations)) {\n return $associations;\n }\n\n $associationIds = $this->extractAssociationIds($opportunityAssociations);\n\n $this->processCompanyAssociations($associationIds, $associations);\n $this->processContactAssociations($associationIds, $associations);\n\n return $associations;\n }\n\n private function initializeAssociationsStructure(): array\n {\n return [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n }\n\n private function extractAssociationIds(array $opportunityAssociations): array\n {\n $associationIds = [];\n\n foreach ($opportunityAssociations as $type => $associationData) {\n if (! empty($associationData)) {\n $associationIds[$type] = $this->convertSingleDealAssociations($associationData);\n }\n }\n\n return $associationIds;\n }\n\n private function processCompanyAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['companies'])) {\n return;\n }\n\n $companyId = $associationIds['companies'][0];\n $account = $this->findOrSyncAccount($companyId);\n\n if ($account instanceof Account) {\n $associations['companies'][$companyId] = $account->getId();\n $associations['account_id'] = $account->getId();\n }\n }\n\n private function processContactAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['contacts'])) {\n return;\n }\n\n foreach ($associationIds['contacts'] as $contactId) {\n $contact = $this->findOrSyncContact($contactId);\n\n if ($contact instanceof Contact) {\n $associations['contacts'][$contactId] = $contact->getId();\n }\n }\n }\n\n private function findOrSyncAccount(string $companyId): ?Account\n {\n $account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);\n\n if (! $account instanceof Account) {\n $account = $this->syncAccount($companyId);\n }\n\n return $account;\n }\n\n private function findOrSyncContact(string $contactId): ?Contact\n {\n $contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);\n\n if (! $contact instanceof Contact) {\n $contact = $this->syncContact($contactId);\n }\n\n return $contact;\n }\n\n private function convertSingleDealAssociations($opportunityAssociations = null): array\n {\n $associationData = [];\n\n if ($opportunityAssociations === null) {\n return $associationData;\n }\n\n // Handle array input (from extractAssociationIds)\n if (is_array($opportunityAssociations)) {\n return $opportunityAssociations;\n }\n\n // Handle CollectionResponseAssociatedId object\n if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {\n foreach ($opportunityAssociations->getResults() as $association) {\n $associationData[] = $association->getId();\n }\n }\n\n return $associationData;\n }\n\n private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity\n {\n if (empty($crmData['properties'])) {\n return null;\n }\n\n $crmId = (string) $crmData['id'];\n $properties = $crmData['properties'];\n $associations = $crmData['associations'] ?? [];\n\n $opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(\n $this->config,\n $crmId\n );\n\n if ($opportunityExists) {\n return $this->updateOpportunity($crmId, $properties, $associations);\n }\n\n return $this->createOpportunity($crmId, $properties, $associations);\n }\n\n /**\n * Create new opportunity\n */\n private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n if (! $accountId) {\n return null;\n }\n\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n if (! $businessProcess) {\n return null;\n }\n\n $stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);\n if (! $stage) {\n return null;\n }\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n if ($opportunity->wasRecentlyCreated) {\n MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());\n }\n\n return $opportunity;\n }\n\n /**\n * Update existing opportunity\n */\n private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n $stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->updateOpportunityAssociations($opportunity, $associations);\n\n return $opportunity;\n }\n\n private function resolveAccountId(array $associations): ?int\n {\n if (! empty($associations['account_id'])) {\n return $associations['account_id'];\n }\n\n if (empty($associations)) {\n return null;\n }\n\n // Fallback: use first company as account (currently SDK returns one company)\n foreach ($associations['companies'] as $accountId) {\n return $accountId;\n }\n\n return null;\n }\n\n private function buildOpportunityData(\n array $properties,\n ?int $accountId,\n ?BusinessProcess $businessProcess,\n ?Stage $stage\n ): array {\n $ownerId = null;\n $profile = null;\n if (! empty($properties['hubspot_owner_id'])) {\n $ownerId = $properties['hubspot_owner_id'];\n $profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);\n }\n\n $name = 'Unknown';\n if (isset($properties['dealname'])) {\n $name = mb_strimwidth($properties['dealname'], 0, 128);\n }\n\n $amount = $this->resolveAmount($properties);\n $currency = $properties['deal_currency_code'] ?? null;\n\n $closeDate = null;\n if (! empty($properties['closedate'])) {\n $closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');\n }\n\n $remotelyCreatedAt = null;\n if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {\n $date = $this->parseCleanDatetime($properties['createdate']);\n $remotelyCreatedAt = $date?->format('Y-m-d H:i:s');\n }\n\n $closedStages = $this->getClosedDealStages();\n $isWon = in_array($properties['dealstage'], $closedStages['won']);\n $isLost = in_array($properties['dealstage'], $closedStages['lost']);\n\n $data = [\n 'team_id' => $this->team->getId(),\n 'user_id' => $profile ? $profile->user_id : null,\n 'owner_id' => $ownerId,\n 'name' => $name,\n 'value' => ! empty($amount) ? $amount : null,\n 'currency_code' => CurrencyFormatter::formatCode($currency),\n 'close_date' => $closeDate,\n 'is_closed' => $isWon || $isLost,\n 'is_won' => $isWon,\n 'remotely_created_at' => $remotelyCreatedAt,\n 'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),\n 'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),\n ];\n\n if ($accountId) {\n $data['account_id'] = $accountId;\n }\n\n if ($stage) {\n $data['stage_id'] = $stage->id;\n }\n\n if ($businessProcess) {\n $recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);\n if ($recordType) {\n $data['record_type_id'] = $recordType->id;\n }\n }\n\n return $data;\n }\n\n private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess\n {\n if ($pipelineId === null) {\n return null;\n }\n\n $cacheKey = $this->getBusinessProcessCacheKey($pipelineId);\n if (isset($this->cachedBusinessProcesses[$cacheKey])) {\n return $this->cachedBusinessProcesses[$cacheKey];\n }\n\n $businessProcess = $this->getBusinessProcess($pipelineId);\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->importStages();\n $businessProcess = $this->getBusinessProcess($pipelineId);\n }\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->logger->info(\n '[HubSpot] Deal is not attached to a pipeline',\n [\n 'pipeline' => $pipelineId]\n );\n }\n\n $this->cachedBusinessProcesses[$cacheKey] = $businessProcess;\n\n return $businessProcess;\n }\n\n private function getBusinessProcess(string $pipelineId): ?BusinessProcess\n {\n return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);\n }\n\n private function getBusinessProcessCacheKey(string $pipelineId): string\n {\n return $this->config->getId() . '_' . $pipelineId;\n }\n\n private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage\n {\n if (empty($stageId)) {\n return null;\n }\n\n $cacheKey = $businessProcess->getId() . ':' . $stageId;\n if (isset($this->cachedStages[$cacheKey])) {\n return $this->cachedStages[$cacheKey];\n }\n\n $stage = $this->crmEntityRepository->getPipelineStageByConditions(\n $businessProcess,\n [\n 'crm_provider_id' => $stageId,\n 'type' => Stage::TYPE_OPPORTUNITY,\n ]\n );\n\n if ($stage === null) {\n $this->importStages(null, $stageId);\n }\n\n if ($stage === null) {\n $this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);\n }\n\n $this->cachedStages[$cacheKey] = $stage;\n\n return $stage;\n }\n\n private function resolveAmount(array $properties): ?string\n {\n $amount = null;\n if (! empty($properties['amount'])) {\n $amount = str_replace(',', '', $properties['amount']);\n }\n\n if ($this->config->hasDefaultCurrencyFieldSet()) {\n $valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();\n $amount = $properties[$valueFieldName] ?? $amount;\n }\n\n return $amount;\n }\n\n private function parseCleanDatetime(string $datetime): ?Carbon\n {\n // Treat pre-1980 values as invalid\n $minValidDate = Carbon::parse('1980-01-01 00:00:00');\n\n try {\n $date = Carbon::parse($datetime);\n\n if ($minValidDate->gt($date)) {\n return null;\n }\n\n return $date;\n } catch (Exception) {\n return null; // On parse error, treat as null\n }\n }\n\n private function resolveDealProbability(?string $stageProbability): int\n {\n if ($stageProbability === null) {\n return 0;\n }\n\n $probability = (float) $stageProbability;\n\n return $probability > 1 ? 0 : (int) ($probability * 100);\n }\n\n private function resolveForecastCategory(?string $forecastCategory): string\n {\n if (! $forecastCategory) {\n return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;\n }\n\n $forecastCategory = str_replace('_', ' ', $forecastCategory);\n\n return ucwords(strtolower($forecastCategory));\n }\n\n private function importExternalFieldData(array $properties, int $opportunityId): void\n {\n $crmFields = $this->getOpportunitySyncableFields();\n $this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);\n }\n\n private function importOpportunityContacts(Opportunity $opportunity, array $associations): void\n {\n // Handle empty or missing contact associations\n if (empty($associations)) {\n // Remove all existing contact associations if none provided\n $this->removeAllOpportunityContacts($opportunity);\n\n return;\n }\n\n // Use differential sync approach for better performance and accuracy\n $this->syncOpportunityContactsDifferential($opportunity, $associations);\n }\n\n /**\n * Sync opportunity contacts using differential approach\n * This compares current vs new associations and only makes necessary changes\n */\n private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void\n {\n $currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);\n $contactAssociationIds = array_keys($contactAssociations);\n\n $contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);\n $contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);\n\n if (empty($contactsToAdd) && empty($contactsToRemove)) {\n return;\n }\n\n $this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);\n\n $this->removeContactAssociations($opportunity, $contactsToRemove);\n $this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);\n }\n\n private function getCurrentContactCrmIds(Opportunity $opportunity): array\n {\n return $opportunity->contacts()\n ->pluck('contacts.crm_provider_id')\n ->toArray();\n }\n\n private function logContactAssociationChanges(\n Opportunity $opportunity,\n array $currentContactCrmIds,\n array $contactAssociations,\n array $contactsToAdd,\n array $contactsToRemove\n ): void {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [\n 'opportunity_id' => $opportunity->getId(),\n 'current_contacts' => $currentContactCrmIds,\n 'new_contacts' => $contactAssociations,\n 'contacts_to_add' => $contactsToAdd,\n 'contacts_to_remove' => $contactsToRemove,\n ]);\n }\n\n private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void\n {\n if (empty($contactsToRemove)) {\n return;\n }\n\n $contactsToDetach = $opportunity->contacts()\n ->whereIn('contacts.crm_provider_id', $contactsToRemove)\n ->pluck('contacts.id')\n ->toArray();\n\n if (! empty($contactsToDetach)) {\n $opportunity->contacts()->detach($contactsToDetach);\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_contact_crm_ids' => $contactsToRemove,\n 'removed_contact_count' => count($contactsToDetach),\n ]);\n }\n }\n\n private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void\n {\n if (empty($contactsToAdd)) {\n return;\n }\n\n $contactsAdded = [];\n foreach ($contactsToAdd as $crmId) {\n $id = $contactAssociations[$crmId];\n\n if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {\n $contactsAdded[] = $crmId;\n }\n }\n\n $this->logAddedContacts($opportunity, $contactsAdded);\n }\n\n private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool\n {\n try {\n $contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);\n\n if (! $contact) {\n return false;\n }\n\n return $this->performContactAttachment($opportunity, $contact, $crmId);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [\n 'opportunity_id' => $opportunity->getId(),\n 'contact_crm_id' => $crmId,\n 'error' => $e->getMessage(),\n ]);\n\n return false;\n }\n }\n\n private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool\n {\n try {\n $opportunity->contacts()->attach($contact->getId(), [\n 'crm_provider_id' => $crmId,\n ]);\n\n return true;\n } catch (\\Illuminate\\Database\\QueryException $e) {\n if (str_contains($e->getMessage(), 'Duplicate entry')) {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [\n 'contact_id' => $contact->getId(),\n 'contact_crm_id' => $crmId,\n 'opportunity_id' => $opportunity->getId(),\n ]);\n\n return false;\n }\n\n throw $e;\n }\n }\n\n private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void\n {\n if (! empty($contactsAdded)) {\n $this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'added_contact_crm_ids' => $contactsAdded,\n 'added_contacts_count' => count($contactsAdded),\n ]);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.5265958,"top":0.074221864,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.5375665,"top":0.074221864,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Script","depth":4,"bounds":{"left":0.54853725,"top":0.074221864,"width":0.020279255,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.94514626,"top":0.09896249,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.95511967,"top":0.09896249,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.9644282,"top":0.09896249,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09736632,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09736632,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"# **************************** HS **************************************\n\nselect * from teams where id = 2; # 2\nselect * from features; # 2\nselect * from team_features where team_id = 2; # 2\nselect * from crm_configurations where id = 2; # 2\nselect * from users where team_id = 2; #\nselect * from playbooks where team_id = 2; # event 38\nselect * from playbook_categories where playbook_id = 38; #\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;\nhttps://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624\n https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0\n\nSELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;\n# 609126 softphone tr. 11241\n\nSELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;\n# 608874 conference tr. 11226 crmId: 103422236596\n\nselect * from ai_prompts where transcription_id IN (11241, 11226);\nselect * from activity_summary_logs where activity_id = 608874;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nselect * from crm_field_data where activity_id = 1223;\n\nselect * from crm_layouts where crm_configuration_id = 2;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);\nselect * from crm_fields where crm_configuration_id = 11 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);\n\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u\n on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 2 and sa.provider = 'hubspot';\n\nselect * from opportunities where team_id = 2\nand crm_provider_id IN ('51317301383');\n\nselect * from contacts where id = 85;\n\nselect * from opportunities where team_id = 2 order by id desc;\nselect * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112\nselect * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112\nselect * from opportunity_contacts where opportunity_id = 5117;\nselect * from crm_field_data where object_id = 1365;\nSELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);\n\nselect * from features;\nselect * from team_features where team_id IN (1);\nselect * from team_features where feature_id IN (36);\n\nSHOW CREATE TABLE opportunity_contacts;\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';\n\n# $slug = 'HUBSPOT_WEBHOOK_SYNC';\n# $team = Jiminny\\Models\\Team::find(2);\n# $feature = Feature::query()->where('slug', $slug)->first();\n# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);\n\n# hubspot_webhook_metrics\n\nselect * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365\nSELECT * FROM opportunity_contacts WHERE opportunity_id = '414';\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';\nselect * from contacts where id in (414, 464);\n\nselect * from activities where crm_configuration_id = 2;\n\nselect settings from crm_configurations where id = 11;\n\nselect * from teams; # 1, 2\nselect * from users;\nselect * from crm_configurations where id = 39;\nselect * from team_features where team_id = 2;\nselect * from features;\n# SELECT * FROM opportunities WHERE crm_configuration_id = 2\n# order by id desc;\n# and crm_provider_id = '49908861993';\n\n\nselect * from activity_providers where id IN (443, 202, 203, 227);\n\nselect * from activity_imports where id = 795889;\n\nselect c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id\nwhere c.provider = 'hubspot';\n\nselect * from crm_configurations crm JOIN teams t on crm.team_id = t.id\nwhere provider = 'hubspot';\nSELECT * FROM teams WHERE id = 31;\nSELECT * FROM users WHERE id = 257;\nSELECT * FROM opportunities WHERE team_id = 2;\n\nselect * from opportunity_contacts where opportunity_id = 5124;\nselect * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)\n\nselect * from activities where crm_configuration_id = 13;\n\nSELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141\n\n\nselect id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;\nSELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 order by id desc;\nselect * from opportunity_contacts where contact_id = 6223;\nSELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;\n\nselect * from crm_profiles where crm_configuration_id = 2;\n\nselect * from activities where account_id = 46;","depth":4,"value":"# **************************** HS **************************************\n\nselect * from teams where id = 2; # 2\nselect * from features; # 2\nselect * from team_features where team_id = 2; # 2\nselect * from crm_configurations where id = 2; # 2\nselect * from users where team_id = 2; #\nselect * from playbooks where team_id = 2; # event 38\nselect * from playbook_categories where playbook_id = 38; #\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;\nhttps://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624\n https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0\n\nSELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;\n# 609126 softphone tr. 11241\n\nSELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;\n# 608874 conference tr. 11226 crmId: 103422236596\n\nselect * from ai_prompts where transcription_id IN (11241, 11226);\nselect * from activity_summary_logs where activity_id = 608874;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nselect * from crm_field_data where activity_id = 1223;\n\nselect * from crm_layouts where crm_configuration_id = 2;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);\nselect * from crm_fields where crm_configuration_id = 11 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);\n\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u\n on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 2 and sa.provider = 'hubspot';\n\nselect * from opportunities where team_id = 2\nand crm_provider_id IN ('51317301383');\n\nselect * from contacts where id = 85;\n\nselect * from opportunities where team_id = 2 order by id desc;\nselect * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112\nselect * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112\nselect * from opportunity_contacts where opportunity_id = 5117;\nselect * from crm_field_data where object_id = 1365;\nSELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);\n\nselect * from features;\nselect * from team_features where team_id IN (1);\nselect * from team_features where feature_id IN (36);\n\nSHOW CREATE TABLE opportunity_contacts;\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';\n\n# $slug = 'HUBSPOT_WEBHOOK_SYNC';\n# $team = Jiminny\\Models\\Team::find(2);\n# $feature = Feature::query()->where('slug', $slug)->first();\n# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);\n\n# hubspot_webhook_metrics\n\nselect * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365\nSELECT * FROM opportunity_contacts WHERE opportunity_id = '414';\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';\nselect * from contacts where id in (414, 464);\n\nselect * from activities where crm_configuration_id = 2;\n\nselect settings from crm_configurations where id = 11;\n\nselect * from teams; # 1, 2\nselect * from users;\nselect * from crm_configurations where id = 39;\nselect * from team_features where team_id = 2;\nselect * from features;\n# SELECT * FROM opportunities WHERE crm_configuration_id = 2\n# order by id desc;\n# and crm_provider_id = '49908861993';\n\n\nselect * from activity_providers where id IN (443, 202, 203, 227);\n\nselect * from activity_imports where id = 795889;\n\nselect c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id\nwhere c.provider = 'hubspot';\n\nselect * from crm_configurations crm JOIN teams t on crm.team_id = t.id\nwhere provider = 'hubspot';\nSELECT * FROM teams WHERE id = 31;\nSELECT * FROM users WHERE id = 257;\nSELECT * FROM opportunities WHERE team_id = 2;\n\nselect * from opportunity_contacts where opportunity_id = 5124;\nselect * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)\n\nselect * from activities where crm_configuration_id = 13;\n\nSELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141\n\n\nselect id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;\nSELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 order by id desc;\nselect * from opportunity_contacts where contact_id = 6223;\nSELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;\n\nselect * from crm_profiles where crm_configuration_id = 2;\n\nselect * from activities where account_id = 46;","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8499420549974583503
|
1048134296968532324
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11976 on JY-20553-debug- Project: faVsco.js, menu
#11976 on JY-20553-debug-crm-sync-delays, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
33
2
19
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot\ServiceTraits;
use Carbon\Carbon;
use HubSpot\Client\Crm\Deals\Model\CollectionResponseAssociatedId;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Models\Account;
use Exception;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Jobs\Crm\MatchActivitiesToNewOpportunity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\BusinessProcess;
use Jiminny\Exceptions\CrmException;
use Jiminny\Models\Opportunity;
use Illuminate\Support\Collection;
use Jiminny\Models\Stage;
use Jiminny\Repositories\Crm\CrmEntityRepository;
use Jiminny\Services\Crm\Hubspot\DealFieldsService;
use Jiminny\Services\Crm\Hubspot\OpportunitySyncStrategy\HubspotSingleSyncStrategy;
use Jiminny\Services\Crm\Hubspot\WebhookSyncBatchProcessor;
use Jiminny\Services\Crm\OpportunitySyncStrategyResolver;
use Jiminny\Utils\CurrencyFormatter;
/**
* Optimized sync methods for better performance
* These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains
*/
trait OpportunitySyncTrait
{
private const int BATCH_SIZE = 100;
private const int BATCH_PROCESS_SIZE = 800;
protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;
protected CrmEntityRepository $crmEntityRepository;
protected DealFieldsService $dealFieldsService;
private ?array $cachedClosedDealStages = null;
private array $cachedBusinessProcesses = [];
private array $cachedStages = [];
public function syncOpportunities(array $parameters, ?string $strategy = null): int
{
$startTime = microtime(true);
$strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);
$parameters['config'] = $this->config;
$syncCount = 0;
$reportedTotal = 0;
$lastSyncedId = [];
$strategyNames = [];
try {
foreach ($strategies as $strategyName => $syncStrategy) {
$strategyNames[] = $strategyName;
$this->logger->info(
'[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,
['team' => $this->team->getId()]
);
$total = 0;
$lastId = null;
$buffer = [];
// HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies
foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {
$buffer[] = $hsOpportunity;
// process every 800 rows (fits < 1 000 association limit)
if (\count($buffer) >= self::BATCH_PROCESS_SIZE) {
$syncCount += $this->processOpportunityBatch($buffer);
$buffer = [];
}
}
// leftovers
if ($buffer) {
$syncCount += $this->processOpportunityBatch($buffer);
}
$reportedTotal += $total;
$lastSyncedId = $lastId;
}
} catch (\HubSpot\Client\Crm\Deals\ApiException | CrmException $e) {
$this->handleSyncException($e, $parameters);
}
$durationMs = round((microtime(true) - $startTime) * 1000, 2);
$this->logger->info(
'[HubSpot] Synced opportunities',
[
'team' => $this->team->getId(),
'strategies' => implode(',', $strategyNames),
'sync_count' => $syncCount,
'total' => $reportedTotal,
'last_synced_id' => $lastSyncedId,
'duration_ms' => $durationMs,
]
);
return $reportedTotal;
}
private function handleSyncException(\Throwable $e, array $parameters): void
{
if (($parameters['since'] ?? null) instanceof Carbon) {
$parameters['since'] = $parameters['since']->toDateTimeString();
}
$parameters['config'] = $this->config->getId();
$this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [
'teamId' => $this->team->getUuid(),
'parameters' => $parameters,
'reason' => $e->getMessage(),
]);
}
/**
* @inheritdoc
*/
public function syncOpportunity(string $crmId): ?Opportunity
{
$strategy = $this->opportunitySyncStrategyResolver->resolve(
$this->config,
OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,
);
$parameters = [
'config' => $this->config,
'crm_id' => $crmId,
];
try {
if (! $strategy instanceof HubspotSingleSyncStrategy) {
throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');
}
$hsOpportunity = $strategy->fetchOpportunity($parameters);
} catch (\HubSpot\Client\Crm\Deals\ApiException $e) {
$this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [
'teamId' => $this->team->getUuid(),
'crmId' => $crmId,
'reason' => $e->getMessage(),
]);
return null;
}
$hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);
return $this->importOrUpdateOpportunity($hsOpportunity);
}
/**
* Process webhook-collected opportunity batches.
*
* Drains Redis sets containing company CRM IDs collected from webhook events
* and dispatches ImportOpportunityBatch jobs for batch processing.
*
* @return int Number of opportunity IDs dispatched to jobs
*/
public function batchSyncOpportunities(): int
{
$configId = $this->team->getCrmConfiguration()->getId();
return $this->batchProcessor->processBatchesForObjectType(
WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,
$configId
);
}
/**
* Import a batch of opportunities by their CRM IDs.
* Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().
*
* @param array<string> $crmIds HubSpot deal CRM IDs
*
* @return array{success: array, failed_ids: array, errors?: array<string, string>}
*/
public function importOpportunityBatchByIds(array $crmIds): array
{
$fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);
$allDeals = [];
foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {
$deals = $this->client->getOpportunitiesByIds($chunk, $fields);
foreach ($deals as $deal) {
$allDeals[] = $deal;
}
}
// IDs not returned by HubSpot are likely deleted or inaccessible deals.
// These are not failures — retrying won't bring them back.
$fetchedIds = array_map('strval', array_column($allDeals, 'id'));
$notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));
if (! empty($notFoundIds)) {
$this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [
'teamId' => $this->team->getId(),
'notFoundCount' => \count($notFoundIds),
'notFoundIds' => $notFoundIds,
'requestedCount' => \count($crmIds),
'fetchedCount' => \count($allDeals),
]);
}
if (empty($allDeals)) {
return ['success' => [], 'failed_ids' => []];
}
return $this->importOpportunityBatch($allDeals);
}
private function getClosedDealStages(): array
{
if ($this->cachedClosedDealStages !== null) {
return $this->cachedClosedDealStages;
}
$stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);
$data = [
'lost' => [],
'won' => [],
];
foreach ($stages as $stage) {
if ($stage->probability == 0.00) {
$data['lost'][] = $stage->crm_provider_id;
}
if ($stage->probability == 100.00) {
$data['won'][] = $stage->crm_provider_id;
}
}
$this->cachedClosedDealStages = $data;
return $data;
}
/**
* Import deals into the database with pre-fetched associations.
*
* API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT
* caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()
* where Laravel retries the whole job with backoff. After all retries exhausted,
* failed() requeues all IDs to Redis.
*
* The per-deal loop catches exceptions individually. A deal can end up in three states:
* - success: imported/updated successfully
* - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)
* These are permanent issues — retrying won't fix them.
* - skipped (null): missing dependencies (no account, unknown pipeline/stage).
* This is acceptable — the deal cannot be imported until those exist.
*/
private function importOpportunityBatch(array $deals): array
{
$syncedOpportunities = [
'success' => [],
'failed_ids' => [],
];
$dealIds = array_column($deals, 'id');
// Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the
// queue job retries the whole batch and eventually requeues all deal IDs back to Redis.
try {
$companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');
$contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');
$associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);
$existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(
$this->config,
array_map('strval', $dealIds)
);
$existingCrmIdSet = array_flip($existingCrmIds);
} catch (\Throwable $e) {
$this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [
'teamId' => $this->team->getId(),
'dealCount' => count($dealIds),
'error' => $e->getMessage(),
]);
throw $e;
}
foreach ($deals as $deal) {
try {
$deal['associations'] = $this->prepareAssociationsForOpportunity(
$deal['id'],
$companyAssociations,
$contactAssociations,
$associationsData
);
$syncedOpportunity = $this->importOrUpdateOpportunity(
$deal,
isset($existingCrmIdSet[(string) $deal['id']])
);
if ($syncedOpportunity) {
$syncedOpportunities['success'][] = $syncedOpportunity;
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [
'teamId' => $this->team->getId(),
'crmId' => $deal['id'],
'error' => $e->getMessage(),
]);
$syncedOpportunities['failed_ids'][] = $deal['id'];
$syncedOpportunities['errors'][$deal['id']] = $e->getMessage();
}
}
return $syncedOpportunities;
}
/**
* Prepare associated entities for opportunities with optimized batch processing
* Returns structured data with CRM ID to DB ID mappings for each opportunity
*/
private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array
{
// Step 1: Collect all unique company and contact IDs from associations
$allCompanyIds = $this->flattenAssociationIds($companyAssociations);
$allContactIds = $this->flattenAssociationIds($contactAssociations);
// Step 2: Batch sync missing entities and get CRM ID to DB ID mappings
$companyIdMappings = [];
$contactIdMappings = [];
if (! empty($allCompanyIds)) {
$companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);
}
if (! empty($allContactIds)) {
$contactIdMappings = $this->prepareAssociatedContacts($allContactIds);
}
return [
'company_id_mappings' => $companyIdMappings,
'contact_id_mappings' => $contactIdMappings,
];
}
/**
* Flatten association data to get unique IDs
*/
private function flattenAssociationIds(array $associations): array
{
$ids = [];
foreach ($associations as $dealAssociations) {
if (is_array($dealAssociations)) {
foreach ($dealAssociations as $id) {
$ids[$id] = true;
}
}
}
return array_keys($ids);
}
/**
* Batch sync missing accounts
*/
private function prepareAssociatedAccounts(array $companyIds): array
{
// Find which accounts already exist
$existingAccounts = $this->crmEntityRepository
->findAccountsByExternalIds($this->config, $companyIds);
$existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();
$existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {
return [$account->getCrmProviderId() => $account->getId()];
})->toArray();
$missingCompanyIds = array_diff($companyIds, $existingCompanyIds);
if (empty($missingCompanyIds)) {
return $existingAccountsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [
'teamId' => $this->team->getUuid(),
'total_companies' => count($companyIds),
'existing_companies' => count($existingCompanyIds),
'missing_companies' => count($missingCompanyIds),
]);
// we already have limit on opportunity ids count
// Initialize variable before try block
$syncedAccountsData = [];
try {
$syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [
'size' => count($missingCompanyIds),
'error' => $e->getMessage(),
]);
$syncedAccountsData = [];
}
return $existingAccountsData + $syncedAccountsData;
}
/**
* Prepare associated contacts - find existing and sync missing ones
* Returns mapping of CRM ID to DB ID
*/
private function prepareAssociatedContacts(array $contactIds): array
{
// Find which contacts already exist
$existingContacts = $this->crmEntityRepository
->findContactsByExternalIds($this->config, $contactIds);
$existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();
// Create mapping for existing contacts
$existingContactsData = $existingContacts->mapWithKeys(function ($contact) {
return [$contact->getCrmProviderId() => $contact->getId()];
})->toArray();
$missingContactIds = array_diff($contactIds, $existingContactIds);
if (empty($missingContactIds)) {
return $existingContactsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [
'teamId' => $this->team->getUuid(),
'total_contacts' => count($contactIds),
'existing_contacts' => count($existingContactIds),
'missing_contacts' => count($missingContactIds),
]);
// Sync missing contacts using batch API
try {
$syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [
'size' => count($missingContactIds),
'error' => $e->getMessage(),
]);
$syncedContactsData = [];
}
return $existingContactsData + $syncedContactsData;
}
private function batchSyncCrmObjects(string $objectType, array $crmIds): array
{
$syncObjects = [];
$crmObjectIds = array_values($crmIds);
foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {
try {
$objects = $objectType === 'companies' ?
$this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :
$this->client->getContactsByIds($chunk, $this->getContactFields());
foreach ($objects as $objectId => $objectData) {
$this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [
'requested_count' => count($chunk),
'synced_count' => count($objects),
]);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [
'ids' => $chunk,
'error' => $e->getMessage(),
]);
}
}
return $syncObjects;
}
private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void
{
try {
$object = $objectType === 'companies' ?
$this->importAccount($objectData) :
$this->importContact($objectData);
if ($object) {
$syncObjects[$object->getCrmProviderId()] = $object->getId();
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [
'id' => $objectId,
'error' => $e->getMessage(),
]);
}
}
/**
* Prepare associations for a single opportunity
*
* The return value is an array with the following structure:
* [
* 'companies' => [
* $companyCrmId => $companyId,
* ...
* ],
* 'contacts' => [
* $contactCrmId => $contactId,
* ...
* ],
* 'account_id' => $accountId,
* ]
*/
private function prepareAssociationsForOpportunity(
string $oppCrmId,
array $companyAssociations,
array $contactAssociations,
array $associationsData
): array {
$associations = [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
$oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];
foreach ($oppCompanyIds as $companyCrmId) {
if (isset($associationsData['company_id_mappings'][$companyCrmId])) {
$associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];
// Set primary account (first company becomes primary account)
if ($associations['account_id'] === null) {
$associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];
}
}
}
$oppContactIds = $contactAssociations[$oppCrmId] ?? [];
foreach ($oppContactIds as $contactCrmId) {
if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {
$associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];
}
}
return $associations;
}
/**
* Update only associations for an opportunity
*/
private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void
{
// Update contact associations
$this->importOpportunityContacts($opportunity, $associations['contacts']);
// Update company (account) associations
$this->updateOpportunityAccount($opportunity, $associations['account_id']);
}
/**
* Remove all contact associations from an opportunity
*/
private function removeAllOpportunityContacts(Opportunity $opportunity): void
{
$currentCount = (int) $opportunity->contacts()->count();
if ($currentCount > 0) {
$opportunity->contacts()->detach();
$this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_count' => $currentCount,
]);
}
}
private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void
{
if ($accountId === null) {
// No account ID provided - keep current account
return;
}
$currentAccountId = $opportunity->getAccountId();
// Only update if account has changed
if ($currentAccountId !== $accountId) {
$opportunity->account_id = $accountId;
$opportunity->save();
$this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [
'opportunity_id' => $opportunity->getId(),
'old_account_id' => $currentAccountId,
'new_account_id' => $accountId,
]);
}
}
/**
* Find existing opportunities by external IDs (OPTIMIZED VERSION)
* Uses batch query for better performance
*/
private function findExistingOpportunities(array $crmIds): Collection
{
return $this->crmEntityRepository
->findOpportunitiesByExternalIds($this->config, $crmIds);
}
private function processOpportunityBatch(array $opportunities): int
{
$syncedOpportunities = $this->importOpportunityBatch($opportunities);
return count($syncedOpportunities['success'] ?? []);
}
/**
* Convert single deal associations from HubSpot format to internal format
* Handles both HubSpot SDK objects and array formats
*
* @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed
*
* @return array Processed associations with DB IDs
*/
private function convertDealAssociations(array $opportunityAssociations): array
{
$associations = $this->initializeAssociationsStructure();
if (empty($opportunityAssociations)) {
return $associations;
}
$associationIds = $this->extractAssociationIds($opportunityAssociations);
$this->processCompanyAssociations($associationIds, $associations);
$this->processContactAssociations($associationIds, $associations);
return $associations;
}
private function initializeAssociationsStructure(): array
{
return [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
}
private function extractAssociationIds(array $opportunityAssociations): array
{
$associationIds = [];
foreach ($opportunityAssociations as $type => $associationData) {
if (! empty($associationData)) {
$associationIds[$type] = $this->convertSingleDealAssociations($associationData);
}
}
return $associationIds;
}
private function processCompanyAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['companies'])) {
return;
}
$companyId = $associationIds['companies'][0];
$account = $this->findOrSyncAccount($companyId);
if ($account instanceof Account) {
$associations['companies'][$companyId] = $account->getId();
$associations['account_id'] = $account->getId();
}
}
private function processContactAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['contacts'])) {
return;
}
foreach ($associationIds['contacts'] as $contactId) {
$contact = $this->findOrSyncContact($contactId);
if ($contact instanceof Contact) {
$associations['contacts'][$contactId] = $contact->getId();
}
}
}
private function findOrSyncAccount(string $companyId): ?Account
{
$account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);
if (! $account instanceof Account) {
$account = $this->syncAccount($companyId);
}
return $account;
}
private function findOrSyncContact(string $contactId): ?Contact
{
$contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);
if (! $contact instanceof Contact) {
$contact = $this->syncContact($contactId);
}
return $contact;
}
private function convertSingleDealAssociations($opportunityAssociations = null): array
{
$associationData = [];
if ($opportunityAssociations === null) {
return $associationData;
}
// Handle array input (from extractAssociationIds)
if (is_array($opportunityAssociations)) {
return $opportunityAssociations;
}
// Handle CollectionResponseAssociatedId object
if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {
foreach ($opportunityAssociations->getResults() as $association) {
$associationData[] = $association->getId();
}
}
return $associationData;
}
private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity
{
if (empty($crmData['properties'])) {
return null;
}
$crmId = (string) $crmData['id'];
$properties = $crmData['properties'];
$associations = $crmData['associations'] ?? [];
$opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(
$this->config,
$crmId
);
if ($opportunityExists) {
return $this->updateOpportunity($crmId, $properties, $associations);
}
return $this->createOpportunity($crmId, $properties, $associations);
}
/**
* Create new opportunity
*/
private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity
{
$accountId = $this->resolveAccountId($associations);
if (! $accountId) {
return null;
}
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
if (! $businessProcess) {
return null;
}
$stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);
if (! $stage) {
return null;
}
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->importOpportunityContacts($opportunity, $associations['contacts']);
if ($opportunity->wasRecentlyCreated) {
MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());
}
return $opportunity;
}
/**
* Update existing opportunity
*/
private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity
{
$accountId = $this->resolveAccountId($associations);
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
$stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->updateOpportunityAssociations($opportunity, $associations);
return $opportunity;
}
private function resolveAccountId(array $associations): ?int
{
if (! empty($associations['account_id'])) {
return $associations['account_id'];
}
if (empty($associations)) {
return null;
}
// Fallback: use first company as account (currently SDK returns one company)
foreach ($associations['companies'] as $accountId) {
return $accountId;
}
return null;
}
private function buildOpportunityData(
array $properties,
?int $accountId,
?BusinessProcess $businessProcess,
?Stage $stage
): array {
$ownerId = null;
$profile = null;
if (! empty($properties['hubspot_owner_id'])) {
$ownerId = $properties['hubspot_owner_id'];
$profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);
}
$name = 'Unknown';
if (isset($properties['dealname'])) {
$name = mb_strimwidth($properties['dealname'], 0, 128);
}
$amount = $this->resolveAmount($properties);
$currency = $properties['deal_currency_code'] ?? null;
$closeDate = null;
if (! empty($properties['closedate'])) {
$closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');
}
$remotelyCreatedAt = null;
if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {
$date = $this->parseCleanDatetime($properties['createdate']);
$remotelyCreatedAt = $date?->format('Y-m-d H:i:s');
}
$closedStages = $this->getClosedDealStages();
$isWon = in_array($properties['dealstage'], $closedStages['won']);
$isLost = in_array($properties['dealstage'], $closedStages['lost']);
$data = [
'team_id' => $this->team->getId(),
'user_id' => $profile ? $profile->user_id : null,
'owner_id' => $ownerId,
'name' => $name,
'value' => ! empty($amount) ? $amount : null,
'currency_code' => CurrencyFormatter::formatCode($currency),
'close_date' => $closeDate,
'is_closed' => $isWon || $isLost,
'is_won' => $isWon,
'remotely_created_at' => $remotelyCreatedAt,
'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),
'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),
];
if ($accountId) {
$data['account_id'] = $accountId;
}
if ($stage) {
$data['stage_id'] = $stage->id;
}
if ($businessProcess) {
$recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);
if ($recordType) {
$data['record_type_id'] = $recordType->id;
}
}
return $data;
}
private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess
{
if ($pipelineId === null) {
return null;
}
$cacheKey = $this->getBusinessProcessCacheKey($pipelineId);
if (isset($this->cachedBusinessProcesses[$cacheKey])) {
return $this->cachedBusinessProcesses[$cacheKey];
}
$businessProcess = $this->getBusinessProcess($pipelineId);
if (! $businessProcess instanceof BusinessProcess) {
$this->importStages();
$businessProcess = $this->getBusinessProcess($pipelineId);
}
if (! $businessProcess instanceof BusinessProcess) {
$this->logger->info(
'[HubSpot] Deal is not attached to a pipeline',
[
'pipeline' => $pipelineId]
);
}
$this->cachedBusinessProcesses[$cacheKey] = $businessProcess;
return $businessProcess;
}
private function getBusinessProcess(string $pipelineId): ?BusinessProcess
{
return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);
}
private function getBusinessProcessCacheKey(string $pipelineId): string
{
return $this->config->getId() . '_' . $pipelineId;
}
private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage
{
if (empty($stageId)) {
return null;
}
$cacheKey = $businessProcess->getId() . ':' . $stageId;
if (isset($this->cachedStages[$cacheKey])) {
return $this->cachedStages[$cacheKey];
}
$stage = $this->crmEntityRepository->getPipelineStageByConditions(
$businessProcess,
[
'crm_provider_id' => $stageId,
'type' => Stage::TYPE_OPPORTUNITY,
]
);
if ($stage === null) {
$this->importStages(null, $stageId);
}
if ($stage === null) {
$this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);
}
$this->cachedStages[$cacheKey] = $stage;
return $stage;
}
private function resolveAmount(array $properties): ?string
{
$amount = null;
if (! empty($properties['amount'])) {
$amount = str_replace(',', '', $properties['amount']);
}
if ($this->config->hasDefaultCurrencyFieldSet()) {
$valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();
$amount = $properties[$valueFieldName] ?? $amount;
}
return $amount;
}
private function parseCleanDatetime(string $datetime): ?Carbon
{
// Treat pre-1980 values as invalid
$minValidDate = Carbon::parse('1980-01-01 00:00:00');
try {
$date = Carbon::parse($datetime);
if ($minValidDate->gt($date)) {
return null;
}
return $date;
} catch (Exception) {
return null; // On parse error, treat as null
}
}
private function resolveDealProbability(?string $stageProbability): int
{
if ($stageProbability === null) {
return 0;
}
$probability = (float) $stageProbability;
return $probability > 1 ? 0 : (int) ($probability * 100);
}
private function resolveForecastCategory(?string $forecastCategory): string
{
if (! $forecastCategory) {
return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;
}
$forecastCategory = str_replace('_', ' ', $forecastCategory);
return ucwords(strtolower($forecastCategory));
}
private function importExternalFieldData(array $properties, int $opportunityId): void
{
$crmFields = $this->getOpportunitySyncableFields();
$this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);
}
private function importOpportunityContacts(Opportunity $opportunity, array $associations): void
{
// Handle empty or missing contact associations
if (empty($associations)) {
// Remove all existing contact associations if none provided
$this->removeAllOpportunityContacts($opportunity);
return;
}
// Use differential sync approach for better performance and accuracy
$this->syncOpportunityContactsDifferential($opportunity, $associations);
}
/**
* Sync opportunity contacts using differential approach
* This compares current vs new associations and only makes necessary changes
*/
private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void
{
$currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);
$contactAssociationIds = array_keys($contactAssociations);
$contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);
$contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);
if (empty($contactsToAdd) && empty($contactsToRemove)) {
return;
}
$this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);
$this->removeContactAssociations($opportunity, $contactsToRemove);
$this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);
}
private function getCurrentContactCrmIds(Opportunity $opportunity): array
{
return $opportunity->contacts()
->pluck('contacts.crm_provider_id')
->toArray();
}
private function logContactAssociationChanges(
Opportunity $opportunity,
array $currentContactCrmIds,
array $contactAssociations,
array $contactsToAdd,
array $contactsToRemove
): void {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [
'opportunity_id' => $opportunity->getId(),
'current_contacts' => $currentContactCrmIds,
'new_contacts' => $contactAssociations,
'contacts_to_add' => $contactsToAdd,
'contacts_to_remove' => $contactsToRemove,
]);
}
private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void
{
if (empty($contactsToRemove)) {
return;
}
$contactsToDetach = $opportunity->contacts()
->whereIn('contacts.crm_provider_id', $contactsToRemove)
->pluck('contacts.id')
->toArray();
if (! empty($contactsToDetach)) {
$opportunity->contacts()->detach($contactsToDetach);
$this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_contact_crm_ids' => $contactsToRemove,
'removed_contact_count' => count($contactsToDetach),
]);
}
}
private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void
{
if (empty($contactsToAdd)) {
return;
}
$contactsAdded = [];
foreach ($contactsToAdd as $crmId) {
$id = $contactAssociations[$crmId];
if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {
$contactsAdded[] = $crmId;
}
}
$this->logAddedContacts($opportunity, $contactsAdded);
}
private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool
{
try {
$contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);
if (! $contact) {
return false;
}
return $this->performContactAttachment($opportunity, $contact, $crmId);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [
'opportunity_id' => $opportunity->getId(),
'contact_crm_id' => $crmId,
'error' => $e->getMessage(),
]);
return false;
}
}
private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool
{
try {
$opportunity->contacts()->attach($contact->getId(), [
'crm_provider_id' => $crmId,
]);
return true;
} catch (\Illuminate\Database\QueryException $e) {
if (str_contains($e->getMessage(), 'Duplicate entry')) {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [
'contact_id' => $contact->getId(),
'contact_crm_id' => $crmId,
'opportunity_id' => $opportunity->getId(),
]);
return false;
}
throw $e;
}
}
private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void
{
if (! empty($contactsAdded)) {
$this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [
'opportunity_id' => $opportunity->getId(),
'added_contact_crm_ids' => $contactsAdded,
'added_contacts_count' => count($contactsAdded),
]);
}
}
}
Execute
Open Query Execution Settings…
Script
Code changed:
Hide
Sync Changes
Hide This Notification
6
1
6
Previous Highlighted Error
Next Highlighted Error
# [PASSWORD_DOTS] HS [PASSWORD_DOTS]
select * from teams where id = 2; # 2
select * from features; # 2
select * from team_features where team_id = 2; # 2
select * from crm_configurations where id = 2; # 2
select * from users where team_id = 2; #
select * from playbooks where team_id = 2; # event 38
select * from playbook_categories where playbook_id = 38; #
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;
https://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624
https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0
SELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;
# 609126 softphone tr. 11241
SELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;
# 608874 conference tr. 11226 crmId: 103422236596
select * from ai_prompts where transcription_id IN (11241, 11226);
select * from activity_summary_logs where activity_id = 608874;
select * from sidekick_settings;
select * from default_activity_types;
select * from crm_field_data where activity_id = 1223;
select * from crm_layouts where crm_configuration_id = 2;
SELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);
select * from crm_fields where crm_configuration_id = 11 and object_type = 'event';
SELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);
SELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;
SELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u
on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 2 and sa.provider = 'hubspot';
select * from opportunities where team_id = 2
and crm_provider_id IN ('51317301383');
select * from contacts where id = 85;
select * from opportunities where team_id = 2 order by id desc;
select * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112
select * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112
select * from opportunity_contacts where opportunity_id = 5117;
select * from crm_field_data where object_id = 1365;
SELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);
select * from features;
select * from team_features where team_id IN (1);
select * from team_features where feature_id IN (36);
SHOW CREATE TABLE opportunity_contacts;
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';
# $slug = 'HUBSPOT_WEBHOOK_SYNC';
# $team = Jiminny\Models\Team::find(2);
# $feature = Feature::query()->where('slug', $slug)->first();
# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);
# hubspot_webhook_metrics
select * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365
SELECT * FROM opportunity_contacts WHERE opportunity_id = '414';
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';
select * from contacts where id in (414, 464);
select * from activities where crm_configuration_id = 2;
select settings from crm_configurations where id = 11;
select * from teams; # 1, 2
select * from users;
select * from crm_configurations where id = 39;
select * from team_features where team_id = 2;
select * from features;
# SELECT * FROM opportunities WHERE crm_configuration_id = 2
# order by id desc;
# and crm_provider_id = '49908861993';
select * from activity_providers where id IN (443, 202, 203, 227);
select * from activity_imports where id = 795889;
select c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id
where c.provider = 'hubspot';
select * from crm_configurations crm JOIN teams t on crm.team_id = t.id
where provider = 'hubspot';
SELECT * FROM teams WHERE id = 31;
SELECT * FROM users WHERE id = 257;
SELECT * FROM opportunities WHERE team_id = 2;
select * from opportunity_contacts where opportunity_id = 5124;
select * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)
select * from activities where crm_configuration_id = 13;
SELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141
select id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;
SELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;
SELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;
SELECT * FROM contacts WHERE team_id = 2 order by id desc;
select * from opportunity_contacts where contact_id = 6223;
SELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;
select * from crm_profiles where crm_configuration_id = 2;
select * from activities where account_id = 46;
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
55469
|
|
55471
|
1195
|
32
|
2026-04-20T09:51:18.992933+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776678678992_m1.jpg...
|
PhpStorm
|
faVsco.js – HS_local [jiminny@localhost]
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11976 on JY-20553-debug- Project: faVsco.js, menu
#11976 on JY-20553-debug-crm-sync-delays, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
33
2
19
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot\ServiceTraits;
use Carbon\Carbon;
use HubSpot\Client\Crm\Deals\Model\CollectionResponseAssociatedId;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Models\Account;
use Exception;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Jobs\Crm\MatchActivitiesToNewOpportunity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\BusinessProcess;
use Jiminny\Exceptions\CrmException;
use Jiminny\Models\Opportunity;
use Illuminate\Support\Collection;
use Jiminny\Models\Stage;
use Jiminny\Repositories\Crm\CrmEntityRepository;
use Jiminny\Services\Crm\Hubspot\DealFieldsService;
use Jiminny\Services\Crm\Hubspot\OpportunitySyncStrategy\HubspotSingleSyncStrategy;
use Jiminny\Services\Crm\Hubspot\WebhookSyncBatchProcessor;
use Jiminny\Services\Crm\OpportunitySyncStrategyResolver;
use Jiminny\Utils\CurrencyFormatter;
/**
* Optimized sync methods for better performance
* These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains
*/
trait OpportunitySyncTrait
{
private const int BATCH_SIZE = 100;
private const int BATCH_PROCESS_SIZE = 800;
protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;
protected CrmEntityRepository $crmEntityRepository;
protected DealFieldsService $dealFieldsService;
private ?array $cachedClosedDealStages = null;
private array $cachedBusinessProcesses = [];
private array $cachedStages = [];
public function syncOpportunities(array $parameters, ?string $strategy = null): int
{
$startTime = microtime(true);
$strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);
$parameters['config'] = $this->config;
$syncCount = 0;
$reportedTotal = 0;
$lastSyncedId = [];
$strategyNames = [];
try {
foreach ($strategies as $strategyName => $syncStrategy) {
$strategyNames[] = $strategyName;
$this->logger->info(
'[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,
['team' => $this->team->getId()]
);
$total = 0;
$lastId = null;
$buffer = [];
// HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies
foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {
$buffer[] = $hsOpportunity;
// process every 800 rows (fits < 1 000 association limit)
if (\count($buffer) >= self::BATCH_PROCESS_SIZE) {
$syncCount += $this->processOpportunityBatch($buffer);
$buffer = [];
}
}
// leftovers
if ($buffer) {
$syncCount += $this->processOpportunityBatch($buffer);
}
$reportedTotal += $total;
$lastSyncedId = $lastId;
}
} catch (\HubSpot\Client\Crm\Deals\ApiException | CrmException $e) {
$this->handleSyncException($e, $parameters);
}
$durationMs = round((microtime(true) - $startTime) * 1000, 2);
$this->logger->info(
'[HubSpot] Synced opportunities',
[
'team' => $this->team->getId(),
'strategies' => implode(',', $strategyNames),
'sync_count' => $syncCount,
'total' => $reportedTotal,
'last_synced_id' => $lastSyncedId,
'duration_ms' => $durationMs,
]
);
return $reportedTotal;
}
private function handleSyncException(\Throwable $e, array $parameters): void
{
if (($parameters['since'] ?? null) instanceof Carbon) {
$parameters['since'] = $parameters['since']->toDateTimeString();
}
$parameters['config'] = $this->config->getId();
$this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [
'teamId' => $this->team->getUuid(),
'parameters' => $parameters,
'reason' => $e->getMessage(),
]);
}
/**
* @inheritdoc
*/
public function syncOpportunity(string $crmId): ?Opportunity
{
$strategy = $this->opportunitySyncStrategyResolver->resolve(
$this->config,
OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,
);
$parameters = [
'config' => $this->config,
'crm_id' => $crmId,
];
try {
if (! $strategy instanceof HubspotSingleSyncStrategy) {
throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');
}
$hsOpportunity = $strategy->fetchOpportunity($parameters);
} catch (\HubSpot\Client\Crm\Deals\ApiException $e) {
$this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [
'teamId' => $this->team->getUuid(),
'crmId' => $crmId,
'reason' => $e->getMessage(),
]);
return null;
}
$hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);
return $this->importOrUpdateOpportunity($hsOpportunity);
}
/**
* Process webhook-collected opportunity batches.
*
* Drains Redis sets containing company CRM IDs collected from webhook events
* and dispatches ImportOpportunityBatch jobs for batch processing.
*
* @return int Number of opportunity IDs dispatched to jobs
*/
public function batchSyncOpportunities(): int
{
$configId = $this->team->getCrmConfiguration()->getId();
return $this->batchProcessor->processBatchesForObjectType(
WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,
$configId
);
}
/**
* Import a batch of opportunities by their CRM IDs.
* Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().
*
* @param array<string> $crmIds HubSpot deal CRM IDs
*
* @return array{success: array, failed_ids: array, errors?: array<string, string>}
*/
public function importOpportunityBatchByIds(array $crmIds): array
{
$fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);
$allDeals = [];
foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {
$deals = $this->client->getOpportunitiesByIds($chunk, $fields);
foreach ($deals as $deal) {
$allDeals[] = $deal;
}
}
// IDs not returned by HubSpot are likely deleted or inaccessible deals.
// These are not failures — retrying won't bring them back.
$fetchedIds = array_map('strval', array_column($allDeals, 'id'));
$notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));
if (! empty($notFoundIds)) {
$this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [
'teamId' => $this->team->getId(),
'notFoundCount' => \count($notFoundIds),
'notFoundIds' => $notFoundIds,
'requestedCount' => \count($crmIds),
'fetchedCount' => \count($allDeals),
]);
}
if (empty($allDeals)) {
return ['success' => [], 'failed_ids' => []];
}
return $this->importOpportunityBatch($allDeals);
}
private function getClosedDealStages(): array
{
if ($this->cachedClosedDealStages !== null) {
return $this->cachedClosedDealStages;
}
$stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);
$data = [
'lost' => [],
'won' => [],
];
foreach ($stages as $stage) {
if ($stage->probability == 0.00) {
$data['lost'][] = $stage->crm_provider_id;
}
if ($stage->probability == 100.00) {
$data['won'][] = $stage->crm_provider_id;
}
}
$this->cachedClosedDealStages = $data;
return $data;
}
/**
* Import deals into the database with pre-fetched associations.
*
* API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT
* caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()
* where Laravel retries the whole job with backoff. After all retries exhausted,
* failed() requeues all IDs to Redis.
*
* The per-deal loop catches exceptions individually. A deal can end up in three states:
* - success: imported/updated successfully
* - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)
* These are permanent issues — retrying won't fix them.
* - skipped (null): missing dependencies (no account, unknown pipeline/stage).
* This is acceptable — the deal cannot be imported until those exist.
*/
private function importOpportunityBatch(array $deals): array
{
$syncedOpportunities = [
'success' => [],
'failed_ids' => [],
];
$dealIds = array_column($deals, 'id');
// Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the
// queue job retries the whole batch and eventually requeues all deal IDs back to Redis.
try {
$companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');
$contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');
$associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);
$existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(
$this->config,
array_map('strval', $dealIds)
);
$existingCrmIdSet = array_flip($existingCrmIds);
} catch (\Throwable $e) {
$this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [
'teamId' => $this->team->getId(),
'dealCount' => count($dealIds),
'error' => $e->getMessage(),
]);
throw $e;
}
foreach ($deals as $deal) {
try {
$deal['associations'] = $this->prepareAssociationsForOpportunity(
$deal['id'],
$companyAssociations,
$contactAssociations,
$associationsData
);
$syncedOpportunity = $this->importOrUpdateOpportunity(
$deal,
isset($existingCrmIdSet[(string) $deal['id']])
);
if ($syncedOpportunity) {
$syncedOpportunities['success'][] = $syncedOpportunity;
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [
'teamId' => $this->team->getId(),
'crmId' => $deal['id'],
'error' => $e->getMessage(),
]);
$syncedOpportunities['failed_ids'][] = $deal['id'];
$syncedOpportunities['errors'][$deal['id']] = $e->getMessage();
}
}
return $syncedOpportunities;
}
/**
* Prepare associated entities for opportunities with optimized batch processing
* Returns structured data with CRM ID to DB ID mappings for each opportunity
*/
private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array
{
// Step 1: Collect all unique company and contact IDs from associations
$allCompanyIds = $this->flattenAssociationIds($companyAssociations);
$allContactIds = $this->flattenAssociationIds($contactAssociations);
// Step 2: Batch sync missing entities and get CRM ID to DB ID mappings
$companyIdMappings = [];
$contactIdMappings = [];
if (! empty($allCompanyIds)) {
$companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);
}
if (! empty($allContactIds)) {
$contactIdMappings = $this->prepareAssociatedContacts($allContactIds);
}
return [
'company_id_mappings' => $companyIdMappings,
'contact_id_mappings' => $contactIdMappings,
];
}
/**
* Flatten association data to get unique IDs
*/
private function flattenAssociationIds(array $associations): array
{
$ids = [];
foreach ($associations as $dealAssociations) {
if (is_array($dealAssociations)) {
foreach ($dealAssociations as $id) {
$ids[$id] = true;
}
}
}
return array_keys($ids);
}
/**
* Batch sync missing accounts
*/
private function prepareAssociatedAccounts(array $companyIds): array
{
// Find which accounts already exist
$existingAccounts = $this->crmEntityRepository
->findAccountsByExternalIds($this->config, $companyIds);
$existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();
$existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {
return [$account->getCrmProviderId() => $account->getId()];
})->toArray();
$missingCompanyIds = array_diff($companyIds, $existingCompanyIds);
if (empty($missingCompanyIds)) {
return $existingAccountsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [
'teamId' => $this->team->getUuid(),
'total_companies' => count($companyIds),
'existing_companies' => count($existingCompanyIds),
'missing_companies' => count($missingCompanyIds),
]);
// we already have limit on opportunity ids count
// Initialize variable before try block
$syncedAccountsData = [];
try {
$syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [
'size' => count($missingCompanyIds),
'error' => $e->getMessage(),
]);
$syncedAccountsData = [];
}
return $existingAccountsData + $syncedAccountsData;
}
/**
* Prepare associated contacts - find existing and sync missing ones
* Returns mapping of CRM ID to DB ID
*/
private function prepareAssociatedContacts(array $contactIds): array
{
// Find which contacts already exist
$existingContacts = $this->crmEntityRepository
->findContactsByExternalIds($this->config, $contactIds);
$existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();
// Create mapping for existing contacts
$existingContactsData = $existingContacts->mapWithKeys(function ($contact) {
return [$contact->getCrmProviderId() => $contact->getId()];
})->toArray();
$missingContactIds = array_diff($contactIds, $existingContactIds);
if (empty($missingContactIds)) {
return $existingContactsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [
'teamId' => $this->team->getUuid(),
'total_contacts' => count($contactIds),
'existing_contacts' => count($existingContactIds),
'missing_contacts' => count($missingContactIds),
]);
// Sync missing contacts using batch API
try {
$syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [
'size' => count($missingContactIds),
'error' => $e->getMessage(),
]);
$syncedContactsData = [];
}
return $existingContactsData + $syncedContactsData;
}
private function batchSyncCrmObjects(string $objectType, array $crmIds): array
{
$syncObjects = [];
$crmObjectIds = array_values($crmIds);
foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {
try {
$objects = $objectType === 'companies' ?
$this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :
$this->client->getContactsByIds($chunk, $this->getContactFields());
foreach ($objects as $objectId => $objectData) {
$this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [
'requested_count' => count($chunk),
'synced_count' => count($objects),
]);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [
'ids' => $chunk,
'error' => $e->getMessage(),
]);
}
}
return $syncObjects;
}
private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void
{
try {
$object = $objectType === 'companies' ?
$this->importAccount($objectData) :
$this->importContact($objectData);
if ($object) {
$syncObjects[$object->getCrmProviderId()] = $object->getId();
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [
'id' => $objectId,
'error' => $e->getMessage(),
]);
}
}
/**
* Prepare associations for a single opportunity
*
* The return value is an array with the following structure:
* [
* 'companies' => [
* $companyCrmId => $companyId,
* ...
* ],
* 'contacts' => [
* $contactCrmId => $contactId,
* ...
* ],
* 'account_id' => $accountId,
* ]
*/
private function prepareAssociationsForOpportunity(
string $oppCrmId,
array $companyAssociations,
array $contactAssociations,
array $associationsData
): array {
$associations = [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
$oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];
foreach ($oppCompanyIds as $companyCrmId) {
if (isset($associationsData['company_id_mappings'][$companyCrmId])) {
$associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];
// Set primary account (first company becomes primary account)
if ($associations['account_id'] === null) {
$associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];
}
}
}
$oppContactIds = $contactAssociations[$oppCrmId] ?? [];
foreach ($oppContactIds as $contactCrmId) {
if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {
$associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];
}
}
return $associations;
}
/**
* Update only associations for an opportunity
*/
private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void
{
// Update contact associations
$this->importOpportunityContacts($opportunity, $associations['contacts']);
// Update company (account) associations
$this->updateOpportunityAccount($opportunity, $associations['account_id']);
}
/**
* Remove all contact associations from an opportunity
*/
private function removeAllOpportunityContacts(Opportunity $opportunity): void
{
$currentCount = (int) $opportunity->contacts()->count();
if ($currentCount > 0) {
$opportunity->contacts()->detach();
$this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_count' => $currentCount,
]);
}
}
private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void
{
if ($accountId === null) {
// No account ID provided - keep current account
return;
}
$currentAccountId = $opportunity->getAccountId();
// Only update if account has changed
if ($currentAccountId !== $accountId) {
$opportunity->account_id = $accountId;
$opportunity->save();
$this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [
'opportunity_id' => $opportunity->getId(),
'old_account_id' => $currentAccountId,
'new_account_id' => $accountId,
]);
}
}
/**
* Find existing opportunities by external IDs (OPTIMIZED VERSION)
* Uses batch query for better performance
*/
private function findExistingOpportunities(array $crmIds): Collection
{
return $this->crmEntityRepository
->findOpportunitiesByExternalIds($this->config, $crmIds);
}
private function processOpportunityBatch(array $opportunities): int
{
$syncedOpportunities = $this->importOpportunityBatch($opportunities);
return count($syncedOpportunities['success'] ?? []);
}
/**
* Convert single deal associations from HubSpot format to internal format
* Handles both HubSpot SDK objects and array formats
*
* @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed
*
* @return array Processed associations with DB IDs
*/
private function convertDealAssociations(array $opportunityAssociations): array
{
$associations = $this->initializeAssociationsStructure();
if (empty($opportunityAssociations)) {
return $associations;
}
$associationIds = $this->extractAssociationIds($opportunityAssociations);
$this->processCompanyAssociations($associationIds, $associations);
$this->processContactAssociations($associationIds, $associations);
return $associations;
}
private function initializeAssociationsStructure(): array
{
return [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
}
private function extractAssociationIds(array $opportunityAssociations): array
{
$associationIds = [];
foreach ($opportunityAssociations as $type => $associationData) {
if (! empty($associationData)) {
$associationIds[$type] = $this->convertSingleDealAssociations($associationData);
}
}
return $associationIds;
}
private function processCompanyAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['companies'])) {
return;
}
$companyId = $associationIds['companies'][0];
$account = $this->findOrSyncAccount($companyId);
if ($account instanceof Account) {
$associations['companies'][$companyId] = $account->getId();
$associations['account_id'] = $account->getId();
}
}
private function processContactAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['contacts'])) {
return;
}
foreach ($associationIds['contacts'] as $contactId) {
$contact = $this->findOrSyncContact($contactId);
if ($contact instanceof Contact) {
$associations['contacts'][$contactId] = $contact->getId();
}
}
}
private function findOrSyncAccount(string $companyId): ?Account
{
$account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);
if (! $account instanceof Account) {
$account = $this->syncAccount($companyId);
}
return $account;
}
private function findOrSyncContact(string $contactId): ?Contact
{
$contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);
if (! $contact instanceof Contact) {
$contact = $this->syncContact($contactId);
}
return $contact;
}
private function convertSingleDealAssociations($opportunityAssociations = null): array
{
$associationData = [];
if ($opportunityAssociations === null) {
return $associationData;
}
// Handle array input (from extractAssociationIds)
if (is_array($opportunityAssociations)) {
return $opportunityAssociations;
}
// Handle CollectionResponseAssociatedId object
if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {
foreach ($opportunityAssociations->getResults() as $association) {
$associationData[] = $association->getId();
}
}
return $associationData;
}
private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity
{
if (empty($crmData['properties'])) {
return null;
}
$crmId = (string) $crmData['id'];
$properties = $crmData['properties'];
$associations = $crmData['associations'] ?? [];
$opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(
$this->config,
$crmId
);
if ($opportunityExists) {
return $this->updateOpportunity($crmId, $properties, $associations);
}
return $this->createOpportunity($crmId, $properties, $associations);
}
/**
* Create new opportunity
*/
private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity
{
$accountId = $this->resolveAccountId($associations);
if (! $accountId) {
return null;
}
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
if (! $businessProcess) {
return null;
}
$stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);
if (! $stage) {
return null;
}
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->importOpportunityContacts($opportunity, $associations['contacts']);
if ($opportunity->wasRecentlyCreated) {
MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());
}
return $opportunity;
}
/**
* Update existing opportunity
*/
private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity
{
$accountId = $this->resolveAccountId($associations);
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
$stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->updateOpportunityAssociations($opportunity, $associations);
return $opportunity;
}
private function resolveAccountId(array $associations): ?int
{
if (! empty($associations['account_id'])) {
return $associations['account_id'];
}
if (empty($associations)) {
return null;
}
// Fallback: use first company as account (currently SDK returns one company)
foreach ($associations['companies'] as $accountId) {
return $accountId;
}
return null;
}
private function buildOpportunityData(
array $properties,
?int $accountId,
?BusinessProcess $businessProcess,
?Stage $stage
): array {
$ownerId = null;
$profile = null;
if (! empty($properties['hubspot_owner_id'])) {
$ownerId = $properties['hubspot_owner_id'];
$profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);
}
$name = 'Unknown';
if (isset($properties['dealname'])) {
$name = mb_strimwidth($properties['dealname'], 0, 128);
}
$amount = $this->resolveAmount($properties);
$currency = $properties['deal_currency_code'] ?? null;
$closeDate = null;
if (! empty($properties['closedate'])) {
$closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');
}
$remotelyCreatedAt = null;
if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {
$date = $this->parseCleanDatetime($properties['createdate']);
$remotelyCreatedAt = $date?->format('Y-m-d H:i:s');
}
$closedStages = $this->getClosedDealStages();
$isWon = in_array($properties['dealstage'], $closedStages['won']);
$isLost = in_array($properties['dealstage'], $closedStages['lost']);
$data = [
'team_id' => $this->team->getId(),
'user_id' => $profile ? $profile->user_id : null,
'owner_id' => $ownerId,
'name' => $name,
'value' => ! empty($amount) ? $amount : null,
'currency_code' => CurrencyFormatter::formatCode($currency),
'close_date' => $closeDate,
'is_closed' => $isWon || $isLost,
'is_won' => $isWon,
'remotely_created_at' => $remotelyCreatedAt,
'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),
'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),
];
if ($accountId) {
$data['account_id'] = $accountId;
}
if ($stage) {
$data['stage_id'] = $stage->id;
}
if ($businessProcess) {
$recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);
if ($recordType) {
$data['record_type_id'] = $recordType->id;
}
}
return $data;
}
private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess
{
if ($pipelineId === null) {
return null;
}
$cacheKey = $this->getBusinessProcessCacheKey($pipelineId);
if (isset($this->cachedBusinessProcesses[$cacheKey])) {
return $this->cachedBusinessProcesses[$cacheKey];
}
$businessProcess = $this->getBusinessProcess($pipelineId);
if (! $businessProcess instanceof BusinessProcess) {
$this->importStages();
$businessProcess = $this->getBusinessProcess($pipelineId);
}
if (! $businessProcess instanceof BusinessProcess) {
$this->logger->info(
'[HubSpot] Deal is not attached to a pipeline',
[
'pipeline' => $pipelineId]
);
}
$this->cachedBusinessProcesses[$cacheKey] = $businessProcess;
return $businessProcess;
}
private function getBusinessProcess(string $pipelineId): ?BusinessProcess
{
return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);
}
private function getBusinessProcessCacheKey(string $pipelineId): string
{
return $this->config->getId() . '_' . $pipelineId;
}
private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage
{
if (empty($stageId)) {
return null;
}
$cacheKey = $businessProcess->getId() . ':' . $stageId;
if (isset($this->cachedStages[$cacheKey])) {
return $this->cachedStages[$cacheKey];
}
$stage = $this->crmEntityRepository->getPipelineStageByConditions(
$businessProcess,
[
'crm_provider_id' => $stageId,
'type' => Stage::TYPE_OPPORTUNITY,
]
);
if ($stage === null) {
$this->importStages(null, $stageId);
}
if ($stage === null) {
$this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);
}
$this->cachedStages[$cacheKey] = $stage;
return $stage;
}
private function resolveAmount(array $properties): ?string
{
$amount = null;
if (! empty($properties['amount'])) {
$amount = str_replace(',', '', $properties['amount']);
}
if ($this->config->hasDefaultCurrencyFieldSet()) {
$valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();
$amount = $properties[$valueFieldName] ?? $amount;
}
return $amount;
}
private function parseCleanDatetime(string $datetime): ?Carbon
{
// Treat pre-1980 values as invalid
$minValidDate = Carbon::parse('1980-01-01 00:00:00');
try {
$date = Carbon::parse($datetime);
if ($minValidDate->gt($date)) {
return null;
}
return $date;
} catch (Exception) {
return null; // On parse error, treat as null
}
}
private function resolveDealProbability(?string $stageProbability): int
{
if ($stageProbability === null) {
return 0;
}
$probability = (float) $stageProbability;
return $probability > 1 ? 0 : (int) ($probability * 100);
}
private function resolveForecastCategory(?string $forecastCategory): string
{
if (! $forecastCategory) {
return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;
}
$forecastCategory = str_replace('_', ' ', $forecastCategory);
return ucwords(strtolower($forecastCategory));
}
private function importExternalFieldData(array $properties, int $opportunityId): void
{
$crmFields = $this->getOpportunitySyncableFields();
$this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);
}
private function importOpportunityContacts(Opportunity $opportunity, array $associations): void
{
// Handle empty or missing contact associations
if (empty($associations)) {
// Remove all existing contact associations if none provided
$this->removeAllOpportunityContacts($opportunity);
return;
}
// Use differential sync approach for better performance and accuracy
$this->syncOpportunityContactsDifferential($opportunity, $associations);
}
/**
* Sync opportunity contacts using differential approach
* This compares current vs new associations and only makes necessary changes
*/
private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void
{
$currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);
$contactAssociationIds = array_keys($contactAssociations);
$contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);
$contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);
if (empty($contactsToAdd) && empty($contactsToRemove)) {
return;
}
$this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);
$this->removeContactAssociations($opportunity, $contactsToRemove);
$this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);
}
private function getCurrentContactCrmIds(Opportunity $opportunity): array
{
return $opportunity->contacts()
->pluck('contacts.crm_provider_id')
->toArray();
}
private function logContactAssociationChanges(
Opportunity $opportunity,
array $currentContactCrmIds,
array $contactAssociations,
array $contactsToAdd,
array $contactsToRemove
): void {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [
'opportunity_id' => $opportunity->getId(),
'current_contacts' => $currentContactCrmIds,
'new_contacts' => $contactAssociations,
'contacts_to_add' => $contactsToAdd,
'contacts_to_remove' => $contactsToRemove,
]);
}
private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void
{
if (empty($contactsToRemove)) {
return;
}
$contactsToDetach = $opportunity->contacts()
->whereIn('contacts.crm_provider_id', $contactsToRemove)
->pluck('contacts.id')
->toArray();
if (! empty($contactsToDetach)) {
$opportunity->contacts()->detach($contactsToDetach);
$this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_contact_crm_ids' => $contactsToRemove,
'removed_contact_count' => count($contactsToDetach),
]);
}
}
private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void
{
if (empty($contactsToAdd)) {
return;
}
$contactsAdded = [];
foreach ($contactsToAdd as $crmId) {
$id = $contactAssociations[$crmId];
if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {
$contactsAdded[] = $crmId;
}
}
$this->logAddedContacts($opportunity, $contactsAdded);
}
private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool
{
try {
$contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);
if (! $contact) {
return false;
}
return $this->performContactAttachment($opportunity, $contact, $crmId);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [
'opportunity_id' => $opportunity->getId(),
'contact_crm_id' => $crmId,
'error' => $e->getMessage(),
]);
return false;
}
}
private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool
{
try {
$opportunity->contacts()->attach($contact->getId(), [
'crm_provider_id' => $crmId,
]);
return true;
} catch (\Illuminate\Database\QueryException $e) {
if (str_contains($e->getMessage(), 'Duplicate entry')) {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [
'contact_id' => $contact->getId(),
'contact_crm_id' => $crmId,
'opportunity_id' => $opportunity->getId(),
]);
return false;
}
throw $e;
}
}
private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void
{
if (! empty($contactsAdded)) {
$this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [
'opportunity_id' => $opportunity->getId(),
'added_contact_crm_ids' => $contactsAdded,
'added_contacts_count' => count($contactsAdded),
]);
}
}
}
Execute
Open Query Execution Settings…
Script
Code changed:
Hide
Sync Changes
Hide This Notification
6
1
6
Previous Highlighted Error
Next Highlighted Error
# [PASSWORD_DOTS] HS [PASSWORD_DOTS]
select * from teams where id = 2; # 2
select * from features; # 2
select * from team_features where team_id = 2; # 2
select * from crm_configurations where id = 2; # 2
select * from users where team_id = 2; #
select * from playbooks where team_id = 2; # event 38
select * from playbook_categories where playbook_id = 38; #
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;
https://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624
https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0
SELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;
# 609126 softphone tr. 11241
SELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;
# 608874 conference tr. 11226 crmId: 103422236596
select * from ai_prompts where transcription_id IN (11241, 11226);
select * from activity_summary_logs where activity_id = 608874;
select * from sidekick_settings;
select * from default_activity_types;
select * from crm_field_data where activity_id = 1223;
select * from crm_layouts where crm_configuration_id = 2;
SELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);
select * from crm_fields where crm_configuration_id = 11 and object_type = 'event';
SELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);
SELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;
SELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u
on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 2 and sa.provider = 'hubspot';
select * from opportunities where team_id = 2
and crm_provider_id IN ('51317301383');
select * from contacts where id = 85;
select * from opportunities where team_id = 2 order by id desc;
select * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112
select * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112
select * from opportunity_contacts where opportunity_id = 5117;
select * from crm_field_data where object_id = 1365;
SELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);
select * from features;
select * from team_features where team_id IN (1);
select * from team_features where feature_id IN (36);
SHOW CREATE TABLE opportunity_contacts;
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';
# $slug = 'HUBSPOT_WEBHOOK_SYNC';
# $team = Jiminny\Models\Team::find(2);
# $feature = Feature::query()->where('slug', $slug)->first();
# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);
# hubspot_webhook_metrics
select * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365
SELECT * FROM opportunity_contacts WHERE opportunity_id = '414';
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';
select * from contacts where id in (414, 464);
select * from activities where crm_configuration_id = 2;
select settings from crm_configurations where id = 11;
select * from teams; # 1, 2
select * from users;
select * from crm_configurations where id = 39;
select * from team_features where team_id = 2;
select * from features;
# SELECT * FROM opportunities WHERE crm_configuration_id = 2
# order by id desc;
# and crm_provider_id = '49908861993';
select * from activity_providers where id IN (443, 202, 203, 227);
select * from activity_imports where id = 795889;
select c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id
where c.provider = 'hubspot';
select * from crm_configurations crm JOIN teams t on crm.team_id = t.id
where provider = 'hubspot';
SELECT * FROM teams WHERE id = 31;
SELECT * FROM users WHERE id = 257;
SELECT * FROM opportunities WHERE team_id = 2;
select * from opportunity_contacts where opportunity_id = 5124;
select * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)
select * from activities where crm_configuration_id = 13;
SELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141
select id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;
SELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;
SELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;
SELECT * FROM contacts WHERE team_id = 2 order by id desc;
select * from opportunity_contacts where contact_id = 6223;
SELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;
select * from crm_profiles where crm_configuration_id = 2;
select * from activities where account_id = 46;
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11976 on JY-20553-debug-crm-sync-delays, menu","depth":5,"help_text":"Pull request #11976 exists for current branch JY-20553-debug-crm-sync-delays","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"33","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"19","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot\\ServiceTraits;\n\nuse Carbon\\Carbon;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\CollectionResponseAssociatedId;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Models\\Account;\nuse Exception;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Jobs\\Crm\\MatchActivitiesToNewOpportunity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\BusinessProcess;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Models\\Opportunity;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Repositories\\Crm\\CrmEntityRepository;\nuse Jiminny\\Services\\Crm\\Hubspot\\DealFieldsService;\nuse Jiminny\\Services\\Crm\\Hubspot\\OpportunitySyncStrategy\\HubspotSingleSyncStrategy;\nuse Jiminny\\Services\\Crm\\Hubspot\\WebhookSyncBatchProcessor;\nuse Jiminny\\Services\\Crm\\OpportunitySyncStrategyResolver;\nuse Jiminny\\Utils\\CurrencyFormatter;\n\n/**\n * Optimized sync methods for better performance\n * These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains\n */\ntrait OpportunitySyncTrait\n{\n private const int BATCH_SIZE = 100;\n private const int BATCH_PROCESS_SIZE = 800;\n\n protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;\n protected CrmEntityRepository $crmEntityRepository;\n protected DealFieldsService $dealFieldsService;\n\n private ?array $cachedClosedDealStages = null;\n private array $cachedBusinessProcesses = [];\n private array $cachedStages = [];\n\n public function syncOpportunities(array $parameters, ?string $strategy = null): int\n {\n $startTime = microtime(true);\n $strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);\n $parameters['config'] = $this->config;\n $syncCount = 0;\n $reportedTotal = 0;\n $lastSyncedId = [];\n $strategyNames = [];\n\n try {\n foreach ($strategies as $strategyName => $syncStrategy) {\n $strategyNames[] = $strategyName;\n $this->logger->info(\n '[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,\n ['team' => $this->team->getId()]\n );\n\n $total = 0;\n $lastId = null;\n $buffer = [];\n\n // HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies\n foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {\n $buffer[] = $hsOpportunity;\n\n // process every 800 rows (fits < 1 000 association limit)\n if (\\count($buffer) >= self::BATCH_PROCESS_SIZE) {\n $syncCount += $this->processOpportunityBatch($buffer);\n $buffer = [];\n }\n }\n\n // leftovers\n if ($buffer) {\n $syncCount += $this->processOpportunityBatch($buffer);\n }\n\n $reportedTotal += $total;\n $lastSyncedId = $lastId;\n }\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException | CrmException $e) {\n $this->handleSyncException($e, $parameters);\n }\n\n $durationMs = round((microtime(true) - $startTime) * 1000, 2);\n $this->logger->info(\n '[HubSpot] Synced opportunities',\n [\n 'team' => $this->team->getId(),\n 'strategies' => implode(',', $strategyNames),\n 'sync_count' => $syncCount,\n 'total' => $reportedTotal,\n 'last_synced_id' => $lastSyncedId,\n 'duration_ms' => $durationMs,\n ]\n );\n\n return $reportedTotal;\n }\n\n private function handleSyncException(\\Throwable $e, array $parameters): void\n {\n if (($parameters['since'] ?? null) instanceof Carbon) {\n $parameters['since'] = $parameters['since']->toDateTimeString();\n }\n $parameters['config'] = $this->config->getId();\n\n $this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [\n 'teamId' => $this->team->getUuid(),\n 'parameters' => $parameters,\n 'reason' => $e->getMessage(),\n ]);\n }\n\n /**\n * @inheritdoc\n */\n public function syncOpportunity(string $crmId): ?Opportunity\n {\n $strategy = $this->opportunitySyncStrategyResolver->resolve(\n $this->config,\n OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,\n );\n\n $parameters = [\n 'config' => $this->config,\n 'crm_id' => $crmId,\n ];\n\n try {\n if (! $strategy instanceof HubspotSingleSyncStrategy) {\n throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');\n }\n\n $hsOpportunity = $strategy->fetchOpportunity($parameters);\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException $e) {\n $this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [\n 'teamId' => $this->team->getUuid(),\n 'crmId' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n return null;\n }\n\n $hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);\n\n return $this->importOrUpdateOpportunity($hsOpportunity);\n }\n\n /**\n * Process webhook-collected opportunity batches.\n *\n * Drains Redis sets containing company CRM IDs collected from webhook events\n * and dispatches ImportOpportunityBatch jobs for batch processing.\n *\n * @return int Number of opportunity IDs dispatched to jobs\n */\n public function batchSyncOpportunities(): int\n {\n $configId = $this->team->getCrmConfiguration()->getId();\n\n return $this->batchProcessor->processBatchesForObjectType(\n WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,\n $configId\n );\n }\n\n /**\n * Import a batch of opportunities by their CRM IDs.\n * Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().\n *\n * @param array<string> $crmIds HubSpot deal CRM IDs\n *\n * @return array{success: array, failed_ids: array, errors?: array<string, string>}\n */\n public function importOpportunityBatchByIds(array $crmIds): array\n {\n $fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);\n\n $allDeals = [];\n foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {\n $deals = $this->client->getOpportunitiesByIds($chunk, $fields);\n foreach ($deals as $deal) {\n $allDeals[] = $deal;\n }\n }\n\n // IDs not returned by HubSpot are likely deleted or inaccessible deals.\n // These are not failures — retrying won't bring them back.\n $fetchedIds = array_map('strval', array_column($allDeals, 'id'));\n $notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));\n\n if (! empty($notFoundIds)) {\n $this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [\n 'teamId' => $this->team->getId(),\n 'notFoundCount' => \\count($notFoundIds),\n 'notFoundIds' => $notFoundIds,\n 'requestedCount' => \\count($crmIds),\n 'fetchedCount' => \\count($allDeals),\n ]);\n }\n\n if (empty($allDeals)) {\n return ['success' => [], 'failed_ids' => []];\n }\n\n return $this->importOpportunityBatch($allDeals);\n }\n\n private function getClosedDealStages(): array\n {\n if ($this->cachedClosedDealStages !== null) {\n return $this->cachedClosedDealStages;\n }\n\n $stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);\n $data = [\n 'lost' => [],\n 'won' => [],\n ];\n\n foreach ($stages as $stage) {\n if ($stage->probability == 0.00) {\n $data['lost'][] = $stage->crm_provider_id;\n }\n if ($stage->probability == 100.00) {\n $data['won'][] = $stage->crm_provider_id;\n }\n }\n\n $this->cachedClosedDealStages = $data;\n\n return $data;\n }\n\n /**\n * Import deals into the database with pre-fetched associations.\n *\n * API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT\n * caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()\n * where Laravel retries the whole job with backoff. After all retries exhausted,\n * failed() requeues all IDs to Redis.\n *\n * The per-deal loop catches exceptions individually. A deal can end up in three states:\n * - success: imported/updated successfully\n * - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)\n * These are permanent issues — retrying won't fix them.\n * - skipped (null): missing dependencies (no account, unknown pipeline/stage).\n * This is acceptable — the deal cannot be imported until those exist.\n */\n private function importOpportunityBatch(array $deals): array\n {\n $syncedOpportunities = [\n 'success' => [],\n 'failed_ids' => [],\n ];\n $dealIds = array_column($deals, 'id');\n\n // Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the\n // queue job retries the whole batch and eventually requeues all deal IDs back to Redis.\n try {\n $companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');\n $contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');\n\n $associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);\n\n $existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(\n $this->config,\n array_map('strval', $dealIds)\n );\n $existingCrmIdSet = array_flip($existingCrmIds);\n } catch (\\Throwable $e) {\n $this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [\n 'teamId' => $this->team->getId(),\n 'dealCount' => count($dealIds),\n 'error' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n foreach ($deals as $deal) {\n try {\n $deal['associations'] = $this->prepareAssociationsForOpportunity(\n $deal['id'],\n $companyAssociations,\n $contactAssociations,\n $associationsData\n );\n\n $syncedOpportunity = $this->importOrUpdateOpportunity(\n $deal,\n isset($existingCrmIdSet[(string) $deal['id']])\n );\n if ($syncedOpportunity) {\n $syncedOpportunities['success'][] = $syncedOpportunity;\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [\n 'teamId' => $this->team->getId(),\n 'crmId' => $deal['id'],\n 'error' => $e->getMessage(),\n ]);\n $syncedOpportunities['failed_ids'][] = $deal['id'];\n $syncedOpportunities['errors'][$deal['id']] = $e->getMessage();\n }\n }\n\n return $syncedOpportunities;\n }\n\n /**\n * Prepare associated entities for opportunities with optimized batch processing\n * Returns structured data with CRM ID to DB ID mappings for each opportunity\n */\n private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array\n {\n // Step 1: Collect all unique company and contact IDs from associations\n $allCompanyIds = $this->flattenAssociationIds($companyAssociations);\n $allContactIds = $this->flattenAssociationIds($contactAssociations);\n\n // Step 2: Batch sync missing entities and get CRM ID to DB ID mappings\n $companyIdMappings = [];\n $contactIdMappings = [];\n\n if (! empty($allCompanyIds)) {\n $companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);\n }\n\n if (! empty($allContactIds)) {\n $contactIdMappings = $this->prepareAssociatedContacts($allContactIds);\n }\n\n return [\n 'company_id_mappings' => $companyIdMappings,\n 'contact_id_mappings' => $contactIdMappings,\n ];\n }\n\n /**\n * Flatten association data to get unique IDs\n */\n private function flattenAssociationIds(array $associations): array\n {\n $ids = [];\n foreach ($associations as $dealAssociations) {\n if (is_array($dealAssociations)) {\n foreach ($dealAssociations as $id) {\n $ids[$id] = true;\n }\n }\n }\n\n return array_keys($ids);\n }\n\n /**\n * Batch sync missing accounts\n */\n private function prepareAssociatedAccounts(array $companyIds): array\n {\n // Find which accounts already exist\n $existingAccounts = $this->crmEntityRepository\n ->findAccountsByExternalIds($this->config, $companyIds);\n\n $existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();\n\n $existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {\n return [$account->getCrmProviderId() => $account->getId()];\n })->toArray();\n\n $missingCompanyIds = array_diff($companyIds, $existingCompanyIds);\n\n if (empty($missingCompanyIds)) {\n return $existingAccountsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [\n 'teamId' => $this->team->getUuid(),\n 'total_companies' => count($companyIds),\n 'existing_companies' => count($existingCompanyIds),\n 'missing_companies' => count($missingCompanyIds),\n ]);\n\n // we already have limit on opportunity ids count\n // Initialize variable before try block\n $syncedAccountsData = [];\n\n try {\n $syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [\n 'size' => count($missingCompanyIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedAccountsData = [];\n }\n\n return $existingAccountsData + $syncedAccountsData;\n }\n\n /**\n * Prepare associated contacts - find existing and sync missing ones\n * Returns mapping of CRM ID to DB ID\n */\n private function prepareAssociatedContacts(array $contactIds): array\n {\n // Find which contacts already exist\n $existingContacts = $this->crmEntityRepository\n ->findContactsByExternalIds($this->config, $contactIds);\n\n $existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();\n\n // Create mapping for existing contacts\n $existingContactsData = $existingContacts->mapWithKeys(function ($contact) {\n return [$contact->getCrmProviderId() => $contact->getId()];\n })->toArray();\n\n $missingContactIds = array_diff($contactIds, $existingContactIds);\n\n if (empty($missingContactIds)) {\n return $existingContactsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [\n 'teamId' => $this->team->getUuid(),\n 'total_contacts' => count($contactIds),\n 'existing_contacts' => count($existingContactIds),\n 'missing_contacts' => count($missingContactIds),\n ]);\n\n // Sync missing contacts using batch API\n try {\n $syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [\n 'size' => count($missingContactIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedContactsData = [];\n }\n\n return $existingContactsData + $syncedContactsData;\n }\n\n private function batchSyncCrmObjects(string $objectType, array $crmIds): array\n {\n $syncObjects = [];\n $crmObjectIds = array_values($crmIds);\n\n foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {\n try {\n $objects = $objectType === 'companies' ?\n $this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :\n $this->client->getContactsByIds($chunk, $this->getContactFields());\n\n foreach ($objects as $objectId => $objectData) {\n $this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [\n 'requested_count' => count($chunk),\n 'synced_count' => count($objects),\n ]);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [\n 'ids' => $chunk,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n return $syncObjects;\n }\n\n private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void\n {\n try {\n $object = $objectType === 'companies' ?\n $this->importAccount($objectData) :\n $this->importContact($objectData);\n\n if ($object) {\n $syncObjects[$object->getCrmProviderId()] = $object->getId();\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [\n 'id' => $objectId,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n /**\n * Prepare associations for a single opportunity\n *\n * The return value is an array with the following structure:\n * [\n * 'companies' => [\n * $companyCrmId => $companyId,\n * ...\n * ],\n * 'contacts' => [\n * $contactCrmId => $contactId,\n * ...\n * ],\n * 'account_id' => $accountId,\n * ]\n */\n private function prepareAssociationsForOpportunity(\n string $oppCrmId,\n array $companyAssociations,\n array $contactAssociations,\n array $associationsData\n ): array {\n $associations = [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n\n $oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];\n foreach ($oppCompanyIds as $companyCrmId) {\n if (isset($associationsData['company_id_mappings'][$companyCrmId])) {\n $associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];\n\n // Set primary account (first company becomes primary account)\n if ($associations['account_id'] === null) {\n $associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];\n }\n }\n }\n\n $oppContactIds = $contactAssociations[$oppCrmId] ?? [];\n foreach ($oppContactIds as $contactCrmId) {\n if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {\n $associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];\n }\n }\n\n return $associations;\n }\n\n /**\n * Update only associations for an opportunity\n */\n private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void\n {\n // Update contact associations\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n // Update company (account) associations\n $this->updateOpportunityAccount($opportunity, $associations['account_id']);\n }\n\n /**\n * Remove all contact associations from an opportunity\n */\n private function removeAllOpportunityContacts(Opportunity $opportunity): void\n {\n $currentCount = (int) $opportunity->contacts()->count();\n\n if ($currentCount > 0) {\n $opportunity->contacts()->detach();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_count' => $currentCount,\n ]);\n }\n }\n\n private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void\n {\n if ($accountId === null) {\n // No account ID provided - keep current account\n return;\n }\n\n $currentAccountId = $opportunity->getAccountId();\n\n // Only update if account has changed\n if ($currentAccountId !== $accountId) {\n $opportunity->account_id = $accountId;\n $opportunity->save();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [\n 'opportunity_id' => $opportunity->getId(),\n 'old_account_id' => $currentAccountId,\n 'new_account_id' => $accountId,\n ]);\n }\n }\n\n /**\n * Find existing opportunities by external IDs (OPTIMIZED VERSION)\n * Uses batch query for better performance\n */\n private function findExistingOpportunities(array $crmIds): Collection\n {\n return $this->crmEntityRepository\n ->findOpportunitiesByExternalIds($this->config, $crmIds);\n }\n\n private function processOpportunityBatch(array $opportunities): int\n {\n $syncedOpportunities = $this->importOpportunityBatch($opportunities);\n\n return count($syncedOpportunities['success'] ?? []);\n }\n\n /**\n * Convert single deal associations from HubSpot format to internal format\n * Handles both HubSpot SDK objects and array formats\n *\n * @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed\n *\n * @return array Processed associations with DB IDs\n */\n private function convertDealAssociations(array $opportunityAssociations): array\n {\n $associations = $this->initializeAssociationsStructure();\n\n if (empty($opportunityAssociations)) {\n return $associations;\n }\n\n $associationIds = $this->extractAssociationIds($opportunityAssociations);\n\n $this->processCompanyAssociations($associationIds, $associations);\n $this->processContactAssociations($associationIds, $associations);\n\n return $associations;\n }\n\n private function initializeAssociationsStructure(): array\n {\n return [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n }\n\n private function extractAssociationIds(array $opportunityAssociations): array\n {\n $associationIds = [];\n\n foreach ($opportunityAssociations as $type => $associationData) {\n if (! empty($associationData)) {\n $associationIds[$type] = $this->convertSingleDealAssociations($associationData);\n }\n }\n\n return $associationIds;\n }\n\n private function processCompanyAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['companies'])) {\n return;\n }\n\n $companyId = $associationIds['companies'][0];\n $account = $this->findOrSyncAccount($companyId);\n\n if ($account instanceof Account) {\n $associations['companies'][$companyId] = $account->getId();\n $associations['account_id'] = $account->getId();\n }\n }\n\n private function processContactAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['contacts'])) {\n return;\n }\n\n foreach ($associationIds['contacts'] as $contactId) {\n $contact = $this->findOrSyncContact($contactId);\n\n if ($contact instanceof Contact) {\n $associations['contacts'][$contactId] = $contact->getId();\n }\n }\n }\n\n private function findOrSyncAccount(string $companyId): ?Account\n {\n $account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);\n\n if (! $account instanceof Account) {\n $account = $this->syncAccount($companyId);\n }\n\n return $account;\n }\n\n private function findOrSyncContact(string $contactId): ?Contact\n {\n $contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);\n\n if (! $contact instanceof Contact) {\n $contact = $this->syncContact($contactId);\n }\n\n return $contact;\n }\n\n private function convertSingleDealAssociations($opportunityAssociations = null): array\n {\n $associationData = [];\n\n if ($opportunityAssociations === null) {\n return $associationData;\n }\n\n // Handle array input (from extractAssociationIds)\n if (is_array($opportunityAssociations)) {\n return $opportunityAssociations;\n }\n\n // Handle CollectionResponseAssociatedId object\n if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {\n foreach ($opportunityAssociations->getResults() as $association) {\n $associationData[] = $association->getId();\n }\n }\n\n return $associationData;\n }\n\n private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity\n {\n if (empty($crmData['properties'])) {\n return null;\n }\n\n $crmId = (string) $crmData['id'];\n $properties = $crmData['properties'];\n $associations = $crmData['associations'] ?? [];\n\n $opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(\n $this->config,\n $crmId\n );\n\n if ($opportunityExists) {\n return $this->updateOpportunity($crmId, $properties, $associations);\n }\n\n return $this->createOpportunity($crmId, $properties, $associations);\n }\n\n /**\n * Create new opportunity\n */\n private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n if (! $accountId) {\n return null;\n }\n\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n if (! $businessProcess) {\n return null;\n }\n\n $stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);\n if (! $stage) {\n return null;\n }\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n if ($opportunity->wasRecentlyCreated) {\n MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());\n }\n\n return $opportunity;\n }\n\n /**\n * Update existing opportunity\n */\n private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n $stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->updateOpportunityAssociations($opportunity, $associations);\n\n return $opportunity;\n }\n\n private function resolveAccountId(array $associations): ?int\n {\n if (! empty($associations['account_id'])) {\n return $associations['account_id'];\n }\n\n if (empty($associations)) {\n return null;\n }\n\n // Fallback: use first company as account (currently SDK returns one company)\n foreach ($associations['companies'] as $accountId) {\n return $accountId;\n }\n\n return null;\n }\n\n private function buildOpportunityData(\n array $properties,\n ?int $accountId,\n ?BusinessProcess $businessProcess,\n ?Stage $stage\n ): array {\n $ownerId = null;\n $profile = null;\n if (! empty($properties['hubspot_owner_id'])) {\n $ownerId = $properties['hubspot_owner_id'];\n $profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);\n }\n\n $name = 'Unknown';\n if (isset($properties['dealname'])) {\n $name = mb_strimwidth($properties['dealname'], 0, 128);\n }\n\n $amount = $this->resolveAmount($properties);\n $currency = $properties['deal_currency_code'] ?? null;\n\n $closeDate = null;\n if (! empty($properties['closedate'])) {\n $closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');\n }\n\n $remotelyCreatedAt = null;\n if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {\n $date = $this->parseCleanDatetime($properties['createdate']);\n $remotelyCreatedAt = $date?->format('Y-m-d H:i:s');\n }\n\n $closedStages = $this->getClosedDealStages();\n $isWon = in_array($properties['dealstage'], $closedStages['won']);\n $isLost = in_array($properties['dealstage'], $closedStages['lost']);\n\n $data = [\n 'team_id' => $this->team->getId(),\n 'user_id' => $profile ? $profile->user_id : null,\n 'owner_id' => $ownerId,\n 'name' => $name,\n 'value' => ! empty($amount) ? $amount : null,\n 'currency_code' => CurrencyFormatter::formatCode($currency),\n 'close_date' => $closeDate,\n 'is_closed' => $isWon || $isLost,\n 'is_won' => $isWon,\n 'remotely_created_at' => $remotelyCreatedAt,\n 'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),\n 'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),\n ];\n\n if ($accountId) {\n $data['account_id'] = $accountId;\n }\n\n if ($stage) {\n $data['stage_id'] = $stage->id;\n }\n\n if ($businessProcess) {\n $recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);\n if ($recordType) {\n $data['record_type_id'] = $recordType->id;\n }\n }\n\n return $data;\n }\n\n private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess\n {\n if ($pipelineId === null) {\n return null;\n }\n\n $cacheKey = $this->getBusinessProcessCacheKey($pipelineId);\n if (isset($this->cachedBusinessProcesses[$cacheKey])) {\n return $this->cachedBusinessProcesses[$cacheKey];\n }\n\n $businessProcess = $this->getBusinessProcess($pipelineId);\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->importStages();\n $businessProcess = $this->getBusinessProcess($pipelineId);\n }\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->logger->info(\n '[HubSpot] Deal is not attached to a pipeline',\n [\n 'pipeline' => $pipelineId]\n );\n }\n\n $this->cachedBusinessProcesses[$cacheKey] = $businessProcess;\n\n return $businessProcess;\n }\n\n private function getBusinessProcess(string $pipelineId): ?BusinessProcess\n {\n return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);\n }\n\n private function getBusinessProcessCacheKey(string $pipelineId): string\n {\n return $this->config->getId() . '_' . $pipelineId;\n }\n\n private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage\n {\n if (empty($stageId)) {\n return null;\n }\n\n $cacheKey = $businessProcess->getId() . ':' . $stageId;\n if (isset($this->cachedStages[$cacheKey])) {\n return $this->cachedStages[$cacheKey];\n }\n\n $stage = $this->crmEntityRepository->getPipelineStageByConditions(\n $businessProcess,\n [\n 'crm_provider_id' => $stageId,\n 'type' => Stage::TYPE_OPPORTUNITY,\n ]\n );\n\n if ($stage === null) {\n $this->importStages(null, $stageId);\n }\n\n if ($stage === null) {\n $this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);\n }\n\n $this->cachedStages[$cacheKey] = $stage;\n\n return $stage;\n }\n\n private function resolveAmount(array $properties): ?string\n {\n $amount = null;\n if (! empty($properties['amount'])) {\n $amount = str_replace(',', '', $properties['amount']);\n }\n\n if ($this->config->hasDefaultCurrencyFieldSet()) {\n $valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();\n $amount = $properties[$valueFieldName] ?? $amount;\n }\n\n return $amount;\n }\n\n private function parseCleanDatetime(string $datetime): ?Carbon\n {\n // Treat pre-1980 values as invalid\n $minValidDate = Carbon::parse('1980-01-01 00:00:00');\n\n try {\n $date = Carbon::parse($datetime);\n\n if ($minValidDate->gt($date)) {\n return null;\n }\n\n return $date;\n } catch (Exception) {\n return null; // On parse error, treat as null\n }\n }\n\n private function resolveDealProbability(?string $stageProbability): int\n {\n if ($stageProbability === null) {\n return 0;\n }\n\n $probability = (float) $stageProbability;\n\n return $probability > 1 ? 0 : (int) ($probability * 100);\n }\n\n private function resolveForecastCategory(?string $forecastCategory): string\n {\n if (! $forecastCategory) {\n return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;\n }\n\n $forecastCategory = str_replace('_', ' ', $forecastCategory);\n\n return ucwords(strtolower($forecastCategory));\n }\n\n private function importExternalFieldData(array $properties, int $opportunityId): void\n {\n $crmFields = $this->getOpportunitySyncableFields();\n $this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);\n }\n\n private function importOpportunityContacts(Opportunity $opportunity, array $associations): void\n {\n // Handle empty or missing contact associations\n if (empty($associations)) {\n // Remove all existing contact associations if none provided\n $this->removeAllOpportunityContacts($opportunity);\n\n return;\n }\n\n // Use differential sync approach for better performance and accuracy\n $this->syncOpportunityContactsDifferential($opportunity, $associations);\n }\n\n /**\n * Sync opportunity contacts using differential approach\n * This compares current vs new associations and only makes necessary changes\n */\n private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void\n {\n $currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);\n $contactAssociationIds = array_keys($contactAssociations);\n\n $contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);\n $contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);\n\n if (empty($contactsToAdd) && empty($contactsToRemove)) {\n return;\n }\n\n $this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);\n\n $this->removeContactAssociations($opportunity, $contactsToRemove);\n $this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);\n }\n\n private function getCurrentContactCrmIds(Opportunity $opportunity): array\n {\n return $opportunity->contacts()\n ->pluck('contacts.crm_provider_id')\n ->toArray();\n }\n\n private function logContactAssociationChanges(\n Opportunity $opportunity,\n array $currentContactCrmIds,\n array $contactAssociations,\n array $contactsToAdd,\n array $contactsToRemove\n ): void {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [\n 'opportunity_id' => $opportunity->getId(),\n 'current_contacts' => $currentContactCrmIds,\n 'new_contacts' => $contactAssociations,\n 'contacts_to_add' => $contactsToAdd,\n 'contacts_to_remove' => $contactsToRemove,\n ]);\n }\n\n private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void\n {\n if (empty($contactsToRemove)) {\n return;\n }\n\n $contactsToDetach = $opportunity->contacts()\n ->whereIn('contacts.crm_provider_id', $contactsToRemove)\n ->pluck('contacts.id')\n ->toArray();\n\n if (! empty($contactsToDetach)) {\n $opportunity->contacts()->detach($contactsToDetach);\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_contact_crm_ids' => $contactsToRemove,\n 'removed_contact_count' => count($contactsToDetach),\n ]);\n }\n }\n\n private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void\n {\n if (empty($contactsToAdd)) {\n return;\n }\n\n $contactsAdded = [];\n foreach ($contactsToAdd as $crmId) {\n $id = $contactAssociations[$crmId];\n\n if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {\n $contactsAdded[] = $crmId;\n }\n }\n\n $this->logAddedContacts($opportunity, $contactsAdded);\n }\n\n private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool\n {\n try {\n $contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);\n\n if (! $contact) {\n return false;\n }\n\n return $this->performContactAttachment($opportunity, $contact, $crmId);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [\n 'opportunity_id' => $opportunity->getId(),\n 'contact_crm_id' => $crmId,\n 'error' => $e->getMessage(),\n ]);\n\n return false;\n }\n }\n\n private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool\n {\n try {\n $opportunity->contacts()->attach($contact->getId(), [\n 'crm_provider_id' => $crmId,\n ]);\n\n return true;\n } catch (\\Illuminate\\Database\\QueryException $e) {\n if (str_contains($e->getMessage(), 'Duplicate entry')) {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [\n 'contact_id' => $contact->getId(),\n 'contact_crm_id' => $crmId,\n 'opportunity_id' => $opportunity->getId(),\n ]);\n\n return false;\n }\n\n throw $e;\n }\n }\n\n private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void\n {\n if (! empty($contactsAdded)) {\n $this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'added_contact_crm_ids' => $contactsAdded,\n 'added_contacts_count' => count($contactsAdded),\n ]);\n }\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot\\ServiceTraits;\n\nuse Carbon\\Carbon;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\CollectionResponseAssociatedId;\nuse Jiminny\\Exceptions\\InvalidArgumentException;\nuse Jiminny\\Models\\Account;\nuse Exception;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Jobs\\Crm\\MatchActivitiesToNewOpportunity;\nuse Jiminny\\Models\\Contact;\nuse Jiminny\\Models\\Crm\\BusinessProcess;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Models\\Opportunity;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Repositories\\Crm\\CrmEntityRepository;\nuse Jiminny\\Services\\Crm\\Hubspot\\DealFieldsService;\nuse Jiminny\\Services\\Crm\\Hubspot\\OpportunitySyncStrategy\\HubspotSingleSyncStrategy;\nuse Jiminny\\Services\\Crm\\Hubspot\\WebhookSyncBatchProcessor;\nuse Jiminny\\Services\\Crm\\OpportunitySyncStrategyResolver;\nuse Jiminny\\Utils\\CurrencyFormatter;\n\n/**\n * Optimized sync methods for better performance\n * These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains\n */\ntrait OpportunitySyncTrait\n{\n private const int BATCH_SIZE = 100;\n private const int BATCH_PROCESS_SIZE = 800;\n\n protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;\n protected CrmEntityRepository $crmEntityRepository;\n protected DealFieldsService $dealFieldsService;\n\n private ?array $cachedClosedDealStages = null;\n private array $cachedBusinessProcesses = [];\n private array $cachedStages = [];\n\n public function syncOpportunities(array $parameters, ?string $strategy = null): int\n {\n $startTime = microtime(true);\n $strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);\n $parameters['config'] = $this->config;\n $syncCount = 0;\n $reportedTotal = 0;\n $lastSyncedId = [];\n $strategyNames = [];\n\n try {\n foreach ($strategies as $strategyName => $syncStrategy) {\n $strategyNames[] = $strategyName;\n $this->logger->info(\n '[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,\n ['team' => $this->team->getId()]\n );\n\n $total = 0;\n $lastId = null;\n $buffer = [];\n\n // HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies\n foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {\n $buffer[] = $hsOpportunity;\n\n // process every 800 rows (fits < 1 000 association limit)\n if (\\count($buffer) >= self::BATCH_PROCESS_SIZE) {\n $syncCount += $this->processOpportunityBatch($buffer);\n $buffer = [];\n }\n }\n\n // leftovers\n if ($buffer) {\n $syncCount += $this->processOpportunityBatch($buffer);\n }\n\n $reportedTotal += $total;\n $lastSyncedId = $lastId;\n }\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException | CrmException $e) {\n $this->handleSyncException($e, $parameters);\n }\n\n $durationMs = round((microtime(true) - $startTime) * 1000, 2);\n $this->logger->info(\n '[HubSpot] Synced opportunities',\n [\n 'team' => $this->team->getId(),\n 'strategies' => implode(',', $strategyNames),\n 'sync_count' => $syncCount,\n 'total' => $reportedTotal,\n 'last_synced_id' => $lastSyncedId,\n 'duration_ms' => $durationMs,\n ]\n );\n\n return $reportedTotal;\n }\n\n private function handleSyncException(\\Throwable $e, array $parameters): void\n {\n if (($parameters['since'] ?? null) instanceof Carbon) {\n $parameters['since'] = $parameters['since']->toDateTimeString();\n }\n $parameters['config'] = $this->config->getId();\n\n $this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [\n 'teamId' => $this->team->getUuid(),\n 'parameters' => $parameters,\n 'reason' => $e->getMessage(),\n ]);\n }\n\n /**\n * @inheritdoc\n */\n public function syncOpportunity(string $crmId): ?Opportunity\n {\n $strategy = $this->opportunitySyncStrategyResolver->resolve(\n $this->config,\n OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,\n );\n\n $parameters = [\n 'config' => $this->config,\n 'crm_id' => $crmId,\n ];\n\n try {\n if (! $strategy instanceof HubspotSingleSyncStrategy) {\n throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');\n }\n\n $hsOpportunity = $strategy->fetchOpportunity($parameters);\n } catch (\\HubSpot\\Client\\Crm\\Deals\\ApiException $e) {\n $this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [\n 'teamId' => $this->team->getUuid(),\n 'crmId' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n return null;\n }\n\n $hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);\n\n return $this->importOrUpdateOpportunity($hsOpportunity);\n }\n\n /**\n * Process webhook-collected opportunity batches.\n *\n * Drains Redis sets containing company CRM IDs collected from webhook events\n * and dispatches ImportOpportunityBatch jobs for batch processing.\n *\n * @return int Number of opportunity IDs dispatched to jobs\n */\n public function batchSyncOpportunities(): int\n {\n $configId = $this->team->getCrmConfiguration()->getId();\n\n return $this->batchProcessor->processBatchesForObjectType(\n WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,\n $configId\n );\n }\n\n /**\n * Import a batch of opportunities by their CRM IDs.\n * Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().\n *\n * @param array<string> $crmIds HubSpot deal CRM IDs\n *\n * @return array{success: array, failed_ids: array, errors?: array<string, string>}\n */\n public function importOpportunityBatchByIds(array $crmIds): array\n {\n $fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);\n\n $allDeals = [];\n foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {\n $deals = $this->client->getOpportunitiesByIds($chunk, $fields);\n foreach ($deals as $deal) {\n $allDeals[] = $deal;\n }\n }\n\n // IDs not returned by HubSpot are likely deleted or inaccessible deals.\n // These are not failures — retrying won't bring them back.\n $fetchedIds = array_map('strval', array_column($allDeals, 'id'));\n $notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));\n\n if (! empty($notFoundIds)) {\n $this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [\n 'teamId' => $this->team->getId(),\n 'notFoundCount' => \\count($notFoundIds),\n 'notFoundIds' => $notFoundIds,\n 'requestedCount' => \\count($crmIds),\n 'fetchedCount' => \\count($allDeals),\n ]);\n }\n\n if (empty($allDeals)) {\n return ['success' => [], 'failed_ids' => []];\n }\n\n return $this->importOpportunityBatch($allDeals);\n }\n\n private function getClosedDealStages(): array\n {\n if ($this->cachedClosedDealStages !== null) {\n return $this->cachedClosedDealStages;\n }\n\n $stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);\n $data = [\n 'lost' => [],\n 'won' => [],\n ];\n\n foreach ($stages as $stage) {\n if ($stage->probability == 0.00) {\n $data['lost'][] = $stage->crm_provider_id;\n }\n if ($stage->probability == 100.00) {\n $data['won'][] = $stage->crm_provider_id;\n }\n }\n\n $this->cachedClosedDealStages = $data;\n\n return $data;\n }\n\n /**\n * Import deals into the database with pre-fetched associations.\n *\n * API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT\n * caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()\n * where Laravel retries the whole job with backoff. After all retries exhausted,\n * failed() requeues all IDs to Redis.\n *\n * The per-deal loop catches exceptions individually. A deal can end up in three states:\n * - success: imported/updated successfully\n * - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)\n * These are permanent issues — retrying won't fix them.\n * - skipped (null): missing dependencies (no account, unknown pipeline/stage).\n * This is acceptable — the deal cannot be imported until those exist.\n */\n private function importOpportunityBatch(array $deals): array\n {\n $syncedOpportunities = [\n 'success' => [],\n 'failed_ids' => [],\n ];\n $dealIds = array_column($deals, 'id');\n\n // Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the\n // queue job retries the whole batch and eventually requeues all deal IDs back to Redis.\n try {\n $companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');\n $contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');\n\n $associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);\n\n $existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(\n $this->config,\n array_map('strval', $dealIds)\n );\n $existingCrmIdSet = array_flip($existingCrmIds);\n } catch (\\Throwable $e) {\n $this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [\n 'teamId' => $this->team->getId(),\n 'dealCount' => count($dealIds),\n 'error' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n foreach ($deals as $deal) {\n try {\n $deal['associations'] = $this->prepareAssociationsForOpportunity(\n $deal['id'],\n $companyAssociations,\n $contactAssociations,\n $associationsData\n );\n\n $syncedOpportunity = $this->importOrUpdateOpportunity(\n $deal,\n isset($existingCrmIdSet[(string) $deal['id']])\n );\n if ($syncedOpportunity) {\n $syncedOpportunities['success'][] = $syncedOpportunity;\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [\n 'teamId' => $this->team->getId(),\n 'crmId' => $deal['id'],\n 'error' => $e->getMessage(),\n ]);\n $syncedOpportunities['failed_ids'][] = $deal['id'];\n $syncedOpportunities['errors'][$deal['id']] = $e->getMessage();\n }\n }\n\n return $syncedOpportunities;\n }\n\n /**\n * Prepare associated entities for opportunities with optimized batch processing\n * Returns structured data with CRM ID to DB ID mappings for each opportunity\n */\n private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array\n {\n // Step 1: Collect all unique company and contact IDs from associations\n $allCompanyIds = $this->flattenAssociationIds($companyAssociations);\n $allContactIds = $this->flattenAssociationIds($contactAssociations);\n\n // Step 2: Batch sync missing entities and get CRM ID to DB ID mappings\n $companyIdMappings = [];\n $contactIdMappings = [];\n\n if (! empty($allCompanyIds)) {\n $companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);\n }\n\n if (! empty($allContactIds)) {\n $contactIdMappings = $this->prepareAssociatedContacts($allContactIds);\n }\n\n return [\n 'company_id_mappings' => $companyIdMappings,\n 'contact_id_mappings' => $contactIdMappings,\n ];\n }\n\n /**\n * Flatten association data to get unique IDs\n */\n private function flattenAssociationIds(array $associations): array\n {\n $ids = [];\n foreach ($associations as $dealAssociations) {\n if (is_array($dealAssociations)) {\n foreach ($dealAssociations as $id) {\n $ids[$id] = true;\n }\n }\n }\n\n return array_keys($ids);\n }\n\n /**\n * Batch sync missing accounts\n */\n private function prepareAssociatedAccounts(array $companyIds): array\n {\n // Find which accounts already exist\n $existingAccounts = $this->crmEntityRepository\n ->findAccountsByExternalIds($this->config, $companyIds);\n\n $existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();\n\n $existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {\n return [$account->getCrmProviderId() => $account->getId()];\n })->toArray();\n\n $missingCompanyIds = array_diff($companyIds, $existingCompanyIds);\n\n if (empty($missingCompanyIds)) {\n return $existingAccountsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [\n 'teamId' => $this->team->getUuid(),\n 'total_companies' => count($companyIds),\n 'existing_companies' => count($existingCompanyIds),\n 'missing_companies' => count($missingCompanyIds),\n ]);\n\n // we already have limit on opportunity ids count\n // Initialize variable before try block\n $syncedAccountsData = [];\n\n try {\n $syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [\n 'size' => count($missingCompanyIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedAccountsData = [];\n }\n\n return $existingAccountsData + $syncedAccountsData;\n }\n\n /**\n * Prepare associated contacts - find existing and sync missing ones\n * Returns mapping of CRM ID to DB ID\n */\n private function prepareAssociatedContacts(array $contactIds): array\n {\n // Find which contacts already exist\n $existingContacts = $this->crmEntityRepository\n ->findContactsByExternalIds($this->config, $contactIds);\n\n $existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();\n\n // Create mapping for existing contacts\n $existingContactsData = $existingContacts->mapWithKeys(function ($contact) {\n return [$contact->getCrmProviderId() => $contact->getId()];\n })->toArray();\n\n $missingContactIds = array_diff($contactIds, $existingContactIds);\n\n if (empty($missingContactIds)) {\n return $existingContactsData;\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [\n 'teamId' => $this->team->getUuid(),\n 'total_contacts' => count($contactIds),\n 'existing_contacts' => count($existingContactIds),\n 'missing_contacts' => count($missingContactIds),\n ]);\n\n // Sync missing contacts using batch API\n try {\n $syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [\n 'size' => count($missingContactIds),\n 'error' => $e->getMessage(),\n ]);\n $syncedContactsData = [];\n }\n\n return $existingContactsData + $syncedContactsData;\n }\n\n private function batchSyncCrmObjects(string $objectType, array $crmIds): array\n {\n $syncObjects = [];\n $crmObjectIds = array_values($crmIds);\n\n foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {\n try {\n $objects = $objectType === 'companies' ?\n $this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :\n $this->client->getContactsByIds($chunk, $this->getContactFields());\n\n foreach ($objects as $objectId => $objectData) {\n $this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);\n }\n\n $this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [\n 'requested_count' => count($chunk),\n 'synced_count' => count($objects),\n ]);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [\n 'ids' => $chunk,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n return $syncObjects;\n }\n\n private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void\n {\n try {\n $object = $objectType === 'companies' ?\n $this->importAccount($objectData) :\n $this->importContact($objectData);\n\n if ($object) {\n $syncObjects[$object->getCrmProviderId()] = $object->getId();\n }\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [\n 'id' => $objectId,\n 'error' => $e->getMessage(),\n ]);\n }\n }\n\n /**\n * Prepare associations for a single opportunity\n *\n * The return value is an array with the following structure:\n * [\n * 'companies' => [\n * $companyCrmId => $companyId,\n * ...\n * ],\n * 'contacts' => [\n * $contactCrmId => $contactId,\n * ...\n * ],\n * 'account_id' => $accountId,\n * ]\n */\n private function prepareAssociationsForOpportunity(\n string $oppCrmId,\n array $companyAssociations,\n array $contactAssociations,\n array $associationsData\n ): array {\n $associations = [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n\n $oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];\n foreach ($oppCompanyIds as $companyCrmId) {\n if (isset($associationsData['company_id_mappings'][$companyCrmId])) {\n $associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];\n\n // Set primary account (first company becomes primary account)\n if ($associations['account_id'] === null) {\n $associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];\n }\n }\n }\n\n $oppContactIds = $contactAssociations[$oppCrmId] ?? [];\n foreach ($oppContactIds as $contactCrmId) {\n if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {\n $associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];\n }\n }\n\n return $associations;\n }\n\n /**\n * Update only associations for an opportunity\n */\n private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void\n {\n // Update contact associations\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n // Update company (account) associations\n $this->updateOpportunityAccount($opportunity, $associations['account_id']);\n }\n\n /**\n * Remove all contact associations from an opportunity\n */\n private function removeAllOpportunityContacts(Opportunity $opportunity): void\n {\n $currentCount = (int) $opportunity->contacts()->count();\n\n if ($currentCount > 0) {\n $opportunity->contacts()->detach();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_count' => $currentCount,\n ]);\n }\n }\n\n private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void\n {\n if ($accountId === null) {\n // No account ID provided - keep current account\n return;\n }\n\n $currentAccountId = $opportunity->getAccountId();\n\n // Only update if account has changed\n if ($currentAccountId !== $accountId) {\n $opportunity->account_id = $accountId;\n $opportunity->save();\n\n $this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [\n 'opportunity_id' => $opportunity->getId(),\n 'old_account_id' => $currentAccountId,\n 'new_account_id' => $accountId,\n ]);\n }\n }\n\n /**\n * Find existing opportunities by external IDs (OPTIMIZED VERSION)\n * Uses batch query for better performance\n */\n private function findExistingOpportunities(array $crmIds): Collection\n {\n return $this->crmEntityRepository\n ->findOpportunitiesByExternalIds($this->config, $crmIds);\n }\n\n private function processOpportunityBatch(array $opportunities): int\n {\n $syncedOpportunities = $this->importOpportunityBatch($opportunities);\n\n return count($syncedOpportunities['success'] ?? []);\n }\n\n /**\n * Convert single deal associations from HubSpot format to internal format\n * Handles both HubSpot SDK objects and array formats\n *\n * @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed\n *\n * @return array Processed associations with DB IDs\n */\n private function convertDealAssociations(array $opportunityAssociations): array\n {\n $associations = $this->initializeAssociationsStructure();\n\n if (empty($opportunityAssociations)) {\n return $associations;\n }\n\n $associationIds = $this->extractAssociationIds($opportunityAssociations);\n\n $this->processCompanyAssociations($associationIds, $associations);\n $this->processContactAssociations($associationIds, $associations);\n\n return $associations;\n }\n\n private function initializeAssociationsStructure(): array\n {\n return [\n 'companies' => [],\n 'contacts' => [],\n 'account_id' => null, // Primary account for opportunity\n ];\n }\n\n private function extractAssociationIds(array $opportunityAssociations): array\n {\n $associationIds = [];\n\n foreach ($opportunityAssociations as $type => $associationData) {\n if (! empty($associationData)) {\n $associationIds[$type] = $this->convertSingleDealAssociations($associationData);\n }\n }\n\n return $associationIds;\n }\n\n private function processCompanyAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['companies'])) {\n return;\n }\n\n $companyId = $associationIds['companies'][0];\n $account = $this->findOrSyncAccount($companyId);\n\n if ($account instanceof Account) {\n $associations['companies'][$companyId] = $account->getId();\n $associations['account_id'] = $account->getId();\n }\n }\n\n private function processContactAssociations(array $associationIds, array &$associations): void\n {\n if (empty($associationIds['contacts'])) {\n return;\n }\n\n foreach ($associationIds['contacts'] as $contactId) {\n $contact = $this->findOrSyncContact($contactId);\n\n if ($contact instanceof Contact) {\n $associations['contacts'][$contactId] = $contact->getId();\n }\n }\n }\n\n private function findOrSyncAccount(string $companyId): ?Account\n {\n $account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);\n\n if (! $account instanceof Account) {\n $account = $this->syncAccount($companyId);\n }\n\n return $account;\n }\n\n private function findOrSyncContact(string $contactId): ?Contact\n {\n $contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);\n\n if (! $contact instanceof Contact) {\n $contact = $this->syncContact($contactId);\n }\n\n return $contact;\n }\n\n private function convertSingleDealAssociations($opportunityAssociations = null): array\n {\n $associationData = [];\n\n if ($opportunityAssociations === null) {\n return $associationData;\n }\n\n // Handle array input (from extractAssociationIds)\n if (is_array($opportunityAssociations)) {\n return $opportunityAssociations;\n }\n\n // Handle CollectionResponseAssociatedId object\n if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {\n foreach ($opportunityAssociations->getResults() as $association) {\n $associationData[] = $association->getId();\n }\n }\n\n return $associationData;\n }\n\n private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity\n {\n if (empty($crmData['properties'])) {\n return null;\n }\n\n $crmId = (string) $crmData['id'];\n $properties = $crmData['properties'];\n $associations = $crmData['associations'] ?? [];\n\n $opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(\n $this->config,\n $crmId\n );\n\n if ($opportunityExists) {\n return $this->updateOpportunity($crmId, $properties, $associations);\n }\n\n return $this->createOpportunity($crmId, $properties, $associations);\n }\n\n /**\n * Create new opportunity\n */\n private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n if (! $accountId) {\n return null;\n }\n\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n if (! $businessProcess) {\n return null;\n }\n\n $stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);\n if (! $stage) {\n return null;\n }\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->importOpportunityContacts($opportunity, $associations['contacts']);\n\n if ($opportunity->wasRecentlyCreated) {\n MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());\n }\n\n return $opportunity;\n }\n\n /**\n * Update existing opportunity\n */\n private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity\n {\n $accountId = $this->resolveAccountId($associations);\n $businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);\n $stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;\n\n $data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);\n\n $attributes = [\n 'crm_configuration_id' => $this->config->getId(),\n 'crm_provider_id' => $crmId,\n ];\n\n $values = array_merge($attributes, $data);\n $opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);\n\n $this->importExternalFieldData($properties, $opportunity->getId());\n $this->updateOpportunityAssociations($opportunity, $associations);\n\n return $opportunity;\n }\n\n private function resolveAccountId(array $associations): ?int\n {\n if (! empty($associations['account_id'])) {\n return $associations['account_id'];\n }\n\n if (empty($associations)) {\n return null;\n }\n\n // Fallback: use first company as account (currently SDK returns one company)\n foreach ($associations['companies'] as $accountId) {\n return $accountId;\n }\n\n return null;\n }\n\n private function buildOpportunityData(\n array $properties,\n ?int $accountId,\n ?BusinessProcess $businessProcess,\n ?Stage $stage\n ): array {\n $ownerId = null;\n $profile = null;\n if (! empty($properties['hubspot_owner_id'])) {\n $ownerId = $properties['hubspot_owner_id'];\n $profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);\n }\n\n $name = 'Unknown';\n if (isset($properties['dealname'])) {\n $name = mb_strimwidth($properties['dealname'], 0, 128);\n }\n\n $amount = $this->resolveAmount($properties);\n $currency = $properties['deal_currency_code'] ?? null;\n\n $closeDate = null;\n if (! empty($properties['closedate'])) {\n $closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');\n }\n\n $remotelyCreatedAt = null;\n if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {\n $date = $this->parseCleanDatetime($properties['createdate']);\n $remotelyCreatedAt = $date?->format('Y-m-d H:i:s');\n }\n\n $closedStages = $this->getClosedDealStages();\n $isWon = in_array($properties['dealstage'], $closedStages['won']);\n $isLost = in_array($properties['dealstage'], $closedStages['lost']);\n\n $data = [\n 'team_id' => $this->team->getId(),\n 'user_id' => $profile ? $profile->user_id : null,\n 'owner_id' => $ownerId,\n 'name' => $name,\n 'value' => ! empty($amount) ? $amount : null,\n 'currency_code' => CurrencyFormatter::formatCode($currency),\n 'close_date' => $closeDate,\n 'is_closed' => $isWon || $isLost,\n 'is_won' => $isWon,\n 'remotely_created_at' => $remotelyCreatedAt,\n 'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),\n 'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),\n ];\n\n if ($accountId) {\n $data['account_id'] = $accountId;\n }\n\n if ($stage) {\n $data['stage_id'] = $stage->id;\n }\n\n if ($businessProcess) {\n $recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);\n if ($recordType) {\n $data['record_type_id'] = $recordType->id;\n }\n }\n\n return $data;\n }\n\n private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess\n {\n if ($pipelineId === null) {\n return null;\n }\n\n $cacheKey = $this->getBusinessProcessCacheKey($pipelineId);\n if (isset($this->cachedBusinessProcesses[$cacheKey])) {\n return $this->cachedBusinessProcesses[$cacheKey];\n }\n\n $businessProcess = $this->getBusinessProcess($pipelineId);\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->importStages();\n $businessProcess = $this->getBusinessProcess($pipelineId);\n }\n\n if (! $businessProcess instanceof BusinessProcess) {\n $this->logger->info(\n '[HubSpot] Deal is not attached to a pipeline',\n [\n 'pipeline' => $pipelineId]\n );\n }\n\n $this->cachedBusinessProcesses[$cacheKey] = $businessProcess;\n\n return $businessProcess;\n }\n\n private function getBusinessProcess(string $pipelineId): ?BusinessProcess\n {\n return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);\n }\n\n private function getBusinessProcessCacheKey(string $pipelineId): string\n {\n return $this->config->getId() . '_' . $pipelineId;\n }\n\n private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage\n {\n if (empty($stageId)) {\n return null;\n }\n\n $cacheKey = $businessProcess->getId() . ':' . $stageId;\n if (isset($this->cachedStages[$cacheKey])) {\n return $this->cachedStages[$cacheKey];\n }\n\n $stage = $this->crmEntityRepository->getPipelineStageByConditions(\n $businessProcess,\n [\n 'crm_provider_id' => $stageId,\n 'type' => Stage::TYPE_OPPORTUNITY,\n ]\n );\n\n if ($stage === null) {\n $this->importStages(null, $stageId);\n }\n\n if ($stage === null) {\n $this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);\n }\n\n $this->cachedStages[$cacheKey] = $stage;\n\n return $stage;\n }\n\n private function resolveAmount(array $properties): ?string\n {\n $amount = null;\n if (! empty($properties['amount'])) {\n $amount = str_replace(',', '', $properties['amount']);\n }\n\n if ($this->config->hasDefaultCurrencyFieldSet()) {\n $valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();\n $amount = $properties[$valueFieldName] ?? $amount;\n }\n\n return $amount;\n }\n\n private function parseCleanDatetime(string $datetime): ?Carbon\n {\n // Treat pre-1980 values as invalid\n $minValidDate = Carbon::parse('1980-01-01 00:00:00');\n\n try {\n $date = Carbon::parse($datetime);\n\n if ($minValidDate->gt($date)) {\n return null;\n }\n\n return $date;\n } catch (Exception) {\n return null; // On parse error, treat as null\n }\n }\n\n private function resolveDealProbability(?string $stageProbability): int\n {\n if ($stageProbability === null) {\n return 0;\n }\n\n $probability = (float) $stageProbability;\n\n return $probability > 1 ? 0 : (int) ($probability * 100);\n }\n\n private function resolveForecastCategory(?string $forecastCategory): string\n {\n if (! $forecastCategory) {\n return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;\n }\n\n $forecastCategory = str_replace('_', ' ', $forecastCategory);\n\n return ucwords(strtolower($forecastCategory));\n }\n\n private function importExternalFieldData(array $properties, int $opportunityId): void\n {\n $crmFields = $this->getOpportunitySyncableFields();\n $this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);\n }\n\n private function importOpportunityContacts(Opportunity $opportunity, array $associations): void\n {\n // Handle empty or missing contact associations\n if (empty($associations)) {\n // Remove all existing contact associations if none provided\n $this->removeAllOpportunityContacts($opportunity);\n\n return;\n }\n\n // Use differential sync approach for better performance and accuracy\n $this->syncOpportunityContactsDifferential($opportunity, $associations);\n }\n\n /**\n * Sync opportunity contacts using differential approach\n * This compares current vs new associations and only makes necessary changes\n */\n private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void\n {\n $currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);\n $contactAssociationIds = array_keys($contactAssociations);\n\n $contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);\n $contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);\n\n if (empty($contactsToAdd) && empty($contactsToRemove)) {\n return;\n }\n\n $this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);\n\n $this->removeContactAssociations($opportunity, $contactsToRemove);\n $this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);\n }\n\n private function getCurrentContactCrmIds(Opportunity $opportunity): array\n {\n return $opportunity->contacts()\n ->pluck('contacts.crm_provider_id')\n ->toArray();\n }\n\n private function logContactAssociationChanges(\n Opportunity $opportunity,\n array $currentContactCrmIds,\n array $contactAssociations,\n array $contactsToAdd,\n array $contactsToRemove\n ): void {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [\n 'opportunity_id' => $opportunity->getId(),\n 'current_contacts' => $currentContactCrmIds,\n 'new_contacts' => $contactAssociations,\n 'contacts_to_add' => $contactsToAdd,\n 'contacts_to_remove' => $contactsToRemove,\n ]);\n }\n\n private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void\n {\n if (empty($contactsToRemove)) {\n return;\n }\n\n $contactsToDetach = $opportunity->contacts()\n ->whereIn('contacts.crm_provider_id', $contactsToRemove)\n ->pluck('contacts.id')\n ->toArray();\n\n if (! empty($contactsToDetach)) {\n $opportunity->contacts()->detach($contactsToDetach);\n\n $this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'removed_contact_crm_ids' => $contactsToRemove,\n 'removed_contact_count' => count($contactsToDetach),\n ]);\n }\n }\n\n private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void\n {\n if (empty($contactsToAdd)) {\n return;\n }\n\n $contactsAdded = [];\n foreach ($contactsToAdd as $crmId) {\n $id = $contactAssociations[$crmId];\n\n if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {\n $contactsAdded[] = $crmId;\n }\n }\n\n $this->logAddedContacts($opportunity, $contactsAdded);\n }\n\n private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool\n {\n try {\n $contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);\n\n if (! $contact) {\n return false;\n }\n\n return $this->performContactAttachment($opportunity, $contact, $crmId);\n } catch (\\Throwable $e) {\n $this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [\n 'opportunity_id' => $opportunity->getId(),\n 'contact_crm_id' => $crmId,\n 'error' => $e->getMessage(),\n ]);\n\n return false;\n }\n }\n\n private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool\n {\n try {\n $opportunity->contacts()->attach($contact->getId(), [\n 'crm_provider_id' => $crmId,\n ]);\n\n return true;\n } catch (\\Illuminate\\Database\\QueryException $e) {\n if (str_contains($e->getMessage(), 'Duplicate entry')) {\n $this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [\n 'contact_id' => $contact->getId(),\n 'contact_crm_id' => $crmId,\n 'opportunity_id' => $opportunity->getId(),\n ]);\n\n return false;\n }\n\n throw $e;\n }\n }\n\n private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void\n {\n if (! empty($contactsAdded)) {\n $this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [\n 'opportunity_id' => $opportunity->getId(),\n 'added_contact_crm_ids' => $contactsAdded,\n 'added_contacts_count' => count($contactsAdded),\n ]);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Script","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"6","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"# **************************** HS **************************************\n\nselect * from teams where id = 2; # 2\nselect * from features; # 2\nselect * from team_features where team_id = 2; # 2\nselect * from crm_configurations where id = 2; # 2\nselect * from users where team_id = 2; #\nselect * from playbooks where team_id = 2; # event 38\nselect * from playbook_categories where playbook_id = 38; #\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;\nhttps://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624\n https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0\n\nSELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;\n# 609126 softphone tr. 11241\n\nSELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;\n# 608874 conference tr. 11226 crmId: 103422236596\n\nselect * from ai_prompts where transcription_id IN (11241, 11226);\nselect * from activity_summary_logs where activity_id = 608874;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nselect * from crm_field_data where activity_id = 1223;\n\nselect * from crm_layouts where crm_configuration_id = 2;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);\nselect * from crm_fields where crm_configuration_id = 11 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);\n\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u\n on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 2 and sa.provider = 'hubspot';\n\nselect * from opportunities where team_id = 2\nand crm_provider_id IN ('51317301383');\n\nselect * from contacts where id = 85;\n\nselect * from opportunities where team_id = 2 order by id desc;\nselect * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112\nselect * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112\nselect * from opportunity_contacts where opportunity_id = 5117;\nselect * from crm_field_data where object_id = 1365;\nSELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);\n\nselect * from features;\nselect * from team_features where team_id IN (1);\nselect * from team_features where feature_id IN (36);\n\nSHOW CREATE TABLE opportunity_contacts;\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';\n\n# $slug = 'HUBSPOT_WEBHOOK_SYNC';\n# $team = Jiminny\\Models\\Team::find(2);\n# $feature = Feature::query()->where('slug', $slug)->first();\n# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);\n\n# hubspot_webhook_metrics\n\nselect * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365\nSELECT * FROM opportunity_contacts WHERE opportunity_id = '414';\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';\nselect * from contacts where id in (414, 464);\n\nselect * from activities where crm_configuration_id = 2;\n\nselect settings from crm_configurations where id = 11;\n\nselect * from teams; # 1, 2\nselect * from users;\nselect * from crm_configurations where id = 39;\nselect * from team_features where team_id = 2;\nselect * from features;\n# SELECT * FROM opportunities WHERE crm_configuration_id = 2\n# order by id desc;\n# and crm_provider_id = '49908861993';\n\n\nselect * from activity_providers where id IN (443, 202, 203, 227);\n\nselect * from activity_imports where id = 795889;\n\nselect c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id\nwhere c.provider = 'hubspot';\n\nselect * from crm_configurations crm JOIN teams t on crm.team_id = t.id\nwhere provider = 'hubspot';\nSELECT * FROM teams WHERE id = 31;\nSELECT * FROM users WHERE id = 257;\nSELECT * FROM opportunities WHERE team_id = 2;\n\nselect * from opportunity_contacts where opportunity_id = 5124;\nselect * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)\n\nselect * from activities where crm_configuration_id = 13;\n\nSELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141\n\n\nselect id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;\nSELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 order by id desc;\nselect * from opportunity_contacts where contact_id = 6223;\nSELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;\n\nselect * from crm_profiles where crm_configuration_id = 2;\n\nselect * from activities where account_id = 46;","depth":4,"value":"# **************************** HS **************************************\n\nselect * from teams where id = 2; # 2\nselect * from features; # 2\nselect * from team_features where team_id = 2; # 2\nselect * from crm_configurations where id = 2; # 2\nselect * from users where team_id = 2; #\nselect * from playbooks where team_id = 2; # event 38\nselect * from playbook_categories where playbook_id = 38; #\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;\nhttps://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624\n https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0\n\nSELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;\n# 609126 softphone tr. 11241\n\nSELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;\n# 608874 conference tr. 11226 crmId: 103422236596\n\nselect * from ai_prompts where transcription_id IN (11241, 11226);\nselect * from activity_summary_logs where activity_id = 608874;\n\nselect * from sidekick_settings;\nselect * from default_activity_types;\n\nselect * from crm_field_data where activity_id = 1223;\n\nselect * from crm_layouts where crm_configuration_id = 2;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);\nselect * from crm_fields where crm_configuration_id = 11 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);\n\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;\nSELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u\n on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 2 and sa.provider = 'hubspot';\n\nselect * from opportunities where team_id = 2\nand crm_provider_id IN ('51317301383');\n\nselect * from contacts where id = 85;\n\nselect * from opportunities where team_id = 2 order by id desc;\nselect * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112\nselect * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112\nselect * from opportunity_contacts where opportunity_id = 5117;\nselect * from crm_field_data where object_id = 1365;\nSELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);\n\nselect * from features;\nselect * from team_features where team_id IN (1);\nselect * from team_features where feature_id IN (36);\n\nSHOW CREATE TABLE opportunity_contacts;\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';\n\n# $slug = 'HUBSPOT_WEBHOOK_SYNC';\n# $team = Jiminny\\Models\\Team::find(2);\n# $feature = Feature::query()->where('slug', $slug)->first();\n# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);\n\n# hubspot_webhook_metrics\n\nselect * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365\nSELECT * FROM opportunity_contacts WHERE opportunity_id = '414';\nSELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';\nselect * from contacts where id in (414, 464);\n\nselect * from activities where crm_configuration_id = 2;\n\nselect settings from crm_configurations where id = 11;\n\nselect * from teams; # 1, 2\nselect * from users;\nselect * from crm_configurations where id = 39;\nselect * from team_features where team_id = 2;\nselect * from features;\n# SELECT * FROM opportunities WHERE crm_configuration_id = 2\n# order by id desc;\n# and crm_provider_id = '49908861993';\n\n\nselect * from activity_providers where id IN (443, 202, 203, 227);\n\nselect * from activity_imports where id = 795889;\n\nselect c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id\nwhere c.provider = 'hubspot';\n\nselect * from crm_configurations crm JOIN teams t on crm.team_id = t.id\nwhere provider = 'hubspot';\nSELECT * FROM teams WHERE id = 31;\nSELECT * FROM users WHERE id = 257;\nSELECT * FROM opportunities WHERE team_id = 2;\n\nselect * from opportunity_contacts where opportunity_id = 5124;\nselect * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)\n\nselect * from activities where crm_configuration_id = 13;\n\nSELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141\n\n\nselect id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;\nSELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;\nSELECT * FROM contacts WHERE team_id = 2 order by id desc;\nselect * from opportunity_contacts where contact_id = 6223;\nSELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;\n\nselect * from crm_profiles where crm_configuration_id = 2;\n\nselect * from activities where account_id = 46;","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8499420549974583503
|
1048134296968532324
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11976 on JY-20553-debug- Project: faVsco.js, menu
#11976 on JY-20553-debug-crm-sync-delays, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
33
2
19
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot\ServiceTraits;
use Carbon\Carbon;
use HubSpot\Client\Crm\Deals\Model\CollectionResponseAssociatedId;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Models\Account;
use Exception;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Jobs\Crm\MatchActivitiesToNewOpportunity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\BusinessProcess;
use Jiminny\Exceptions\CrmException;
use Jiminny\Models\Opportunity;
use Illuminate\Support\Collection;
use Jiminny\Models\Stage;
use Jiminny\Repositories\Crm\CrmEntityRepository;
use Jiminny\Services\Crm\Hubspot\DealFieldsService;
use Jiminny\Services\Crm\Hubspot\OpportunitySyncStrategy\HubspotSingleSyncStrategy;
use Jiminny\Services\Crm\Hubspot\WebhookSyncBatchProcessor;
use Jiminny\Services\Crm\OpportunitySyncStrategyResolver;
use Jiminny\Utils\CurrencyFormatter;
/**
* Optimized sync methods for better performance
* These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains
*/
trait OpportunitySyncTrait
{
private const int BATCH_SIZE = 100;
private const int BATCH_PROCESS_SIZE = 800;
protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;
protected CrmEntityRepository $crmEntityRepository;
protected DealFieldsService $dealFieldsService;
private ?array $cachedClosedDealStages = null;
private array $cachedBusinessProcesses = [];
private array $cachedStages = [];
public function syncOpportunities(array $parameters, ?string $strategy = null): int
{
$startTime = microtime(true);
$strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);
$parameters['config'] = $this->config;
$syncCount = 0;
$reportedTotal = 0;
$lastSyncedId = [];
$strategyNames = [];
try {
foreach ($strategies as $strategyName => $syncStrategy) {
$strategyNames[] = $strategyName;
$this->logger->info(
'[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' . $strategyName,
['team' => $this->team->getId()]
);
$total = 0;
$lastId = null;
$buffer = [];
// HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies
foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {
$buffer[] = $hsOpportunity;
// process every 800 rows (fits < 1 000 association limit)
if (\count($buffer) >= self::BATCH_PROCESS_SIZE) {
$syncCount += $this->processOpportunityBatch($buffer);
$buffer = [];
}
}
// leftovers
if ($buffer) {
$syncCount += $this->processOpportunityBatch($buffer);
}
$reportedTotal += $total;
$lastSyncedId = $lastId;
}
} catch (\HubSpot\Client\Crm\Deals\ApiException | CrmException $e) {
$this->handleSyncException($e, $parameters);
}
$durationMs = round((microtime(true) - $startTime) * 1000, 2);
$this->logger->info(
'[HubSpot] Synced opportunities',
[
'team' => $this->team->getId(),
'strategies' => implode(',', $strategyNames),
'sync_count' => $syncCount,
'total' => $reportedTotal,
'last_synced_id' => $lastSyncedId,
'duration_ms' => $durationMs,
]
);
return $reportedTotal;
}
private function handleSyncException(\Throwable $e, array $parameters): void
{
if (($parameters['since'] ?? null) instanceof Carbon) {
$parameters['since'] = $parameters['since']->toDateTimeString();
}
$parameters['config'] = $this->config->getId();
$this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [
'teamId' => $this->team->getUuid(),
'parameters' => $parameters,
'reason' => $e->getMessage(),
]);
}
/**
* @inheritdoc
*/
public function syncOpportunity(string $crmId): ?Opportunity
{
$strategy = $this->opportunitySyncStrategyResolver->resolve(
$this->config,
OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,
);
$parameters = [
'config' => $this->config,
'crm_id' => $crmId,
];
try {
if (! $strategy instanceof HubspotSingleSyncStrategy) {
throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');
}
$hsOpportunity = $strategy->fetchOpportunity($parameters);
} catch (\HubSpot\Client\Crm\Deals\ApiException $e) {
$this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [
'teamId' => $this->team->getUuid(),
'crmId' => $crmId,
'reason' => $e->getMessage(),
]);
return null;
}
$hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);
return $this->importOrUpdateOpportunity($hsOpportunity);
}
/**
* Process webhook-collected opportunity batches.
*
* Drains Redis sets containing company CRM IDs collected from webhook events
* and dispatches ImportOpportunityBatch jobs for batch processing.
*
* @return int Number of opportunity IDs dispatched to jobs
*/
public function batchSyncOpportunities(): int
{
$configId = $this->team->getCrmConfiguration()->getId();
return $this->batchProcessor->processBatchesForObjectType(
WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,
$configId
);
}
/**
* Import a batch of opportunities by their CRM IDs.
* Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().
*
* @param array<string> $crmIds HubSpot deal CRM IDs
*
* @return array{success: array, failed_ids: array, errors?: array<string, string>}
*/
public function importOpportunityBatchByIds(array $crmIds): array
{
$fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);
$allDeals = [];
foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {
$deals = $this->client->getOpportunitiesByIds($chunk, $fields);
foreach ($deals as $deal) {
$allDeals[] = $deal;
}
}
// IDs not returned by HubSpot are likely deleted or inaccessible deals.
// These are not failures — retrying won't bring them back.
$fetchedIds = array_map('strval', array_column($allDeals, 'id'));
$notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));
if (! empty($notFoundIds)) {
$this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [
'teamId' => $this->team->getId(),
'notFoundCount' => \count($notFoundIds),
'notFoundIds' => $notFoundIds,
'requestedCount' => \count($crmIds),
'fetchedCount' => \count($allDeals),
]);
}
if (empty($allDeals)) {
return ['success' => [], 'failed_ids' => []];
}
return $this->importOpportunityBatch($allDeals);
}
private function getClosedDealStages(): array
{
if ($this->cachedClosedDealStages !== null) {
return $this->cachedClosedDealStages;
}
$stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);
$data = [
'lost' => [],
'won' => [],
];
foreach ($stages as $stage) {
if ($stage->probability == 0.00) {
$data['lost'][] = $stage->crm_provider_id;
}
if ($stage->probability == 100.00) {
$data['won'][] = $stage->crm_provider_id;
}
}
$this->cachedClosedDealStages = $data;
return $data;
}
/**
* Import deals into the database with pre-fetched associations.
*
* API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT
* caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()
* where Laravel retries the whole job with backoff. After all retries exhausted,
* failed() requeues all IDs to Redis.
*
* The per-deal loop catches exceptions individually. A deal can end up in three states:
* - success: imported/updated successfully
* - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)
* These are permanent issues — retrying won't fix them.
* - skipped (null): missing dependencies (no account, unknown pipeline/stage).
* This is acceptable — the deal cannot be imported until those exist.
*/
private function importOpportunityBatch(array $deals): array
{
$syncedOpportunities = [
'success' => [],
'failed_ids' => [],
];
$dealIds = array_column($deals, 'id');
// Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the
// queue job retries the whole batch and eventually requeues all deal IDs back to Redis.
try {
$companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');
$contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');
$associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);
$existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(
$this->config,
array_map('strval', $dealIds)
);
$existingCrmIdSet = array_flip($existingCrmIds);
} catch (\Throwable $e) {
$this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [
'teamId' => $this->team->getId(),
'dealCount' => count($dealIds),
'error' => $e->getMessage(),
]);
throw $e;
}
foreach ($deals as $deal) {
try {
$deal['associations'] = $this->prepareAssociationsForOpportunity(
$deal['id'],
$companyAssociations,
$contactAssociations,
$associationsData
);
$syncedOpportunity = $this->importOrUpdateOpportunity(
$deal,
isset($existingCrmIdSet[(string) $deal['id']])
);
if ($syncedOpportunity) {
$syncedOpportunities['success'][] = $syncedOpportunity;
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [
'teamId' => $this->team->getId(),
'crmId' => $deal['id'],
'error' => $e->getMessage(),
]);
$syncedOpportunities['failed_ids'][] = $deal['id'];
$syncedOpportunities['errors'][$deal['id']] = $e->getMessage();
}
}
return $syncedOpportunities;
}
/**
* Prepare associated entities for opportunities with optimized batch processing
* Returns structured data with CRM ID to DB ID mappings for each opportunity
*/
private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array
{
// Step 1: Collect all unique company and contact IDs from associations
$allCompanyIds = $this->flattenAssociationIds($companyAssociations);
$allContactIds = $this->flattenAssociationIds($contactAssociations);
// Step 2: Batch sync missing entities and get CRM ID to DB ID mappings
$companyIdMappings = [];
$contactIdMappings = [];
if (! empty($allCompanyIds)) {
$companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);
}
if (! empty($allContactIds)) {
$contactIdMappings = $this->prepareAssociatedContacts($allContactIds);
}
return [
'company_id_mappings' => $companyIdMappings,
'contact_id_mappings' => $contactIdMappings,
];
}
/**
* Flatten association data to get unique IDs
*/
private function flattenAssociationIds(array $associations): array
{
$ids = [];
foreach ($associations as $dealAssociations) {
if (is_array($dealAssociations)) {
foreach ($dealAssociations as $id) {
$ids[$id] = true;
}
}
}
return array_keys($ids);
}
/**
* Batch sync missing accounts
*/
private function prepareAssociatedAccounts(array $companyIds): array
{
// Find which accounts already exist
$existingAccounts = $this->crmEntityRepository
->findAccountsByExternalIds($this->config, $companyIds);
$existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();
$existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {
return [$account->getCrmProviderId() => $account->getId()];
})->toArray();
$missingCompanyIds = array_diff($companyIds, $existingCompanyIds);
if (empty($missingCompanyIds)) {
return $existingAccountsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [
'teamId' => $this->team->getUuid(),
'total_companies' => count($companyIds),
'existing_companies' => count($existingCompanyIds),
'missing_companies' => count($missingCompanyIds),
]);
// we already have limit on opportunity ids count
// Initialize variable before try block
$syncedAccountsData = [];
try {
$syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [
'size' => count($missingCompanyIds),
'error' => $e->getMessage(),
]);
$syncedAccountsData = [];
}
return $existingAccountsData + $syncedAccountsData;
}
/**
* Prepare associated contacts - find existing and sync missing ones
* Returns mapping of CRM ID to DB ID
*/
private function prepareAssociatedContacts(array $contactIds): array
{
// Find which contacts already exist
$existingContacts = $this->crmEntityRepository
->findContactsByExternalIds($this->config, $contactIds);
$existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();
// Create mapping for existing contacts
$existingContactsData = $existingContacts->mapWithKeys(function ($contact) {
return [$contact->getCrmProviderId() => $contact->getId()];
})->toArray();
$missingContactIds = array_diff($contactIds, $existingContactIds);
if (empty($missingContactIds)) {
return $existingContactsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [
'teamId' => $this->team->getUuid(),
'total_contacts' => count($contactIds),
'existing_contacts' => count($existingContactIds),
'missing_contacts' => count($missingContactIds),
]);
// Sync missing contacts using batch API
try {
$syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [
'size' => count($missingContactIds),
'error' => $e->getMessage(),
]);
$syncedContactsData = [];
}
return $existingContactsData + $syncedContactsData;
}
private function batchSyncCrmObjects(string $objectType, array $crmIds): array
{
$syncObjects = [];
$crmObjectIds = array_values($crmIds);
foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {
try {
$objects = $objectType === 'companies' ?
$this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :
$this->client->getContactsByIds($chunk, $this->getContactFields());
foreach ($objects as $objectId => $objectData) {
$this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [
'requested_count' => count($chunk),
'synced_count' => count($objects),
]);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [
'ids' => $chunk,
'error' => $e->getMessage(),
]);
}
}
return $syncObjects;
}
private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void
{
try {
$object = $objectType === 'companies' ?
$this->importAccount($objectData) :
$this->importContact($objectData);
if ($object) {
$syncObjects[$object->getCrmProviderId()] = $object->getId();
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [
'id' => $objectId,
'error' => $e->getMessage(),
]);
}
}
/**
* Prepare associations for a single opportunity
*
* The return value is an array with the following structure:
* [
* 'companies' => [
* $companyCrmId => $companyId,
* ...
* ],
* 'contacts' => [
* $contactCrmId => $contactId,
* ...
* ],
* 'account_id' => $accountId,
* ]
*/
private function prepareAssociationsForOpportunity(
string $oppCrmId,
array $companyAssociations,
array $contactAssociations,
array $associationsData
): array {
$associations = [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
$oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];
foreach ($oppCompanyIds as $companyCrmId) {
if (isset($associationsData['company_id_mappings'][$companyCrmId])) {
$associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];
// Set primary account (first company becomes primary account)
if ($associations['account_id'] === null) {
$associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];
}
}
}
$oppContactIds = $contactAssociations[$oppCrmId] ?? [];
foreach ($oppContactIds as $contactCrmId) {
if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {
$associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];
}
}
return $associations;
}
/**
* Update only associations for an opportunity
*/
private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void
{
// Update contact associations
$this->importOpportunityContacts($opportunity, $associations['contacts']);
// Update company (account) associations
$this->updateOpportunityAccount($opportunity, $associations['account_id']);
}
/**
* Remove all contact associations from an opportunity
*/
private function removeAllOpportunityContacts(Opportunity $opportunity): void
{
$currentCount = (int) $opportunity->contacts()->count();
if ($currentCount > 0) {
$opportunity->contacts()->detach();
$this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_count' => $currentCount,
]);
}
}
private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void
{
if ($accountId === null) {
// No account ID provided - keep current account
return;
}
$currentAccountId = $opportunity->getAccountId();
// Only update if account has changed
if ($currentAccountId !== $accountId) {
$opportunity->account_id = $accountId;
$opportunity->save();
$this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [
'opportunity_id' => $opportunity->getId(),
'old_account_id' => $currentAccountId,
'new_account_id' => $accountId,
]);
}
}
/**
* Find existing opportunities by external IDs (OPTIMIZED VERSION)
* Uses batch query for better performance
*/
private function findExistingOpportunities(array $crmIds): Collection
{
return $this->crmEntityRepository
->findOpportunitiesByExternalIds($this->config, $crmIds);
}
private function processOpportunityBatch(array $opportunities): int
{
$syncedOpportunities = $this->importOpportunityBatch($opportunities);
return count($syncedOpportunities['success'] ?? []);
}
/**
* Convert single deal associations from HubSpot format to internal format
* Handles both HubSpot SDK objects and array formats
*
* @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed
*
* @return array Processed associations with DB IDs
*/
private function convertDealAssociations(array $opportunityAssociations): array
{
$associations = $this->initializeAssociationsStructure();
if (empty($opportunityAssociations)) {
return $associations;
}
$associationIds = $this->extractAssociationIds($opportunityAssociations);
$this->processCompanyAssociations($associationIds, $associations);
$this->processContactAssociations($associationIds, $associations);
return $associations;
}
private function initializeAssociationsStructure(): array
{
return [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
}
private function extractAssociationIds(array $opportunityAssociations): array
{
$associationIds = [];
foreach ($opportunityAssociations as $type => $associationData) {
if (! empty($associationData)) {
$associationIds[$type] = $this->convertSingleDealAssociations($associationData);
}
}
return $associationIds;
}
private function processCompanyAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['companies'])) {
return;
}
$companyId = $associationIds['companies'][0];
$account = $this->findOrSyncAccount($companyId);
if ($account instanceof Account) {
$associations['companies'][$companyId] = $account->getId();
$associations['account_id'] = $account->getId();
}
}
private function processContactAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['contacts'])) {
return;
}
foreach ($associationIds['contacts'] as $contactId) {
$contact = $this->findOrSyncContact($contactId);
if ($contact instanceof Contact) {
$associations['contacts'][$contactId] = $contact->getId();
}
}
}
private function findOrSyncAccount(string $companyId): ?Account
{
$account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);
if (! $account instanceof Account) {
$account = $this->syncAccount($companyId);
}
return $account;
}
private function findOrSyncContact(string $contactId): ?Contact
{
$contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);
if (! $contact instanceof Contact) {
$contact = $this->syncContact($contactId);
}
return $contact;
}
private function convertSingleDealAssociations($opportunityAssociations = null): array
{
$associationData = [];
if ($opportunityAssociations === null) {
return $associationData;
}
// Handle array input (from extractAssociationIds)
if (is_array($opportunityAssociations)) {
return $opportunityAssociations;
}
// Handle CollectionResponseAssociatedId object
if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {
foreach ($opportunityAssociations->getResults() as $association) {
$associationData[] = $association->getId();
}
}
return $associationData;
}
private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity
{
if (empty($crmData['properties'])) {
return null;
}
$crmId = (string) $crmData['id'];
$properties = $crmData['properties'];
$associations = $crmData['associations'] ?? [];
$opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(
$this->config,
$crmId
);
if ($opportunityExists) {
return $this->updateOpportunity($crmId, $properties, $associations);
}
return $this->createOpportunity($crmId, $properties, $associations);
}
/**
* Create new opportunity
*/
private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity
{
$accountId = $this->resolveAccountId($associations);
if (! $accountId) {
return null;
}
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
if (! $businessProcess) {
return null;
}
$stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);
if (! $stage) {
return null;
}
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->importOpportunityContacts($opportunity, $associations['contacts']);
if ($opportunity->wasRecentlyCreated) {
MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());
}
return $opportunity;
}
/**
* Update existing opportunity
*/
private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity
{
$accountId = $this->resolveAccountId($associations);
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
$stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->updateOpportunityAssociations($opportunity, $associations);
return $opportunity;
}
private function resolveAccountId(array $associations): ?int
{
if (! empty($associations['account_id'])) {
return $associations['account_id'];
}
if (empty($associations)) {
return null;
}
// Fallback: use first company as account (currently SDK returns one company)
foreach ($associations['companies'] as $accountId) {
return $accountId;
}
return null;
}
private function buildOpportunityData(
array $properties,
?int $accountId,
?BusinessProcess $businessProcess,
?Stage $stage
): array {
$ownerId = null;
$profile = null;
if (! empty($properties['hubspot_owner_id'])) {
$ownerId = $properties['hubspot_owner_id'];
$profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);
}
$name = 'Unknown';
if (isset($properties['dealname'])) {
$name = mb_strimwidth($properties['dealname'], 0, 128);
}
$amount = $this->resolveAmount($properties);
$currency = $properties['deal_currency_code'] ?? null;
$closeDate = null;
if (! empty($properties['closedate'])) {
$closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');
}
$remotelyCreatedAt = null;
if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {
$date = $this->parseCleanDatetime($properties['createdate']);
$remotelyCreatedAt = $date?->format('Y-m-d H:i:s');
}
$closedStages = $this->getClosedDealStages();
$isWon = in_array($properties['dealstage'], $closedStages['won']);
$isLost = in_array($properties['dealstage'], $closedStages['lost']);
$data = [
'team_id' => $this->team->getId(),
'user_id' => $profile ? $profile->user_id : null,
'owner_id' => $ownerId,
'name' => $name,
'value' => ! empty($amount) ? $amount : null,
'currency_code' => CurrencyFormatter::formatCode($currency),
'close_date' => $closeDate,
'is_closed' => $isWon || $isLost,
'is_won' => $isWon,
'remotely_created_at' => $remotelyCreatedAt,
'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),
'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),
];
if ($accountId) {
$data['account_id'] = $accountId;
}
if ($stage) {
$data['stage_id'] = $stage->id;
}
if ($businessProcess) {
$recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);
if ($recordType) {
$data['record_type_id'] = $recordType->id;
}
}
return $data;
}
private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess
{
if ($pipelineId === null) {
return null;
}
$cacheKey = $this->getBusinessProcessCacheKey($pipelineId);
if (isset($this->cachedBusinessProcesses[$cacheKey])) {
return $this->cachedBusinessProcesses[$cacheKey];
}
$businessProcess = $this->getBusinessProcess($pipelineId);
if (! $businessProcess instanceof BusinessProcess) {
$this->importStages();
$businessProcess = $this->getBusinessProcess($pipelineId);
}
if (! $businessProcess instanceof BusinessProcess) {
$this->logger->info(
'[HubSpot] Deal is not attached to a pipeline',
[
'pipeline' => $pipelineId]
);
}
$this->cachedBusinessProcesses[$cacheKey] = $businessProcess;
return $businessProcess;
}
private function getBusinessProcess(string $pipelineId): ?BusinessProcess
{
return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);
}
private function getBusinessProcessCacheKey(string $pipelineId): string
{
return $this->config->getId() . '_' . $pipelineId;
}
private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage
{
if (empty($stageId)) {
return null;
}
$cacheKey = $businessProcess->getId() . ':' . $stageId;
if (isset($this->cachedStages[$cacheKey])) {
return $this->cachedStages[$cacheKey];
}
$stage = $this->crmEntityRepository->getPipelineStageByConditions(
$businessProcess,
[
'crm_provider_id' => $stageId,
'type' => Stage::TYPE_OPPORTUNITY,
]
);
if ($stage === null) {
$this->importStages(null, $stageId);
}
if ($stage === null) {
$this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);
}
$this->cachedStages[$cacheKey] = $stage;
return $stage;
}
private function resolveAmount(array $properties): ?string
{
$amount = null;
if (! empty($properties['amount'])) {
$amount = str_replace(',', '', $properties['amount']);
}
if ($this->config->hasDefaultCurrencyFieldSet()) {
$valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();
$amount = $properties[$valueFieldName] ?? $amount;
}
return $amount;
}
private function parseCleanDatetime(string $datetime): ?Carbon
{
// Treat pre-1980 values as invalid
$minValidDate = Carbon::parse('1980-01-01 00:00:00');
try {
$date = Carbon::parse($datetime);
if ($minValidDate->gt($date)) {
return null;
}
return $date;
} catch (Exception) {
return null; // On parse error, treat as null
}
}
private function resolveDealProbability(?string $stageProbability): int
{
if ($stageProbability === null) {
return 0;
}
$probability = (float) $stageProbability;
return $probability > 1 ? 0 : (int) ($probability * 100);
}
private function resolveForecastCategory(?string $forecastCategory): string
{
if (! $forecastCategory) {
return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;
}
$forecastCategory = str_replace('_', ' ', $forecastCategory);
return ucwords(strtolower($forecastCategory));
}
private function importExternalFieldData(array $properties, int $opportunityId): void
{
$crmFields = $this->getOpportunitySyncableFields();
$this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);
}
private function importOpportunityContacts(Opportunity $opportunity, array $associations): void
{
// Handle empty or missing contact associations
if (empty($associations)) {
// Remove all existing contact associations if none provided
$this->removeAllOpportunityContacts($opportunity);
return;
}
// Use differential sync approach for better performance and accuracy
$this->syncOpportunityContactsDifferential($opportunity, $associations);
}
/**
* Sync opportunity contacts using differential approach
* This compares current vs new associations and only makes necessary changes
*/
private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void
{
$currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);
$contactAssociationIds = array_keys($contactAssociations);
$contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);
$contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);
if (empty($contactsToAdd) && empty($contactsToRemove)) {
return;
}
$this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);
$this->removeContactAssociations($opportunity, $contactsToRemove);
$this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);
}
private function getCurrentContactCrmIds(Opportunity $opportunity): array
{
return $opportunity->contacts()
->pluck('contacts.crm_provider_id')
->toArray();
}
private function logContactAssociationChanges(
Opportunity $opportunity,
array $currentContactCrmIds,
array $contactAssociations,
array $contactsToAdd,
array $contactsToRemove
): void {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [
'opportunity_id' => $opportunity->getId(),
'current_contacts' => $currentContactCrmIds,
'new_contacts' => $contactAssociations,
'contacts_to_add' => $contactsToAdd,
'contacts_to_remove' => $contactsToRemove,
]);
}
private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void
{
if (empty($contactsToRemove)) {
return;
}
$contactsToDetach = $opportunity->contacts()
->whereIn('contacts.crm_provider_id', $contactsToRemove)
->pluck('contacts.id')
->toArray();
if (! empty($contactsToDetach)) {
$opportunity->contacts()->detach($contactsToDetach);
$this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_contact_crm_ids' => $contactsToRemove,
'removed_contact_count' => count($contactsToDetach),
]);
}
}
private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void
{
if (empty($contactsToAdd)) {
return;
}
$contactsAdded = [];
foreach ($contactsToAdd as $crmId) {
$id = $contactAssociations[$crmId];
if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {
$contactsAdded[] = $crmId;
}
}
$this->logAddedContacts($opportunity, $contactsAdded);
}
private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool
{
try {
$contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);
if (! $contact) {
return false;
}
return $this->performContactAttachment($opportunity, $contact, $crmId);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [
'opportunity_id' => $opportunity->getId(),
'contact_crm_id' => $crmId,
'error' => $e->getMessage(),
]);
return false;
}
}
private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool
{
try {
$opportunity->contacts()->attach($contact->getId(), [
'crm_provider_id' => $crmId,
]);
return true;
} catch (\Illuminate\Database\QueryException $e) {
if (str_contains($e->getMessage(), 'Duplicate entry')) {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [
'contact_id' => $contact->getId(),
'contact_crm_id' => $crmId,
'opportunity_id' => $opportunity->getId(),
]);
return false;
}
throw $e;
}
}
private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void
{
if (! empty($contactsAdded)) {
$this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [
'opportunity_id' => $opportunity->getId(),
'added_contact_crm_ids' => $contactsAdded,
'added_contacts_count' => count($contactsAdded),
]);
}
}
}
Execute
Open Query Execution Settings…
Script
Code changed:
Hide
Sync Changes
Hide This Notification
6
1
6
Previous Highlighted Error
Next Highlighted Error
# [PASSWORD_DOTS] HS [PASSWORD_DOTS]
select * from teams where id = 2; # 2
select * from features; # 2
select * from team_features where team_id = 2; # 2
select * from crm_configurations where id = 2; # 2
select * from users where team_id = 2; #
select * from playbooks where team_id = 2; # event 38
select * from playbook_categories where playbook_id = 38; #
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id is not null order by id desc;
https://app.hubspot.com/contacts/4392066/deal/16964514951/?engagement=96069102624
https://app.staging.jiminny.com/playback/d5df34dc-bd66-4ff5-a7b3-8d3be30322a0
SELECT * FROM activities WHERE uuid_to_bin('04fdcd0d-818f-4c53-92dc-6f18bc753ffd') = uuid;
# 609126 softphone tr. 11241
SELECT * FROM activities WHERE uuid_to_bin('6521bfcd-5a30-46e5-9f74-5440fd48befd') = uuid;
# 608874 conference tr. 11226 crmId: 103422236596
select * from ai_prompts where transcription_id IN (11241, 11226);
select * from activity_summary_logs where activity_id = 608874;
select * from sidekick_settings;
select * from default_activity_types;
select * from crm_field_data where activity_id = 1223;
select * from crm_layouts where crm_configuration_id = 2;
SELECT * FROM crm_layout_entities WHERE crm_layout_id IN (554);
select * from crm_fields where crm_configuration_id = 11 and object_type = 'event';
SELECT * FROM crm_field_values WHERE crm_field_id IN (1455,1450);
SELECT * FROM crm_field_data WHERE crm_layout_entity_id = 971;
SELECT * FROM crm_field_data WHERE crm_layout_entity_id IN (6494,6495,6496,6497,6498,6499);
SELECT
CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,
u.email,
sa.*,
t.owner_id FROM social_accounts sa
JOIN users u
on u.id = sa.sociable_id
JOIN teams t on t.id = u.team_id
WHERE u.team_id = 2 and sa.provider = 'hubspot';
select * from opportunities where team_id = 2
and crm_provider_id IN ('51317301383');
select * from contacts where id = 85;
select * from opportunities where team_id = 2 order by id desc;
select * from opportunities where team_id = 2 and crm_provider_id = '51317301383'; # 5112
select * from opportunities where team_id = 2 and crm_provider_id = '55976759904'; # 5112
select * from opportunity_contacts where opportunity_id = 5117;
select * from crm_field_data where object_id = 1365;
SELECT * FROM crm_fields WHERE id IN (1405, 1407, 1972, 2128);
select * from features;
select * from team_features where team_id IN (1);
select * from team_features where feature_id IN (36);
SHOW CREATE TABLE opportunity_contacts;
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '111751';
# $slug = 'HUBSPOT_WEBHOOK_SYNC';
# $team = Jiminny\Models\Team::find(2);
# $feature = Feature::query()->where('slug', $slug)->first();
# TeamFeature::query()->create(['feature_id' => $feature->getId(),'team_id' => $team->getId()]);
# hubspot_webhook_metrics
select * from opportunities where team_id = 2 and crm_provider_id IN ('374720564','14527423589','49908861993','50435771779'); # 1365
SELECT * FROM opportunity_contacts WHERE opportunity_id = '414';
SELECT * FROM opportunity_contacts WHERE crm_provider_id = '131501';
select * from contacts where id in (414, 464);
select * from activities where crm_configuration_id = 2;
select settings from crm_configurations where id = 11;
select * from teams; # 1, 2
select * from users;
select * from crm_configurations where id = 39;
select * from team_features where team_id = 2;
select * from features;
# SELECT * FROM opportunities WHERE crm_configuration_id = 2
# order by id desc;
# and crm_provider_id = '49908861993';
select * from activity_providers where id IN (443, 202, 203, 227);
select * from activity_imports where id = 795889;
select c.id, c.provider, c.settings, t.* from teams t join crm_configurations c on t.id = c.team_id
where c.provider = 'hubspot';
select * from crm_configurations crm JOIN teams t on crm.team_id = t.id
where provider = 'hubspot';
SELECT * FROM teams WHERE id = 31;
SELECT * FROM users WHERE id = 257;
SELECT * FROM opportunities WHERE team_id = 2;
select * from opportunity_contacts where opportunity_id = 5124;
select * from contacts where id IN (3850,3853,3851,4073,4140,4155,4480,4530,4623,5986,513,687,1806,1523,3613)
select * from activities where crm_configuration_id = 13;
SELECT * FROM activities WHERE uuid_to_bin('826619ce-ec8e-4e59-8467-a01f5f6ad71e') = uuid; # 418141
select id, team_id, crm_provider_id from crm_configurations where provider = 'hubspot' and crm_provider_id IS NOT NULL;
SELECT * FROM accounts WHERE team_id = 2 and crm_provider_id = '1212213464' order by id desc;
SELECT * FROM contacts WHERE team_id = 2 and account_id = 5189 order by id desc;
SELECT * FROM contacts WHERE team_id = 2 order by id desc;
select * from opportunity_contacts where contact_id = 6223;
SELECT * FROM opportunities WHERE team_id = 2 and account_id = 5189 order by id desc;
select * from crm_profiles where crm_configuration_id = 2;
select * from activities where account_id = 46;
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
36710
|
746
|
9
|
2026-04-16T10:54:45.921008+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776336885921_m1.jpg...
|
Firefox
|
Jiminny — Work
|
True
|
app.dev.jiminny.com/connect/zohocrm
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Inbox (1,561) - [EMAIL] - Jiminny Mail
Jiminny x Shiji - Reconnecting the platform
Close tab
For you - Confluence
Close tab
Lukas Kovalik - Time Off
Close tab
Product Growth Platform | Userpilot
Close tab
Userpilot
Close tab
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Close tab
Jiminny
Close tab
New Tab
Close tab
Jiminny
Close tab
Google
Close tab
IntegrationAccessor | Membrane SDK - v0.28.1
Close tab
Jiminny · Membrane...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.072222225,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":4,"bounds":{"left":0.0,"top":0.11777778,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.16333333,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.20888889,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.25444445,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox (1,561) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.3,"width":0.033680554,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Jiminny x Shiji - Reconnecting the platform","depth":4,"bounds":{"left":0.0,"top":0.33055556,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.33055556,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"For you - Confluence","depth":4,"bounds":{"left":0.0,"top":0.37611112,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.37611112,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Lukas Kovalik - Time Off","depth":4,"bounds":{"left":0.0,"top":0.42166665,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.42166665,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Product Growth Platform | Userpilot","depth":4,"bounds":{"left":0.0,"top":0.4672222,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.4672222,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Userpilot","depth":4,"bounds":{"left":0.0,"top":0.5127778,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.5127778,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.55833334,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.55833334,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.60388887,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.60388887,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"bounds":{"left":0.0,"top":0.64944446,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.64944446,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.695,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.695,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Google","depth":4,"bounds":{"left":0.0,"top":0.7405556,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.7405556,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"IntegrationAccessor | Membrane SDK - v0.28.1","depth":4,"bounds":{"left":0.0,"top":0.7861111,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.0013888889,"top":0.7861111,"width":0.010416667,"height":0.016666668},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Jiminny · Membrane","depth":4,"bounds":{"left":0.0,"top":0.83166665,"width":0.026041666,"height":0.045555554},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
8499217021328397105
|
3897092660655597259
|
visual_change
|
hybrid
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
JY-20543 add AJ reports User pilot tracking by LakyLak · Pull Request #11932 · jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Inbox (1,561) - [EMAIL] - Jiminny Mail
Jiminny x Shiji - Reconnecting the platform
Close tab
For you - Confluence
Close tab
Lukas Kovalik - Time Off
Close tab
Product Growth Platform | Userpilot
Close tab
Userpilot
Close tab
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Close tab
Jiminny
Close tab
New Tab
Close tab
Jiminny
Close tab
Google
Close tab
IntegrationAccessor | Membrane SDK - v0.28.1
Close tab
Jiminny · Membrane
FirefoxFileEditViewHistoryBookmarksProfilesToolsWindow→Help• app.dev.jiminny.com/connect/zohocrm‹ →0 ll§ Support Daily • in 1h 6 m100% C8• Thu 16 Apr 13:54:45JIMINNY(*+Account disconnectedIt looks like your Zoho CRM account has become disconnectedPlease re-connect to continueSign in with Zoho CRM• InspectorConsoleDebuggerN Network{} Style Editor@ Performance7 Filter URLs+Disable CacheNo Throttling+AllHTMLCSSJSXHRDoECOECAECOECAECOEFOECOECAECOECOEFOECOECOECOECOECOEFOECOECOECOECOEFOECAECOECAECOEFOFileconnect incS...5...connect xhself-aut incj3....self-aut xh0 Bzohocrn incj2...zohoern xh r 0Bconnect incj5..connect xhF 0Bself-aut incj3..self-aut xhFOBzohoern inc j 2...connect inc izohoern xh F 0Bconnect xhFOBself-aut inc3..self-aut xh0 Bzohoern inc2...zohocrn xhconnect inc5..connect xhf0 Bself-aut inc3....self-aut xhzohocrn inc2...connect incjb..zohoern xhconnect xhself-aut incjzohoern inc j2...connect incj5..94 requestsFontsImagesMediaWSOtherHeadersCookiesRequestResponseTimingsStack TraceSecurityY Filter propertiesJSONRawisDeactivated: falselogoUri: "https://static.integration.app/connectors/zoho-crm/logo.png"connectorid: "64a158e7d2605720d232e07b"connectorVersion: "3.0.3"oAuthCallbackUri: "https://api.integration.app/oauth-callback"hasMissingParameters: falsehasDocumentation: falsehasOperations: trueoperationsCount: 569hasData: truedataCollectionsCount: 20hasEvents: falseeventsCount: 0hasGlobalWebhooks: falsehasUdm: trueauthType: "client-credentials"• connection: (id: "69e0b983da98fa74f98aebfb", name: "Connection to 66fe6c913202f3a165e3c14d for Dev ZohoCRM client", userld: "1ece66c8-feb1-4df1-b321-21607daf4623", ...)id: "69e0b983da98fa74f98aebfb"name: "Connection to 66fe6c913202f3a165e3c14d for Dev Zoho CRM client"userld: "1ece66c8-feb1-4df1-b321-21607daf4623"tenantid: "69e0b3faef3e7b6248189289"isTest: falseO Bconnected: truestate: "READY"errors: 0integrationld: "66fe6c913202f3a165e3c14d"externalAppld: "6671653e7e2d642e4e41b0fa"authOptionKey: "oauth"3....createdAt: "2026-04-16T10:27:15.579Z"updatedAt: "2026-04-16T10:34:08.702Z"retryAttempts: 0isDeactivated: false92.01 k • authOptions: [(...), (..} ]...
|
NULL
|
|
30018
|
611
|
54
|
2026-04-15T14:53:45.700617+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776264825700_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
171422252748711935155/200VImperial AgePlayer 8 Alm 171422252748711935155/200VImperial AgePlayer 8 Almish Yiltawar!!!-1 kovaliklukas: 44689/44689IVE Magnus Olafsson: 40021/40821 3 IV8 Almish Yiltawar: 34634/34634ONRajyapala: 23275/23275NV6/1 Aezl6 Io 12424/12424 IVMaximilian of Habsbung: 6531/6531 W V3 HuascÁn: 5931/5031 M IV1 Louig VI: 5792/5702 0...
|
NULL
|
8499064170328071360
|
NULL
|
click
|
ocr
|
NULL
|
171422252748711935155/200VImperial AgePlayer 8 Alm 171422252748711935155/200VImperial AgePlayer 8 Almish Yiltawar!!!-1 kovaliklukas: 44689/44689IVE Magnus Olafsson: 40021/40821 3 IV8 Almish Yiltawar: 34634/34634ONRajyapala: 23275/23275NV6/1 Aezl6 Io 12424/12424 IVMaximilian of Habsbung: 6531/6531 W V3 HuascÁn: 5931/5031 M IV1 Louig VI: 5792/5702 0...
|
NULL
|
|
33282
|
672
|
35
|
2026-04-16T07:42:11.227708+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776325331227_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxBookmarksWindow HelpFileEoitViewDMsAchivity FirefoxBookmarksWindow HelpFileEoitViewDMsAchivityFilesLaterMoreHistoryProfilesToolsSearch Jiminny IncJiminny ...# 0. Aneliya Angelova DQ= Unreads@ Threads6 HuddlesDrafts & sent:8 Directories• Messagest Add canvasEh External connections# Starred& platform-inner-team# Channels# ai-chapter# alerts# backend# confusion-clinia# curiosity_labiengineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# randomit releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messazes. Nikolay Ivanov0. A..e. Vasil Vasilev3 Aneliya Angelova, ...R. Stoyan Tanev• VesP. Galya Dimitrova E. Steliyan Georgiev3Adelina Petrova. Il..(Q. Adelina Petrova LD. Nikolay NikolovE FilesAneliya Angelova 9:33 AMFriday, March 27th~ох ясно защо не съм видяла този проблем след като ретествах след като оправи код смелареално нищо не се е деплойвало - в Circle Cl всичко е зелено, а нищо не се е качвало на QAIi realno sym si testwala s промените от преди последния къмитimage.pngLukas Kovalik 9-34 AMда но то беше и малко edge caseAneliya Angelova ® 9:34 AMвчера като трьгнах да качвам на ники бранча и уж се деплойва на ОAI - отварям средата и гледам че стои. ХубспотLukas Kovalik Q 9:35 AMслучва сеиначе май си оаоотимоссалюуо несьм виждалліAneliya Angelova 9:35 AMдаже не знам от кога вес е счупил деплоя и колко време все съм тествала едно и сьшо без да се усетя че деплойванетоне е оаоотелоLukas Kovalik 9:36 AMToday~Aneliya Angelova 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMайде след 15 мин че се зарових в зохоLukas Kovalik 10:32 AMако искaшi лa ce uveм ceraAneliya Angelova 10:33 AMYou joined the huddle (LIVE10:33 AMAneliva Angelova iS nere 1ooLukas Kovalik1O:41 AMphp artisan automated-reports --report-id 39php artisan automated-reports: send --result-id 64**:AppsToastJira CloudMessage Aneliya AngelovaAa6d Huddle with Aneliya AngelovaAl Notes: OffPE Al Notes: OffLukas KovalikScreen shareLeave&lobl6d Huddle with Aneliya Angelova{ Support Daily • in 4h 18mA100% C•8 • Thu 16 Apr 10:42:11JIMINNYVAALeave...
|
NULL
|
8498686516386316732
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxBookmarksWindow HelpFileEoitViewDMsAchivity FirefoxBookmarksWindow HelpFileEoitViewDMsAchivityFilesLaterMoreHistoryProfilesToolsSearch Jiminny IncJiminny ...# 0. Aneliya Angelova DQ= Unreads@ Threads6 HuddlesDrafts & sent:8 Directories• Messagest Add canvasEh External connections# Starred& platform-inner-team# Channels# ai-chapter# alerts# backend# confusion-clinia# curiosity_labiengineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# randomit releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messazes. Nikolay Ivanov0. A..e. Vasil Vasilev3 Aneliya Angelova, ...R. Stoyan Tanev• VesP. Galya Dimitrova E. Steliyan Georgiev3Adelina Petrova. Il..(Q. Adelina Petrova LD. Nikolay NikolovE FilesAneliya Angelova 9:33 AMFriday, March 27th~ох ясно защо не съм видяла този проблем след като ретествах след като оправи код смелареално нищо не се е деплойвало - в Circle Cl всичко е зелено, а нищо не се е качвало на QAIi realno sym si testwala s промените от преди последния къмитimage.pngLukas Kovalik 9-34 AMда но то беше и малко edge caseAneliya Angelova ® 9:34 AMвчера като трьгнах да качвам на ники бранча и уж се деплойва на ОAI - отварям средата и гледам че стои. ХубспотLukas Kovalik Q 9:35 AMслучва сеиначе май си оаоотимоссалюуо несьм виждалліAneliya Angelova 9:35 AMдаже не знам от кога вес е счупил деплоя и колко време все съм тествала едно и сьшо без да се усетя че деплойванетоне е оаоотелоLukas Kovalik 9:36 AMToday~Aneliya Angelova 10:00 AMЛукаш кога искаш да се чуемза командитеLukas Kovalik 10:01 AMайде след 15 мин че се зарових в зохоLukas Kovalik 10:32 AMако искaшi лa ce uveм ceraAneliya Angelova 10:33 AMYou joined the huddle (LIVE10:33 AMAneliva Angelova iS nere 1ooLukas Kovalik1O:41 AMphp artisan automated-reports --report-id 39php artisan automated-reports: send --result-id 64**:AppsToastJira CloudMessage Aneliya AngelovaAa6d Huddle with Aneliya AngelovaAl Notes: OffPE Al Notes: OffLukas KovalikScreen shareLeave&lobl6d Huddle with Aneliya Angelova{ Support Daily • in 4h 18mA100% C•8 • Thu 16 Apr 10:42:11JIMINNYVAALeave...
|
NULL
|
|
63424
|
1376
|
7
|
2026-04-21T09:00:47.251994+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776762047251_m2.jpg...
|
iTerm2
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackcalVIewMistonWindowHelprTavsco.s?9 masterProl SlackcalVIewMistonWindowHelprTavsco.s?9 masterProledey"C) ActivitvController.ohoConvertLeadActivities.php XD DTO> D Fields# Journalm Metadatal© SaveActivity.phpSyncToPlanhat.php© salestorce/service.pnp© createPlayedevent.pnpLopportunttysynestralegy› D Pagination• ProspectSearchStrategy> D RedisC) PlanhatActivityListener.php(C)AskAnythingPromptService.phpC)AskJiminnyReportsController.phpC) AutomatedReportsRepository.phpC) AutomatedReportsCommand.php® AutomatedReportsController.phpv W service IraitsT OpportunitySyncTrait.plphp api v2.oholC) TrackProviderinstalledEvent.ohoC) RequestGenerateReport.Job.ohoC) AutomatedReportResult.ohow syncermentiuestrai.onC) AutomatedReport.ohowsyncrielaslrait.onp<?phpHTAY" writecrmirait.onoDUuISdeclare(strict_types=1);• WebhookC) BatchSvncCollector.ohonamespace Jiminhy services crm Lisceners,C) BatchSvncRedisService.ohc) client.oho› use ...© ClosedDealStagesService.F 17C DealFieldsService.onoC) DecorateActivitv.oho* Convert all scheduled tuture activitles, wnich are assoczated with a lead, wnich nas been con 15/1C) FieldDefinitions.ohvC) FieldTivoeConverter.oho© HubspotClientinterface.php 21Greaconly class convertleadactivitzes zmolements shoul duueue© HubspotTokenManager.phpc) DavloadRuilder nhn© RemoteCrmObjectManipula 23© ResponseNormalize.phppublic function -_constructprivate OpportunityRepository SopportunityRepository,(c) Service.phpprivate LeadRepository SleadRenositorv.© SyncFieldAction.phpprivate CrmConfigurationSettingsService $settingsService,© SyncRelatedActivity Manage 27© WebhookSyncBatchProces: ,sv D IntegrationApp> @J Accessors> DApi• C Confia> DDTOnrivate Loggeriinterface slogden.nublic function handleLeadConverted Sevent: void• D FiltersSconventedl pad = Cevent-slead.•DJobs$configuration = $convertedLead->getCrmConfiguration:• ProspectSearchStrateav• Service iraitsif (! $configuration instanceof Configuration) {c) Dataclient.onoSthis->logger->warning('[ConvertLeadActivities] Lead has no CRM configuration'. 0C) DecorateActivitv.oho'lead id' => $convertedLead->getIdoC) LocalSearch.ohv1)LocalSearchinterface.ohv1):C) RemoteSearch.ohoreturniC) Service.ohov Mlicteners@ Convertl eadActivities nhnSsvncPeriod = Sthis->settingsService->getSvncPeriod(Sconfiquration):@ Purdel aokunCache shn> IM Metadata** ovar iterable<Activitu> Sactavities *=custom.log= laravel.log4 SF jiminny@localA console [STAGING] / %159%15981599Tx: Autovwner 15256 525/85088ontact 1 16779180 665587441856 - 8ontact 2 19247565 742123547700 - 8ompany 4176155 47158650569leaL 7100955 410150124747CONCAT(u.id, CASE WHEN V.id = t.oU.emailt.owner_id FROM social_accounts saN users u on v.id = sa.sociable_icN teams t l..n<->1: on t.id = u.tearRE u.team_id = 400 and sa.providerect * from features;ect * from team_features where fea'ect * from teams where id = 556; #ect * tron cri_contiguracions wherECT * FROM users WHERE id = 18101:CONCAT(u.id, CASE WHEN u.id = t.owU.emall,sa.*t.owner 1d FRuM soclal accounts saN users u on u.1d = sa.soclable 1dN teams + 1..n<->1: on t.id = u.teanRE U.team 1d = 556 and sa.providenect * from opportunities where id =ect * from opportunity_stages wherect * from business processes wherect * from business_process_stagesect * From husiness nrocess stadesect * from stades where team id=ect * from teams whene id = 459.CONCAT(u.id, CASE WHEN u.id = t.ou. email.]t.owner_id FROM social_accounts saNucene n on mid= ca cociablo ioN teams t 1..n<->1: on t.id = u.tearP5 u toam id = 150 and ca nnovidorECT os.stage id, s.crm provider idM opportunity stages osHomeActivityMoreJiminny... ~6 nlate.m_innar-teamTMore unreads#* Channels…al-chaoter# alerts# backend# c-learning-people# confusion-clinic# curiositv lab# engineering# frontend# general# infra-changestt fiminny-bg• people-with-copilo..8 people-with-zoom-….# platform-team# platform-tickets#t product launches# random# releases# support# thank-vous# the_people_of_jimi..^ Direct messagesaVasil Vaciley. Nikolay Nikolovf Aneliva AneelovaGalva Dimitrova2 Stefka StovanovaAa Stovan Tomov3 Aneliva Angelova.9 Stovan TanevNikolay Ivanov( Vec@ Dotkn Kochincki#:: Annd#" Jira CloudB Toast"suppont Dally • In sh100% LzTue 21 Apr 12:00:47Q Search Jiminny Inc& e. Vasil Vasilev• MessagestAdd canvas( Files& Pins+Vasil Vasilev 6:41 PMЛукаш приветnevermind, утре ще те занимавамceга вилях колко в станал часаVasil Vasilev 11:21 AMЛукаш, сигнализирай като си наоколода те питам за един event в Salesforce::saveActivityLukas Kovalllk 11-22AMIлa тука сьMVasil Vasiley 11:24 AMима един event* @TODO: Why does an event dispatch another event?vertedsactivitv. sactivitvlead)):който се хвипля когато се мачне конвептипан leadiобаче интересното е, че в конструктора на тоя event се вика друг event11:26class ActivityLeadConverteduse InteractsWithSocketsnublic Activity Sactivity:nublic lead Sconventedl end.public function __construct(Activity Sactivity, Lead SconvertedLead)Sthis->activity = Sactivity:Sthis->convertedLead = SconvertedLeadevent(new LeadConverted($convertedLead));Lukas Kovalik 11.26 ANла помня че имаше нешо по негоVasil Vasiley 11-26 AMMessage Vasil Vasilev+ АаYesterdayvTodav vщedn:...
|
NULL
|
8498656637965402655
|
NULL
|
visual_change
|
ocr
|
NULL
|
SlackcalVIewMistonWindowHelprTavsco.s?9 masterProl SlackcalVIewMistonWindowHelprTavsco.s?9 masterProledey"C) ActivitvController.ohoConvertLeadActivities.php XD DTO> D Fields# Journalm Metadatal© SaveActivity.phpSyncToPlanhat.php© salestorce/service.pnp© createPlayedevent.pnpLopportunttysynestralegy› D Pagination• ProspectSearchStrategy> D RedisC) PlanhatActivityListener.php(C)AskAnythingPromptService.phpC)AskJiminnyReportsController.phpC) AutomatedReportsRepository.phpC) AutomatedReportsCommand.php® AutomatedReportsController.phpv W service IraitsT OpportunitySyncTrait.plphp api v2.oholC) TrackProviderinstalledEvent.ohoC) RequestGenerateReport.Job.ohoC) AutomatedReportResult.ohow syncermentiuestrai.onC) AutomatedReport.ohowsyncrielaslrait.onp<?phpHTAY" writecrmirait.onoDUuISdeclare(strict_types=1);• WebhookC) BatchSvncCollector.ohonamespace Jiminhy services crm Lisceners,C) BatchSvncRedisService.ohc) client.oho› use ...© ClosedDealStagesService.F 17C DealFieldsService.onoC) DecorateActivitv.oho* Convert all scheduled tuture activitles, wnich are assoczated with a lead, wnich nas been con 15/1C) FieldDefinitions.ohvC) FieldTivoeConverter.oho© HubspotClientinterface.php 21Greaconly class convertleadactivitzes zmolements shoul duueue© HubspotTokenManager.phpc) DavloadRuilder nhn© RemoteCrmObjectManipula 23© ResponseNormalize.phppublic function -_constructprivate OpportunityRepository SopportunityRepository,(c) Service.phpprivate LeadRepository SleadRenositorv.© SyncFieldAction.phpprivate CrmConfigurationSettingsService $settingsService,© SyncRelatedActivity Manage 27© WebhookSyncBatchProces: ,sv D IntegrationApp> @J Accessors> DApi• C Confia> DDTOnrivate Loggeriinterface slogden.nublic function handleLeadConverted Sevent: void• D FiltersSconventedl pad = Cevent-slead.•DJobs$configuration = $convertedLead->getCrmConfiguration:• ProspectSearchStrateav• Service iraitsif (! $configuration instanceof Configuration) {c) Dataclient.onoSthis->logger->warning('[ConvertLeadActivities] Lead has no CRM configuration'. 0C) DecorateActivitv.oho'lead id' => $convertedLead->getIdoC) LocalSearch.ohv1)LocalSearchinterface.ohv1):C) RemoteSearch.ohoreturniC) Service.ohov Mlicteners@ Convertl eadActivities nhnSsvncPeriod = Sthis->settingsService->getSvncPeriod(Sconfiquration):@ Purdel aokunCache shn> IM Metadata** ovar iterable<Activitu> Sactavities *=custom.log= laravel.log4 SF jiminny@localA console [STAGING] / %159%15981599Tx: Autovwner 15256 525/85088ontact 1 16779180 665587441856 - 8ontact 2 19247565 742123547700 - 8ompany 4176155 47158650569leaL 7100955 410150124747CONCAT(u.id, CASE WHEN V.id = t.oU.emailt.owner_id FROM social_accounts saN users u on v.id = sa.sociable_icN teams t l..n<->1: on t.id = u.tearRE u.team_id = 400 and sa.providerect * from features;ect * from team_features where fea'ect * from teams where id = 556; #ect * tron cri_contiguracions wherECT * FROM users WHERE id = 18101:CONCAT(u.id, CASE WHEN u.id = t.owU.emall,sa.*t.owner 1d FRuM soclal accounts saN users u on u.1d = sa.soclable 1dN teams + 1..n<->1: on t.id = u.teanRE U.team 1d = 556 and sa.providenect * from opportunities where id =ect * from opportunity_stages wherect * from business processes wherect * from business_process_stagesect * From husiness nrocess stadesect * from stades where team id=ect * from teams whene id = 459.CONCAT(u.id, CASE WHEN u.id = t.ou. email.]t.owner_id FROM social_accounts saNucene n on mid= ca cociablo ioN teams t 1..n<->1: on t.id = u.tearP5 u toam id = 150 and ca nnovidorECT os.stage id, s.crm provider idM opportunity stages osHomeActivityMoreJiminny... ~6 nlate.m_innar-teamTMore unreads#* Channels…al-chaoter# alerts# backend# c-learning-people# confusion-clinic# curiositv lab# engineering# frontend# general# infra-changestt fiminny-bg• people-with-copilo..8 people-with-zoom-….# platform-team# platform-tickets#t product launches# random# releases# support# thank-vous# the_people_of_jimi..^ Direct messagesaVasil Vaciley. Nikolay Nikolovf Aneliva AneelovaGalva Dimitrova2 Stefka StovanovaAa Stovan Tomov3 Aneliva Angelova.9 Stovan TanevNikolay Ivanov( Vec@ Dotkn Kochincki#:: Annd#" Jira CloudB Toast"suppont Dally • In sh100% LzTue 21 Apr 12:00:47Q Search Jiminny Inc& e. Vasil Vasilev• MessagestAdd canvas( Files& Pins+Vasil Vasilev 6:41 PMЛукаш приветnevermind, утре ще те занимавамceга вилях колко в станал часаVasil Vasilev 11:21 AMЛукаш, сигнализирай като си наоколода те питам за един event в Salesforce::saveActivityLukas Kovalllk 11-22AMIлa тука сьMVasil Vasiley 11:24 AMима един event* @TODO: Why does an event dispatch another event?vertedsactivitv. sactivitvlead)):който се хвипля когато се мачне конвептипан leadiобаче интересното е, че в конструктора на тоя event се вика друг event11:26class ActivityLeadConverteduse InteractsWithSocketsnublic Activity Sactivity:nublic lead Sconventedl end.public function __construct(Activity Sactivity, Lead SconvertedLead)Sthis->activity = Sactivity:Sthis->convertedLead = SconvertedLeadevent(new LeadConverted($convertedLead));Lukas Kovalik 11.26 ANла помня че имаше нешо по негоVasil Vasiley 11-26 AMMessage Vasil Vasilev+ АаYesterdayvTodav vщedn:...
|
NULL
|
|
72711
|
1773
|
41
|
2026-04-22T16:04:44.363570+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-22/1776 /Users/lukas/.screenpipe/data/data/2026-04-22/1776873884363_m2.jpg...
|
QuickTime Player
|
compact_monitor_2_1776753058588.mp4
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rewind
play/pause
fast forward
More Controls
toggl rewind
play/pause
fast forward
More Controls
toggle full screen
show external playback menu
show external playback menu
show media selection menu
toggle picture-in-picture playback
show action menu
share
show chapter menu
zoom
zoom
playback speed
02:13
toggle elapsed time, timecode and framecount
05:04
toggle duration and remaining time
document actions
compact_monitor_2_1776753058588.mp4...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"rewind","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXCheckBox","text":"play/pause","depth":1,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"fast forward","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"More Controls","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXCheckBox","text":"toggle full screen","depth":1,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"show external playback menu","depth":1,"role_description":"button","is_focused":false},{"role":"AXButton","text":"show external playback menu","depth":2,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"show media selection menu","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"toggle picture-in-picture playback","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"show action menu","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"share","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"show chapter menu","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXCheckBox","text":"zoom","depth":1,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false},{"role":"AXCheckBox","text":"zoom","depth":1,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false},{"role":"AXButton","text":"playback speed","depth":1,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"02:13","depth":1,"role_description":"text"},{"role":"AXCheckBox","text":"toggle elapsed time, timecode and framecount","depth":1,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"05:04","depth":1,"role_description":"text"},{"role":"AXCheckBox","text":"toggle duration and remaining time","depth":1,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false},{"role":"AXMenuButton","text":"document actions","depth":1,"bounds":{"left":0.55984044,"top":1.0,"width":0.0033244682,"height":-0.06384683},"role_description":"menu button","is_enabled":false,"is_focused":false},{"role":"AXStaticText","text":"compact_monitor_2_1776753058588.mp4","depth":1,"bounds":{"left":0.46542552,"top":1.0,"width":0.0944149,"height":-0.06384683},"role_description":"text"}]...
|
8498146914936849628
|
8244732804943592182
|
click
|
hybrid
|
NULL
|
rewind
play/pause
fast forward
More Controls
toggl rewind
play/pause
fast forward
More Controls
toggle full screen
show external playback menu
show external playback menu
show media selection menu
toggle picture-in-picture playback
show action menu
share
show chapter menu
zoom
zoom
playback speed
02:13
toggle elapsed time, timecode and framecount
05:04
toggle duration and remaining time
document actions
compact_monitor_2_1776753058588.mp4
cuickllme PlaverrlleWinaowdb.sqlite-wal — screenpipe SSH: nass apo settinas.isonE db.salite-wal XE db.sqlite-walGenerate code (X1, or select a lanquage (*K M. Start typ1to dismiss or don't show this again.** ...•1 CClaude Code XUntitled08000V SCREENPIPE [SSH: NAS]> #recycle> app> datapipes() app_settings.jsonE archive.dbE db.saliteE db.salite-shmE db.salite-wal$ screenpipe_svnc.sh= screenpipe.d% Claude CodeUse Claude Code in the terminal to contigureMCP servers. They'll work here, too!ravountesE jiminny(* AirDrop• Recents* Applications© Documents© Downloadsii lukasiCloudiCloud Drive992 Svnc toldeLocationsO DXP4800PLUS-B5F A® Network• CRM• Orange• Red• Yellow• Green• Blue• PurpleO All Tags...> OUTLINESõ31 TIMEIINESSH: nas @oA0 (o5= Prefer the Terminal experience? Switch back in Settinas+01db.sqlite-walLn 1, Col 1 Spaces: 4 UTF-8 LF () Plain Textlestcompact_monitor_1_1776772592311.mp4- compact_monitor_2_1776756760717.mp4compact_monitor_2_1776786042237.mp4compact_monitor_2_1776758914322.mp4compact_monitor_2_1776789493765.mp4• compact_monitor_2_1776787617984.mp4a compact_monitor_1_1776773217969.mp4•a comoact monitor 2 1776756143688 mo4• compact_monitor_2_1776759535311.mp4• compact_monitor_2_1776756452946.mp4n compact_monitor 1 1776758908569.mp4r- compact_monitor_1_1776783537226.mp4n compact_monitor_1_1776775064772.mp4a compact monitor 2 1776783541082 mo4t- compact_monitor_1_1776784162084.mp4n- compact_monitor_1_1776774756791.mp4compact monitor 1 1776758598100.mp4compact_monitor_2_1776789819462.mp4compact_monitor_2_1776761988171.mp4*• comnact monitor 1 1776782226867 mn/compact_monitor_2_1776761681410.mp4= compact_monitor_2_1776785112030.mp4compact monitor 2 1776785731509.mo4compact monitor 2_1776790456362.mp4- compact_monitor z 1//6//0931489.mp4P= comnact monitor 1 177678/700208 mn/compact_monitor_2_1776761070425.mp4compact_monitor_1_1776789484956.mp4- comnact monitor 1 1776789816527m04=: compact_monitor_1_1776755522051.mp4_ compact_monitor 2 1776/8/292732.mp4•comnaet monitor 2 1776760762200 mn/• comnact monitor 1 1776786358293.mol- compact_monitor_2_1776772287751.mp4n- compact _monitor_1_1776783850457.mp4• comoact monitor 2 1776753058588.mo4.comnaet monitor 2 1776750915529 mn/compact_monitor_2_1776791089868.mp4- compact_monitor_2_1776767999099.mp4comnact monitor 2 1776761377421.mn/compact_monitor_2_1776753366025.mp4compact_monitor_2_1776773221603.mp4comoact monitor 1 1776753055208.mo4- compact monitor 2 1776759228080.mp4compact_monitor_1_1776759531737.mp4ra comnact monitor 1 1776772526221 mn/compact monitor 2 1776766567612.mn4Date ModifiedToday at 9:16Yesterday at 14:56Yesterday at 15:01Yecterdav at 10:22Yesterday at 18:40Yesterday at 11:08Yesterdav at 19:3%Yesterday at 19:07Yesterday at 15:07Yesterdav at 10:27Yesterday at 11:19Yesterday at 11:08Yecterdav at 17:59Yesterday at 9:41Yesterday at 15:37Yesterdav at 17:59Yesterday at 18:09Yesterday at 15:32Yesterday at 11:03Voctorday at 10:12Yesterday at 15:43Yesterday at 11:59Yesterday at 11:54Yesterday at 18:25Yesterday at 18:31Voctordau at 10•5/Yesterday at 16:08Yecterdav at 18:20Yesterday at 11:44Yesterday at 19:38Yesterdav at 19:43Yoctordau at 10:19Yesterday at 19:01Yecterdav at 11:20Yesterday at 9:25Yesterday at 9:56Yesterdav at 18:46Voctordou ot 14+54Yesterdav at 9:31lVocterdav at 11:21Yesterday at 20:04Yesterday at 13:40Yesterday at 9:36Yesterdav at 9:30Voctordav at 11:12Yesterday at 18:56Yesterday at 11:18Vontaedau dt 10.1d1 of 272 selected, 2,02 TB availableWed 22 Apr 19:04:45450,8 MB Folder79 MBMPEG-4 movie76MR MDEG_A movie6 MBMPEG-4 movie5,6 MBMPEG-4 movie5.5 MBMPEG-4 movie5,4 MBMPEG-4 movie5,4 MBMPEG-4 movie5.3 M:CAMP MOECA mavid4,8 MB4,5 MBMPEG-4 movie45 MBMPEG-A movid4,4 MB MPEG-4 movie4,3 MB4.2 M:MPEG-4 movie4,2 MB MPEG-4 movie4,1 MBMPEG-4 movieA4MPMDSG.A movid4MBMPEG-4 movie4MBMPEG-4 movie30MPMPEG-A movie3.9 MB MPEG-4 movie3,8 MB29 MP3,7 MBMPEG-4 movieMDEG.A movidMPEG-4 movie3,7 MB27 MPMDEG-A movid3.7 MBMPEG-4 movie3,6 MBMPEG-4 movie3.6 M:MPEG-4 movie2GMPMDEC.A movid3,5 MBMPEG-4 movie3,5 MBMPEG-4 movie25 MPMDSG-A movid3,5 MB MPEG-4 movie3.4M:22MP3,3 MBMPEG-4 movieMDEC.A movidMPEG-4 movie3,3 MB22 MRMDSG.A movid3,2 MB MPEG-4 movie3,2 MB MPEG-4 movie3.2 MEMDEC.A movid3,1 MB3,1 MBMPEG-4 movie24 MDMDEG.A movid3MBMPEG-4 movieMPEG-4 movie2MPMPFG-A movidnaMo Mnza Anai...
|
72708
|
|
73898
|
1837
|
4
|
2026-04-23T08:50:32.801600+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776934232801_m2.jpg...
|
Firefox
|
Userpilot | Workflows — Work
|
True
|
run.userpilot.io/workflows
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
Userpilot | Workflows
Userpilot | Workflows
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Engagement
:r16l:
Flows
:r16n:
Spotlights
:r16p:
Banners
:r16r:
Checklists
:r16t:
Resource Center...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.23321144,"top":0.0518755,"width":0.07596409,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"New Tab","depth":4,"bounds":{"left":0.23138298,"top":0.09497207,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"bounds":{"left":0.24468085,"top":0.10614525,"width":0.014960106,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":4,"bounds":{"left":0.23138298,"top":0.12769353,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":5,"bounds":{"left":0.24468085,"top":0.13886672,"width":0.14128989,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":4,"bounds":{"left":0.23138298,"top":0.16041501,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app","depth":5,"bounds":{"left":0.24468085,"top":0.17158818,"width":0.16555852,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.23138298,"top":0.19313647,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.24468085,"top":0.20430966,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Userpilot | Workflows","depth":4,"bounds":{"left":0.23138298,"top":0.22585794,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Userpilot | Workflows","depth":5,"bounds":{"left":0.24468085,"top":0.23703113,"width":0.036901597,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.29870346,"top":0.2330407,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.23420878,"top":0.2601756,"width":0.07413564,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.23420878,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.24517952,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.25631648,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.26745346,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.2785904,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Engagement","depth":9,"bounds":{"left":0.23420878,"top":0.10933759,"width":0.032081116,"height":0.017956903},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":":r16l:","depth":9,"bounds":{"left":0.2315492,"top":0.13567439,"width":0.07413564,"height":0.027134877},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Flows","depth":11,"bounds":{"left":0.2421875,"top":0.14126097,"width":0.012632979,"height":0.01556265},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":":r16n:","depth":9,"bounds":{"left":0.2315492,"top":0.16440542,"width":0.07413564,"height":0.027134877},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Spotlights","depth":11,"bounds":{"left":0.2421875,"top":0.16999201,"width":0.02244016,"height":0.01556265},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":":r16p:","depth":9,"bounds":{"left":0.2315492,"top":0.19313647,"width":0.07413564,"height":0.027134877},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Banners","depth":11,"bounds":{"left":0.2421875,"top":0.19872306,"width":0.01861702,"height":0.01556265},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":":r16r:","depth":9,"bounds":{"left":0.2315492,"top":0.22186752,"width":0.07413564,"height":0.027134877},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Checklists","depth":11,"bounds":{"left":0.2421875,"top":0.22745411,"width":0.022606382,"height":0.01556265},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":":r16t:","depth":9,"bounds":{"left":0.2315492,"top":0.25059855,"width":0.07413564,"height":0.027134877},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Resource Center","depth":11,"bounds":{"left":0.2421875,"top":0.25618514,"width":0.03706782,"height":0.01556265},"role_description":"text","subrole":"AXUnknown"}]...
|
8498087503137672930
|
1462072153436484338
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
JY-9712 | Nuges to expire after one year by nikolaybiaivanov · Pull Request #11981 · jiminny/app
Jiminny
Jiminny
Userpilot | Workflows
Userpilot | Workflows
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Engagement
:r16l:
Flows
:r16n:
Spotlights
:r16p:
Banners
:r16r:
Checklists
:r16t:
Resource Center...
|
NULL
|
|
27430
|
572
|
54
|
2026-04-15T13:48:52.394609+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776260932394_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina PetrovaD. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira Cloud# releases8 22Messagesnewdou+O Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+AaActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefoxCP Isolated Web ContentFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)claudeClaude Helper (Renderer)FirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentMEMORY PRESSUREMem...2,03 GB1,20 GB992,6 MB962,7 MB836,2 MB793,5 MB783,4 MB554,1 MB547,4 MB543,9 MB515,8 MB464,9 MB453,4 MB445,2 MB426,1 MB393,7 MB391,5 MB389,9 MB372,5 MB346,6 MB328,7 MB326,1 MB325,9 MB303,5 MB252,2 MB244,8 MB244,6 MB224,0 MBPhysical Memory:Memory Used:Cached Files:Swap Used:100% <478Wed 15 Apr 16:48:52CPUMemoryDiskThreads3923742585282925271124162425262524231521131528272724EnergyPorts60319 7557271241 20220 019130242128253166121199123123124125122121174330722201261 832127125122PID93892407801442974146648424203080193671314673938993548041863352763583143652430163689848173265481148509106051935833487848298561387429516,00 GB14,19 GB <1,76 GB2,90 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,65 GB2,89 GB7,10 GB...
|
NULL
|
8497701148236933192
|
NULL
|
click
|
ocr
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina PetrovaD. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira Cloud# releases8 22Messagesnewdou+O Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+AaActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefoxCP Isolated Web ContentFirefox GPU HelperVTDecoderXPCServiceFirefoxCP Isolated Web ContentSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentNotion Calendar Helper (Renderer)Notion Helper (Renderer)claudeClaude Helper (Renderer)FirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentMEMORY PRESSUREMem...2,03 GB1,20 GB992,6 MB962,7 MB836,2 MB793,5 MB783,4 MB554,1 MB547,4 MB543,9 MB515,8 MB464,9 MB453,4 MB445,2 MB426,1 MB393,7 MB391,5 MB389,9 MB372,5 MB346,6 MB328,7 MB326,1 MB325,9 MB303,5 MB252,2 MB244,8 MB244,6 MB224,0 MBPhysical Memory:Memory Used:Cached Files:Swap Used:100% <478Wed 15 Apr 16:48:52CPUMemoryDiskThreads3923742585282925271124162425262524231521131528272724EnergyPorts60319 7557271241 20220 019130242128253166121199123123124125122121174330722201261 832127125122PID93892407801442974146648424203080193671314673938993548041863352763583143652430163689848173265481148509106051935833487848298561387429516,00 GB14,19 GB <1,76 GB2,90 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,65 GB2,89 GB7,10 GB...
|
27428
|
|
55136
|
1189
|
35
|
2026-04-20T09:35:10.325552+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776677710325_m1.jpg...
|
Firefox
|
Jiminny — Work
|
True
|
app.dev.jiminny.com/ai-reports/manage
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
JY-20553 | Improve crm-sync delays by yalokin-jiminny · Pull Request #11976 · jiminny/app
JY-20553 | Improve crm-sync delays by yalokin-jiminny · Pull Request #11976 · jiminny/app
[SRD-6793] Les Mills activity types not pulling in - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
JY-20698 handle failed field sync on playbook import activity types by LakyLak · Pull Request #11988 · jiminny/app
JY-20698 handle failed field sync on playbook import activity types by LakyLak · Pull Request #11988 · jiminny/app
JY-20692 change confirmation parameter by LakyLak · Pull Request #11986 · jiminny/app
JY-20692 change confirmation parameter by LakyLak · Pull Request #11986 · jiminny/app
[JY-20543] AJ Reports > Tracking - Jira
[JY-20543] AJ Reports > Tracking - Jira
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
Ask Jiminny Reports by nikolay-yankov · Pull Request #11894 · jiminny/app
Ask Jiminny Reports by nikolay-yankov · Pull Request #11894 · jiminny/app
New Tab
New Tab
Product Growth Platform | Userpilot
Product Growth Platform | Userpilot
Userpilot | Logged-activity
Userpilot | Logged-activity
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Jiminny
Jiminny
Close tab
Jiminny
Jiminny
Jiminny
Jiminny
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Ask Jiminny Reports
Ask Jiminny Reports
Create
Report name
Prompt Prompt
Prompt
Prompt
Saved search Saved search
Saved search
Saved search
All statuses All statuses
All statuses
All statuses
Clear all
NAME
FREQUENCY
SHARED
EXPIRING
ACTIONS
Test
Weekly
21/04/2026
Exp
Weekly
20/04/2027
Monthly Ask J Report
Monthly
Jiminny Mobile SA
30/06/2026
Search One
Daily
Jiminny Mobile SA
30/04/2026
Create report
Save
1. General
1. General
NAME
dasda
Clear
*
FREQUENCY
Select Daily
Select
Daily
*
EXPIRES ON
20 Apr, 2027
*
Share with
TEAM
Select Select
Select
Select
TEAM MEMBER
Select Select
Select
Select
2. Report parameters
2. Report parameters
SAVED SEARCH
Select deleteme
Select
deleteme
*
ASK JIMINNY PROMPT
Select Summarize
Select
Summarize
*
Disable report definition
Clear
Filter URLs
Pause/Resume recording network log
New Request
Search
Request Blocking
All
HTML
CSS
JS
XHR
Fonts
Images
Media
WS
Other
Disable Cache
Disable Cache
No Throttling
Network Settings
Status
Status
Method
Method
Domain
Domain
File
File
Initiator
Initiator
Type
Type
Transferred
Transferred
Size
Size
200
GET
app.dev.jiminny.com
form-data
xhr
json
7.56 kB
10.67 kB
200
POST
app.dev.jiminny.com
aj-reports
xhr
json
3.03 kB
493 B
200
GET
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
7.57 kB
10.83 kB
422
PUT
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
2.73 kB
63 B
200
GET
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
7.57 kB
10.83 kB
422
PUT
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
2.73 kB
63 B
200
GET
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
7.57 kB...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"JY-20553 | Improve crm-sync delays by yalokin-jiminny · Pull Request #11976 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20553 | Improve crm-sync delays by yalokin-jiminny · Pull Request #11976 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20698 handle failed field sync on playbook import activity types by LakyLak · Pull Request #11988 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20698 handle failed field sync on playbook import activity types by LakyLak · Pull Request #11988 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20692 change confirmation parameter by LakyLak · Pull Request #11986 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20692 change confirmation parameter by LakyLak · Pull Request #11986 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20543] AJ Reports > Tracking - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20543] AJ Reports > Tracking - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Ask Jiminny Reports by nikolay-yankov · Pull Request #11894 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Jiminny Reports by nikolay-yankov · Pull Request #11894 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Product Growth Platform | Userpilot","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Product Growth Platform | Userpilot","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Userpilot | Logged-activity","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Userpilot | Logged-activity","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Ask Jiminny Reports","depth":10,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Ask Jiminny Reports","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextField","text":"Report name","depth":11,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXComboBox","text":"Prompt Prompt","depth":10,"value":"Prompt Prompt","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Prompt","depth":11,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Prompt","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Saved search Saved search","depth":10,"value":"Saved search Saved search","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Saved search","depth":11,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Saved search","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"All statuses All statuses","depth":10,"value":"All statuses All statuses","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"All statuses","depth":11,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"All statuses","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Clear all","depth":10,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"NAME","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"FREQUENCY","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SHARED","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"EXPIRING","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ACTIONS","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Test","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Weekly","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"21/04/2026","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exp","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Weekly","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"20/04/2027","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monthly Ask J Report","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Monthly","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Mobile SA","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"30/06/2026","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Search One","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Daily","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Mobile SA","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"30/04/2026","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Create report","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Save","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"1. General","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. General","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"NAME","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"dasda","depth":12,"value":"dasda","help_text":"","placeholder":"Enter name","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Clear","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"*","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"FREQUENCY","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select Daily","depth":11,"value":"Select Daily","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":12,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Daily","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"EXPIRES ON","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"20 Apr, 2027","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Share with","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TEAM","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select Select","depth":11,"value":"Select Select","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":12,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Select","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TEAM MEMBER","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select Select","depth":11,"value":"Select Select","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":12,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Select","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Report parameters","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Report parameters","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SAVED SEARCH","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select deleteme","depth":11,"value":"Select deleteme","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":12,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"deleteme","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ASK JIMINNY PROMPT","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select Summarize","depth":11,"value":"Select Summarize","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":12,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Summarize","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Disable report definition","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Clear","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextField","text":"Filter URLs","depth":16,"help_text":"","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Pause/Resume recording network log","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"New Request","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Search","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Request Blocking","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"All","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"HTML","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"CSS","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"JS","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"XHR","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Fonts","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Images","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Media","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"WS","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Other","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Disable Cache","depth":17,"help_text":"","role_description":"checkbox","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Disable Cache","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"No Throttling","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Network Settings","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Status","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Status","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Method","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Method","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Domain","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Domain","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"File","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"File","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Initiator","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Initiator","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Type","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Type","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Transferred","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Transferred","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Size","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Size","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"200","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"GET","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app.dev.jiminny.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"form-data","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7.56 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10.67 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"200","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"POST","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app.dev.jiminny.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"aj-reports","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3.03 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"493 B","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"200","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"GET","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app.dev.jiminny.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7361706f-ffbe-4d3d-a22b-9fab33d05094","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7.57 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10.83 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"422","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PUT","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app.dev.jiminny.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7361706f-ffbe-4d3d-a22b-9fab33d05094","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2.73 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"63 B","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"200","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"GET","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app.dev.jiminny.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7361706f-ffbe-4d3d-a22b-9fab33d05094","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7.57 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"10.83 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"422","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PUT","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app.dev.jiminny.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7361706f-ffbe-4d3d-a22b-9fab33d05094","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2.73 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"63 B","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"200","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"GET","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app.dev.jiminny.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7361706f-ffbe-4d3d-a22b-9fab33d05094","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"7.57 kB","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8497213081027420535
|
6794911501793519346
|
click
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
JY-20553 | Improve crm-sync delays by yalokin-jiminny · Pull Request #11976 · jiminny/app
JY-20553 | Improve crm-sync delays by yalokin-jiminny · Pull Request #11976 · jiminny/app
[SRD-6793] Les Mills activity types not pulling in - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
JY-20698 handle failed field sync on playbook import activity types by LakyLak · Pull Request #11988 · jiminny/app
JY-20698 handle failed field sync on playbook import activity types by LakyLak · Pull Request #11988 · jiminny/app
JY-20692 change confirmation parameter by LakyLak · Pull Request #11986 · jiminny/app
JY-20692 change confirmation parameter by LakyLak · Pull Request #11986 · jiminny/app
[JY-20543] AJ Reports > Tracking - Jira
[JY-20543] AJ Reports > Tracking - Jira
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
Ask Jiminny Reports by nikolay-yankov · Pull Request #11894 · jiminny/app
Ask Jiminny Reports by nikolay-yankov · Pull Request #11894 · jiminny/app
New Tab
New Tab
Product Growth Platform | Userpilot
Product Growth Platform | Userpilot
Userpilot | Logged-activity
Userpilot | Logged-activity
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
fix(security): composer dependency updates – 2026-04-15 by github-actions[bot] · Pull Request #11970 · jiminny/app
Jiminny
Jiminny
Close tab
Jiminny
Jiminny
Jiminny
Jiminny
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Ask Jiminny Reports
Ask Jiminny Reports
Create
Report name
Prompt Prompt
Prompt
Prompt
Saved search Saved search
Saved search
Saved search
All statuses All statuses
All statuses
All statuses
Clear all
NAME
FREQUENCY
SHARED
EXPIRING
ACTIONS
Test
Weekly
21/04/2026
Exp
Weekly
20/04/2027
Monthly Ask J Report
Monthly
Jiminny Mobile SA
30/06/2026
Search One
Daily
Jiminny Mobile SA
30/04/2026
Create report
Save
1. General
1. General
NAME
dasda
Clear
*
FREQUENCY
Select Daily
Select
Daily
*
EXPIRES ON
20 Apr, 2027
*
Share with
TEAM
Select Select
Select
Select
TEAM MEMBER
Select Select
Select
Select
2. Report parameters
2. Report parameters
SAVED SEARCH
Select deleteme
Select
deleteme
*
ASK JIMINNY PROMPT
Select Summarize
Select
Summarize
*
Disable report definition
Clear
Filter URLs
Pause/Resume recording network log
New Request
Search
Request Blocking
All
HTML
CSS
JS
XHR
Fonts
Images
Media
WS
Other
Disable Cache
Disable Cache
No Throttling
Network Settings
Status
Status
Method
Method
Domain
Domain
File
File
Initiator
Initiator
Type
Type
Transferred
Transferred
Size
Size
200
GET
app.dev.jiminny.com
form-data
xhr
json
7.56 kB
10.67 kB
200
POST
app.dev.jiminny.com
aj-reports
xhr
json
3.03 kB
493 B
200
GET
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
7.57 kB
10.83 kB
422
PUT
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
2.73 kB
63 B
200
GET
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
7.57 kB
10.83 kB
422
PUT
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
2.73 kB
63 B
200
GET
app.dev.jiminny.com
7361706f-ffbe-4d3d-a22b-9fab33d05094
xhr
json
7.57 kB...
|
NULL
|
|
79001
|
2027
|
4
|
2026-04-24T14:12:45.982536+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777039965982_m1.jpg...
|
Firefox
|
Pipelines - jiminny/app — Work
|
True
|
app.circleci.com/pipelines/github/jiminny/app
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-20738 add debug logs on AJ report UP tracking by LakyLak · Pull Request #12013 · jiminny/app
JY-20738 add debug logs on AJ report UP tracking by LakyLak · Pull Request #12013 · jiminny/app
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
Jiminny
Jiminny
Userpilot | Nudge-created
Userpilot | Nudge-created
Pipelines - jiminny/app
Pipelines - jiminny/app
Close tab
Inbox (1,610) - [EMAIL] - Jiminny Mail
Inbox (1,610) - [EMAIL] - Jiminny Mail
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Unnamed Group
Jiminny
Jiminny
503 Service Temporarily Unavailable
503 Service Temporarily Unavailable
app/app/Http/Controllers/FrontendControllerTrait.php at fb01b96ae7a4635bc86648b82c2435789cddf693 · jiminny/app
app/app/Http/Controllers/FrontendControllerTrait.php at fb01b96ae7a4635bc86648b82c2435789cddf693 · jiminny/app
favicon.ico - Object in S3 bucket stage.ext.jiminny.public | S3 | us-east-2
favicon.ico - Object in S3 bucket stage.ext.jiminny.public | S3 | us-east-2
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Go to home page
Auto theme
Open notifications
Open support menu
Open user menu
org avatar Current organization: jiminny
Home
Home
Pipelines
Pipelines
Projects
Projects
Deploys
Deploys
Insights
Insights
Runners
Runners
Org
Org
Plan
Plan
Chunk
Chunk
Dashboard All Pipelines
All Pipelines
Project Outline app
app
app
app
Overview
Overview
Settings
Settings
Deploys
Deploys
Lightning Manage triggers
Manage triggers
Trigger Pipeline
Pipelines All pipelines my-pipelines-filter
All pipelines
app Project Filter. Selected "app"
app
All branches Branch Filter. Selected "All branches"
All branches
Start Time Cutoff date Arrow Drop Down
Cutoff date
All statuses Arrow Drop Down
All
statuses
Filter Display options
Display options
Pipeline
Status
Workflow
Checkout source
Trigger event
Start
Duration
Actions
app
57734
57734
SUCCESS workflow build_accept_deploy. Collapse the workflow jobs list.
Status Passed Success
Success
build_accept_deploy
build_accept_deploy
JY-20733-autoloader-optimization
JY-20733-autoloader-optimization
Open commit on version control site
80a4e91
API
Copy timestamp to clipboard
1h ago
Copy timestamp duration to clipboard
Rerun workflow from start
Rerun workflow from failed
Cancel workflow
Fix workflow
More Actions
Jobs
SUCCESS job checkout-code
checkout-code
877240
1m 21s
1m 21s
SUCCESS job build-frontend
build-frontend
877241
1m 34s
1m 34s
SUCCESS job test-frontend
test-frontend
877252
1m 46s
1m 46s
SUCCESS job build-backend
build-backend
877242
1m 22s
1m 22s
SUCCESS job phpstan
phpstan
877253
1m 29s
1m 29s
SUCCESS job prepare_deploy_revision_subenv
prepare_deploy_revision_subenv
877245
53s
53s
SUCCESS job build_docker_backend_code_subenv
build_docker_backend_code_subenv
877248
1m 57s
1m 57s
SUCCESS job build_docker_worker_code_subenv
build_docker_worker_code_subenv
877247
2m 2s
2m 2s
SUCCESS job build_docker_worker_video_code_subenv
build_docker_worker_video_code_subenv
877246
1m 51s
1m 51s
SUCCESS job db_migrations_subenv
db_migrations_subenv
877249
6s
6s
SUCCESS job deploy_docker_backend_code_subenv
deploy_docker_backend_code_subenv
877250
5m 39s
5m 39s
SUCCESS job deploy_docker_worker_code_subenv
deploy_docker_worker_code_subenv
877254
2m 9s
2m 9s
SUCCESS job deploy_docker_worker_video_code_subenv
deploy_docker_worker_video_code_subenv
877251
52s
52s
SUCCESS job deploy_frontend_assets_to_s3_subenv
deploy_frontend_assets_to_s3_subenv
877257
23s
23s
SUCCESS job setup
setup
877243
45s
45s
SUCCESS job test
test
877244
9m 43s
9m 43s
SUCCESS job test-backend-lint
test-backend-lint...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20738 add debug logs on AJ report UP tracking by LakyLak · Pull Request #12013 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20738 add debug logs on AJ report UP tracking by LakyLak · Pull Request #12013 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Userpilot | Nudge-created","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Userpilot | Nudge-created","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Inbox (1,610) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Inbox (1,610) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Unnamed Group","depth":4,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"503 Service Temporarily Unavailable","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"503 Service Temporarily Unavailable","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"app/app/Http/Controllers/FrontendControllerTrait.php at fb01b96ae7a4635bc86648b82c2435789cddf693 · jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app/app/Http/Controllers/FrontendControllerTrait.php at fb01b96ae7a4635bc86648b82c2435789cddf693 · jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"favicon.ico - Object in S3 bucket stage.ext.jiminny.public | S3 | us-east-2","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"favicon.ico - Object in S3 bucket stage.ext.jiminny.public | S3 | us-east-2","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.0,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.016666668,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Go to home page","depth":9,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Auto theme","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open notifications","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"Open support menu","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"Open user menu","depth":9,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"org avatar Current organization: jiminny","depth":9,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Home","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Home","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pipelines","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Projects","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Projects","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Deploys","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Deploys","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Insights","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Insights","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Runners","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Runners","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Org","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Org","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Plan","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Plan","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Chunk","depth":10,"bounds":{"left":0.10034722,"top":0.0,"width":0.029166667,"height":0.064444445},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Chunk","depth":12,"bounds":{"left":0.10034722,"top":0.0,"width":0.029166667,"height":0.019444445},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Dashboard All Pipelines","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"All Pipelines","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Project Outline app","depth":15,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"app","depth":13,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Overview","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Overview","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Settings","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Deploys","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Deploys","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Lightning Manage triggers","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Manage triggers","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Trigger Pipeline","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Pipelines All pipelines my-pipelines-filter","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"All pipelines","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"app Project Filter. Selected \"app\"","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"All branches Branch Filter. Selected \"All branches\"","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"All branches","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Start Time Cutoff date Arrow Drop Down","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Cutoff date","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"All statuses Arrow Drop Down","depth":13,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"All","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"statuses","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Filter Display options","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Display options","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Pipeline","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Status","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Workflow","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Checkout source","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Trigger event","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Start","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Duration","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Actions","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"app","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"57734","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"57734","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"SUCCESS workflow build_accept_deploy. Collapse the workflow jobs list.","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Status Passed Success","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Success","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"build_accept_deploy","depth":12,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"build_accept_deploy","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"JY-20733-autoloader-optimization","depth":13,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20733-autoloader-optimization","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Open commit on version control site","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"80a4e91","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"API","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy timestamp to clipboard","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1h ago","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy timestamp duration to clipboard","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Rerun workflow from start","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Rerun workflow from failed","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Cancel workflow","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Fix workflow","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"More Actions","depth":12,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Jobs","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job checkout-code","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"checkout-code","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877240","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1m 21s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1m 21s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job build-frontend","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"build-frontend","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877241","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1m 34s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1m 34s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job test-frontend","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"test-frontend","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877252","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1m 46s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1m 46s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job build-backend","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"build-backend","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877242","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1m 22s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1m 22s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job phpstan","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"phpstan","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877253","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1m 29s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1m 29s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job prepare_deploy_revision_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"prepare_deploy_revision_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877245","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"53s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"53s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job build_docker_backend_code_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"build_docker_backend_code_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877248","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1m 57s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1m 57s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job build_docker_worker_code_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"build_docker_worker_code_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877247","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"2m 2s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2m 2s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job build_docker_worker_video_code_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"build_docker_worker_video_code_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877246","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"1m 51s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1m 51s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job db_migrations_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"db_migrations_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877249","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"6s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"6s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job deploy_docker_backend_code_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"deploy_docker_backend_code_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877250","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"5m 39s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"5m 39s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job deploy_docker_worker_code_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"deploy_docker_worker_code_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877254","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"2m 9s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2m 9s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job deploy_docker_worker_video_code_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"deploy_docker_worker_video_code_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877251","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"52s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"52s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job deploy_frontend_assets_to_s3_subenv","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"deploy_frontend_assets_to_s3_subenv","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877257","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"23s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"23s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job setup","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"setup","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877243","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"45s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"45s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job test","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"test","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"877244","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"9m 43s","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9m 43s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SUCCESS job test-backend-lint","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"test-backend-lint","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8496730869700764321
|
-6047631573707230998
|
click
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
JY-20489 | Optimize Nudges - Phase 2 by yalokin-jiminny · Pull Request #11997 · jiminny/app
New Tab
New Tab
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
AI reports promotion pages by nikolay-yankov · Pull Request #11998 · jiminny/app
JY-20738 add debug logs on AJ report UP tracking by LakyLak · Pull Request #12013 · jiminny/app
JY-20738 add debug logs on AJ report UP tracking by LakyLak · Pull Request #12013 · jiminny/app
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
JY-20157 add not enough activities notification by LakyLak · Pull Request #12011 · jiminny/app
Jiminny
Jiminny
Userpilot | Nudge-created
Userpilot | Nudge-created
Pipelines - jiminny/app
Pipelines - jiminny/app
Close tab
Inbox (1,610) - [EMAIL] - Jiminny Mail
Inbox (1,610) - [EMAIL] - Jiminny Mail
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Unnamed Group
Jiminny
Jiminny
503 Service Temporarily Unavailable
503 Service Temporarily Unavailable
app/app/Http/Controllers/FrontendControllerTrait.php at fb01b96ae7a4635bc86648b82c2435789cddf693 · jiminny/app
app/app/Http/Controllers/FrontendControllerTrait.php at fb01b96ae7a4635bc86648b82c2435789cddf693 · jiminny/app
favicon.ico - Object in S3 bucket stage.ext.jiminny.public | S3 | us-east-2
favicon.ico - Object in S3 bucket stage.ext.jiminny.public | S3 | us-east-2
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Go to home page
Auto theme
Open notifications
Open support menu
Open user menu
org avatar Current organization: jiminny
Home
Home
Pipelines
Pipelines
Projects
Projects
Deploys
Deploys
Insights
Insights
Runners
Runners
Org
Org
Plan
Plan
Chunk
Chunk
Dashboard All Pipelines
All Pipelines
Project Outline app
app
app
app
Overview
Overview
Settings
Settings
Deploys
Deploys
Lightning Manage triggers
Manage triggers
Trigger Pipeline
Pipelines All pipelines my-pipelines-filter
All pipelines
app Project Filter. Selected "app"
app
All branches Branch Filter. Selected "All branches"
All branches
Start Time Cutoff date Arrow Drop Down
Cutoff date
All statuses Arrow Drop Down
All
statuses
Filter Display options
Display options
Pipeline
Status
Workflow
Checkout source
Trigger event
Start
Duration
Actions
app
57734
57734
SUCCESS workflow build_accept_deploy. Collapse the workflow jobs list.
Status Passed Success
Success
build_accept_deploy
build_accept_deploy
JY-20733-autoloader-optimization
JY-20733-autoloader-optimization
Open commit on version control site
80a4e91
API
Copy timestamp to clipboard
1h ago
Copy timestamp duration to clipboard
Rerun workflow from start
Rerun workflow from failed
Cancel workflow
Fix workflow
More Actions
Jobs
SUCCESS job checkout-code
checkout-code
877240
1m 21s
1m 21s
SUCCESS job build-frontend
build-frontend
877241
1m 34s
1m 34s
SUCCESS job test-frontend
test-frontend
877252
1m 46s
1m 46s
SUCCESS job build-backend
build-backend
877242
1m 22s
1m 22s
SUCCESS job phpstan
phpstan
877253
1m 29s
1m 29s
SUCCESS job prepare_deploy_revision_subenv
prepare_deploy_revision_subenv
877245
53s
53s
SUCCESS job build_docker_backend_code_subenv
build_docker_backend_code_subenv
877248
1m 57s
1m 57s
SUCCESS job build_docker_worker_code_subenv
build_docker_worker_code_subenv
877247
2m 2s
2m 2s
SUCCESS job build_docker_worker_video_code_subenv
build_docker_worker_video_code_subenv
877246
1m 51s
1m 51s
SUCCESS job db_migrations_subenv
db_migrations_subenv
877249
6s
6s
SUCCESS job deploy_docker_backend_code_subenv
deploy_docker_backend_code_subenv
877250
5m 39s
5m 39s
SUCCESS job deploy_docker_worker_code_subenv
deploy_docker_worker_code_subenv
877254
2m 9s
2m 9s
SUCCESS job deploy_docker_worker_video_code_subenv
deploy_docker_worker_video_code_subenv
877251
52s
52s
SUCCESS job deploy_frontend_assets_to_s3_subenv
deploy_frontend_assets_to_s3_subenv
877257
23s
23s
SUCCESS job setup
setup
877243
45s
45s
SUCCESS job test
test
877244
9m 43s
9m 43s
SUCCESS job test-backend-lint
test-backend-lint...
|
NULL
|
|
51281
|
1107
|
1
|
2026-04-18T10:56:42.330828+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-18/1776 /Users/lukas/.screenpipe/data/data/2026-04-18/1776509802330_m1.jpg...
|
Claude
|
Claude
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Skip to content
Skip to content
Click to collapse
Skip to content
Skip to content
Click to collapse
⌘B
Drag to resize
Open sidebar
Chat
Cowork
Code
New chat ⌘N
New chat
⌘N
Projects
Customize
Artifacts
Pinned
Bulgarian citizenship application process for EU residents
More options for Bulgarian citizenship application process for EU residents
Dawarich location tracking project
More options for Dawarich location tracking project
Recents
View all
Reviewing recent conversation highlights
More options for Reviewing recent conversation highlights
Screenpipe WAL processing when stopped
More options for Screenpipe WAL processing when stopped
Mac aliases not recognized
More options for Mac aliases not recognized
Boosteroid still recording despite ignored windows setting
More options for Boosteroid still recording despite ignored windows setting
Missing JavaScript promise in authorization response
More options for Missing JavaScript promise in authorization response
Linux SQLite UI for NAS
More options for Linux SQLite UI for NAS
Claude API 500 internal server error
More options for Claude API 500 internal server error
Screenpipe query capabilities and usage
More options for Screenpipe query capabilities and usage
eGPU compatibility with Mac mini and Studio
More options for eGPU compatibility with Mac mini and Studio
Understanding OpenRouter API gateway
More options for Understanding OpenRouter API gateway
Screenpipe 14-day retention explained
More options for Screenpipe 14-day retention explained
Reddit homepage feed overview
More options for Reddit homepage feed overview
Docker container not visible in console
More options for Docker container not visible in console
DIA browser RAM configuration
More options for DIA browser RAM configuration
Screenpipe company background
More options for Screenpipe company background
DSK Bank Bulgaria secure investing explained
More options for DSK Bank Bulgaria secure investing explained
Building a comprehensive personal homelab system
More options for Building a comprehensive personal homelab system
Importing Google Timeline data to Owntracks
More options for Importing Google Timeline data to Owntracks
Chromecast remote volume buttons not working
More options for Chromecast remote volume buttons not working
Screenpipe free version guide
More options for Screenpipe free version guide
Updated to 1.3109.0
Relaunch to apply
Relaunch
Lukas Kovalik
Lukas Kovalik
Get apps and extensions
Screenpipe WAL processing when stopped
Screenpipe WAL processing when stopped
More options for Screenpipe WAL processing when stopped
Open sidebar
Share chat
how to make the screenpipe wal being processed. It seems it is not working when the screenpipe is stopped
17 Apr
Retry
Edit
Copy
WAL (Write-Ahead Log) in SQLite needs to be checkpointed to merge pending data into the main database file. When Screenpipe stops, the WAL may not be fully checkpointed.
The issue
The issue
SQLite WAL mode keeps a separate
.sqlite-wal
file. Data there is "pending" until a checkpoint runs. Screenpipe normally handles this while running, but if it stops abruptly or you query while it's stopped, the WAL data may appear missing to external readers.
Solutions
Solutions
Option 1: Force a checkpoint manually (safest)
Copy to clipboard
bash
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
This flushes all WAL data into the main DB file and truncates the WAL. Run this after Screenpipe stops.
Option 2: Make your queries WAL-aware
When querying with
sqlite3
directly, open in WAL mode explicitly so it reads both the DB and WAL together:
Copy to clipboard
bash
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA journal_mode=WAL; SELECT ..."
Actually,
sqlite3
already reads WAL automatically — but only if the WAL file is present and not corrupted. The real fix is ensuring the connection sees both files.
Option 3: Add to your
spq
alias
Update the alias to always checkpoint first:
Copy to clipboard
bash
alias
spq
=
'sqlite3 -json ~/.screenpipe/db.sqlite'
alias
spqcheckpoint
=
'sqlite3 ~/.screenpipe/db.sqlite "PRAGMA wal_checkpoint(TRUNCATE);" && echo "WAL checkpointed"'
Option 4: Checkpoint on Screenpipe shutdown (via a wrapper script)
If you start Screenpipe via a script, wrap it:
Copy to clipboard
bash
#!/bin/bash
screenpipe
"
$@
"
# On exit (Ctrl+C or normal stop), checkpoint WAL
echo
"Screenpipe stopped. Checkpointing WAL..."
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
echo
"Done."
Quick diagnosis
Quick diagnosis
Check if a WAL file exists and its size:
Copy to clipboard
bash
ls
-lh ~/.screenpipe/db.sqlite*
If
db.sqlite-wal
is large (MBs), that's unprocessed data. After checkpointing it should shrink to near 0.
Recommended approach:
Use Option 1 whenever you want to query after stopping Screenpipe, and optionally wrap it in your
screenpipe-day.sh
report script at the top.
Copy
Give positive feedback
Give negative feedback
Retry
what about jpg to mp4 conversion
17 Apr
Retry
Edit
Copy
That's unrelated to Screenpipe — are you asking about a general tool/command for converting a sequence of JPG images into an MP4 video?
If so,
FFmpeg
is the standard tool:
Image sequence (e.g. frame001.jpg, frame002.jpg...):
Copy to clipboard
bash
ffmpeg -framerate
24
-i frame%03d.jpg -c:v libx264 -pix_fmt yuv420p output.mp4
Single JPG as a still video (e.g. 5 seconds):
Copy to clipboard
bash
ffmpeg -loop
1
-i input.jpg -t
5
-c:v libx264 -pix_fmt yuv420p output.mp4
All JPGs in a folder (sorted by name):
Copy to clipboard
bash
ffmpeg -framerate
24
-pattern_type glob -i
'*.jpg'
-c:v libx264 -pix_fmt yuv420p output.mp4
Install on Mac:
Copy to clipboard
bash
brew
install
ffmpeg
Let me know the actual use case (slideshow, timelapse, Screenpipe frames, something else?) and I can tailor the command.
Copy
Give positive feedback
Give negative feedback
Retry
but this seems to also happen automatically when the screenpipe is running
17 Apr
Retry
Edit
Copy
Yes, exactly — SQLite automatically checkpoints the WAL when:
The WAL reaches
1000 pages
(default threshold)
A connection closes cleanly
You explicitly call
PRAGMA wal_checkpoint
So when Screenpipe is
running
, the WAL gets checkpointed automatically in the background and you never notice it. The problem only surfaces when Screenpipe is
stopped abruptly
(Ctrl+C, crash, system sleep) and the WAL hasn't been flushed yet at that moment.
The real-world impact
The real-world impact
If you run your
screenpipe-day.sh
or
spq
queries
while Screenpipe is stopped
, you may be missing the last chunk of data that was buffered in the WAL but never checkpointed.
Practical fix
Practical fix
Just prepend the checkpoint to your query script:
Copy to clipboard
bash
# In screenpipe-day.sh or before any spq usage after stopping screenpipe
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
2
>
/dev/null
Or as a shell function in your
.zshrc
:
Copy to clipboard
bash
spqready
(
)
{
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
>
/dev/null
2
>
&1
echo
"WAL checkpointed, DB ready."
}
Then just run
spqready
once after stopping Screenpipe before querying.
Copy
Give positive feedback
Give negative feedback
Retry
how to stop screenpipe but make sure that jpg to mp4 is converted
17 Apr
Retry
Edit
Copy
These two things aren't connected — where does JPG to MP4 come into your Screenpipe setup? Are you thinking of:
Screenpipe's frame captures
(it stores screenshots as JPGs) that you want converted into a timelapse MP4 when stopping?
Something else entirely?
Copy
Give positive feedback
Give negative feedback
Retry
No I mean there are /data/data/{date} with jpgs that are converted to mp4. It seems that if stopped via sp-stop='pkill -f screenpipe && echo '\''screenpipe stopped'\' then these are not converted unless I staert it again
17 Apr
Retry
Edit
Copy
Ah, got it — Screenpipe has an internal process that converts the captured JPG frames into MP4 chunks. When you
pkill -f screenpipe
hard, that conversion pipeline gets killed mid-flight, leaving unconverted JPGs behind.
The fix
The fix
You need to let Screenpipe finish its current conversion before killing it. The cleanest approach is to
send SIGTERM first, then wait
, instead of SIGKILL-equivalent
pkill
:
Copy to clipboard
bash
# Graceful stop — sends SIGTERM and waits
alias
sp-stop
=
'pkill -SIGTERM -f screenpipe && while pgrep -f screenpipe > /dev/null; do sleep 1; done && echo "screenpipe stopped"'
This gives Screenpipe time to finish in-flight conversions before actually dying.
If that's not enough — convert remaining JPGs manually
If that's not enough — convert remaining JPGs manually
After stopping, find any unconverted JPG sequences and process them:
Copy to clipboard
bash
find
~/.screenpipe/data -type d
|
while
read
dir
;
do
jpgs
=
$(
ls
"
$dir
"
/*.jpg
2
>
/dev/null
|
wc
-l
)
mp4
=
$(
ls
"
$dir
"
/*.mp4
2
>
/dev/null
|
wc
-l
)
if
[
"
$jpgs
"
-gt
0
]
&&
[
"
$mp4
"
-eq
0
]
;
then
echo
"Converting
$dir
..."
ffmpeg -framerate
10
-pattern_type glob -i
"
$dir
/*.jpg"
\
-c:v libx264 -pix_fmt yuv420p
"
$dir
/output.mp4"
-loglevel error
fi
done
Adjust
~/.screenpipe/data
to your actual path and
framerate
to whatever Screenpipe uses.
Combined stop alias
Combined stop alias
Copy to clipboard
bash
alias
sp-stop
=
'
pkill -SIGTERM -f screenpipe && \
while pgrep -f screenpipe > /dev/null; do sleep 1; done && \
echo "screenpipe stopped, converting remaining frames..." && \
find ~/.screenpipe/data -type d | while read dir; do
jpgs=$(ls "$dir"/*.jpg 2>/dev/null | wc -l)
mp4=$(ls "$dir"/*.mp4 2>/dev/null | wc -l)
if [ "$jpgs" -gt 0 ] && [ "$mp4" -eq 0 ]; then
ffmpeg -framerate 10 -pattern_type glob -i "$dir/*.jpg" \
-c:v libx264 -pix_fmt yuv420p "$dir/output.mp4" -loglevel error && \
echo "Converted $dir"
fi
done && \
echo "all done"
'
Do you know the actual framerate Screenpipe uses for its captures?
Copy
Give positive feedback
Give negative feedback
Retry
Pasted Text, pasted, 255 lines
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data/2026-04-17 $ ll total 1388520 drwxr-xr-x 252 lukas staff 8064 17 Apr 21:06 . drwxr-xr-x 11 lukas staff 352 17 Apr 08:56 .. -rw-r--r-- 1 lukas staff 199318 17 Apr 21:04 1776449072292_m1.jpg -rw-r--r-- 1 lukas
PASTED
17 Apr
Retry
Edit
Copy
Now the picture is clear. Looking at your directory:
JPGs are named like
1776449072292_m1.jpg
(timestamp + monitor)
MP4s are named like
compact_monitor_1_1776406033543.mp4
(monitor + timestamp)
MP4s are produced in ~5 minute chunks per monitor
The loose JPGs at the bottom (21
:04
–21
:06
) have
no corresponding MP4
yet — that's the unconverted tail
So Screenpipe batches JPGs into 5-minute MP4 chunks. When killed, the current batch is abandoned.
Script to convert remaining JPGs
Script to convert remaining JPGs
The key insight from the filenames: JPGs are
{timestamp}_m{N}.jpg
— group them by monitor number (
m1
,
m2
...) and convert each group.
Copy to clipboard
bash
#!/bin/bash
# sp-convert-remaining.sh
# Converts any unconverted JPGs in ~/.screenpipe/data/data/<date>/
DATE
=
"
${1
:-
$(date +
%
Y-
%
m-
%
d)}
"
DIR
=
"
$HOME
/.screenpipe/data/data/
$DATE
"
echo
"Scanning
$DIR
for unconverted JPGs..."
for
monitor
in
1
2
;
do
jpgs
=
(
"
$DIR
"
/*_m
${monitor}
.jpg
)
[
[
!
-e
"
${jpgs
[
0
]
}
"
]
]
&&
continue
echo
"Monitor
$monitor
: found
${
#
jpgs
[
@
]
}
JPGs"
# Sort by timestamp (they're already timestamp-prefixed, so glob order is fine)
# Use the first frame's timestamp for the output filename
first_ts
=
$(
basename
"
${jpgs
[
0
]
}
"
|
cut
-d_ -f1
)
outfile
=
"
$DIR
/compact_monitor_
${monitor}
_
${first_ts}
.mp4"
if
[
[
-f
"
$outfile
"
]
]
;
then
echo
" Already converted:
$outfile
— skipping"
continue
fi
echo
" Converting to
$outfile
..."
# Write sorted filelist for ffmpeg
tmplist
=
$(
mktemp /tmp/sp_frames_XXXXXX.txt
)
for
f
in
$(
ls
"
$DIR
"
/*_m$
{
monitor
}
.jpg
|
sort
)
;
do
echo
"file '
$f
'"
>>
"
$tmplist
"
done
ffmpeg -f concat -safe
0
-i
"
$tmplist
"
\
-c:v libx264 -pix_fmt yuv420p
\
-loglevel error
\
"
$outfile
"
&&
echo
" Done:
$outfile
"
rm
"
$tmplist
"
done
Updated sp-stop alias
Updated sp-stop alias
Copy to clipboard
bash
alias
sp-stop
=
'
pkill -SIGTERM -f screenpipe && \
while pgrep -f screenpipe > /dev/null; do sleep 1; done && \
echo "Screenpipe stopped. Converting remaining frames..." && \
bash ~/.screenpipe/sp-convert-remaining.sh && \
sqlite3 ~/.screenpipe/db.sqlite "PRAGMA wal_checkpoint(TRUNCATE);" > /dev/null && \
echo "All done."
'
Notes
Notes
ffmpeg -f concat
preserves the exact frame order by timestamp rather than assuming a fixed framerate — safer since Screenpipe's capture interval isn't perfectly regular
After conversion you may want to delete the JPGs: add
rm "$DIR"/*_m${monitor}.jpg
after the ffmpeg call if desired
The script accepts a date arg:
sp-convert-remaining.sh 2026-04-16
to fix a previous day
Copy
Give positive feedback
Give negative feedback
Retry
well I run pkill -SIGTERM -f screenpipe && while pgrep -f screenpipe > /dev/null; do sleep 1; done && echo "screenpipe stopped" but it didn't help
17 Apr...
|
[{"role":"AXLink","text":& [{"role":"AXLink","text":"Skip to content","depth":14,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Skip to content","depth":15,"role_description":"text"},{"role":"AXStaticText","text":"Click to collapse","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"⌘B","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"Drag to resize","depth":16,"role_description":"text"},{"role":"AXButton","text":"Open sidebar","depth":14,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chat","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cowork","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New chat ⌘N","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"New chat","depth":17,"role_description":"text"},{"role":"AXStaticText","text":"⌘N","depth":18,"role_description":"text"},{"role":"AXButton","text":"Projects","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Customize","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Artifacts","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Pinned","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXButton","text":"Bulgarian citizenship application process for EU residents","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Bulgarian citizenship application process for EU residents","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Dawarich location tracking project","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Dawarich location tracking project","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Recents","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXButton","text":"View all","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reviewing recent conversation highlights","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Reviewing recent conversation highlights","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screenpipe WAL processing when stopped","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Screenpipe WAL processing when stopped","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Mac aliases not recognized","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Mac aliases not recognized","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Boosteroid still recording despite ignored windows setting","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Boosteroid still recording despite ignored windows setting","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Missing JavaScript promise in authorization response","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Missing JavaScript promise in authorization response","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Linux SQLite UI for NAS","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Linux SQLite UI for NAS","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Claude API 500 internal server error","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Claude API 500 internal server error","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screenpipe query capabilities and usage","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Screenpipe query capabilities and usage","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"eGPU compatibility with Mac mini and Studio","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for eGPU compatibility with Mac mini and Studio","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Understanding OpenRouter API gateway","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Understanding OpenRouter API gateway","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screenpipe 14-day retention explained","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Screenpipe 14-day retention explained","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Reddit homepage feed overview","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Reddit homepage feed overview","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Docker container not visible in console","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Docker container not visible in console","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"DIA browser RAM configuration","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for DIA browser RAM configuration","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screenpipe company background","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Screenpipe company background","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"DSK Bank Bulgaria secure investing explained","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for DSK Bank Bulgaria secure investing explained","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Building a comprehensive personal homelab system","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Building a comprehensive personal homelab system","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Importing Google Timeline data to Owntracks","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Importing Google Timeline data to Owntracks","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chromecast remote volume buttons not working","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Chromecast remote volume buttons not working","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screenpipe free version guide","depth":17,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"More options for Screenpipe free version guide","depth":18,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Updated to 1.3109.0","depth":16,"role_description":"text"},{"role":"AXStaticText","text":"Relaunch to apply","depth":16,"role_description":"text"},{"role":"AXButton","text":"Relaunch","depth":16,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXPopUpButton","text":"Lukas Kovalik","depth":16,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Lukas Kovalik","depth":17,"role_description":"text"},{"role":"AXButton","text":"Get apps and extensions","depth":15,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Screenpipe WAL processing when stopped","depth":19,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Screenpipe WAL processing when stopped","depth":21,"role_description":"text"},{"role":"AXPopUpButton","text":"More options for Screenpipe WAL processing when stopped","depth":19,"role_description":"pop-up button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open sidebar","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Share chat","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"how to make the screenpipe wal being processed. It seems it is not working when the screenpipe is stopped","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"17 Apr","depth":22,"role_description":"text"},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Edit","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"WAL (Write-Ahead Log) in SQLite needs to be checkpointed to merge pending data into the main database file. When Screenpipe stops, the WAL may not be fully checkpointed.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"The issue","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"The issue","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"SQLite WAL mode keeps a separate","depth":23,"role_description":"text"},{"role":"AXStaticText","text":".sqlite-wal","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"file. Data there is \"pending\" until a checkpoint runs. Screenpipe normally handles this while running, but if it stops abruptly or you query while it's stopped, the WAL data may appear missing to external readers.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Solutions","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"Solutions","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Option 1: Force a checkpoint manually (safest)","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 ~/.screenpipe/db.sqlite","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"PRAGMA wal_checkpoint(TRUNCATE);\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"This flushes all WAL data into the main DB file and truncates the WAL. Run this after Screenpipe stops.","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Option 2: Make your queries WAL-aware","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"When querying with","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"directly, open in WAL mode explicitly so it reads both the DB and WAL together:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 ~/.screenpipe/db.sqlite","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"PRAGMA journal_mode=WAL; SELECT ...\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Actually,","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"already reads WAL automatically — but only if the WAL file is present and not corrupted. The real fix is ensuring the connection sees both files.","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Option 3: Add to your","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"spq","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"alias","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Update the alias to always checkpoint first:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"alias","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"spq","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"'sqlite3 -json ~/.screenpipe/db.sqlite'","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"alias","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"spqcheckpoint","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"'sqlite3 ~/.screenpipe/db.sqlite \"PRAGMA wal_checkpoint(TRUNCATE);\" && echo \"WAL checkpointed\"'","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"Option 4: Checkpoint on Screenpipe shutdown (via a wrapper script)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"If you start Screenpipe via a script, wrap it:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"#!/bin/bash","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"screenpipe","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$@","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"# On exit (Ctrl+C or normal stop), checkpoint WAL","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"Screenpipe stopped. Checkpointing WAL...\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 ~/.screenpipe/db.sqlite","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"PRAGMA wal_checkpoint(TRUNCATE);\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"Done.\"","depth":25,"role_description":"text"},{"role":"AXHeading","text":"Quick diagnosis","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"Quick diagnosis","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Check if a WAL file exists and its size:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ls","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-lh ~/.screenpipe/db.sqlite*","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"If","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"db.sqlite-wal","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"is large (MBs), that's unprocessed data. After checkpointing it should shrink to near 0.","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Recommended approach:","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Use Option 1 whenever you want to query after stopping Screenpipe, and optionally wrap it in your","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"screenpipe-day.sh","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"report script at the top.","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give positive feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give negative feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"what about jpg to mp4 conversion","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"17 Apr","depth":22,"role_description":"text"},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Edit","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"That's unrelated to Screenpipe — are you asking about a general tool/command for converting a sequence of JPG images into an MP4 video?","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"If so,","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"FFmpeg","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"is the standard tool:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Image sequence (e.g. frame001.jpg, frame002.jpg...):","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg -framerate","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"24","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-i frame%03d.jpg -c:v libx264 -pix_fmt yuv420p output.mp4","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Single JPG as a still video (e.g. 5 seconds):","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg -loop","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-i input.jpg -t","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-c:v libx264 -pix_fmt yuv420p output.mp4","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"All JPGs in a folder (sorted by name):","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg -framerate","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"24","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"-pattern_type glob -i","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"'*.jpg'","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"-c:v libx264 -pix_fmt yuv420p output.mp4","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"Install on Mac:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"brew","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"install","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Let me know the actual use case (slideshow, timelapse, Screenpipe frames, something else?) and I can tailor the command.","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give positive feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give negative feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"but this seems to also happen automatically when the screenpipe is running","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"17 Apr","depth":22,"role_description":"text"},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Edit","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Yes, exactly — SQLite automatically checkpoints the WAL when:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"The WAL reaches","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"1000 pages","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"(default threshold)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"A connection closes cleanly","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"You explicitly call","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"PRAGMA wal_checkpoint","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"So when Screenpipe is","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"running","depth":24,"role_description":"text"},{"role":"AXStaticText","text":", the WAL gets checkpointed automatically in the background and you never notice it. The problem only surfaces when Screenpipe is","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"stopped abruptly","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"(Ctrl+C, crash, system sleep) and the WAL hasn't been flushed yet at that moment.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"The real-world impact","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"The real-world impact","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"If you run your","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"screenpipe-day.sh","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"or","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"spq","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"queries","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"while Screenpipe is stopped","depth":24,"role_description":"text"},{"role":"AXStaticText","text":", you may be missing the last chunk of data that was buffered in the WAL but never checkpointed.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Practical fix","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"Practical fix","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Just prepend the checkpoint to your query script:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"# In screenpipe-day.sh or before any spq usage after stopping screenpipe","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 ~/.screenpipe/db.sqlite","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"PRAGMA wal_checkpoint(TRUNCATE);\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":25,"role_description":"text"},{"role":"AXStaticText","text":">","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/dev/null","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Or as a shell function in your","depth":23,"role_description":"text"},{"role":"AXStaticText","text":".zshrc","depth":24,"role_description":"text"},{"role":"AXStaticText","text":":","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"spqready","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"(","depth":25,"role_description":"text"},{"role":"AXStaticText","text":")","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"{","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 ~/.screenpipe/db.sqlite","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"PRAGMA wal_checkpoint(TRUNCATE);\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":">","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/dev/null","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":25,"role_description":"text"},{"role":"AXStaticText","text":">","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"&1","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"WAL checkpointed, DB ready.\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Then just run","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"spqready","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"once after stopping Screenpipe before querying.","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give positive feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give negative feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"how to stop screenpipe but make sure that jpg to mp4 is converted","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"17 Apr","depth":22,"role_description":"text"},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Edit","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"These two things aren't connected — where does JPG to MP4 come into your Screenpipe setup? Are you thinking of:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"Screenpipe's frame captures","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"(it stores screenshots as JPGs) that you want converted into a timelapse MP4 when stopping?","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"Something else entirely?","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give positive feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give negative feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"No I mean there are /data/data/{date} with jpgs that are converted to mp4. It seems that if stopped via sp-stop='pkill -f screenpipe && echo '\\''screenpipe stopped'\\' then these are not converted unless I staert it again","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"17 Apr","depth":22,"role_description":"text"},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Edit","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Ah, got it — Screenpipe has an internal process that converts the captured JPG frames into MP4 chunks. When you","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pkill -f screenpipe","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"hard, that conversion pipeline gets killed mid-flight, leaving unconverted JPGs behind.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"The fix","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"The fix","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"You need to let Screenpipe finish its current conversion before killing it. The cleanest approach is to","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"send SIGTERM first, then wait","depth":24,"role_description":"text"},{"role":"AXStaticText","text":", instead of SIGKILL-equivalent","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"pkill","depth":24,"role_description":"text"},{"role":"AXStaticText","text":":","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"# Graceful stop — sends SIGTERM and waits","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"alias","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"sp-stop","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"'pkill -SIGTERM -f screenpipe && while pgrep -f screenpipe > /dev/null; do sleep 1; done && echo \"screenpipe stopped\"'","depth":26,"role_description":"text"},{"role":"AXStaticText","text":"This gives Screenpipe time to finish in-flight conversions before actually dying.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"If that's not enough — convert remaining JPGs manually","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"If that's not enough — convert remaining JPGs manually","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"After stopping, find any unconverted JPG sequences and process them:","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"find","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"~/.screenpipe/data -type d","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"|","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"while","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"read","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"dir","depth":25,"role_description":"text"},{"role":"AXStaticText","text":";","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"do","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"jpgs","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$(","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ls","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$dir","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/*.jpg","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":25,"role_description":"text"},{"role":"AXStaticText","text":">","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/dev/null","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"|","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"wc","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-l","depth":25,"role_description":"text"},{"role":"AXStaticText","text":")","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"mp4","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$(","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ls","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$dir","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/*.mp4","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":25,"role_description":"text"},{"role":"AXStaticText","text":">","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/dev/null","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"|","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"wc","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-l","depth":25,"role_description":"text"},{"role":"AXStaticText","text":")","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"if","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$jpgs","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-gt","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"&&","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$mp4","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-eq","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":";","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"then","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"Converting","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$dir","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"...\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg -framerate","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"10","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-pattern_type glob -i","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$dir","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/*.jpg\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-c:v libx264 -pix_fmt yuv420p","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$dir","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/output.mp4\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-loglevel error","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"fi","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"done","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Adjust","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"~/.screenpipe/data","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"to your actual path and","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"framerate","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"to whatever Screenpipe uses.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Combined stop alias","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"Combined stop alias","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"alias","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"sp-stop","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"'","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"pkill -SIGTERM -f screenpipe && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"while pgrep -f screenpipe > /dev/null; do sleep 1; done && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo \"screenpipe stopped, converting remaining frames...\" && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"find ~/.screenpipe/data -type d | while read dir; do","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"jpgs=$(ls \"$dir\"/*.jpg 2>/dev/null | wc -l)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"mp4=$(ls \"$dir\"/*.mp4 2>/dev/null | wc -l)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"if [ \"$jpgs\" -gt 0 ] && [ \"$mp4\" -eq 0 ]; then","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg -framerate 10 -pattern_type glob -i \"$dir/*.jpg\" \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-c:v libx264 -pix_fmt yuv420p \"$dir/output.mp4\" -loglevel error && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo \"Converted $dir\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"fi","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"done && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo \"all done\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"'","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Do you know the actual framerate Screenpipe uses for its captures?","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give positive feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give negative feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Pasted Text, pasted, 255 lines","depth":21,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data/2026-04-17 $ ll total 1388520 drwxr-xr-x 252 lukas staff 8064 17 Apr 21:06 . drwxr-xr-x 11 lukas staff 352 17 Apr 08:56 .. -rw-r--r-- 1 lukas staff 199318 17 Apr 21:04 1776449072292_m1.jpg -rw-r--r-- 1 lukas","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"PASTED","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"17 Apr","depth":22,"role_description":"text"},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Edit","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Now the picture is clear. Looking at your directory:","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"JPGs are named like","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"1776449072292_m1.jpg","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"(timestamp + monitor)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"MP4s are named like","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"compact_monitor_1_1776406033543.mp4","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"(monitor + timestamp)","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"MP4s are produced in ~5 minute chunks per monitor","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"The loose JPGs at the bottom (21","depth":24,"role_description":"text"},{"role":"AXStaticText","text":":04","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"–21","depth":24,"role_description":"text"},{"role":"AXStaticText","text":":06","depth":24,"role_description":"text"},{"role":"AXStaticText","text":") have","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"no corresponding MP4","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"yet — that's the unconverted tail","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"So Screenpipe batches JPGs into 5-minute MP4 chunks. When killed, the current batch is abandoned.","depth":23,"role_description":"text"},{"role":"AXHeading","text":"Script to convert remaining JPGs","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"Script to convert remaining JPGs","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"The key insight from the filenames: JPGs are","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"{timestamp}_m{N}.jpg","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"— group them by monitor number (","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"m1","depth":24,"role_description":"text"},{"role":"AXStaticText","text":",","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"m2","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"...) and convert each group.","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"#!/bin/bash","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"# sp-convert-remaining.sh","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"# Converts any unconverted JPGs in ~/.screenpipe/data/data/<date>/","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"DATE","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"${1","depth":25,"role_description":"text"},{"role":"AXStaticText","text":":-","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$(date +","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"%","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"Y-","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"%","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"m-","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"%","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"d)}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"DIR","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$HOME","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/.screenpipe/data/data/","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$DATE","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"Scanning","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$DIR","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"for unconverted JPGs...\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"for","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"monitor","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"in","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":25,"role_description":"text"},{"role":"AXStaticText","text":";","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"do","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"jpgs","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"(","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$DIR","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/*_m","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"${monitor}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":".jpg","depth":25,"role_description":"text"},{"role":"AXStaticText","text":")","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"!","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-e","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"${jpgs","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"&&","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"continue","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"Monitor","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$monitor","depth":25,"role_description":"text"},{"role":"AXStaticText","text":": found","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"${","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"#","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"jpgs","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"@","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"JPGs\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"# Sort by timestamp (they're already timestamp-prefixed, so glob order is fine)","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"# Use the first frame's timestamp for the output filename","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"first_ts","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$(","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"basename","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"${jpgs","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"|","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"cut","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-d_ -f1","depth":25,"role_description":"text"},{"role":"AXStaticText","text":")","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"outfile","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$DIR","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/compact_monitor_","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"${monitor}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"_","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"${first_ts}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":".mp4\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"if","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"[","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-f","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$outfile","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"]","depth":25,"role_description":"text"},{"role":"AXStaticText","text":";","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"then","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\" Already converted:","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$outfile","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"— skipping\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"continue","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"fi","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\" Converting to","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$outfile","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"...\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"# Write sorted filelist for ffmpeg","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"tmplist","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$(","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"mktemp /tmp/sp_frames_XXXXXX.txt","depth":25,"role_description":"text"},{"role":"AXStaticText","text":")","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"for","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"f","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"in","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$(","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ls","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$DIR","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"/*_m$","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"{","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"monitor","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"}","depth":25,"role_description":"text"},{"role":"AXStaticText","text":".jpg","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"|","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"sort","depth":25,"role_description":"text"},{"role":"AXStaticText","text":")","depth":25,"role_description":"text"},{"role":"AXStaticText","text":";","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"do","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"file '","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$f","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"'\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":">>","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$tmplist","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"done","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg -f concat -safe","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"0","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-i","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$tmplist","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-c:v libx264 -pix_fmt yuv420p","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"-loglevel error","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$outfile","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"&&","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\" Done:","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$outfile","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"rm","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"$tmplist","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"done","depth":25,"role_description":"text"},{"role":"AXHeading","text":"Updated sp-stop alias","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"Updated sp-stop alias","depth":23,"role_description":"text"},{"role":"AXButton","text":"Copy to clipboard","depth":25,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"bash","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"alias","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"sp-stop","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"=","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"'","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"pkill -SIGTERM -f screenpipe && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"while pgrep -f screenpipe > /dev/null; do sleep 1; done && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo \"Screenpipe stopped. Converting remaining frames...\" && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"bash ~/.screenpipe/sp-convert-remaining.sh && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"sqlite3 ~/.screenpipe/db.sqlite \"PRAGMA wal_checkpoint(TRUNCATE);\" > /dev/null && \\","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"echo \"All done.\"","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"'","depth":25,"role_description":"text"},{"role":"AXHeading","text":"Notes","depth":22,"role_description":"heading"},{"role":"AXStaticText","text":"Notes","depth":23,"role_description":"text"},{"role":"AXStaticText","text":"ffmpeg -f concat","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"preserves the exact frame order by timestamp rather than assuming a fixed framerate — safer since Screenpipe's capture interval isn't perfectly regular","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"After conversion you may want to delete the JPGs: add","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"rm \"$DIR\"/*_m${monitor}.jpg","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"after the ffmpeg call if desired","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"The script accepts a date arg:","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"sp-convert-remaining.sh 2026-04-16","depth":25,"role_description":"text"},{"role":"AXStaticText","text":"to fix a previous day","depth":24,"role_description":"text"},{"role":"AXButton","text":"Copy","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give positive feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Give negative feedback","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Retry","depth":22,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"well I run pkill -SIGTERM -f screenpipe && while pgrep -f screenpipe > /dev/null; do sleep 1; done && echo \"screenpipe stopped\" but it didn't help","depth":24,"role_description":"text"},{"role":"AXStaticText","text":"17 Apr","depth":22,"role_description":"text"}]...
|
8496544899175506085
|
-8134245997456041838
|
idle
|
accessibility
|
NULL
|
Skip to content
Skip to content
Click to collapse
Skip to content
Skip to content
Click to collapse
⌘B
Drag to resize
Open sidebar
Chat
Cowork
Code
New chat ⌘N
New chat
⌘N
Projects
Customize
Artifacts
Pinned
Bulgarian citizenship application process for EU residents
More options for Bulgarian citizenship application process for EU residents
Dawarich location tracking project
More options for Dawarich location tracking project
Recents
View all
Reviewing recent conversation highlights
More options for Reviewing recent conversation highlights
Screenpipe WAL processing when stopped
More options for Screenpipe WAL processing when stopped
Mac aliases not recognized
More options for Mac aliases not recognized
Boosteroid still recording despite ignored windows setting
More options for Boosteroid still recording despite ignored windows setting
Missing JavaScript promise in authorization response
More options for Missing JavaScript promise in authorization response
Linux SQLite UI for NAS
More options for Linux SQLite UI for NAS
Claude API 500 internal server error
More options for Claude API 500 internal server error
Screenpipe query capabilities and usage
More options for Screenpipe query capabilities and usage
eGPU compatibility with Mac mini and Studio
More options for eGPU compatibility with Mac mini and Studio
Understanding OpenRouter API gateway
More options for Understanding OpenRouter API gateway
Screenpipe 14-day retention explained
More options for Screenpipe 14-day retention explained
Reddit homepage feed overview
More options for Reddit homepage feed overview
Docker container not visible in console
More options for Docker container not visible in console
DIA browser RAM configuration
More options for DIA browser RAM configuration
Screenpipe company background
More options for Screenpipe company background
DSK Bank Bulgaria secure investing explained
More options for DSK Bank Bulgaria secure investing explained
Building a comprehensive personal homelab system
More options for Building a comprehensive personal homelab system
Importing Google Timeline data to Owntracks
More options for Importing Google Timeline data to Owntracks
Chromecast remote volume buttons not working
More options for Chromecast remote volume buttons not working
Screenpipe free version guide
More options for Screenpipe free version guide
Updated to 1.3109.0
Relaunch to apply
Relaunch
Lukas Kovalik
Lukas Kovalik
Get apps and extensions
Screenpipe WAL processing when stopped
Screenpipe WAL processing when stopped
More options for Screenpipe WAL processing when stopped
Open sidebar
Share chat
how to make the screenpipe wal being processed. It seems it is not working when the screenpipe is stopped
17 Apr
Retry
Edit
Copy
WAL (Write-Ahead Log) in SQLite needs to be checkpointed to merge pending data into the main database file. When Screenpipe stops, the WAL may not be fully checkpointed.
The issue
The issue
SQLite WAL mode keeps a separate
.sqlite-wal
file. Data there is "pending" until a checkpoint runs. Screenpipe normally handles this while running, but if it stops abruptly or you query while it's stopped, the WAL data may appear missing to external readers.
Solutions
Solutions
Option 1: Force a checkpoint manually (safest)
Copy to clipboard
bash
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
This flushes all WAL data into the main DB file and truncates the WAL. Run this after Screenpipe stops.
Option 2: Make your queries WAL-aware
When querying with
sqlite3
directly, open in WAL mode explicitly so it reads both the DB and WAL together:
Copy to clipboard
bash
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA journal_mode=WAL; SELECT ..."
Actually,
sqlite3
already reads WAL automatically — but only if the WAL file is present and not corrupted. The real fix is ensuring the connection sees both files.
Option 3: Add to your
spq
alias
Update the alias to always checkpoint first:
Copy to clipboard
bash
alias
spq
=
'sqlite3 -json ~/.screenpipe/db.sqlite'
alias
spqcheckpoint
=
'sqlite3 ~/.screenpipe/db.sqlite "PRAGMA wal_checkpoint(TRUNCATE);" && echo "WAL checkpointed"'
Option 4: Checkpoint on Screenpipe shutdown (via a wrapper script)
If you start Screenpipe via a script, wrap it:
Copy to clipboard
bash
#!/bin/bash
screenpipe
"
$@
"
# On exit (Ctrl+C or normal stop), checkpoint WAL
echo
"Screenpipe stopped. Checkpointing WAL..."
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
echo
"Done."
Quick diagnosis
Quick diagnosis
Check if a WAL file exists and its size:
Copy to clipboard
bash
ls
-lh ~/.screenpipe/db.sqlite*
If
db.sqlite-wal
is large (MBs), that's unprocessed data. After checkpointing it should shrink to near 0.
Recommended approach:
Use Option 1 whenever you want to query after stopping Screenpipe, and optionally wrap it in your
screenpipe-day.sh
report script at the top.
Copy
Give positive feedback
Give negative feedback
Retry
what about jpg to mp4 conversion
17 Apr
Retry
Edit
Copy
That's unrelated to Screenpipe — are you asking about a general tool/command for converting a sequence of JPG images into an MP4 video?
If so,
FFmpeg
is the standard tool:
Image sequence (e.g. frame001.jpg, frame002.jpg...):
Copy to clipboard
bash
ffmpeg -framerate
24
-i frame%03d.jpg -c:v libx264 -pix_fmt yuv420p output.mp4
Single JPG as a still video (e.g. 5 seconds):
Copy to clipboard
bash
ffmpeg -loop
1
-i input.jpg -t
5
-c:v libx264 -pix_fmt yuv420p output.mp4
All JPGs in a folder (sorted by name):
Copy to clipboard
bash
ffmpeg -framerate
24
-pattern_type glob -i
'*.jpg'
-c:v libx264 -pix_fmt yuv420p output.mp4
Install on Mac:
Copy to clipboard
bash
brew
install
ffmpeg
Let me know the actual use case (slideshow, timelapse, Screenpipe frames, something else?) and I can tailor the command.
Copy
Give positive feedback
Give negative feedback
Retry
but this seems to also happen automatically when the screenpipe is running
17 Apr
Retry
Edit
Copy
Yes, exactly — SQLite automatically checkpoints the WAL when:
The WAL reaches
1000 pages
(default threshold)
A connection closes cleanly
You explicitly call
PRAGMA wal_checkpoint
So when Screenpipe is
running
, the WAL gets checkpointed automatically in the background and you never notice it. The problem only surfaces when Screenpipe is
stopped abruptly
(Ctrl+C, crash, system sleep) and the WAL hasn't been flushed yet at that moment.
The real-world impact
The real-world impact
If you run your
screenpipe-day.sh
or
spq
queries
while Screenpipe is stopped
, you may be missing the last chunk of data that was buffered in the WAL but never checkpointed.
Practical fix
Practical fix
Just prepend the checkpoint to your query script:
Copy to clipboard
bash
# In screenpipe-day.sh or before any spq usage after stopping screenpipe
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
2
>
/dev/null
Or as a shell function in your
.zshrc
:
Copy to clipboard
bash
spqready
(
)
{
sqlite3 ~/.screenpipe/db.sqlite
"PRAGMA wal_checkpoint(TRUNCATE);"
>
/dev/null
2
>
&1
echo
"WAL checkpointed, DB ready."
}
Then just run
spqready
once after stopping Screenpipe before querying.
Copy
Give positive feedback
Give negative feedback
Retry
how to stop screenpipe but make sure that jpg to mp4 is converted
17 Apr
Retry
Edit
Copy
These two things aren't connected — where does JPG to MP4 come into your Screenpipe setup? Are you thinking of:
Screenpipe's frame captures
(it stores screenshots as JPGs) that you want converted into a timelapse MP4 when stopping?
Something else entirely?
Copy
Give positive feedback
Give negative feedback
Retry
No I mean there are /data/data/{date} with jpgs that are converted to mp4. It seems that if stopped via sp-stop='pkill -f screenpipe && echo '\''screenpipe stopped'\' then these are not converted unless I staert it again
17 Apr
Retry
Edit
Copy
Ah, got it — Screenpipe has an internal process that converts the captured JPG frames into MP4 chunks. When you
pkill -f screenpipe
hard, that conversion pipeline gets killed mid-flight, leaving unconverted JPGs behind.
The fix
The fix
You need to let Screenpipe finish its current conversion before killing it. The cleanest approach is to
send SIGTERM first, then wait
, instead of SIGKILL-equivalent
pkill
:
Copy to clipboard
bash
# Graceful stop — sends SIGTERM and waits
alias
sp-stop
=
'pkill -SIGTERM -f screenpipe && while pgrep -f screenpipe > /dev/null; do sleep 1; done && echo "screenpipe stopped"'
This gives Screenpipe time to finish in-flight conversions before actually dying.
If that's not enough — convert remaining JPGs manually
If that's not enough — convert remaining JPGs manually
After stopping, find any unconverted JPG sequences and process them:
Copy to clipboard
bash
find
~/.screenpipe/data -type d
|
while
read
dir
;
do
jpgs
=
$(
ls
"
$dir
"
/*.jpg
2
>
/dev/null
|
wc
-l
)
mp4
=
$(
ls
"
$dir
"
/*.mp4
2
>
/dev/null
|
wc
-l
)
if
[
"
$jpgs
"
-gt
0
]
&&
[
"
$mp4
"
-eq
0
]
;
then
echo
"Converting
$dir
..."
ffmpeg -framerate
10
-pattern_type glob -i
"
$dir
/*.jpg"
\
-c:v libx264 -pix_fmt yuv420p
"
$dir
/output.mp4"
-loglevel error
fi
done
Adjust
~/.screenpipe/data
to your actual path and
framerate
to whatever Screenpipe uses.
Combined stop alias
Combined stop alias
Copy to clipboard
bash
alias
sp-stop
=
'
pkill -SIGTERM -f screenpipe && \
while pgrep -f screenpipe > /dev/null; do sleep 1; done && \
echo "screenpipe stopped, converting remaining frames..." && \
find ~/.screenpipe/data -type d | while read dir; do
jpgs=$(ls "$dir"/*.jpg 2>/dev/null | wc -l)
mp4=$(ls "$dir"/*.mp4 2>/dev/null | wc -l)
if [ "$jpgs" -gt 0 ] && [ "$mp4" -eq 0 ]; then
ffmpeg -framerate 10 -pattern_type glob -i "$dir/*.jpg" \
-c:v libx264 -pix_fmt yuv420p "$dir/output.mp4" -loglevel error && \
echo "Converted $dir"
fi
done && \
echo "all done"
'
Do you know the actual framerate Screenpipe uses for its captures?
Copy
Give positive feedback
Give negative feedback
Retry
Pasted Text, pasted, 255 lines
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/.screenpipe/data/data/2026-04-17 $ ll total 1388520 drwxr-xr-x 252 lukas staff 8064 17 Apr 21:06 . drwxr-xr-x 11 lukas staff 352 17 Apr 08:56 .. -rw-r--r-- 1 lukas staff 199318 17 Apr 21:04 1776449072292_m1.jpg -rw-r--r-- 1 lukas
PASTED
17 Apr
Retry
Edit
Copy
Now the picture is clear. Looking at your directory:
JPGs are named like
1776449072292_m1.jpg
(timestamp + monitor)
MP4s are named like
compact_monitor_1_1776406033543.mp4
(monitor + timestamp)
MP4s are produced in ~5 minute chunks per monitor
The loose JPGs at the bottom (21
:04
–21
:06
) have
no corresponding MP4
yet — that's the unconverted tail
So Screenpipe batches JPGs into 5-minute MP4 chunks. When killed, the current batch is abandoned.
Script to convert remaining JPGs
Script to convert remaining JPGs
The key insight from the filenames: JPGs are
{timestamp}_m{N}.jpg
— group them by monitor number (
m1
,
m2
...) and convert each group.
Copy to clipboard
bash
#!/bin/bash
# sp-convert-remaining.sh
# Converts any unconverted JPGs in ~/.screenpipe/data/data/<date>/
DATE
=
"
${1
:-
$(date +
%
Y-
%
m-
%
d)}
"
DIR
=
"
$HOME
/.screenpipe/data/data/
$DATE
"
echo
"Scanning
$DIR
for unconverted JPGs..."
for
monitor
in
1
2
;
do
jpgs
=
(
"
$DIR
"
/*_m
${monitor}
.jpg
)
[
[
!
-e
"
${jpgs
[
0
]
}
"
]
]
&&
continue
echo
"Monitor
$monitor
: found
${
#
jpgs
[
@
]
}
JPGs"
# Sort by timestamp (they're already timestamp-prefixed, so glob order is fine)
# Use the first frame's timestamp for the output filename
first_ts
=
$(
basename
"
${jpgs
[
0
]
}
"
|
cut
-d_ -f1
)
outfile
=
"
$DIR
/compact_monitor_
${monitor}
_
${first_ts}
.mp4"
if
[
[
-f
"
$outfile
"
]
]
;
then
echo
" Already converted:
$outfile
— skipping"
continue
fi
echo
" Converting to
$outfile
..."
# Write sorted filelist for ffmpeg
tmplist
=
$(
mktemp /tmp/sp_frames_XXXXXX.txt
)
for
f
in
$(
ls
"
$DIR
"
/*_m$
{
monitor
}
.jpg
|
sort
)
;
do
echo
"file '
$f
'"
>>
"
$tmplist
"
done
ffmpeg -f concat -safe
0
-i
"
$tmplist
"
\
-c:v libx264 -pix_fmt yuv420p
\
-loglevel error
\
"
$outfile
"
&&
echo
" Done:
$outfile
"
rm
"
$tmplist
"
done
Updated sp-stop alias
Updated sp-stop alias
Copy to clipboard
bash
alias
sp-stop
=
'
pkill -SIGTERM -f screenpipe && \
while pgrep -f screenpipe > /dev/null; do sleep 1; done && \
echo "Screenpipe stopped. Converting remaining frames..." && \
bash ~/.screenpipe/sp-convert-remaining.sh && \
sqlite3 ~/.screenpipe/db.sqlite "PRAGMA wal_checkpoint(TRUNCATE);" > /dev/null && \
echo "All done."
'
Notes
Notes
ffmpeg -f concat
preserves the exact frame order by timestamp rather than assuming a fixed framerate — safer since Screenpipe's capture interval isn't perfectly regular
After conversion you may want to delete the JPGs: add
rm "$DIR"/*_m${monitor}.jpg
after the ffmpeg call if desired
The script accepts a date arg:
sp-convert-remaining.sh 2026-04-16
to fix a previous day
Copy
Give positive feedback
Give negative feedback
Retry
well I run pkill -SIGTERM -f screenpipe && while pgrep -f screenpipe > /dev/null; do sleep 1; done && echo "screenpipe stopped" but it didn't help
17 Apr...
|
NULL
|
|
38408
|
784
|
57
|
2026-04-16T13:10:46.694407+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-16/1776 /Users/lukas/.screenpipe/data/data/2026-04-16/1776345046694_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
+SlackFileEditViewEDHomeDMsActivityFilesLater..•Mo +SlackFileEditViewEDHomeDMsActivityFilesLater..•More+Jiminny ...w Starred& jiminny-x-integrati...8platform-inner-teamChannels# ai-chapter# alerts# backend# confusion-clinic# curiosity_lab# engineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...• Direct messages0. Nikolay Nikolov. Stoyan TanevG. Vasil Vasilev1% Galya DimitrovaNibolav lanovGoHistoryWindowHelp→CSearch Jiminny Inc*& platform-inner-...& 10MessagesP Channel OverviewMore v+create PDF verToday ~ uest_id: 822fa41b-afd3-43a9-a248-ooDue36f3131: (2013, 'Lostconnection to MySQL server during query([Errno 104] Connection reset by peer)"):correlation_id:ae2e38ff-ed04-401e-b77c-1e02e9d788c6 trace_id:f0194348-cece-4ca8-8413-21e32eef1d4f[2026-04-13 01:09:56] app.ERROR: Failed tocreate PDF version for request_id: 822fa41b-afd3-43a9-a248-86b0e36f3131: (2013, 'Lostconnection to MySQL server during query([Errno 104] Connection reset by peer)"):correlation_id:ae2e38ff-ed04-401e-b77c-1e02e9d788c6 trace_id:f0194348-cece-4ca8-8413-21e32eef1d4fecs/jiminny-prophet/fbd19ab3fe8d4775bb936af467904a55някакво connectivity ишу с MySql изглеждаSteliyan Georgiev 12:53 PMно това е само 1 случай2 replies Last reply today at 1:02 PMNewSteliyan Georgiev 4:09 PMМоже ли едно малко ревю на един ПР,който вече го прецизирах с @claude ревюhttps://github.com/jiminny/prophet/pull/479Message & platform-inner-team+Aa@ .••*3>0 lbl-zsh100% <478Thu 16 Apr 16:10:46• $84-zsh85window_name FROM ocr_text WHERE app_name LIKE "%Safari%' OR window_name LIKE "%Boostwindow_name FROM ocr_text WHERE app_name LIKE "%Safari%' OR window_name LIKE '%Boostwindow_name FROM ocr_text WHERE app_name LIKE "%Safari%' OR window_name LIKE "%Boostwindow_name FROM ocr_text WHERE app_name LIKE "%Safari%" OR window_name LIKE '%Boostit DATETIME);...
|
NULL
|
8496504722904554264
|
NULL
|
click
|
ocr
|
NULL
|
+SlackFileEditViewEDHomeDMsActivityFilesLater..•Mo +SlackFileEditViewEDHomeDMsActivityFilesLater..•More+Jiminny ...w Starred& jiminny-x-integrati...8platform-inner-teamChannels# ai-chapter# alerts# backend# confusion-clinic# curiosity_lab# engineering# frontend# general# infra-changes# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...• Direct messages0. Nikolay Nikolov. Stoyan TanevG. Vasil Vasilev1% Galya DimitrovaNibolav lanovGoHistoryWindowHelp→CSearch Jiminny Inc*& platform-inner-...& 10MessagesP Channel OverviewMore v+create PDF verToday ~ uest_id: 822fa41b-afd3-43a9-a248-ooDue36f3131: (2013, 'Lostconnection to MySQL server during query([Errno 104] Connection reset by peer)"):correlation_id:ae2e38ff-ed04-401e-b77c-1e02e9d788c6 trace_id:f0194348-cece-4ca8-8413-21e32eef1d4f[2026-04-13 01:09:56] app.ERROR: Failed tocreate PDF version for request_id: 822fa41b-afd3-43a9-a248-86b0e36f3131: (2013, 'Lostconnection to MySQL server during query([Errno 104] Connection reset by peer)"):correlation_id:ae2e38ff-ed04-401e-b77c-1e02e9d788c6 trace_id:f0194348-cece-4ca8-8413-21e32eef1d4fecs/jiminny-prophet/fbd19ab3fe8d4775bb936af467904a55някакво connectivity ишу с MySql изглеждаSteliyan Georgiev 12:53 PMно това е само 1 случай2 replies Last reply today at 1:02 PMNewSteliyan Georgiev 4:09 PMМоже ли едно малко ревю на един ПР,който вече го прецизирах с @claude ревюhttps://github.com/jiminny/prophet/pull/479Message & platform-inner-team+Aa@ .••*3>0 lbl-zsh100% <478Thu 16 Apr 16:10:46• $84-zsh85window_name FROM ocr_text WHERE app_name LIKE "%Safari%' OR window_name LIKE "%Boostwindow_name FROM ocr_text WHERE app_name LIKE "%Safari%' OR window_name LIKE '%Boostwindow_name FROM ocr_text WHERE app_name LIKE "%Safari%' OR window_name LIKE "%Boostwindow_name FROM ocr_text WHERE app_name LIKE "%Safari%" OR window_name LIKE '%Boostit DATETIME);...
|
38404
|
|
67264
|
1514
|
5
|
2026-04-21T15:30:47.105581+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776785447105_m1.jpg...
|
Firefox
|
Jiminny — Work
|
True
|
app.staging.jiminny.com/kiosk/automated-reports
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
Project Phoenix – Figma
Project Phoenix – Figma
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny Mail
Jiminny Mail
[JY-20500] Batch initial sync for Salesforce - Jira
[JY-20500] Batch initial sync for Salesforce - Jira
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Jiminny
Jiminny
Pipelines - jiminny/app
Pipelines - jiminny/app
Formalize
Formalize
[SRD-6793] Les Mills activity types not pulling in - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
Search results: calendar | Jiminny Help Center
Search results: calendar | Jiminny Help Center
Jiminny
Jiminny
Jiminny
Jiminny
Close tab
Edit - Engineering - Confluence
Edit - Engineering - Confluence
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
JY-18909-automated-reports-ask-jiminny ■ 874667
75
75
Kiosk
Organizations
Organizations
Setup Account
Setup Account
Users
Users
Activities
Activities
Automated Reports
Automated Reports
Mobile version
Mobile version
Automated Reports
Automated Reports
Create
ORGANIZATION
Select Select
Select
Select
TEAM
Select Select
Select
Select
REPORT TYPE
Select Select
Select
Select
STATUS
Select All
Select
All
ORGANIZATION
TYPE
TEAMS
FREQUENCY
RECIPIENTS
CREATOR
CREATED AT
Hubspot Staging Renamed
Exec Summary
Weekly
20/03/2026
Hubspot Staging Renamed
Exec Summary
Sales Team
Weekly
17/12/2025
Jiminny Staging
Exec Summary
Engineering, Client Success, Client Success - ENT
One-Off
21/04/2026
Jiminny Staging
Exec Summary
One-Off
20/04/2026
Jiminny Staging
Exec Summary
One-Off
15/04/2026
Jiminny Staging
Exec Summary
Engineering
One-Off
31/03/2026
Jiminny Staging
Exec Summary
Weekly
31/03/2026
Jiminny Staging
Coaching Profiles
Weekly
31/03/2026
Jiminny Staging
Exec Summary
One-Off
31/03/2026
Jiminny Staging
Exec Summary
One-Off
27/03/2026
Jiminny Staging
Exec Summary
One-Off
26/03/2026
Jiminny Staging
Exec Summary
One-Off
26/03/2026
Jiminny Staging
Product Feedback
One-Off
27/02/2026
Jiminny Staging
Product Feedback
UK Sales, Engineering
One-Off
27/02/2026
Jiminny Staging
Exec Summary
One-Off
26/02/2026
Create Report
Save
Open Intercom Messenger
Clear
Filter URLs
Pause/Resume recording network log
New Request
Search
Request Blocking
Disable Cache
Disable Cache
No Throttling
Network Settings
All
HTML
CSS
JS
XHR
Fonts
Images
Media
WS
Other
Status
Status
Method
Method
Domain
Domain
File
File
Initiator
Initiator
Type
Type
Transferred
Transferred
Size
Size
0 ms
0 ms
POST
r.logr-in.com
i?a=ponxaf/platform-staging&r=6-019db076-935d-752f-a86a-815df5ba66f7&t=acafc3b4-a7db-4547-8fb4-64f393c9c62e&s=0&hr=t&u=c4fb084a-b33a-46fe-904b-351b592a4b0f&is=IDENTIFIED&rs=0,t
xhr
200
POST
o36719.ingest.sentry.io
/api/5627310/envelope/?sentry_version=7&sentry_key=8cba05ef3e3f4f68a86d3a6d31465998&sentry_client=sentry.javascript.vue/10.43.0
sentry-B6v5fcc5.js
:2
(fetch)
json
500 B
2 B
38 ms
200
POST
o36719.ingest.sentry.io
/api/5627310/envelope/?sentry_version=7&sentry_key=8cba05ef3e3f4f68a86d3a6d31465998&sentry_client=sentry.javascript.vue/10.43.0
sentry-B6v5fcc5.js...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny Mail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny Mail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20500] Batch initial sync for Salesforce - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20500] Batch initial sync for Salesforce - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Formalize","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Formalize","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Search results: calendar | Jiminny Help Center","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search results: calendar | Jiminny Help Center","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jiminny","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Edit - Engineering - Confluence","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Edit - Engineering - Confluence","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-18909-automated-reports-ask-jiminny ■ 874667","depth":9,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"75","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"75","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Kiosk","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Organizations","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Organizations","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Setup Account","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Setup Account","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Users","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Users","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Activities","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Activities","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Automated Reports","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Automated Reports","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Mobile version","depth":14,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Mobile version","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Automated Reports","depth":13,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Automated Reports","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":true,"is_selected":false},{"role":"AXStaticText","text":"ORGANIZATION","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select Select","depth":13,"value":"Select Select","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":14,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Select","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TEAM","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select Select","depth":13,"value":"Select Select","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":14,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Select","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"REPORT TYPE","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select Select","depth":13,"value":"Select Select","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":14,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Select","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"STATUS","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXComboBox","text":"Select All","depth":13,"value":"Select All","help_text":"","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Select","depth":14,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"All","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ORGANIZATION","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TYPE","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TEAMS","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"FREQUENCY","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"RECIPIENTS","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"CREATOR","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"CREATED AT","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Hubspot Staging Renamed","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Weekly","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"20/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Hubspot Staging Renamed","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sales Team","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Weekly","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"17/12/2025","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Engineering, Client Success, Client Success - ENT","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"21/04/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"20/04/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"15/04/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Engineering","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"31/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Weekly","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"31/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Coaching Profiles","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Weekly","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"31/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"31/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"27/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"26/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"26/03/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Product Feedback","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"27/02/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Product Feedback","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"UK Sales, Engineering","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"27/02/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jiminny Staging","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Exec Summary","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"One-Off","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"26/02/2026","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Create Report","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Save","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open Intercom Messenger","depth":7,"bounds":{"left":0.83819443,"top":0.0,"width":0.033333335,"height":0.053333335},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Clear","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextField","text":"Filter URLs","depth":16,"help_text":"","role_description":"search text field","subrole":"AXSearchField","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Pause/Resume recording network log","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"New Request","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Search","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Request Blocking","depth":16,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Disable Cache","depth":17,"help_text":"","role_description":"checkbox","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Disable Cache","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"No Throttling","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Network Settings","depth":16,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"All","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"HTML","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"CSS","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"JS","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"XHR","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Fonts","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Images","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Media","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"WS","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Other","depth":17,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Status","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Status","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Method","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Method","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Domain","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Domain","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"File","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"File","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Initiator","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Initiator","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Type","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Type","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Transferred","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Transferred","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Size","depth":24,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Size","depth":26,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"0 ms","depth":24,"help_text":"Timeline","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"0 ms","depth":27,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"POST","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"r.logr-in.com","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"i?a=ponxaf/platform-staging&r=6-019db076-935d-752f-a86a-815df5ba66f7&t=acafc3b4-a7db-4547-8fb4-64f393c9c62e&s=0&hr=t&u=c4fb084a-b33a-46fe-904b-351b592a4b0f&is=IDENTIFIED&rs=0,t","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"xhr","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"200","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"POST","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"o36719.ingest.sentry.io","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/api/5627310/envelope/?sentry_version=7&sentry_key=8cba05ef3e3f4f68a86d3a6d31465998&sentry_client=sentry.javascript.vue/10.43.0","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sentry-B6v5fcc5.js","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":":2","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(fetch)","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"json","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"500 B","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2 B","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"38 ms","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"200","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"POST","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"o36719.ingest.sentry.io","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/api/5627310/envelope/?sentry_version=7&sentry_key=8cba05ef3e3f4f68a86d3a6d31465998&sentry_client=sentry.javascript.vue/10.43.0","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sentry-B6v5fcc5.js","depth":24,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8495914237256999860
|
-5280782911234496051
|
click
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
Project Phoenix – Figma
Project Phoenix – Figma
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny Mail
Jiminny Mail
[JY-20500] Batch initial sync for Salesforce - Jira
[JY-20500] Batch initial sync for Salesforce - Jira
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Jiminny
Jiminny
Pipelines - jiminny/app
Pipelines - jiminny/app
Formalize
Formalize
[SRD-6793] Les Mills activity types not pulling in - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
Search results: calendar | Jiminny Help Center
Search results: calendar | Jiminny Help Center
Jiminny
Jiminny
Jiminny
Jiminny
Close tab
Edit - Engineering - Confluence
Edit - Engineering - Confluence
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
JY-18909-automated-reports-ask-jiminny ■ 874667
75
75
Kiosk
Organizations
Organizations
Setup Account
Setup Account
Users
Users
Activities
Activities
Automated Reports
Automated Reports
Mobile version
Mobile version
Automated Reports
Automated Reports
Create
ORGANIZATION
Select Select
Select
Select
TEAM
Select Select
Select
Select
REPORT TYPE
Select Select
Select
Select
STATUS
Select All
Select
All
ORGANIZATION
TYPE
TEAMS
FREQUENCY
RECIPIENTS
CREATOR
CREATED AT
Hubspot Staging Renamed
Exec Summary
Weekly
20/03/2026
Hubspot Staging Renamed
Exec Summary
Sales Team
Weekly
17/12/2025
Jiminny Staging
Exec Summary
Engineering, Client Success, Client Success - ENT
One-Off
21/04/2026
Jiminny Staging
Exec Summary
One-Off
20/04/2026
Jiminny Staging
Exec Summary
One-Off
15/04/2026
Jiminny Staging
Exec Summary
Engineering
One-Off
31/03/2026
Jiminny Staging
Exec Summary
Weekly
31/03/2026
Jiminny Staging
Coaching Profiles
Weekly
31/03/2026
Jiminny Staging
Exec Summary
One-Off
31/03/2026
Jiminny Staging
Exec Summary
One-Off
27/03/2026
Jiminny Staging
Exec Summary
One-Off
26/03/2026
Jiminny Staging
Exec Summary
One-Off
26/03/2026
Jiminny Staging
Product Feedback
One-Off
27/02/2026
Jiminny Staging
Product Feedback
UK Sales, Engineering
One-Off
27/02/2026
Jiminny Staging
Exec Summary
One-Off
26/02/2026
Create Report
Save
Open Intercom Messenger
Clear
Filter URLs
Pause/Resume recording network log
New Request
Search
Request Blocking
Disable Cache
Disable Cache
No Throttling
Network Settings
All
HTML
CSS
JS
XHR
Fonts
Images
Media
WS
Other
Status
Status
Method
Method
Domain
Domain
File
File
Initiator
Initiator
Type
Type
Transferred
Transferred
Size
Size
0 ms
0 ms
POST
r.logr-in.com
i?a=ponxaf/platform-staging&r=6-019db076-935d-752f-a86a-815df5ba66f7&t=acafc3b4-a7db-4547-8fb4-64f393c9c62e&s=0&hr=t&u=c4fb084a-b33a-46fe-904b-351b592a4b0f&is=IDENTIFIED&rs=0,t
xhr
200
POST
o36719.ingest.sentry.io
/api/5627310/envelope/?sentry_version=7&sentry_key=8cba05ef3e3f4f68a86d3a6d31465998&sentry_client=sentry.javascript.vue/10.43.0
sentry-B6v5fcc5.js
:2
(fetch)
json
500 B
2 B
38 ms
200
POST
o36719.ingest.sentry.io
/api/5627310/envelope/?sentry_version=7&sentry_key=8cba05ef3e3f4f68a86d3a6d31465998&sentry_client=sentry.javascript.vue/10.43.0
sentry-B6v5fcc5.js...
|
NULL
|
|
77904
|
1968
|
39
|
2026-04-24T10:44:00.612642+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777027440612_m2.jpg...
|
Finder
|
Copy
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Copying 3 items to “BT”
stop progress
Stopping…
Pr Copying 3 items to “BT”
stop progress
Stopping…
Preparing to copy to “Test”
stop progress
Preparing to copy 2 items...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Copying 3 items to “BT”","depth":2,"bounds":{"left":0.042220745,"top":0.89864326,"width":0.10405585,"height":0.011173184},"automation_id":"_NS:59","role_description":"text"},{"role":"AXButton","text":"stop progress","depth":2,"bounds":{"left":0.14827128,"top":0.9102155,"width":0.0043218085,"height":0.0103751},"automation_id":"_NS:8","role_description":"button","is_enabled":false,"is_focused":false},{"role":"AXStaticText","text":"Stopping…","depth":2,"bounds":{"left":0.042220745,"top":0.92178774,"width":0.10405585,"height":0.011173184},"automation_id":"_NS:89","role_description":"text"},{"role":"AXStaticText","text":"Preparing to copy to “Test”","depth":2,"bounds":{"left":0.042220745,"top":0.85155624,"width":0.10405585,"height":0.011173184},"automation_id":"_NS:59","role_description":"text"},{"role":"AXButton","text":"stop progress","depth":2,"bounds":{"left":0.14827128,"top":0.8631285,"width":0.0043218085,"height":0.0103751},"automation_id":"_NS:8","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Preparing to copy 2 items","depth":2,"bounds":{"left":0.042220745,"top":0.8747007,"width":0.10405585,"height":0.011173184},"automation_id":"_NS:89","role_description":"text"}]...
|
8495661271621520850
|
5195187031854324321
|
visual_change
|
accessibility
|
NULL
|
Copying 3 items to “BT”
stop progress
Stopping…
Pr Copying 3 items to “BT”
stop progress
Stopping…
Preparing to copy to “Test”
stop progress
Preparing to copy 2 items...
|
NULL
|
|
77905
|
1967
|
32
|
2026-04-24T10:44:01.632222+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777027441632_m1.jpg...
|
Finder
|
Copy
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Copying 3 items to “BT”
stop progress
Stopping…
Pr Copying 3 items to “BT”
stop progress
Stopping…
Preparing to copy to “Test”
stop progress
Preparing to copy 2 items...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Copying 3 items to “BT”","depth":2,"automation_id":"_NS:59","role_description":"text"},{"role":"AXButton","text":"stop progress","depth":2,"automation_id":"_NS:8","role_description":"button","is_enabled":false,"is_focused":false},{"role":"AXStaticText","text":"Stopping…","depth":2,"automation_id":"_NS:89","role_description":"text"},{"role":"AXStaticText","text":"Preparing to copy to “Test”","depth":2,"automation_id":"_NS:59","role_description":"text"},{"role":"AXButton","text":"stop progress","depth":2,"automation_id":"_NS:8","role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Preparing to copy 2 items","depth":2,"automation_id":"_NS:89","role_description":"text"}]...
|
8495661271621520850
|
5195187031854324321
|
click
|
accessibility
|
NULL
|
Copying 3 items to “BT”
stop progress
Stopping…
Pr Copying 3 items to “BT”
stop progress
Stopping…
Preparing to copy to “Test”
stop progress
Preparing to copy 2 items...
|
77903
|
|
57907
|
1244
|
24
|
2026-04-20T12:16:22.765158+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776687382765_m1.jpg...
|
Firefox
|
Firefox
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxFileProfilesToolsWindowEdit→ViewHistoryBook FirefoxFileProfilesToolsWindowEdit→ViewHistoryBookmarksCNikolay Yankov (Presenting)Helpmeet.google.com/cxs-eips-npt?authuser=0+fleEditM inbeСлe xCовіXhttps://www.figma.com/design,iXcUety9mxSFiz8KosLAUn/Project-Phoenix?node-id=5183a doreC ProjacteProject Phoenieondeeawh Nothing marked as reaty for develogment yetLiat0 Create,Cal|0. Repertieg.FituntU Reporting. Fitunt |etting0 дд- 161 A4 - 18|Dutasog!This arompt is used in reports"[Prompt Naonsed in on or morinmy reports. DeletingIf you want to keep them running, edit the reports to use a different ]prompt first.CancelDelete000700000 KBR YOUrKreeCwtraine3:16 PM | [Platform] Refinement ®Lộ3>0 lobl100% <478• Mon 20 Apr 15:16:22=L Ai dockmarx.00₴dl Al Reports8 taoluCяNikolay YankovNikolay IvanovCurace, Clrude Code.2 othersPop out this videoNikolay NikolovLukas Kovalik14:34...
|
NULL
|
8495620022995467964
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxFileProfilesToolsWindowEdit→ViewHistoryBook FirefoxFileProfilesToolsWindowEdit→ViewHistoryBookmarksCNikolay Yankov (Presenting)Helpmeet.google.com/cxs-eips-npt?authuser=0+fleEditM inbeСлe xCовіXhttps://www.figma.com/design,iXcUety9mxSFiz8KosLAUn/Project-Phoenix?node-id=5183a doreC ProjacteProject Phoenieondeeawh Nothing marked as reaty for develogment yetLiat0 Create,Cal|0. Repertieg.FituntU Reporting. Fitunt |etting0 дд- 161 A4 - 18|Dutasog!This arompt is used in reports"[Prompt Naonsed in on or morinmy reports. DeletingIf you want to keep them running, edit the reports to use a different ]prompt first.CancelDelete000700000 KBR YOUrKreeCwtraine3:16 PM | [Platform] Refinement ®Lộ3>0 lobl100% <478• Mon 20 Apr 15:16:22=L Ai dockmarx.00₴dl Al Reports8 taoluCяNikolay YankovNikolay IvanovCurace, Clrude Code.2 othersPop out this videoNikolay NikolovLukas Kovalik14:34...
|
57905
|
|
5148
|
96
|
26
|
2026-04-13T13:00:59.812304+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-13/1776 /Users/lukas/.screenpipe/data/data/2026-04-13/1776085259812_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl•DOCKER* Unable to access screenpipe activity dataO $1DEV (-zsh)O 882APP (-zsh)• *3-zsh84-zsh• 25-zsh86-zsh®Bash(curl -s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"im..)L Running..95% <422>&1RO 87python3Mon 13 Apr 16:00:59181* Unable to access s...-с "sash commandcurl -s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"import json, sysfrom collections import defaultdictdata = json.load(sys.stdin)items = data.get('data', [])apps = defaultdict(int)windows = defaultdict(int)for item in items:c = item.get('content', (})app = c.get('app_name'"Unknown") orwindow = C.get('window_name', ""S or Unknown'apps[app] += 1if window:windows [f'[{app}] {window}'] += 1print(f'Total frames: {len(items)}')printOprint('=== Apps (frames) ===')for app, count in sorted(apps.items(), key=lambda x: -x[1]):print(f'{app}: {count}')printOprint('=== Top Windows ===')for w, count in sorted(windows.items(), key=lambda x: -x[1])[:25]:print(f' {count:4d}x {w[:110]}')" 2>81Run shell commando you want to proceed?• 1.Yes2.Yes, and don't ask again for similar commands in /Users/lukas3. Noisc to cancel • Tab to amend• ctrl+e to explainpython3 -c"...
|
NULL
|
8495218599747037411
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp(ahl•DOCKER* Unable to access screenpipe activity dataO $1DEV (-zsh)O 882APP (-zsh)• *3-zsh84-zsh• 25-zsh86-zsh®Bash(curl -s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"im..)L Running..95% <422>&1RO 87python3Mon 13 Apr 16:00:59181* Unable to access s...-с "sash commandcurl -s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"import json, sysfrom collections import defaultdictdata = json.load(sys.stdin)items = data.get('data', [])apps = defaultdict(int)windows = defaultdict(int)for item in items:c = item.get('content', (})app = c.get('app_name'"Unknown") orwindow = C.get('window_name', ""S or Unknown'apps[app] += 1if window:windows [f'[{app}] {window}'] += 1print(f'Total frames: {len(items)}')printOprint('=== Apps (frames) ===')for app, count in sorted(apps.items(), key=lambda x: -x[1]):print(f'{app}: {count}')printOprint('=== Top Windows ===')for w, count in sorted(windows.items(), key=lambda x: -x[1])[:25]:print(f' {count:4d}x {w[:110]}')" 2>81Run shell commando you want to proceed?• 1.Yes2.Yes, and don't ask again for similar commands in /Users/lukas3. Noisc to cancel • Tab to amend• ctrl+e to explainpython3 -c"...
|
5147
|
|
80263
|
2103
|
2
|
2026-04-24T18:16:41.838352+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777054601838_m1.jpg...
|
PhpStorm
|
NULL
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Activity MonitorFileEditViewWindowHelp‹$0(23APP (- Activity MonitorFileEditViewWindowHelp‹$0(23APP (-zsh)APP (-zsh)DOCKERDEV (-zsh)882*3What's next:Try Dockerseamless, persistent debugging tools in any containeror image + docker debugdocker_1amp_1at [URL_WITH_CREDENTIALS] (JY-20508-notify-before-AJ-report-expiration) $ csfixdocker exec-it docker_lamp_1./vendor/bin/php-cs-fixer fix--config=.php-cs-fixer.dist.php-v--using-cache=no --diffPHP CS Fixer 3.87.1 Alexander by Fabien Potencier, Dariusz Ruminski andcontributors.PHPruntime:8.3.30Running analysis on 7 coreswith 10 files perprocess.Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!Loaded config default from".php-cs-fixer.dist.php".5623/5623100%1) app/Console/Commands/JiminnyDebugCommand.php (statement_indentation)begin diff/home/jiminny/app/Console/Commands/JiminnyDebugCommand.php+++/home/jiminny/app/Console/Commands/JiminnyDebugCommand.php-40,7 +40,7AutomatedReportsRepository SautomatedReportsRepository,UserPilotClient SuserPilotClient): void {Suser= User::find(143);Suser = User::find(143);////Scount = SautomatedReportsRepository->countUserReports(Suser);Sthis->info("Count: {Scount}");Scount = SautomatedReportsRepository->countAllUserReports(Suser);end diffscreenpipe"O $4100% C8Fri 24 Apr 21:16:41-zsh*5APPFixed 1 of 5623 files in 148.623 seconds, 60.00 MB memory usedWhat's next:Try Docker Debug for seamless, persistent debugging tools in any container or image » docker debug docker_lamp_1Learn more at https://docs.docker.com/go/debug-cli/lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20508-notify-before-AJ-report-expiration) $ I...
|
NULL
|
8494725129987124941
|
NULL
|
idle
|
ocr
|
NULL
|
Activity MonitorFileEditViewWindowHelp‹$0(23APP (- Activity MonitorFileEditViewWindowHelp‹$0(23APP (-zsh)APP (-zsh)DOCKERDEV (-zsh)882*3What's next:Try Dockerseamless, persistent debugging tools in any containeror image + docker debugdocker_1amp_1at [URL_WITH_CREDENTIALS] (JY-20508-notify-before-AJ-report-expiration) $ csfixdocker exec-it docker_lamp_1./vendor/bin/php-cs-fixer fix--config=.php-cs-fixer.dist.php-v--using-cache=no --diffPHP CS Fixer 3.87.1 Alexander by Fabien Potencier, Dariusz Ruminski andcontributors.PHPruntime:8.3.30Running analysis on 7 coreswith 10 files perprocess.Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!Loaded config default from".php-cs-fixer.dist.php".5623/5623100%1) app/Console/Commands/JiminnyDebugCommand.php (statement_indentation)begin diff/home/jiminny/app/Console/Commands/JiminnyDebugCommand.php+++/home/jiminny/app/Console/Commands/JiminnyDebugCommand.php-40,7 +40,7AutomatedReportsRepository SautomatedReportsRepository,UserPilotClient SuserPilotClient): void {Suser= User::find(143);Suser = User::find(143);////Scount = SautomatedReportsRepository->countUserReports(Suser);Sthis->info("Count: {Scount}");Scount = SautomatedReportsRepository->countAllUserReports(Suser);end diffscreenpipe"O $4100% C8Fri 24 Apr 21:16:41-zsh*5APPFixed 1 of 5623 files in 148.623 seconds, 60.00 MB memory usedWhat's next:Try Docker Debug for seamless, persistent debugging tools in any container or image » docker debug docker_lamp_1Learn more at https://docs.docker.com/go/debug-cli/lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20508-notify-before-AJ-report-expiration) $ I...
|
NULL
|
|
73070
|
1789
|
3
|
2026-04-23T06:45:33.522611+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776926733522_m2.jpg...
|
Firefox
|
Meet - Daily - Platform — Work
|
True
|
meet.google.com/agt-teir-cwt?authuser=lukas.kovali meet.google.com/agt-teir-cwt?authuser=lukas.kovalik%40jiminny.com...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
People
3
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Nikolay Yankov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Nikolay Ivanov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Others might still see your full video.
9:45
AM
Daily - Platform
Daily - Platform
Audio settings
Turn off microphone
Video settings
Turn off camera
Share screen
Send a reaction
Turn on captions
Raise hand (ctrl + ⌘ + h)
More options
Leave call
Meeting details
Chat with everyone
Meeting tools
Nikolay Ivanov joined...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Meet - Daily - Platform","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.016123671,"height":-0.051875472},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.27094415,"top":1.0,"width":0.004986702,"height":-0.051875472},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.27310506,"top":1.0,"width":0.010638298,"height":-0.086193085},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"People","depth":15,"bounds":{"left":0.69481385,"top":1.0,"width":0.019614361,"height":-0.06264961},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":22,"bounds":{"left":0.7081117,"top":1.0,"width":0.0023271276,"height":-0.071029544},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Take notes with Gemini","depth":14,"bounds":{"left":0.71708775,"top":1.0,"width":0.011968086,"height":-0.06264961},"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Take notes with Gemini","depth":17,"bounds":{"left":0.7184175,"top":1.0,"width":0.043550532,"height":-0.071029544},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini","depth":22,"bounds":{"left":0.7330452,"top":1.0,"width":0.013464096,"height":-0.071029544},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Gemini","depth":21,"bounds":{"left":0.73204786,"top":1.0,"width":0.011303191,"height":-0.063447714},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Yankov","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Nikolay Ivanov","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Pop out this video More screens are more fun. Play this video while you do other things.","depth":15,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pop out this video","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"More screens are more fun. Play this video while you do other things.","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Others might still see your full video.","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"9:45","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AM","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Daily - Platform","depth":12,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Daily - Platform","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Audio settings","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Turn off microphone","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Video settings","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Turn off camera","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share screen","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Send a reaction","depth":12,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Turn on captions","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Raise hand (ctrl + ⌘ + h)","depth":12,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"More options","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Leave call","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Meeting details","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chat with everyone","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Meeting tools","depth":12,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Nikolay Ivanov joined","depth":8,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8494720718686260363
|
-6570497331797755084
|
click
|
accessibility
|
NULL
|
Meet - Daily - Platform
Close tab
New Tab
Open Goo Meet - Daily - Platform
Close tab
New Tab
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Customize sidebar
People
3
Take notes with Gemini
Take notes with Gemini
Gemini
Gemini
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Nikolay Yankov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Nikolay Ivanov
Pop out this video More screens are more fun. Play this video while you do other things.
Pop out this video
More screens are more fun. Play this video while you do other things.
Others might still see your full video.
9:45
AM
Daily - Platform
Daily - Platform
Audio settings
Turn off microphone
Video settings
Turn off camera
Share screen
Send a reaction
Turn on captions
Raise hand (ctrl + ⌘ + h)
More options
Leave call
Meeting details
Chat with everyone
Meeting tools
Nikolay Ivanov joined...
|
NULL
|
|
50517
|
1084
|
13
|
2026-04-17T14:58:45.195999+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776437925195_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
CaudeFileEditViewWindowHelpboosteroid sull recorai CaudeFileEditViewWindowHelpboosteroid sull recoraing despite ignored windows settingg chat+ New chat• Prolects@ Customize8ó ArtiractsBulgarian citizenship application processDawarich location tracking projectBoosteroid still recording despite ignoreMissing JavaScript promise in authorizatunux solTeUnoriNAsClaude APl 500 internal server errorScreenpipe query capabilities and usageeoru comoat ol wunvac mimiano oiUnderstanaing openkouter ArlgalewaScreenpipe 14-day retention explainedReddit homepage feed overviewDocker container not visible in consoleDA Drowser kAM conr curationScreenpipe company backgroundDSK Bank Bulgaria secure investing exploulle1n2 a como crensve oersonainemdImporting Google limeline data to owndenromacact remora volume bulttans notiScreenpipe free version guideSelf-hosted LLM alternatives to Claude Sscreenpipe database query setupRunning screenpipe for treeI want to test it before running it. How ot run itReply +You've commented out all the sync code -or tesung the prerlignt ana counungsteos. But there are two issues at the end -and STOTAL ELAPSED arereterencedbut never set since those lines are commented out.vuick nlx to test just prerlignt + counting, comment out the droken lastlines too:hachl# Make executablechmod +x ~.screenpipe/screenpipe sync.sh# kun wich a speciric daue to test~/. screenplpe/screenplpe sync.sn 2020-04-15But first comment our the last 4 lines in your file:oash#echo ""#1og "Archive DB size:SDB SIZE"#10g"Total time:#10g "Sync complete for STARGET_DATE"# 10g "=============================:$ TOTAL ELAPSED 60m$ TOTAL ELAPSED % 60 s"Or just do a quick inline test without touching the hle - paste this directly in terminal to testlust the prerlight logie.DB SRC="$HOME.screenpipe/db.sqlite"NAS MOUNT=" Volumes Testscreenpipe"TARGET DATE="2026-04-15"-t "sDB SRC"a& echo" Source DB:-" "SNAs MOUN" *x ecno"V NAS mount:$(du -sh $DB_SRC | cut -f1)" ll echo "x SouUx"ecnel"X NAS not mounted"echo "Counting rows..."sqlite3 "SDB SRC" "SELECT COUNT(*frames'FROM framesWHERE date(timestampsqlite3"SDB SRC" "SELECT COUNT(*elementsFROM elementsWHERE frame id INSsqlite3 "$DB_SRC" "SELECT COUNT(*) || ' ui_events'FROM vi_events WHERE date (timestampUpdated to 1.3109.0Ke auncho aooyRelaunch…Lukas KovalkReply...Sonnet 40Clauae is Al ana can make mistakes. rlease double-cneck responses.libdA100% 1Z7Fri 17 Apr 17:58:44copyscreenpipe sync shsqlite3 "$DB SRC" <<< "$sql" &local pid=s!local spin=[PASSWORD] aaa88'8local 1=0while k1ll -0 "sp1d" 2>/dev/null; do%-36s %S " "$label" "${spin[$i]}"printt "\ri=$(( (i + 1) % 10 ))sleed 0.2conewait "Spid'local dur=s(( $(date +%s) - start ))printf "r%-36s v %dm%02ds n" "Slabel" "$( dur / 60""$ dur % 60 )" tee -a "SLOG FILE"DAUT ARGUMENIN1T L"921:-5" = "today" J; therARGE DATTES date +%y-/m-/dxelif [ -n "$51:-3" 1: thenLARGEI DAIL-"S1"if ! "STARGET DATE" =- ^ 0-91347- 0-91321- 0-91 215 17: thenecho "ERROR: Invalid date format. Use YYYY-MM-DD, 'today', or no argument for vesterday."exit 1fielseTARGET DATE=S(date -v-1d +%Y-%m-%d)LogLog"Screenpipe sync starting for: $TARGET_DATE"LogFACrLLOhlstep "Preflight checks"if !-f"$DB SRC" : ther1og "ERROR: Source DB not found at SDB SRC"; exit 1orintt"%-Dos%c(%s)\n" "Source DB." "OK" "S(du -sh "SDR SRG" | cut -f1)"if[! -d "$NAS MOUNT" 1: thenlog "ERROR: NAS not mounted at $NAS_MOUNT"; exit 1printf "%-20s %s\n" "NAS mount:" "OK$NAS MOUNT'if [-f "$NAS DB" ]: thenEXISTING=$(sqlite3 "SNAS DB" "SELECT COUNT(* FROM frames WHERE date timestamp = 'STARGET DATE':" 2>/dev/null echo "0")if "SEXISTING"-gt "0" ; thenlog "Date STARGET DATE already has SEXISTING frames in archive - skippingAX1T 1Oprintf "%-20s %s (%s)\n" "Archive DB:" "exists" "$(du -sh "$NAS_DB" | cut -f1) "elseprintf "%-20s %s\n" "Archive DB:" "will be created"100101102COUNT SOURCE ROWSstep "Counting source rows for STARGET DATE'...
|
NULL
|
8494689050077046482
|
NULL
|
click
|
ocr
|
NULL
|
CaudeFileEditViewWindowHelpboosteroid sull recorai CaudeFileEditViewWindowHelpboosteroid sull recoraing despite ignored windows settingg chat+ New chat• Prolects@ Customize8ó ArtiractsBulgarian citizenship application processDawarich location tracking projectBoosteroid still recording despite ignoreMissing JavaScript promise in authorizatunux solTeUnoriNAsClaude APl 500 internal server errorScreenpipe query capabilities and usageeoru comoat ol wunvac mimiano oiUnderstanaing openkouter ArlgalewaScreenpipe 14-day retention explainedReddit homepage feed overviewDocker container not visible in consoleDA Drowser kAM conr curationScreenpipe company backgroundDSK Bank Bulgaria secure investing exploulle1n2 a como crensve oersonainemdImporting Google limeline data to owndenromacact remora volume bulttans notiScreenpipe free version guideSelf-hosted LLM alternatives to Claude Sscreenpipe database query setupRunning screenpipe for treeI want to test it before running it. How ot run itReply +You've commented out all the sync code -or tesung the prerlignt ana counungsteos. But there are two issues at the end -and STOTAL ELAPSED arereterencedbut never set since those lines are commented out.vuick nlx to test just prerlignt + counting, comment out the droken lastlines too:hachl# Make executablechmod +x ~.screenpipe/screenpipe sync.sh# kun wich a speciric daue to test~/. screenplpe/screenplpe sync.sn 2020-04-15But first comment our the last 4 lines in your file:oash#echo ""#1og "Archive DB size:SDB SIZE"#10g"Total time:#10g "Sync complete for STARGET_DATE"# 10g "=============================:$ TOTAL ELAPSED 60m$ TOTAL ELAPSED % 60 s"Or just do a quick inline test without touching the hle - paste this directly in terminal to testlust the prerlight logie.DB SRC="$HOME.screenpipe/db.sqlite"NAS MOUNT=" Volumes Testscreenpipe"TARGET DATE="2026-04-15"-t "sDB SRC"a& echo" Source DB:-" "SNAs MOUN" *x ecno"V NAS mount:$(du -sh $DB_SRC | cut -f1)" ll echo "x SouUx"ecnel"X NAS not mounted"echo "Counting rows..."sqlite3 "SDB SRC" "SELECT COUNT(*frames'FROM framesWHERE date(timestampsqlite3"SDB SRC" "SELECT COUNT(*elementsFROM elementsWHERE frame id INSsqlite3 "$DB_SRC" "SELECT COUNT(*) || ' ui_events'FROM vi_events WHERE date (timestampUpdated to 1.3109.0Ke auncho aooyRelaunch…Lukas KovalkReply...Sonnet 40Clauae is Al ana can make mistakes. rlease double-cneck responses.libdA100% 1Z7Fri 17 Apr 17:58:44copyscreenpipe sync shsqlite3 "$DB SRC" <<< "$sql" &local pid=s!local spin=[PASSWORD] aaa88'8local 1=0while k1ll -0 "sp1d" 2>/dev/null; do%-36s %S " "$label" "${spin[$i]}"printt "\ri=$(( (i + 1) % 10 ))sleed 0.2conewait "Spid'local dur=s(( $(date +%s) - start ))printf "r%-36s v %dm%02ds n" "Slabel" "$( dur / 60""$ dur % 60 )" tee -a "SLOG FILE"DAUT ARGUMENIN1T L"921:-5" = "today" J; therARGE DATTES date +%y-/m-/dxelif [ -n "$51:-3" 1: thenLARGEI DAIL-"S1"if ! "STARGET DATE" =- ^ 0-91347- 0-91321- 0-91 215 17: thenecho "ERROR: Invalid date format. Use YYYY-MM-DD, 'today', or no argument for vesterday."exit 1fielseTARGET DATE=S(date -v-1d +%Y-%m-%d)LogLog"Screenpipe sync starting for: $TARGET_DATE"LogFACrLLOhlstep "Preflight checks"if !-f"$DB SRC" : ther1og "ERROR: Source DB not found at SDB SRC"; exit 1orintt"%-Dos%c(%s)\n" "Source DB." "OK" "S(du -sh "SDR SRG" | cut -f1)"if[! -d "$NAS MOUNT" 1: thenlog "ERROR: NAS not mounted at $NAS_MOUNT"; exit 1printf "%-20s %s\n" "NAS mount:" "OK$NAS MOUNT'if [-f "$NAS DB" ]: thenEXISTING=$(sqlite3 "SNAS DB" "SELECT COUNT(* FROM frames WHERE date timestamp = 'STARGET DATE':" 2>/dev/null echo "0")if "SEXISTING"-gt "0" ; thenlog "Date STARGET DATE already has SEXISTING frames in archive - skippingAX1T 1Oprintf "%-20s %s (%s)\n" "Archive DB:" "exists" "$(du -sh "$NAS_DB" | cut -f1) "elseprintf "%-20s %s\n" "Archive DB:" "will be created"100101102COUNT SOURCE ROWSstep "Counting source rows for STARGET DATE'...
|
50515
|
|
57690
|
1239
|
72
|
2026-04-20T12:02:44.797284+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776686564797_m2.jpg...
|
PhpStorm
|
PhpStorm
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormViewINavigarecodeLaravelKeractorJOOISWindo PhostormViewINavigarecodeLaravelKeractorJOOISWindowFV faVsco.js?9 master vProjectC ActivityController.pnp©AskAnythingController.php© AskAnythingPromptService.php(C)Ask JiminnvReportscontroller.onp(c)JiminnyDebugcommana.ongM+ WEBHOOK FILTERING_IMPLEMENTATION.moC) AutomatedReportsRepository.pnpAutomatedkeporssewice.ong© AutomatedReportsCommand.phpUpaatencuviyclasticsearenDocumentcommana.onp>0b External Librariesv E Scratches and Consoles© Updaterlasticsearch.php xC) AutomatedReportsController.ong© TrackProviderInstalledEvent.phpv _ Database consolesC) AutomatedReportkesult.pngC) Automatedkeport.phpV AEU& console (euJA DEAL RISKS (EUJADITEUIA EU (EU37 €cLass upaatetLastzcsearch extends iterateActavatiesprotected bool SwithTrashed = true:v / lminnv@localhost40* Render a oroqress oor, so we know what the current orogress 1s.console fiminnv@localhost)l* @var bo0l4 D| lliminnv@localhostl4 HS_local jiminny@localhost)4 SF jiminny@localhost]A zoho_dev [jiminny@localhost]V A PRODA console [PROD]A console_1 (PROD]A DI (PROD]44 0)orotected SwithProaressBar = true:* The console command description.ServicesOutputh liminnv.crm confiaurationsv D Databaseie1rowvTx: Auto vDDL# Q GA®consolev A liminnv@localhostA HS local0 iduuid (UUID)492Zafo2h£.Tedd_ZhAQchd0Z-112424e0229ASF 978 msAPRODfo team id711(notifiable user id<null>4 console 41ASTAGINGo providersallesforceIconsoleWedition<nul1)DockerI instanceUSA560is sandboxM version50.0installed aop version! sync_metadata! sync_objectsauto sync activity1 crm_provider id00D90000000fUszEAEM crm base urlhttos:lesmills.nv.salesforce.comlhast sunced an2026-03-10 08-17•08leads_synced_at2026-04-20 11-47-42I accounts synced at2026-04-20 11:47:35I contacts synced_ at2026-04-20 11:48:45I opportunities synced at2026-04-20 11:47:47contact roles svnced at2026-04-20 11:48•53imoven qunta at1 raw rotriovod ctortina from 1 in 602 me (ovodution: 120 me fotchina. 15/mcll100% Lz• Mon 20 Apr 15:02:44L AskJiminnyReportActivityServiceTest v= custom.log= laravel.l0gм V 596599V601$ 3 3A SF [jiminny@localhost]« HS_local [jiminny@localhost]& console [PROD] >« console (EU]« console [STAGING]PlaygroundvSELECT * FROM teams WHERE name LIKE '%Les%': # 711, 692, 16067B- [EMAIL] * from playbooks where team_id = 711: # event 226147SELECT * FROM playbook categories WHERE playbook id = 5515:So jiminny034 A1 A34 V 62 ^SELECT * FROM crm tieldsWHERE 10 = 226147SELECT * FROM crm_field_values WHERE crm_field_id = 226147;SELECT * FROM crm configurations WHERE id = 692;SELEMCONCAT(U.id, CASE WHEN U.id = t.owner_id THEN' (owner)' ELSE "" END) AS user_id,U.emailt.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJOIN teams t .n<->1: on t.id = u.team_idWHERE U.team_id = 711 and sa.provider = 'salesforce';ТОО ТПО ПОНОО П ОООПО 01 00111111 11 400CSL→OW Windsurf Teams 599:28 UTF-8 4 spaces...
|
NULL
|
8494608239888578184
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormViewINavigarecodeLaravelKeractorJOOISWindo PhostormViewINavigarecodeLaravelKeractorJOOISWindowFV faVsco.js?9 master vProjectC ActivityController.pnp©AskAnythingController.php© AskAnythingPromptService.php(C)Ask JiminnvReportscontroller.onp(c)JiminnyDebugcommana.ongM+ WEBHOOK FILTERING_IMPLEMENTATION.moC) AutomatedReportsRepository.pnpAutomatedkeporssewice.ong© AutomatedReportsCommand.phpUpaatencuviyclasticsearenDocumentcommana.onp>0b External Librariesv E Scratches and Consoles© Updaterlasticsearch.php xC) AutomatedReportsController.ong© TrackProviderInstalledEvent.phpv _ Database consolesC) AutomatedReportkesult.pngC) Automatedkeport.phpV AEU& console (euJA DEAL RISKS (EUJADITEUIA EU (EU37 €cLass upaatetLastzcsearch extends iterateActavatiesprotected bool SwithTrashed = true:v / lminnv@localhost40* Render a oroqress oor, so we know what the current orogress 1s.console fiminnv@localhost)l* @var bo0l4 D| lliminnv@localhostl4 HS_local jiminny@localhost)4 SF jiminny@localhost]A zoho_dev [jiminny@localhost]V A PRODA console [PROD]A console_1 (PROD]A DI (PROD]44 0)orotected SwithProaressBar = true:* The console command description.ServicesOutputh liminnv.crm confiaurationsv D Databaseie1rowvTx: Auto vDDL# Q GA®consolev A liminnv@localhostA HS local0 iduuid (UUID)492Zafo2h£.Tedd_ZhAQchd0Z-112424e0229ASF 978 msAPRODfo team id711(notifiable user id<null>4 console 41ASTAGINGo providersallesforceIconsoleWedition<nul1)DockerI instanceUSA560is sandboxM version50.0installed aop version! sync_metadata! sync_objectsauto sync activity1 crm_provider id00D90000000fUszEAEM crm base urlhttos:lesmills.nv.salesforce.comlhast sunced an2026-03-10 08-17•08leads_synced_at2026-04-20 11-47-42I accounts synced at2026-04-20 11:47:35I contacts synced_ at2026-04-20 11:48:45I opportunities synced at2026-04-20 11:47:47contact roles svnced at2026-04-20 11:48•53imoven qunta at1 raw rotriovod ctortina from 1 in 602 me (ovodution: 120 me fotchina. 15/mcll100% Lz• Mon 20 Apr 15:02:44L AskJiminnyReportActivityServiceTest v= custom.log= laravel.l0gм V 596599V601$ 3 3A SF [jiminny@localhost]« HS_local [jiminny@localhost]& console [PROD] >« console (EU]« console [STAGING]PlaygroundvSELECT * FROM teams WHERE name LIKE '%Les%': # 711, 692, 16067B- [EMAIL] * from playbooks where team_id = 711: # event 226147SELECT * FROM playbook categories WHERE playbook id = 5515:So jiminny034 A1 A34 V 62 ^SELECT * FROM crm tieldsWHERE 10 = 226147SELECT * FROM crm_field_values WHERE crm_field_id = 226147;SELECT * FROM crm configurations WHERE id = 692;SELEMCONCAT(U.id, CASE WHEN U.id = t.owner_id THEN' (owner)' ELSE "" END) AS user_id,U.emailt.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJOIN teams t .n<->1: on t.id = u.team_idWHERE U.team_id = 711 and sa.provider = 'salesforce';ТОО ТПО ПОНОО П ОООПО 01 00111111 11 400CSL→OW Windsurf Teams 599:28 UTF-8 4 spaces...
|
NULL
|
|
5277
|
98
|
46
|
2026-04-13T13:06:33.245528+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-13/1776 /Users/lukas/.screenpipe/data/data/2026-04-13/1776085593245_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp>0.* Unable to access screenpipe activity dataDOCKERO ₴1DEV (-zsh)O 882APP (-zsh)• ₴3-zsh• 84-zsh• 25-zsh86-zsh®Bash(curl -s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"im..)L Running..96% C2>&1RO 87python3Mon 13 Apr 16:06:33181* Unable to access s...-с "sash commandcurl-s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"import json, sysfrom collections import defaultdictdata = json.load(sys.stdin)items = data.get('data', [])apps = defaultdict(int)windows = defaultdict(int)for item in items:c = item.get('content', (})app = c.get('app_name'"Unknown") orwindow = C.get('window_name', ""S or Unknown'apps[app] += 1if window:windows [f'[{app}] {window}'] += 1print(f'Total frames: {len(items)}')printOprint('=== Apps (frames) ===')for app, count in sorted(apps.items(), key=lambda x: -x[1]):print(f'{app}: {count}')printOprint('=== Top Windows ===')for w, count in sorted(windows.items(), key=lambda x: -x[1])[:25]:print(f' {count:4d}x {w[:110]}')" 2>81Run shell commando you want to proceed?• 1.Yes2.Yes, and don't ask again for similar commands in /Users/lukas3. Noisc to cancel • Tab to amend• ctrl+e to explainpython3 -c "...
|
NULL
|
8494538326045824979
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp>0.* Unable to access screenpipe activity dataDOCKERO ₴1DEV (-zsh)O 882APP (-zsh)• ₴3-zsh• 84-zsh• 25-zsh86-zsh®Bash(curl -s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"im..)L Running..96% C2>&1RO 87python3Mon 13 Apr 16:06:33181* Unable to access s...-с "sash commandcurl-s "http://localhost:3030/search?content_type=ocr&start_time=2026-04-11T00:00:00Z&end_time=2026-04-13T23:59:59Z&limit=5000&offset=0"import json, sysfrom collections import defaultdictdata = json.load(sys.stdin)items = data.get('data', [])apps = defaultdict(int)windows = defaultdict(int)for item in items:c = item.get('content', (})app = c.get('app_name'"Unknown") orwindow = C.get('window_name', ""S or Unknown'apps[app] += 1if window:windows [f'[{app}] {window}'] += 1print(f'Total frames: {len(items)}')printOprint('=== Apps (frames) ===')for app, count in sorted(apps.items(), key=lambda x: -x[1]):print(f'{app}: {count}')printOprint('=== Top Windows ===')for w, count in sorted(windows.items(), key=lambda x: -x[1])[:25]:print(f' {count:4d}x {w[:110]}')" 2>81Run shell commando you want to proceed?• 1.Yes2.Yes, and don't ask again for similar commands in /Users/lukas3. Noisc to cancel • Tab to amend• ctrl+e to explainpython3 -c "...
|
NULL
|
|
1032
|
24
|
50
|
2026-04-11T12:47:01.712409+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-11/1775 /Users/lukas/.screenpipe/data/data/2026-04-11/1775911621712_m1.jpg...
|
Firefox
|
DXP4800PLUS-B5F8 — Personal
|
True
|
nas.lakylak.xyz/desktop/#/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox (1) - [EMAIL] - Gmail
How t DXP4800PLUS-B5F8
Inbox (1) - [EMAIL] - Gmail
How to Get Through the Product Management Bottleneck
How to Get Through the Product Management Bottleneck
Simple Pleasures • HBO Max
Simple Pleasures • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Denník N - Nezávislý denník
Denník N - Nezávislý denník
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Problem loading page
Problem loading page
Machines - Tailscale
Machines - Tailscale
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
702.3
KB/s
39.9
MB/s
Files
Control Panel
Storage
App Center
Logs
Support
Task Manager
Universal Search
Music...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXRadioButton","text":"Inbox (1) - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"How to Get Through the Product Management Bottleneck","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to Get Through the Product Management Bottleneck","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Simple Pleasures • HBO Max","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Simple Pleasures • HBO Max","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Settings","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Settings","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"firefox sidebar - Google Search","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"firefox sidebar - Google Search","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"How to use AI-enhanced tab groups | Firefox Help","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"How to use AI-enhanced tab groups | Firefox Help","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Add-ons Manager","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Add-ons Manager","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Vimium – Get this Extension for 🦊 Firefox (en-US)","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Denník N - Nezávislý denník","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Denník N - Nezávislý denník","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Problem loading page","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Problem loading page","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Machines - Tailscale","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Machines - Tailscale","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.4798611,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.5034722,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.5263889,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.54930556,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.5729167,"top":0.0,"width":0.022222223,"height":0.035555556},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"702.3","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KB/s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"39.9","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"MB/s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Files","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Control Panel","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Storage","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"App Center","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Logs","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Support","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Task Manager","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Universal Search","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Music","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8494352667182948869
|
-5412411423615857114
|
click
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox (1) - [EMAIL] - Gmail
How t DXP4800PLUS-B5F8
Inbox (1) - [EMAIL] - Gmail
How to Get Through the Product Management Bottleneck
How to Get Through the Product Management Bottleneck
Simple Pleasures • HBO Max
Simple Pleasures • HBO Max
Settings
Settings
firefox sidebar - Google Search
firefox sidebar - Google Search
How to use AI-enhanced tab groups | Firefox Help
How to use AI-enhanced tab groups | Firefox Help
Add-ons Manager
Add-ons Manager
Vimium – Get this Extension for 🦊 Firefox (en-US)
Vimium – Get this Extension for 🦊 Firefox (en-US)
Denník N - Nezávislý denník
Denník N - Nezávislý denník
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Loď Orion úspešne pristála na Zemi. Desať vecí, ktoré si pamätať o misii Artemis II — Denník N
Problem loading page
Problem loading page
Machines - Tailscale
Machines - Tailscale
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
702.3
KB/s
39.9
MB/s
Files
Control Panel
Storage
App Center
Logs
Support
Task Manager
Universal Search
Music...
|
1031
|
|
26307
|
558
|
54
|
2026-04-15T13:17:59.813797+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776259079813_m1.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi…..Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina Petrova0. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira CloudGoogle Cale...# releases8 22Messagesnewdou+@ Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Aa• Sprint Review • 13 m leftRActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefoxCP Isolated Web ContentFirefox GPU HelperVTDecoderXPCServiceSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaude Helper (Renderer)Notion Calendar Helper (Renderer)Notion Helper (Renderer)claudeFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentCode Helper (Renderer)Mem...2,03 GB1,18 GB971,1 MB962,6 MB845,2 MB793,5 MB750,8 MB554,1 MB547,2 MB543,9 MB516,2 MB455,3 MB449,5 MB436,8 MB434,1 MB389,2 MB388,0 MB386,7 MB372,4 MB347,0 MB345,1 MB327,3 MB326,2 MB302,5 MB251,7 MB238,5 MB233,5 MB214,9 MBMEMORY PRESSUREPhysical Memory:Memory Used:Cached Files:Swap Used:100% C78Wed 15 Apr 16:17:59CPUMemoryDiskThreads39227225842829242711162325242623262216152113286272819EnergyPorts61119 7557231241 20120 023129242126254166199120123124127119124119223172328721261 832124128255PID93892407801442974146648424203080193671314673938994186335480358313527643016368984365248173605192654811485091035833487856138482988534016,00 GB14,13 GB <1,81 GB2,93 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,90 GB2,88 GB6,80 GB...
|
NULL
|
8494306913472141642
|
NULL
|
click
|
ocr
|
NULL
|
+SlackFileEditViewGoEDHomeActivity..•More+HistoryW +SlackFileEditViewGoEDHomeActivity..•More+HistoryWindowHelp→Search Jiminny IncJiminny ...sos+# general# infra-changes# jiminny-bg# platform-tickets# product _launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi…..Direct messagesStoyan TanevVesGalya DimitrovaAneliya Angelova, ...Vasil VasilevSteliyan GeorgievAdelina Petrova, Ili...P. Adelina Petrova0. Nikolay Nikolov2 Galya Dimitrova, Ni...ii: AppsToastJira CloudGoogle Cale...# releases8 22Messagesnewdou+@ Files• Bookmarksv 2 new messagesGitHub APP3:28 PM7 new commits pushed tomaster by nikolay-yankovNew24b989ee - Enhance SECFIXdocumentation and policiesa3a0a742 - Update SECFIX Slack channelreference in documentation and workflowfiles071c999d - Merge branch 'master' intoimprove-secfix-bot-15-04-2026981e9a1a - Update SECFIX_PROMPT.mdto enhance clarity on upgrade safety andchangelog reviews6e938e53 - Enhance SECFIX workflow withSlack notification optionsShow more( jiminny/app Added by GitHubCircleCl APP3:53 PMDeployment Successful!Project: appWhen:04/15/202612:53:30Tag:View JobMessage #releases+Aa• Sprint Review • 13 m leftRActivity MonitorAll ProcessesProcess NameBoosteroidWindowServerFirefoxFirefoxCP Isolated Web ContentFirefoxCursorUlViewService (Not Responding)FirefoxCP Isolated Web ContentFirefox GPU HelperFirefoxCP Isolated Web ContentFirefox GPU HelperVTDecoderXPCServiceSlack Helper (Renderer)FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentClaude Helper (Renderer)Notion Calendar Helper (Renderer)Notion Helper (Renderer)claudeFirefoxCP Isolated Web ContentiTerm2FirefoxCP Isolated Web ContentFirefoxCP Isolated Web ContentCode Helper (Renderer)Mem...2,03 GB1,18 GB971,1 MB962,6 MB845,2 MB793,5 MB750,8 MB554,1 MB547,2 MB543,9 MB516,2 MB455,3 MB449,5 MB436,8 MB434,1 MB389,2 MB388,0 MB386,7 MB372,4 MB347,0 MB345,1 MB327,3 MB326,2 MB302,5 MB251,7 MB238,5 MB233,5 MB214,9 MBMEMORY PRESSUREPhysical Memory:Memory Used:Cached Files:Swap Used:100% C78Wed 15 Apr 16:17:59CPUMemoryDiskThreads39227225842829242711162325242623262216152113286272819EnergyPorts61119 7557231241 20120 023129242126254166199120123124127119124119223172328721261 832124128255PID93892407801442974146648424203080193671314673938994186335480358313527643016368984365248173605192654811485091035833487856138482988534016,00 GB14,13 GB <1,81 GB2,93 GBApp Memory:Wired Memory:Compressed:NetworkUserlukas_windowserverlukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukaslukas3,90 GB2,88 GB6,80 GB...
|
NULL
|
|
50006
|
1066
|
22
|
2026-04-17T14:12:48.344262+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776435168344_m2.jpg...
|
NULL
|
NULL
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhpStormFileEditToolsWindowHelpFV faVsco.s vProjec PhpStormFileEditToolsWindowHelpFV faVsco.s vProject vPlaylists> D Teams> Transcription> DUsers>MWebhook=.gitkeepC) ChangeLoaContextCor> D Mail|v D Models> D ActivityViewNavigateCodeLaravelRefactor99 JY-20698-fix-SF-activity-types-on-new-playbook ~© PlaybookRepository.php© ImportActivityTypes.php© ImportActivity TypesTest.phpC Playbook.php© ReportController.php• AutomatedReportsCommand.phpC CreateHeldActivityEvent.php> D AskAnything> C Calendar> D Connection|> D Contractsv MCrmC BusinessProcess.phg© Configuration.php© ContactRole.php© Field.php© FieldData.php©FieldValue.php©Layout.php©LayoutEntity.php©Log.php© Profile.php© RecordType.php© SyncBatch.php> M ElasticSearch> MFeature> → OpportunityDParticipant> D PlaybackThemePlaylistScorecard> [ Webhook© Account.php© Activity.php© Address.php© AiPrompt.php© AutomatedReport.phpC AutomatedReportResullC) Calendar.php© Calllmport.php© CoachingFeedback.php© CoachingFeedbackVisik© CoachingSection.php© CoachingSectionCriteric© CoachingSectionCriterk© CoachingSectionFeedb:© CommentAbstract.php• Commentintertace.pnpc contaacronoC) Device.php© EmailMessage.php© GenericAiPrompt.php© Group.php© Inbox.php© InboxEmail.php© Group.phpC Service.phpc rield.onp• FieldRepository.php© JiminnyDebugCommand.php© AutomatedReportsSendCommand.php© AutomatedReportsRepository.php© AutomatedReportsService.phpC TrackProviderInstalledEvent.php© CreateActivityLoqgedEvent.php© UserPilotActivityListener.php© ActivityLogged.phpC RequestGenerateReportJob.phpc) AutomatedReportResult.ongclass Group extends Model implements GroupContract© AutomatedReportsCallbackService.php© AutomatedReport.php202207219440224225226227228250LOJLO4233234238240143251404257262400LILpublic function getCountryName(): stringf...public function getCountryCode(): stringf...}pubLic tunccion gecderauLtLanguaged: scringt...public function getDefaultTimezone(): stringf...;public function getPhotoUrlAttribute(): stringf...}public function getPhotoUrl(): string/** Ovar string */return Sthis-›getAttribute( Key! fhodt_utAccept FiteX Reiect Fie +*xll co vascade, dsl co/***/* The channels the user receives notification broadcasts on.public function receivesBroadcastNotificationsOn(): stringf...}/** @return BelongsTo<User, self> */public function user(): BelongsTof..}public function getOwner(): Userf...}public function members(): HasMany{.}public function membersActive(): HasMany{...}public function playbookO){...;public function teamO)f...}public function scorecards(): HasMany{..}Pushed JY-20698-fix-SF-activity-types-on-new-playbook to new branch origin/JY-20698-fix-SF-activity-types-on-new-playbook // Create pull request (22 minutes ago)g Tech Day Review • 18 m leftARequestGenerateAskJiminnyReportJobTest v100% C•CascadeAutomated Report RetiPlaybook Categories InNew CascadeFri 17 Apr 17:12:48+0 •= custom.logA console [EU] XA SF [jiminny@localhost]A HS_local [iminny@localhost]A console [PROD][CREDIT_CARD]-1589159015911592159515741596JoYl1oyd1601160216031604160516061607— 1699- 10071610161116121613161416151616161716181619162016211622= laravel.logA console [STAGING]Ix. AutovMlaycroundvSa jiminny ~027 49 A23 Х3 X 105 ^u.emailsarxt.owner_id FROM social_accounts saJOIN users u on U.id= sa.sociable idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE U.team_id = 556 and sa.provider = 'integration-app';select * from opportunities where id = 7594349;select * from opportunity_stages where opportunity_id = 7594349 order by created_at desc;select * from business_processes where id = 6024;select * from business_process_stages where stage_id = 16352;Iselect * from business process stages where business process id = 6024:Iselect * from stages where team id = 459select * from teams where id = 459;SELECTCONCAT(U.id, LASE WHEN U.id = t.owner_id THEN ' (owner)' ELSE "'END) AS user_id,u.email,sa.*,t.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE u. team_id = 459 and sa.provider ='hubspot';SELECT os.stage_id, s.crm_provider_id, s.name, COUNT(*) as cntFROM opportunity_stages osJOIN stages s1..n<->1: ON s.id = os.stage_idWHERE os.opportunity_id = 7594349GROUP BY os.stage_id, s.crm_provider_id, s.nameORDER BY Cnt DESCHSELECT s.id, s.crm_provider_id, s.name, s.team_id, s.crm_configuration_idFROM stages sJOIN business_process_stages bps 1<-›1..n: ON bps.stage_id = s.idWHERE bps.business_process_id = 6024AND s.crm_provider_id ='sontractsent':select * from stages where id IN (16352,20612, 18281, 7344, 16378, 16309,5036, 15223, 14535, 6293, 12098,SELECT * FROM teams WHERE name LIKEselect * from playbooks where team_id = 472; # event 226147SELECT * FROM playbook_categories WHERE playbook_id = 5515;SELECT * FROM crm_fields WHERE id = 226147;SELECT * FROM crm_field_values WHERE crm_field_id = 226147;15138,raza.gilani@vuelio.com1627 V16281629163016311632163316341475SELECT * FROM crm_configurations WHERE id = 380;SELECTCONCAT(u.id, CASE WHEN U.id = t.owner_id THEN ' (owner)' ELSE "' END) AS user_id,u.email,sa.*,t.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idLnid teams Ti.n<->': on t.1d = 1.team 161WHERE u.team_id = 472 and sa.provider = 'salesforce':WCascade Code*•.Kick off a new project. Make changesduross you ente couebase,© Playbook Categories Import Debugging and Testing© Update Connection Logic@ Debugging Opportunity Stage OscillationsWhy is there group+ ‹› Codel§ AdaptiveW Windsur leams230:1ulr-o( 4 spaces...
|
NULL
|
8493891693294374491
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhpStormFileEditToolsWindowHelpFV faVsco.s vProjec PhpStormFileEditToolsWindowHelpFV faVsco.s vProject vPlaylists> D Teams> Transcription> DUsers>MWebhook=.gitkeepC) ChangeLoaContextCor> D Mail|v D Models> D ActivityViewNavigateCodeLaravelRefactor99 JY-20698-fix-SF-activity-types-on-new-playbook ~© PlaybookRepository.php© ImportActivityTypes.php© ImportActivity TypesTest.phpC Playbook.php© ReportController.php• AutomatedReportsCommand.phpC CreateHeldActivityEvent.php> D AskAnything> C Calendar> D Connection|> D Contractsv MCrmC BusinessProcess.phg© Configuration.php© ContactRole.php© Field.php© FieldData.php©FieldValue.php©Layout.php©LayoutEntity.php©Log.php© Profile.php© RecordType.php© SyncBatch.php> M ElasticSearch> MFeature> → OpportunityDParticipant> D PlaybackThemePlaylistScorecard> [ Webhook© Account.php© Activity.php© Address.php© AiPrompt.php© AutomatedReport.phpC AutomatedReportResullC) Calendar.php© Calllmport.php© CoachingFeedback.php© CoachingFeedbackVisik© CoachingSection.php© CoachingSectionCriteric© CoachingSectionCriterk© CoachingSectionFeedb:© CommentAbstract.php• Commentintertace.pnpc contaacronoC) Device.php© EmailMessage.php© GenericAiPrompt.php© Group.php© Inbox.php© InboxEmail.php© Group.phpC Service.phpc rield.onp• FieldRepository.php© JiminnyDebugCommand.php© AutomatedReportsSendCommand.php© AutomatedReportsRepository.php© AutomatedReportsService.phpC TrackProviderInstalledEvent.php© CreateActivityLoqgedEvent.php© UserPilotActivityListener.php© ActivityLogged.phpC RequestGenerateReportJob.phpc) AutomatedReportResult.ongclass Group extends Model implements GroupContract© AutomatedReportsCallbackService.php© AutomatedReport.php202207219440224225226227228250LOJLO4233234238240143251404257262400LILpublic function getCountryName(): stringf...public function getCountryCode(): stringf...}pubLic tunccion gecderauLtLanguaged: scringt...public function getDefaultTimezone(): stringf...;public function getPhotoUrlAttribute(): stringf...}public function getPhotoUrl(): string/** Ovar string */return Sthis-›getAttribute( Key! fhodt_utAccept FiteX Reiect Fie +*xll co vascade, dsl co/***/* The channels the user receives notification broadcasts on.public function receivesBroadcastNotificationsOn(): stringf...}/** @return BelongsTo<User, self> */public function user(): BelongsTof..}public function getOwner(): Userf...}public function members(): HasMany{.}public function membersActive(): HasMany{...}public function playbookO){...;public function teamO)f...}public function scorecards(): HasMany{..}Pushed JY-20698-fix-SF-activity-types-on-new-playbook to new branch origin/JY-20698-fix-SF-activity-types-on-new-playbook // Create pull request (22 minutes ago)g Tech Day Review • 18 m leftARequestGenerateAskJiminnyReportJobTest v100% C•CascadeAutomated Report RetiPlaybook Categories InNew CascadeFri 17 Apr 17:12:48+0 •= custom.logA console [EU] XA SF [jiminny@localhost]A HS_local [iminny@localhost]A console [PROD][CREDIT_CARD]-1589159015911592159515741596JoYl1oyd1601160216031604160516061607— 1699- 10071610161116121613161416151616161716181619162016211622= laravel.logA console [STAGING]Ix. AutovMlaycroundvSa jiminny ~027 49 A23 Х3 X 105 ^u.emailsarxt.owner_id FROM social_accounts saJOIN users u on U.id= sa.sociable idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE U.team_id = 556 and sa.provider = 'integration-app';select * from opportunities where id = 7594349;select * from opportunity_stages where opportunity_id = 7594349 order by created_at desc;select * from business_processes where id = 6024;select * from business_process_stages where stage_id = 16352;Iselect * from business process stages where business process id = 6024:Iselect * from stages where team id = 459select * from teams where id = 459;SELECTCONCAT(U.id, LASE WHEN U.id = t.owner_id THEN ' (owner)' ELSE "'END) AS user_id,u.email,sa.*,t.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idJOIN teams t 1.n<->1: on t.id = u.team_idWHERE u. team_id = 459 and sa.provider ='hubspot';SELECT os.stage_id, s.crm_provider_id, s.name, COUNT(*) as cntFROM opportunity_stages osJOIN stages s1..n<->1: ON s.id = os.stage_idWHERE os.opportunity_id = 7594349GROUP BY os.stage_id, s.crm_provider_id, s.nameORDER BY Cnt DESCHSELECT s.id, s.crm_provider_id, s.name, s.team_id, s.crm_configuration_idFROM stages sJOIN business_process_stages bps 1<-›1..n: ON bps.stage_id = s.idWHERE bps.business_process_id = 6024AND s.crm_provider_id ='sontractsent':select * from stages where id IN (16352,20612, 18281, 7344, 16378, 16309,5036, 15223, 14535, 6293, 12098,SELECT * FROM teams WHERE name LIKEselect * from playbooks where team_id = 472; # event 226147SELECT * FROM playbook_categories WHERE playbook_id = 5515;SELECT * FROM crm_fields WHERE id = 226147;SELECT * FROM crm_field_values WHERE crm_field_id = 226147;15138,raza.gilani@vuelio.com1627 V16281629163016311632163316341475SELECT * FROM crm_configurations WHERE id = 380;SELECTCONCAT(u.id, CASE WHEN U.id = t.owner_id THEN ' (owner)' ELSE "' END) AS user_id,u.email,sa.*,t.owner_id FROM social_accounts saJOIN users u on u.id = sa.sociable_idLnid teams Ti.n<->': on t.1d = 1.team 161WHERE u.team_id = 472 and sa.provider = 'salesforce':WCascade Code*•.Kick off a new project. Make changesduross you ente couebase,© Playbook Categories Import Debugging and Testing© Update Connection Logic@ Debugging Opportunity Stage OscillationsWhy is there group+ ‹› Codel§ AdaptiveW Windsur leams230:1ulr-o( 4 spaces...
|
NULL
|
|
29879
|
609
|
13
|
2026-04-15T14:50:08.531091+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-15/1776 /Users/lukas/.screenpipe/data/data/2026-04-15/1776264608531_m2.jpg...
|
Boosteroid
|
Boosteroid
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
157202043935671935177/200Imperial AgeClick a villa 157202043935671935177/200Imperial AgeClick a villager to gather wood from this tree.1 kovaliklukas: 42607/42607IV5 Magnus Olafsson: 41292/41292NV8 Almish Yiltawar: 34008/34008Rajyapala: 23275/23275NNV6 LÁszlố I: 12424/12424 € IVE Maximilian of Hababurg: 6531/6531 W IV3 HuaseÁm: 5931/50311 Lewig VI: 5792/5702IV...
|
NULL
|
8493559029497861722
|
NULL
|
visual_change
|
ocr
|
NULL
|
157202043935671935177/200Imperial AgeClick a villa 157202043935671935177/200Imperial AgeClick a villager to gather wood from this tree.1 kovaliklukas: 42607/42607IV5 Magnus Olafsson: 41292/41292NV8 Almish Yiltawar: 34008/34008Rajyapala: 23275/23275NNV6 LÁszlố I: 12424/12424 € IVE Maximilian of Hababurg: 6531/6531 W IV3 HuaseÁm: 5931/50311 Lewig VI: 5792/5702IV...
|
NULL
|
|
81323
|
2162
|
12
|
2026-04-25T16:13:34.827006+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-25/1777 /Users/lukas/.screenpipe/data/data/2026-04-25/1777133614827_m1.jpg...
|
Firefox
|
DXP4800PLUS-B5F8 — Personal
|
True
|
nas.lakylak.xyz/desktop/#/
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
DXP4800PLUS-B5F8
Inbox (7) - [EMAIL] - Gmail
(56) DXP4800PLUS-B5F8
Inbox (7) - [EMAIL] - Gmail
(56) Inbox | [EMAIL] | Proton Mail
Welcome back
Welcome back
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Today's Deals
Today's Deals
architecture - screenpipe docs
architecture - screenpipe docs
Claude Code works better when you stop treating it like a machine - [EMAIL] - Gmail
Claude Code works better when you stop treating it like a machine - [EMAIL] - Gmail
Screenpipe — Archive
Screenpipe — Archive
SQLite Web: archive.db
SQLite Web: archive.db
SQLite Web: db.sqlite
SQLite Web: db.sqlite
Claude Platform
Claude Platform
Hey @louis030195 Ill check during my - screenpipe.com
Hey @louis030195 Ill check during my - screenpipe.com
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
GLM 5.1 Thinks Strategically, Data-Center Revolt Intensifies, When Helpful LLMs Turn Unhelpful, Humanoid Robots Get to Work - [EMAIL] - Gmail
GLM 5.1 Thinks Strategically, Data-Center Revolt Intensifies, When Helpful LLMs Turn Unhelpful, Humanoid Robots Get to Work - [EMAIL] - Gmail
Gitea Official Website
Gitea Official Website
lakylak/screenpipe - screenpipe - Gitea: Git with a cup of tea
lakylak/screenpipe - screenpipe - Gitea: Git with a cup of tea
New Tab
New Tab
MikroTik · CRS304-4XG-IN
MikroTik · CRS304-4XG-IN
New Tab
Customize sidebar
Open Le Chat Mistral (⌃X)
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
851
B/s
4.4
KB/s
Files
Control Panel
Storage
App Center
Logs
Support
Task Manager
Universal Search
Music
Cloud Drives
Theater
Photos
Online Office
TextEdit
Virtual Machine
Downloads
DLNA
File Version Explorer
Security
Jellyfin-HT
SAN Manager
Vault
Snapshot
Comics
Sync & Backup
Control Panel
Search
Connection & Access
User Management
File Service
Device Connection
Domain/LDAP
Terminal
General
Hardware & Power
Time & Language
Network
Security
Indexing Service
Service
About
Update & Restore
Files
Personal Folder
Shared Folder
User Folder
Tag
Recycle Bin
User Folder
User Folder
Please enter
Name
Size
Type
Modification date
Tester...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"DXP4800PLUS-B5F8","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXRadioButton","text":"Inbox (7) - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"(56) Inbox | kovaliklukas@proton.me | Proton Mail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Welcome back","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Welcome back","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Today's Deals","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Today's Deals","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"architecture - screenpipe docs","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"architecture - screenpipe docs","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Claude Code works better when you stop treating it like a machine - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Claude Code works better when you stop treating it like a machine - kovaliklukas@gmail.com - Gmail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Screenpipe — Archive","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Screenpipe — Archive","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"SQLite Web: archive.db","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SQLite Web: archive.db","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"SQLite Web: db.sqlite","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SQLite Web: db.sqlite","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Claude Platform","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Claude Platform","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Hey @louis030195 Ill check during my - screenpipe.com","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Hey @louis030195 Ill check during my - screenpipe.com","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Gong Pricing in 2026: Costs, Plans & Is It Worth It?","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gong Pricing in 2026: Costs, Plans & Is It Worth It?","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"GLM 5.1 Thinks Strategically, Data-Center Revolt Intensifies, When Helpful LLMs Turn Unhelpful, Humanoid Robots Get to Work - kovaliklukas@gmail.com - Gmail","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"GLM 5.1 Thinks Strategically, Data-Center Revolt Intensifies, When Helpful LLMs Turn Unhelpful, Humanoid Robots Get to Work - kovaliklukas@gmail.com - Gmail","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Gitea Official Website","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gitea Official Website","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"lakylak/screenpipe - screenpipe - Gitea: Git with a cup of tea","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"lakylak/screenpipe - screenpipe - Gitea: Git with a cup of tea","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"New Tab","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"New Tab","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"MikroTik · CRS304-4XG-IN","depth":4,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"MikroTik · CRS304-4XG-IN","depth":5,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Le Chat Mistral (⌃X)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bitwarden","depth":6,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"851","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"B/s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"4.4","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"KB/s","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Files","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Control Panel","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Storage","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"App Center","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Logs","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Support","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Task Manager","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Universal Search","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Music","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Cloud Drives","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Theater","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Photos","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Online Office","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"TextEdit","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Virtual Machine","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Downloads","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"DLNA","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"File Version Explorer","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Security","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Jellyfin-HT","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"SAN Manager","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Vault","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Snapshot","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Comics","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync & Backup","depth":13,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Control Panel","depth":10,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Search","depth":15,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Connection & Access","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Management","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"File Service","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Device Connection","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Domain/LDAP","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Terminal","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"General","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Hardware & Power","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Time & Language","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Network","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Security","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Indexing Service","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Service","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"About","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Update & Restore","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"","depth":10,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":11,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Files","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Personal Folder","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Shared Folder","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Folder","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tag","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recycle Bin","depth":17,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":18,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Folder","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Folder","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":21,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXTextField","text":"Please enter","depth":19,"help_text":"","role_description":"text field","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"","depth":18,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":18,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":" ","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":19,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Name","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Size","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Type","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Modification date","depth":23,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Tester","depth":25,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8492556456075007164
|
5257175491883883279
|
idle
|
accessibility
|
NULL
|
DXP4800PLUS-B5F8
Inbox (7) - [EMAIL] - Gmail
(56) DXP4800PLUS-B5F8
Inbox (7) - [EMAIL] - Gmail
(56) Inbox | [EMAIL] | Proton Mail
Welcome back
Welcome back
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Western Digital Red Plus 3.5 6TB 5400rpm 256MB SATA3 (WD60EFPX) от 241,72 € (472,76 лв.) Вътрешен хард диск Western Digital - Pazaruvaj.com
Today's Deals
Today's Deals
architecture - screenpipe docs
architecture - screenpipe docs
Claude Code works better when you stop treating it like a machine - [EMAIL] - Gmail
Claude Code works better when you stop treating it like a machine - [EMAIL] - Gmail
Screenpipe — Archive
Screenpipe — Archive
SQLite Web: archive.db
SQLite Web: archive.db
SQLite Web: db.sqlite
SQLite Web: db.sqlite
Claude Platform
Claude Platform
Hey @louis030195 Ill check during my - screenpipe.com
Hey @louis030195 Ill check during my - screenpipe.com
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
GitHub - screenpipe/screenpipe: Run agents that work for you based on what you do. AI finally knows what you are doing · GitHub
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
Gong Pricing in 2026: Costs, Plans & Is It Worth It?
GLM 5.1 Thinks Strategically, Data-Center Revolt Intensifies, When Helpful LLMs Turn Unhelpful, Humanoid Robots Get to Work - [EMAIL] - Gmail
GLM 5.1 Thinks Strategically, Data-Center Revolt Intensifies, When Helpful LLMs Turn Unhelpful, Humanoid Robots Get to Work - [EMAIL] - Gmail
Gitea Official Website
Gitea Official Website
lakylak/screenpipe - screenpipe - Gitea: Git with a cup of tea
lakylak/screenpipe - screenpipe - Gitea: Git with a cup of tea
New Tab
New Tab
MikroTik · CRS304-4XG-IN
MikroTik · CRS304-4XG-IN
New Tab
Customize sidebar
Open Le Chat Mistral (⌃X)
Open history (⇧⌘H)
Open bookmarks (⌘B)
Bitwarden
851
B/s
4.4
KB/s
Files
Control Panel
Storage
App Center
Logs
Support
Task Manager
Universal Search
Music
Cloud Drives
Theater
Photos
Online Office
TextEdit
Virtual Machine
Downloads
DLNA
File Version Explorer
Security
Jellyfin-HT
SAN Manager
Vault
Snapshot
Comics
Sync & Backup
Control Panel
Search
Connection & Access
User Management
File Service
Device Connection
Domain/LDAP
Terminal
General
Hardware & Power
Time & Language
Network
Security
Indexing Service
Service
About
Update & Restore
Files
Personal Folder
Shared Folder
User Folder
Tag
Recycle Bin
User Folder
User Folder
Please enter
Name
Size
Type
Modification date
Tester...
|
81321
|
|
73409
|
1806
|
7
|
2026-04-23T07:33:11.947199+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776929591947_m1.jpg...
|
Finder
|
Work
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
2026
Today at 10:32
--
Folder
CleanShot 2026-04-23 at 09.45.36.mp4
Today at 10:32
1,74 GB
MPEG-4 movie
Daily 2026-04-22.mp4
Yesterday at 10:21
1,36 GB
MPEG-4 movie
Refinement 2026-04-06.mp4
21 Apr 2026 at 11:02
2,41 GB
MPEG-4 movie
Daily 2026-04-21.mp4
21 Apr 2026 at 10:00
567,8 MB
MPEG-4 movie
Refinement 2026-04-20.mp4
20 Apr 2026 at 16:56
4,25 GB
MPEG-4 movie
Daily 2026-04-20.mp4
20 Apr 2026 at 10:06
698,5 MB
MPEG-4 movie
Daily 2026-04-17.mp4
17 Apr 2026 at 10:16
1,16 GB
MPEG-4 movie
Daily 2026-04-16.mp4
16 Apr 2026 at 10:00
513,4 MB
MPEG-4 movie
Planning 2026-04-15.mp4
15 Apr 2026 at 11:14
2,75 GB
MPEG-4 movie
Retro 2026-04-14.mp4
14 Apr 2026 at 17:37
1,44 GB
MPEG-4 movie
Daily 2026-04-14.mp4
14 Apr 2026 at 10:09
924,4 MB
MPEG-4 movie
User pilot (Adi) 2026-04-09.mp4
9 Apr 2026 at 14:47
362,6 MB
MPEG-4 movie
Daily 2026-04-09.mp4
9 Apr 2026 at 10:07
748,8 MB
MPEG-4 movie
Daily 2026-04-08.mp4
8 Apr 2026 at 10:13
1,04 GB
MPEG-4 movie
Daily 2026-04-07.mp4
7 Apr 2026 at 10:01
575,5 MB
MPEG-4 movie
Daily 2026-04-06.mp4
6 Apr 2026 at 10:08
720,5 MB
MPEG-4 movie
Daily 2026-04-03.mp4
3 Apr 2026 at 10:21
1,02 GB
MPEG-4 movie
Planning 2026-04-01 & task split.mp4
1 Apr 2026 at 12:20
4,68 GB
MPEG-4 movie
Retro 2026-03-31.mp4
31 Mar 2026 at 18:29
3,4 GB
MPEG-4 movie
Daily 2026-03-31.mp4
31 Mar 2026 at 10:10
923,6 MB
MPEG-4 movie
Refinement 2026-03-30.mp4
30 Mar 2026 at 17:12
2,77 GB
MPEG-4 movie
Daily 2026-03-30.mp4
30 Mar 2026 at 10:05
641,8 MB
MPEG-4 movie
Daily 2026-03-27.mp4
27 Mar 2026 at 10:09
884,3 MB
MPEG-4 movie
Daily 2026-03-26.mp4
26 Mar 2026 at 9:59
476,6 MB
MPEG-4 movie
Daily 2026-03-24.mp4
24 Mar 2026 at 10:00
550,8 MB
MPEG-4 movie
Refinement 2026-03-23.mp4
23 Mar 2026 at 17:03
3,44 GB
MPEG-4 movie
Daily 2026-03-23.mp4
23 Mar 2026 at 10:00
438,9 MB
MPEG-4 movie
BE chapter 2026-03-20.mp4
20 Mar 2026 at 11:46
1,68 GB
MPEG-4 movie
Daily 2026-03-20.mp4
20 Mar 2026 at 10:06
430,4 MB
MPEG-4 movie
Planing 2026-03-18-converted.mp4
19 Mar 2026 at 12:01
2,38 GB
MPEG-4 movie
Refinement 2026-02-09-converted.mp4
19 Mar 2026 at 11:35
2,26 GB
MPEG-4 movie
Daily 2026-03-19.mp4
19 Mar 2026 at 9:57
386,3 MB
MPEG-4 movie
Review 2026-03-18.mp4
18 Mar 2026 at 16:20
705,8 MB
MPEG-4 movie
Planing 2026-03-18.mp4
18 Mar 2026 at 11:14
2,78 GB
MPEG-4 movie
Retro 2026-03-17.mp4
17 Mar 2026 at 17:40
1,53 GB
MPEG-4 movie
Daily 2026-03-17.mp4
17 Mar 2026 at 10:18
1,2 GB
MPEG-4 movie
Refinement 2026-03-16.mp4
16 Mar 2026 at 16:55
4,19 GB
MPEG-4 movie
Daily 2026-03-16.mp4
16 Mar 2026 at 10:02
592,2 MB
MPEG-4 movie
Daily 2026-03-13.mp4
13 Mar 2026 at 10:12
1,02 GB
MPEG-4 movie
1-1 2026-03-12.mp4
12 Mar 2026 at 18:35
637,6 MB
MPEG-4 movie
Daily 2026-03-12.mp4
12 Mar 2026 at 10:10
978,7 MB
MPEG-4 movie
Daily 2026-03-11.mp4
11 Mar 2026 at 10:06
798,7 MB
MPEG-4 movie
Daily 2026-03-10.mp4
10 Mar 2026 at 9:57
404,6 MB
MPEG-4 movie
Refinement 2026-03-09.mp4
9 Mar 2026 at 17:04
4,16 GB
MPEG-4 movie
Daily 2026-03-09.mp4
9 Mar 2026 at 9:56
319,7 MB
MPEG-4 movie
Daily 2026-03-06.mp4
6 Mar 2026 at 9:57
291,7 MB
MPEG-4 movie
Planning 2026-03-04.mp4
4 Mar 2026 at 11:09
2,62 GB
MPEG-4 movie
Daily 2026-03-02.mp4
2 Mar 2026 at 10:07
768,5 MB
MPEG-4 movie
Daily 2026-02-27.mp4
27 Feb 2026 at 10:02
546,8 MB
MPEG-4 movie
Daily 2026-02-26.mov
26 Feb 2026 at 9:53
96,6 MB
QT movie
Daily 2026-02-25.mov
25 Feb 2026 at 9:59
503,5 MB
QT movie
Opportunity-Contacts 2026-02-24.mp4
24 Feb 2026 at 12:03
791,7 MB
MPEG-4 movie
Daily 2026-02-24.mp4
24 Feb 2026 at 10:02
520,7 MB
MPEG-4 movie
Refinement 2026-02-23.mov
23 Feb 2026 at 16:31
2 GB
QT movie
Daily 2026-02-20 & Ani.mp4
20 Feb 2026 at 10:53
2,52 GB
MPEG-4 movie
Daily 2026-02-19.mp4
19 Feb 2026 at 9:54
234,2 MB
MPEG-4 movie
Review 2026-02-18.mp4
18 Feb 2026 at 16:25
925,1 MB
MPEG-4 movie
Planning 2026-02-18.mp4
18 Feb 2026 at 10:57
2,04 GB
MPEG-4 movie
Retro 2026-02-17.mp4
17 Feb 2026 at 17:38
1,31 GB
MPEG-4 movie
Refinement & P1 debugging 2026-02-16.mp4
16 Feb 2026 at 17:36
4,53 GB
MPEG-4 movie
SyncObjects3 2026-02-16.mp4
16 Feb 2026 at 15:10
1,42 GB
MPEG-4 movie
SyncObjects2 2026-02-16.mp4...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Favourites","depth":6,"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"jiminny","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"AirDrop","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Recents","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Applications","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Documents","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Downloads","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"iCloud","depth":6,"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"iCloud Drive","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Sync folder","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Locations","depth":6,"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"DXP4800PLUS-B5F","depth":6,"role_description":"text"},{"role":"AXButton","text":"Eject","depth":6,"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Network","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Tags","depth":6,"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"CRM","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Orange","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Red","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Yellow","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Green","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Blue","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Purple","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"All Tags…","depth":6,"role_description":"text"},{"role":"AXStaticText","text":"Name","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Date Modified","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Size","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Kind","depth":7,"role_description":"text"},{"role":"AXTextField","text":"2026","depth":7,"value":"2026","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Today at 10:32","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"--","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":7,"role_description":"text"},{"role":"AXTextField","text":"CleanShot 2026-04-23 at 09.45.36.mp4","depth":7,"value":"CleanShot 2026-04-23 at 09.45.36.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Today at 10:32","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,74 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-22.mp4","depth":7,"value":"Daily 2026-04-22.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Yesterday at 10:21","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,36 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-04-06.mp4","depth":7,"value":"Refinement 2026-04-06.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"21 Apr 2026 at 11:02","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,41 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-21.mp4","depth":7,"value":"Daily 2026-04-21.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"21 Apr 2026 at 10:00","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"567,8 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-04-20.mp4","depth":7,"value":"Refinement 2026-04-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Apr 2026 at 16:56","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"4,25 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-20.mp4","depth":7,"value":"Daily 2026-04-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Apr 2026 at 10:06","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"698,5 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-17.mp4","depth":7,"value":"Daily 2026-04-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Apr 2026 at 10:16","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,16 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-16.mp4","depth":7,"value":"Daily 2026-04-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Apr 2026 at 10:00","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"513,4 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-04-15.mp4","depth":7,"value":"Planning 2026-04-15.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"15 Apr 2026 at 11:14","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,75 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-04-14.mp4","depth":7,"value":"Retro 2026-04-14.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"14 Apr 2026 at 17:37","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,44 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-14.mp4","depth":7,"value":"Daily 2026-04-14.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"14 Apr 2026 at 10:09","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"924,4 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"User pilot (Adi) 2026-04-09.mp4","depth":7,"value":"User pilot (Adi) 2026-04-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Apr 2026 at 14:47","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"362,6 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-09.mp4","depth":7,"value":"Daily 2026-04-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Apr 2026 at 10:07","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"748,8 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-08.mp4","depth":7,"value":"Daily 2026-04-08.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"8 Apr 2026 at 10:13","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,04 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-07.mp4","depth":7,"value":"Daily 2026-04-07.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"7 Apr 2026 at 10:01","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"575,5 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-06.mp4","depth":7,"value":"Daily 2026-04-06.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"6 Apr 2026 at 10:08","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"720,5 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-03.mp4","depth":7,"value":"Daily 2026-04-03.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3 Apr 2026 at 10:21","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,02 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-04-01 & task split.mp4","depth":7,"value":"Planning 2026-04-01 & task split.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1 Apr 2026 at 12:20","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"4,68 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-03-31.mp4","depth":7,"value":"Retro 2026-03-31.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"31 Mar 2026 at 18:29","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"3,4 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-31.mp4","depth":7,"value":"Daily 2026-03-31.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"31 Mar 2026 at 10:10","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"923,6 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-30.mp4","depth":7,"value":"Refinement 2026-03-30.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"30 Mar 2026 at 17:12","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,77 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-30.mp4","depth":7,"value":"Daily 2026-03-30.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"30 Mar 2026 at 10:05","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"641,8 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-27.mp4","depth":7,"value":"Daily 2026-03-27.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27 Mar 2026 at 10:09","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"884,3 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-26.mp4","depth":7,"value":"Daily 2026-03-26.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"26 Mar 2026 at 9:59","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"476,6 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-24.mp4","depth":7,"value":"Daily 2026-03-24.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"24 Mar 2026 at 10:00","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"550,8 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-23.mp4","depth":7,"value":"Refinement 2026-03-23.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"23 Mar 2026 at 17:03","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"3,44 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-23.mp4","depth":7,"value":"Daily 2026-03-23.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"23 Mar 2026 at 10:00","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"438,9 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"BE chapter 2026-03-20.mp4","depth":7,"value":"BE chapter 2026-03-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Mar 2026 at 11:46","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,68 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-20.mp4","depth":7,"value":"Daily 2026-03-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Mar 2026 at 10:06","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"430,4 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Planing 2026-03-18-converted.mp4","depth":7,"value":"Planing 2026-03-18-converted.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Mar 2026 at 12:01","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,38 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-02-09-converted.mp4","depth":7,"value":"Refinement 2026-02-09-converted.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Mar 2026 at 11:35","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,26 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-19.mp4","depth":7,"value":"Daily 2026-03-19.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Mar 2026 at 9:57","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"386,3 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Review 2026-03-18.mp4","depth":7,"value":"Review 2026-03-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Mar 2026 at 16:20","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"705,8 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Planing 2026-03-18.mp4","depth":7,"value":"Planing 2026-03-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Mar 2026 at 11:14","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,78 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-03-17.mp4","depth":7,"value":"Retro 2026-03-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Mar 2026 at 17:40","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,53 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-17.mp4","depth":7,"value":"Daily 2026-03-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Mar 2026 at 10:18","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,2 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-16.mp4","depth":7,"value":"Refinement 2026-03-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Mar 2026 at 16:55","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"4,19 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-16.mp4","depth":7,"value":"Daily 2026-03-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Mar 2026 at 10:02","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"592,2 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-13.mp4","depth":7,"value":"Daily 2026-03-13.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"13 Mar 2026 at 10:12","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,02 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"1-1 2026-03-12.mp4","depth":7,"value":"1-1 2026-03-12.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"12 Mar 2026 at 18:35","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"637,6 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-12.mp4","depth":7,"value":"Daily 2026-03-12.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"12 Mar 2026 at 10:10","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"978,7 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-11.mp4","depth":7,"value":"Daily 2026-03-11.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"11 Mar 2026 at 10:06","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"798,7 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-10.mp4","depth":7,"value":"Daily 2026-03-10.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"10 Mar 2026 at 9:57","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"404,6 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-09.mp4","depth":7,"value":"Refinement 2026-03-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Mar 2026 at 17:04","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"4,16 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-09.mp4","depth":7,"value":"Daily 2026-03-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Mar 2026 at 9:56","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"319,7 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-06.mp4","depth":7,"value":"Daily 2026-03-06.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"6 Mar 2026 at 9:57","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"291,7 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-03-04.mp4","depth":7,"value":"Planning 2026-03-04.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4 Mar 2026 at 11:09","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,62 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-02.mp4","depth":7,"value":"Daily 2026-03-02.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2 Mar 2026 at 10:07","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"768,5 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-27.mp4","depth":7,"value":"Daily 2026-02-27.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27 Feb 2026 at 10:02","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"546,8 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-26.mov","depth":7,"value":"Daily 2026-02-26.mov","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"26 Feb 2026 at 9:53","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"96,6 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"QT movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-25.mov","depth":7,"value":"Daily 2026-02-25.mov","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"25 Feb 2026 at 9:59","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"503,5 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"QT movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Opportunity-Contacts 2026-02-24.mp4","depth":7,"value":"Opportunity-Contacts 2026-02-24.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"24 Feb 2026 at 12:03","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"791,7 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-24.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.0,"width":0.10277778,"height":0.017777778},"value":"Daily 2026-02-24.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"24 Feb 2026 at 10:02","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"520,7 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-02-23.mov","depth":7,"bounds":{"left":0.6631944,"top":0.0,"width":0.12916666,"height":0.017777778},"value":"Refinement 2026-02-23.mov","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"23 Feb 2026 at 16:31","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"QT movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-20 & Ani.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.0,"width":0.12777779,"height":0.017777778},"value":"Daily 2026-02-20 & Ani.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Feb 2026 at 10:53","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,52 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-19.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.0,"width":0.10208333,"height":0.017777778},"value":"Daily 2026-02-19.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Feb 2026 at 9:54","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"234,2 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Review 2026-02-18.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.0,"width":0.11111111,"height":0.017777778},"value":"Review 2026-02-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Feb 2026 at 16:25","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"925,1 MB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-02-18.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.02111111,"width":0.11736111,"height":0.017777778},"value":"Planning 2026-02-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Feb 2026 at 10:57","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"2,04 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-02-17.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.043333333,"width":0.11736111,"height":0.017777778},"value":"Retro 2026-02-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Feb 2026 at 17:38","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,31 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"Refinement & P1 debugging 2026-02-16.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.06555556,"width":0.10208333,"height":0.017777778},"value":"Refinement & P1 debugging 2026-02-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Feb 2026 at 17:36","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"4,53 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"SyncObjects3 2026-02-16.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.08777778,"width":0.19791667,"height":0.017777778},"value":"SyncObjects3 2026-02-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Feb 2026 at 15:10","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"1,42 GB","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"role_description":"text"},{"role":"AXTextField","text":"SyncObjects2 2026-02-16.mp4","depth":7,"bounds":{"left":0.6631944,"top":0.11,"width":0.14027777,"height":0.017777778},"value":"SyncObjects2 2026-02-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
8492372441560665975
|
-2910781458352528949
|
visual_change
|
accessibility
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
2026
Today at 10:32
--
Folder
CleanShot 2026-04-23 at 09.45.36.mp4
Today at 10:32
1,74 GB
MPEG-4 movie
Daily 2026-04-22.mp4
Yesterday at 10:21
1,36 GB
MPEG-4 movie
Refinement 2026-04-06.mp4
21 Apr 2026 at 11:02
2,41 GB
MPEG-4 movie
Daily 2026-04-21.mp4
21 Apr 2026 at 10:00
567,8 MB
MPEG-4 movie
Refinement 2026-04-20.mp4
20 Apr 2026 at 16:56
4,25 GB
MPEG-4 movie
Daily 2026-04-20.mp4
20 Apr 2026 at 10:06
698,5 MB
MPEG-4 movie
Daily 2026-04-17.mp4
17 Apr 2026 at 10:16
1,16 GB
MPEG-4 movie
Daily 2026-04-16.mp4
16 Apr 2026 at 10:00
513,4 MB
MPEG-4 movie
Planning 2026-04-15.mp4
15 Apr 2026 at 11:14
2,75 GB
MPEG-4 movie
Retro 2026-04-14.mp4
14 Apr 2026 at 17:37
1,44 GB
MPEG-4 movie
Daily 2026-04-14.mp4
14 Apr 2026 at 10:09
924,4 MB
MPEG-4 movie
User pilot (Adi) 2026-04-09.mp4
9 Apr 2026 at 14:47
362,6 MB
MPEG-4 movie
Daily 2026-04-09.mp4
9 Apr 2026 at 10:07
748,8 MB
MPEG-4 movie
Daily 2026-04-08.mp4
8 Apr 2026 at 10:13
1,04 GB
MPEG-4 movie
Daily 2026-04-07.mp4
7 Apr 2026 at 10:01
575,5 MB
MPEG-4 movie
Daily 2026-04-06.mp4
6 Apr 2026 at 10:08
720,5 MB
MPEG-4 movie
Daily 2026-04-03.mp4
3 Apr 2026 at 10:21
1,02 GB
MPEG-4 movie
Planning 2026-04-01 & task split.mp4
1 Apr 2026 at 12:20
4,68 GB
MPEG-4 movie
Retro 2026-03-31.mp4
31 Mar 2026 at 18:29
3,4 GB
MPEG-4 movie
Daily 2026-03-31.mp4
31 Mar 2026 at 10:10
923,6 MB
MPEG-4 movie
Refinement 2026-03-30.mp4
30 Mar 2026 at 17:12
2,77 GB
MPEG-4 movie
Daily 2026-03-30.mp4
30 Mar 2026 at 10:05
641,8 MB
MPEG-4 movie
Daily 2026-03-27.mp4
27 Mar 2026 at 10:09
884,3 MB
MPEG-4 movie
Daily 2026-03-26.mp4
26 Mar 2026 at 9:59
476,6 MB
MPEG-4 movie
Daily 2026-03-24.mp4
24 Mar 2026 at 10:00
550,8 MB
MPEG-4 movie
Refinement 2026-03-23.mp4
23 Mar 2026 at 17:03
3,44 GB
MPEG-4 movie
Daily 2026-03-23.mp4
23 Mar 2026 at 10:00
438,9 MB
MPEG-4 movie
BE chapter 2026-03-20.mp4
20 Mar 2026 at 11:46
1,68 GB
MPEG-4 movie
Daily 2026-03-20.mp4
20 Mar 2026 at 10:06
430,4 MB
MPEG-4 movie
Planing 2026-03-18-converted.mp4
19 Mar 2026 at 12:01
2,38 GB
MPEG-4 movie
Refinement 2026-02-09-converted.mp4
19 Mar 2026 at 11:35
2,26 GB
MPEG-4 movie
Daily 2026-03-19.mp4
19 Mar 2026 at 9:57
386,3 MB
MPEG-4 movie
Review 2026-03-18.mp4
18 Mar 2026 at 16:20
705,8 MB
MPEG-4 movie
Planing 2026-03-18.mp4
18 Mar 2026 at 11:14
2,78 GB
MPEG-4 movie
Retro 2026-03-17.mp4
17 Mar 2026 at 17:40
1,53 GB
MPEG-4 movie
Daily 2026-03-17.mp4
17 Mar 2026 at 10:18
1,2 GB
MPEG-4 movie
Refinement 2026-03-16.mp4
16 Mar 2026 at 16:55
4,19 GB
MPEG-4 movie
Daily 2026-03-16.mp4
16 Mar 2026 at 10:02
592,2 MB
MPEG-4 movie
Daily 2026-03-13.mp4
13 Mar 2026 at 10:12
1,02 GB
MPEG-4 movie
1-1 2026-03-12.mp4
12 Mar 2026 at 18:35
637,6 MB
MPEG-4 movie
Daily 2026-03-12.mp4
12 Mar 2026 at 10:10
978,7 MB
MPEG-4 movie
Daily 2026-03-11.mp4
11 Mar 2026 at 10:06
798,7 MB
MPEG-4 movie
Daily 2026-03-10.mp4
10 Mar 2026 at 9:57
404,6 MB
MPEG-4 movie
Refinement 2026-03-09.mp4
9 Mar 2026 at 17:04
4,16 GB
MPEG-4 movie
Daily 2026-03-09.mp4
9 Mar 2026 at 9:56
319,7 MB
MPEG-4 movie
Daily 2026-03-06.mp4
6 Mar 2026 at 9:57
291,7 MB
MPEG-4 movie
Planning 2026-03-04.mp4
4 Mar 2026 at 11:09
2,62 GB
MPEG-4 movie
Daily 2026-03-02.mp4
2 Mar 2026 at 10:07
768,5 MB
MPEG-4 movie
Daily 2026-02-27.mp4
27 Feb 2026 at 10:02
546,8 MB
MPEG-4 movie
Daily 2026-02-26.mov
26 Feb 2026 at 9:53
96,6 MB
QT movie
Daily 2026-02-25.mov
25 Feb 2026 at 9:59
503,5 MB
QT movie
Opportunity-Contacts 2026-02-24.mp4
24 Feb 2026 at 12:03
791,7 MB
MPEG-4 movie
Daily 2026-02-24.mp4
24 Feb 2026 at 10:02
520,7 MB
MPEG-4 movie
Refinement 2026-02-23.mov
23 Feb 2026 at 16:31
2 GB
QT movie
Daily 2026-02-20 & Ani.mp4
20 Feb 2026 at 10:53
2,52 GB
MPEG-4 movie
Daily 2026-02-19.mp4
19 Feb 2026 at 9:54
234,2 MB
MPEG-4 movie
Review 2026-02-18.mp4
18 Feb 2026 at 16:25
925,1 MB
MPEG-4 movie
Planning 2026-02-18.mp4
18 Feb 2026 at 10:57
2,04 GB
MPEG-4 movie
Retro 2026-02-17.mp4
17 Feb 2026 at 17:38
1,31 GB
MPEG-4 movie
Refinement & P1 debugging 2026-02-16.mp4
16 Feb 2026 at 17:36
4,53 GB
MPEG-4 movie
SyncObjects3 2026-02-16.mp4
16 Feb 2026 at 15:10
1,42 GB
MPEG-4 movie
SyncObjects2 2026-02-16.mp4...
|
73407
|
|
73444
|
1811
|
0
|
2026-04-23T07:41:39.386403+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776930099386_m2.jpg...
|
Finder
|
Work
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
2026
Today at 10:32
--
Folder
CleanShot 2026-04-23 at 09.45.36.mp4
Today at 10:32
1,74 GB
MPEG-4 movie
Daily 2026-04-22.mp4
Yesterday at 10:21
1,36 GB
MPEG-4 movie
Refinement 2026-04-06.mp4
21 Apr 2026 at 11:02
2,41 GB
MPEG-4 movie
Daily 2026-04-21.mp4
21 Apr 2026 at 10:00
567,8 MB
MPEG-4 movie
Refinement 2026-04-20.mp4
20 Apr 2026 at 16:56
4,25 GB
MPEG-4 movie
Daily 2026-04-20.mp4
20 Apr 2026 at 10:06
698,5 MB
MPEG-4 movie
Daily 2026-04-17.mp4
17 Apr 2026 at 10:16
1,16 GB
MPEG-4 movie
Daily 2026-04-16.mp4
16 Apr 2026 at 10:00
513,4 MB
MPEG-4 movie
Planning 2026-04-15.mp4
15 Apr 2026 at 11:14
2,75 GB
MPEG-4 movie
Retro 2026-04-14.mp4
14 Apr 2026 at 17:37
1,44 GB
MPEG-4 movie
Daily 2026-04-14.mp4
14 Apr 2026 at 10:09
924,4 MB
MPEG-4 movie
User pilot (Adi) 2026-04-09.mp4
9 Apr 2026 at 14:47
362,6 MB
MPEG-4 movie
Daily 2026-04-09.mp4
9 Apr 2026 at 10:07
748,8 MB
MPEG-4 movie
Daily 2026-04-08.mp4
8 Apr 2026 at 10:13
1,04 GB
MPEG-4 movie
Daily 2026-04-07.mp4
7 Apr 2026 at 10:01
575,5 MB
MPEG-4 movie
Daily 2026-04-06.mp4
6 Apr 2026 at 10:08
720,5 MB
MPEG-4 movie
Daily 2026-04-03.mp4
3 Apr 2026 at 10:21
1,02 GB
MPEG-4 movie
Planning 2026-04-01 & task split.mp4
1 Apr 2026 at 12:20
4,68 GB
MPEG-4 movie
Retro 2026-03-31.mp4
31 Mar 2026 at 18:29
3,4 GB
MPEG-4 movie
Daily 2026-03-31.mp4
31 Mar 2026 at 10:10
923,6 MB
MPEG-4 movie
Refinement 2026-03-30.mp4
30 Mar 2026 at 17:12
2,77 GB
MPEG-4 movie
Daily 2026-03-30.mp4
30 Mar 2026 at 10:05
641,8 MB
MPEG-4 movie
Daily 2026-03-27.mp4
27 Mar 2026 at 10:09
884,3 MB
MPEG-4 movie
Daily 2026-03-26.mp4
26 Mar 2026 at 9:59
476,6 MB
MPEG-4 movie
Daily 2026-03-24.mp4
24 Mar 2026 at 10:00
550,8 MB
MPEG-4 movie
Refinement 2026-03-23.mp4
23 Mar 2026 at 17:03
3,44 GB
MPEG-4 movie
Daily 2026-03-23.mp4
23 Mar 2026 at 10:00
438,9 MB
MPEG-4 movie
BE chapter 2026-03-20.mp4
20 Mar 2026 at 11:46
1,68 GB
MPEG-4 movie
Daily 2026-03-20.mp4
20 Mar 2026 at 10:06
430,4 MB
MPEG-4 movie
Planing 2026-03-18-converted.mp4
19 Mar 2026 at 12:01
2,38 GB
MPEG-4 movie
Refinement 2026-02-09-converted.mp4
19 Mar 2026 at 11:35
2,26 GB
MPEG-4 movie
Daily 2026-03-19.mp4
19 Mar 2026 at 9:57
386,3 MB
MPEG-4 movie
Review 2026-03-18.mp4
18 Mar 2026 at 16:20
705,8 MB
MPEG-4 movie
Planing 2026-03-18.mp4
18 Mar 2026 at 11:14
2,78 GB
MPEG-4 movie
Retro 2026-03-17.mp4
17 Mar 2026 at 17:40
1,53 GB
MPEG-4 movie
Daily 2026-03-17.mp4
17 Mar 2026 at 10:18
1,2 GB
MPEG-4 movie
Refinement 2026-03-16.mp4
16 Mar 2026 at 16:55
4,19 GB
MPEG-4 movie
Daily 2026-03-16.mp4
16 Mar 2026 at 10:02
592,2 MB
MPEG-4 movie
Daily 2026-03-13.mp4
13 Mar 2026 at 10:12
1,02 GB
MPEG-4 movie
1-1 2026-03-12.mp4
12 Mar 2026 at 18:35
637,6 MB
MPEG-4 movie
Daily 2026-03-12.mp4
12 Mar 2026 at 10:10
978,7 MB
MPEG-4 movie
Daily 2026-03-11.mp4
11 Mar 2026 at 10:06
798,7 MB
MPEG-4 movie
Daily 2026-03-10.mp4
10 Mar 2026 at 9:57
404,6 MB
MPEG-4 movie
Refinement 2026-03-09.mp4
9 Mar 2026 at 17:04
4,16 GB
MPEG-4 movie
Daily 2026-03-09.mp4
9 Mar 2026 at 9:56
319,7 MB
MPEG-4 movie
Daily 2026-03-06.mp4
6 Mar 2026 at 9:57
291,7 MB
MPEG-4 movie
Planning 2026-03-04.mp4
4 Mar 2026 at 11:09
2,62 GB
MPEG-4 movie
Daily 2026-03-02.mp4
2 Mar 2026 at 10:07
768,5 MB
MPEG-4 movie
Daily 2026-02-27.mp4
27 Feb 2026 at 10:02
546,8 MB
MPEG-4 movie
Daily 2026-02-26.mov
26 Feb 2026 at 9:53
96,6 MB
QT movie
Daily 2026-02-25.mov
25 Feb 2026 at 9:59
503,5 MB
QT movie
Opportunity-Contacts 2026-02-24.mp4
24 Feb 2026 at 12:03
791,7 MB
MPEG-4 movie
Daily 2026-02-24.mp4
24 Feb 2026 at 10:02
520,7 MB
MPEG-4 movie
Refinement 2026-02-23.mov
23 Feb 2026 at 16:31
2 GB
QT movie
Daily 2026-02-20 & Ani.mp4
20 Feb 2026 at 10:53
2,52 GB
MPEG-4 movie
Daily 2026-02-19.mp4
19 Feb 2026 at 9:54
234,2 MB
MPEG-4 movie
Review 2026-02-18.mp4
18 Feb 2026 at 16:25
925,1 MB
MPEG-4 movie
Planning 2026-02-18.mp4
18 Feb 2026 at 10:57
2,04 GB
MPEG-4 movie
Retro 2026-02-17.mp4
17 Feb 2026 at 17:38
1,31 GB
MPEG-4 movie
Refinement & P1 debugging 2026-02-16.mp4
16 Feb 2026 at 17:36
4,53 GB
MPEG-4 movie
SyncObjects3 2026-02-16.mp4
16 Feb 2026 at 15:10
1,42 GB
MPEG-4 movie
SyncObjects2 2026-02-16.mp4...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Favourites","depth":6,"bounds":{"left":0.5046542,"top":0.061452515,"width":0.06216755,"height":0.015163607},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"jiminny","depth":6,"bounds":{"left":0.51263297,"top":0.08140463,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"AirDrop","depth":6,"bounds":{"left":0.51263297,"top":0.103751,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Recents","depth":6,"bounds":{"left":0.51263297,"top":0.12609737,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Applications","depth":6,"bounds":{"left":0.51263297,"top":0.14844373,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Documents","depth":6,"bounds":{"left":0.51263297,"top":0.1707901,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Downloads","depth":6,"bounds":{"left":0.51263297,"top":0.19313647,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"lukas","depth":6,"bounds":{"left":0.51263297,"top":0.21548285,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"iCloud","depth":6,"bounds":{"left":0.5046542,"top":0.2434158,"width":0.06216755,"height":0.015163607},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"iCloud Drive","depth":6,"bounds":{"left":0.51263297,"top":0.26336792,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Sync folder","depth":6,"bounds":{"left":0.51263297,"top":0.2857143,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Locations","depth":6,"bounds":{"left":0.5046542,"top":0.31364724,"width":0.06216755,"height":0.015163607},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"DXP4800PLUS-B5F","depth":6,"bounds":{"left":0.51263297,"top":0.33359936,"width":0.043218084,"height":0.012769354},"role_description":"text"},{"role":"AXButton","text":"Eject","depth":6,"bounds":{"left":0.55651593,"top":0.33519554,"width":0.0043218085,"height":0.009577015},"role_description":"button","is_enabled":true,"is_focused":false},{"role":"AXStaticText","text":"Network","depth":6,"bounds":{"left":0.51263297,"top":0.35594574,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Tags","depth":6,"bounds":{"left":0.5046542,"top":0.38387868,"width":0.06216755,"height":0.015163607},"automation_id":"xSidebarHeader","role_description":"text"},{"role":"AXStaticText","text":"CRM","depth":6,"bounds":{"left":0.51263297,"top":0.4038308,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Orange","depth":6,"bounds":{"left":0.51263297,"top":0.42617717,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Red","depth":6,"bounds":{"left":0.51263297,"top":0.44852355,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Yellow","depth":6,"bounds":{"left":0.51263297,"top":0.4708699,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Green","depth":6,"bounds":{"left":0.51263297,"top":0.49321628,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Blue","depth":6,"bounds":{"left":0.51263297,"top":0.51556265,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Purple","depth":6,"bounds":{"left":0.51263297,"top":0.53790903,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"All Tags…","depth":6,"bounds":{"left":0.51263297,"top":0.5602554,"width":0.049534574,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Name","depth":7,"bounds":{"left":0.5827792,"top":0.06624102,"width":0.011635638,"height":0.011173184},"role_description":"text"},{"role":"AXStaticText","text":"Date Modified","depth":7,"bounds":{"left":0.8656915,"top":0.06624102,"width":0.026928192,"height":0.011173184},"role_description":"text"},{"role":"AXStaticText","text":"Size","depth":7,"bounds":{"left":0.92586434,"top":0.06624102,"width":0.008976064,"height":0.011173184},"role_description":"text"},{"role":"AXStaticText","text":"Kind","depth":7,"bounds":{"left":0.9581117,"top":0.06624102,"width":0.00930851,"height":0.011173184},"role_description":"text"},{"role":"AXTextField","text":"2026","depth":7,"bounds":{"left":0.5827792,"top":0.08938547,"width":0.013297873,"height":0.012769354},"value":"2026","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Today at 10:32","depth":7,"bounds":{"left":0.8656915,"top":0.08938547,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"--","depth":7,"bounds":{"left":0.9494681,"top":0.08938547,"width":0.0056515955,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"Folder","depth":7,"bounds":{"left":0.9581117,"top":0.08938547,"width":0.014295213,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"CleanShot 2026-04-23 at 09.45.36.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.105347164,"width":0.084773935,"height":0.012769354},"value":"CleanShot 2026-04-23 at 09.45.36.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Today at 10:32","depth":7,"bounds":{"left":0.8656915,"top":0.105347164,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,74 GB","depth":7,"bounds":{"left":0.9368351,"top":0.105347164,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.105347164,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-22.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.121308856,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-04-22.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Yesterday at 10:21","depth":7,"bounds":{"left":0.8656915,"top":0.121308856,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,36 GB","depth":7,"bounds":{"left":0.9368351,"top":0.121308856,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.121308856,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-04-06.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.13727055,"width":0.0625,"height":0.012769354},"value":"Refinement 2026-04-06.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"21 Apr 2026 at 11:02","depth":7,"bounds":{"left":0.8656915,"top":0.13727055,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,41 GB","depth":7,"bounds":{"left":0.9368351,"top":0.13727055,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.13727055,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-21.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.15323225,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-04-21.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"21 Apr 2026 at 10:00","depth":7,"bounds":{"left":0.8656915,"top":0.15323225,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"567,8 MB","depth":7,"bounds":{"left":0.93351066,"top":0.15323225,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.15323225,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-04-20.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.16919394,"width":0.0625,"height":0.012769354},"value":"Refinement 2026-04-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Apr 2026 at 16:56","depth":7,"bounds":{"left":0.8656915,"top":0.16919394,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"4,25 GB","depth":7,"bounds":{"left":0.9368351,"top":0.16919394,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.16919394,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-20.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.18515563,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-04-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Apr 2026 at 10:06","depth":7,"bounds":{"left":0.8656915,"top":0.18515563,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"698,5 MB","depth":7,"bounds":{"left":0.93351066,"top":0.18515563,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.18515563,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-17.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.20111732,"width":0.048204787,"height":0.012769354},"value":"Daily 2026-04-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Apr 2026 at 10:16","depth":7,"bounds":{"left":0.8656915,"top":0.20111732,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,16 GB","depth":7,"bounds":{"left":0.9368351,"top":0.20111732,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.20111732,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-16.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.21707901,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-04-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Apr 2026 at 10:00","depth":7,"bounds":{"left":0.8656915,"top":0.21707901,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"513,4 MB","depth":7,"bounds":{"left":0.93351066,"top":0.21707901,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.21707901,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-04-15.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.2330407,"width":0.05618351,"height":0.012769354},"value":"Planning 2026-04-15.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"15 Apr 2026 at 11:14","depth":7,"bounds":{"left":0.8656915,"top":0.2330407,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,75 GB","depth":7,"bounds":{"left":0.9368351,"top":0.2330407,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.2330407,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-04-14.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.2490024,"width":0.049867023,"height":0.012769354},"value":"Retro 2026-04-14.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"14 Apr 2026 at 17:37","depth":7,"bounds":{"left":0.8656915,"top":0.2490024,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,44 GB","depth":7,"bounds":{"left":0.9368351,"top":0.2490024,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.2490024,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-14.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.26496407,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-04-14.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"14 Apr 2026 at 10:09","depth":7,"bounds":{"left":0.8656915,"top":0.26496407,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"924,4 MB","depth":7,"bounds":{"left":0.93351066,"top":0.26496407,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.26496407,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"User pilot (Adi) 2026-04-09.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.28092578,"width":0.07014628,"height":0.012769354},"value":"User pilot (Adi) 2026-04-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Apr 2026 at 14:47","depth":7,"bounds":{"left":0.8656915,"top":0.28092578,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"362,6 MB","depth":7,"bounds":{"left":0.93351066,"top":0.28092578,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.28092578,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-09.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.29688746,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-04-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Apr 2026 at 10:07","depth":7,"bounds":{"left":0.8656915,"top":0.29688746,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"748,8 MB","depth":7,"bounds":{"left":0.93351066,"top":0.29688746,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.29688746,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-08.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.31284916,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-04-08.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"8 Apr 2026 at 10:13","depth":7,"bounds":{"left":0.8656915,"top":0.31284916,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,04 GB","depth":7,"bounds":{"left":0.9368351,"top":0.31284916,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.31284916,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-07.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.32881084,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-04-07.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"7 Apr 2026 at 10:01","depth":7,"bounds":{"left":0.8656915,"top":0.32881084,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"575,5 MB","depth":7,"bounds":{"left":0.93351066,"top":0.32881084,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.32881084,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-06.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.34477255,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-04-06.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"6 Apr 2026 at 10:08","depth":7,"bounds":{"left":0.8656915,"top":0.34477255,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"720,5 MB","depth":7,"bounds":{"left":0.93351066,"top":0.34477255,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.34477255,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-04-03.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.36073422,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-04-03.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"3 Apr 2026 at 10:21","depth":7,"bounds":{"left":0.8656915,"top":0.36073422,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,02 GB","depth":7,"bounds":{"left":0.9368351,"top":0.36073422,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.36073422,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-04-01 & task split.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.37669593,"width":0.080119684,"height":0.012769354},"value":"Planning 2026-04-01 & task split.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"1 Apr 2026 at 12:20","depth":7,"bounds":{"left":0.8656915,"top":0.37669593,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"4,68 GB","depth":7,"bounds":{"left":0.9368351,"top":0.37669593,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.37669593,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-03-31.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.3926576,"width":0.049867023,"height":0.012769354},"value":"Retro 2026-03-31.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"31 Mar 2026 at 18:29","depth":7,"bounds":{"left":0.8656915,"top":0.3926576,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"3,4 GB","depth":7,"bounds":{"left":0.93949467,"top":0.3926576,"width":0.015292553,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.3926576,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-31.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.4086193,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-03-31.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"31 Mar 2026 at 10:10","depth":7,"bounds":{"left":0.8656915,"top":0.4086193,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"923,6 MB","depth":7,"bounds":{"left":0.93351066,"top":0.4086193,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.4086193,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-30.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.424581,"width":0.0625,"height":0.012769354},"value":"Refinement 2026-03-30.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"30 Mar 2026 at 17:12","depth":7,"bounds":{"left":0.8656915,"top":0.424581,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,77 GB","depth":7,"bounds":{"left":0.9368351,"top":0.424581,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.424581,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-30.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.4405427,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-30.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"30 Mar 2026 at 10:05","depth":7,"bounds":{"left":0.8656915,"top":0.4405427,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"641,8 MB","depth":7,"bounds":{"left":0.93351066,"top":0.4405427,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.4405427,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-27.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.45650437,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-03-27.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27 Mar 2026 at 10:09","depth":7,"bounds":{"left":0.8656915,"top":0.45650437,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"884,3 MB","depth":7,"bounds":{"left":0.93351066,"top":0.45650437,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.45650437,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-26.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.47246608,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-26.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"26 Mar 2026 at 9:59","depth":7,"bounds":{"left":0.8656915,"top":0.47246608,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"476,6 MB","depth":7,"bounds":{"left":0.93351066,"top":0.47246608,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.47246608,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-24.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.4884278,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-24.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"24 Mar 2026 at 10:00","depth":7,"bounds":{"left":0.8656915,"top":0.4884278,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"550,8 MB","depth":7,"bounds":{"left":0.93351066,"top":0.4884278,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.4884278,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-23.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.50438946,"width":0.0625,"height":0.012769354},"value":"Refinement 2026-03-23.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"23 Mar 2026 at 17:03","depth":7,"bounds":{"left":0.8656915,"top":0.50438946,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"3,44 GB","depth":7,"bounds":{"left":0.9368351,"top":0.50438946,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.50438946,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-23.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.5203512,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-23.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"23 Mar 2026 at 10:00","depth":7,"bounds":{"left":0.8656915,"top":0.5203512,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"438,9 MB","depth":7,"bounds":{"left":0.93351066,"top":0.5203512,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.5203512,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"BE chapter 2026-03-20.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.5363129,"width":0.061502658,"height":0.012769354},"value":"BE chapter 2026-03-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Mar 2026 at 11:46","depth":7,"bounds":{"left":0.8656915,"top":0.5363129,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,68 GB","depth":7,"bounds":{"left":0.9368351,"top":0.5363129,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.5363129,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-20.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.5522745,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-20.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Mar 2026 at 10:06","depth":7,"bounds":{"left":0.8656915,"top":0.5522745,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"430,4 MB","depth":7,"bounds":{"left":0.93351066,"top":0.5522745,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.5522745,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Planing 2026-03-18-converted.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.56823623,"width":0.076130316,"height":0.012769354},"value":"Planing 2026-03-18-converted.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Mar 2026 at 12:01","depth":7,"bounds":{"left":0.8656915,"top":0.56823623,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,38 GB","depth":7,"bounds":{"left":0.9368351,"top":0.56823623,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.56823623,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-02-09-converted.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.58419794,"width":0.084773935,"height":0.012769354},"value":"Refinement 2026-02-09-converted.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Mar 2026 at 11:35","depth":7,"bounds":{"left":0.8656915,"top":0.58419794,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,26 GB","depth":7,"bounds":{"left":0.9368351,"top":0.58419794,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.58419794,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-19.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.60015965,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-03-19.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Mar 2026 at 9:57","depth":7,"bounds":{"left":0.8656915,"top":0.60015965,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"386,3 MB","depth":7,"bounds":{"left":0.93351066,"top":0.60015965,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.60015965,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Review 2026-03-18.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.6161213,"width":0.05319149,"height":0.012769354},"value":"Review 2026-03-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Mar 2026 at 16:20","depth":7,"bounds":{"left":0.8656915,"top":0.6161213,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"705,8 MB","depth":7,"bounds":{"left":0.93351066,"top":0.6161213,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.6161213,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Planing 2026-03-18.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.632083,"width":0.053856384,"height":0.012769354},"value":"Planing 2026-03-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Mar 2026 at 11:14","depth":7,"bounds":{"left":0.8656915,"top":0.632083,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,78 GB","depth":7,"bounds":{"left":0.9368351,"top":0.632083,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.632083,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-03-17.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.6480447,"width":0.04920213,"height":0.012769354},"value":"Retro 2026-03-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Mar 2026 at 17:40","depth":7,"bounds":{"left":0.8656915,"top":0.6480447,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,53 GB","depth":7,"bounds":{"left":0.9368351,"top":0.6480447,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.6480447,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-17.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.6640064,"width":0.048204787,"height":0.012769354},"value":"Daily 2026-03-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Mar 2026 at 10:18","depth":7,"bounds":{"left":0.8656915,"top":0.6640064,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,2 GB","depth":7,"bounds":{"left":0.93949467,"top":0.6640064,"width":0.015292553,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.6640064,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-16.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.67996806,"width":0.061835106,"height":0.012769354},"value":"Refinement 2026-03-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Mar 2026 at 16:55","depth":7,"bounds":{"left":0.8656915,"top":0.67996806,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"4,19 GB","depth":7,"bounds":{"left":0.9368351,"top":0.67996806,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.67996806,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-16.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.69592977,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-03-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Mar 2026 at 10:02","depth":7,"bounds":{"left":0.8656915,"top":0.69592977,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"592,2 MB","depth":7,"bounds":{"left":0.93351066,"top":0.69592977,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.69592977,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-13.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.7118915,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-03-13.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"13 Mar 2026 at 10:12","depth":7,"bounds":{"left":0.8656915,"top":0.7118915,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"1,02 GB","depth":7,"bounds":{"left":0.9368351,"top":0.7118915,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.7118915,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"1-1 2026-03-12.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.7278532,"width":0.04488032,"height":0.012769354},"value":"1-1 2026-03-12.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"12 Mar 2026 at 18:35","depth":7,"bounds":{"left":0.8656915,"top":0.7278532,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"637,6 MB","depth":7,"bounds":{"left":0.93351066,"top":0.7278532,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.7278532,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-12.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.7438148,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-03-12.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"12 Mar 2026 at 10:10","depth":7,"bounds":{"left":0.8656915,"top":0.7438148,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"978,7 MB","depth":7,"bounds":{"left":0.93351066,"top":0.7438148,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.7438148,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-11.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.75977653,"width":0.048204787,"height":0.012769354},"value":"Daily 2026-03-11.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"11 Mar 2026 at 10:06","depth":7,"bounds":{"left":0.8656915,"top":0.75977653,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"798,7 MB","depth":7,"bounds":{"left":0.93351066,"top":0.75977653,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.75977653,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-10.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.77573824,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-03-10.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"10 Mar 2026 at 9:57","depth":7,"bounds":{"left":0.8656915,"top":0.77573824,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"404,6 MB","depth":7,"bounds":{"left":0.93351066,"top":0.77573824,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.77573824,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-03-09.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.79169995,"width":0.0625,"height":0.012769354},"value":"Refinement 2026-03-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Mar 2026 at 17:04","depth":7,"bounds":{"left":0.8656915,"top":0.79169995,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"4,16 GB","depth":7,"bounds":{"left":0.9368351,"top":0.79169995,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.79169995,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-09.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.8076616,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-09.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"9 Mar 2026 at 9:56","depth":7,"bounds":{"left":0.8656915,"top":0.8076616,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"319,7 MB","depth":7,"bounds":{"left":0.93351066,"top":0.8076616,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.8076616,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-06.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.8236233,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-06.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"6 Mar 2026 at 9:57","depth":7,"bounds":{"left":0.8656915,"top":0.8236233,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"291,7 MB","depth":7,"bounds":{"left":0.93351066,"top":0.8236233,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.8236233,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-03-04.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.839585,"width":0.056848403,"height":0.012769354},"value":"Planning 2026-03-04.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"4 Mar 2026 at 11:09","depth":7,"bounds":{"left":0.8656915,"top":0.839585,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,62 GB","depth":7,"bounds":{"left":0.9368351,"top":0.839585,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.839585,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-03-02.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.8555467,"width":0.049534574,"height":0.012769354},"value":"Daily 2026-03-02.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"2 Mar 2026 at 10:07","depth":7,"bounds":{"left":0.8656915,"top":0.8555467,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"768,5 MB","depth":7,"bounds":{"left":0.93351066,"top":0.8555467,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.8555467,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-27.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.87150836,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-02-27.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"27 Feb 2026 at 10:02","depth":7,"bounds":{"left":0.8656915,"top":0.87150836,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"546,8 MB","depth":7,"bounds":{"left":0.93351066,"top":0.87150836,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.87150836,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-26.mov","depth":7,"bounds":{"left":0.58776593,"top":0.88747007,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-02-26.mov","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"26 Feb 2026 at 9:53","depth":7,"bounds":{"left":0.8656915,"top":0.88747007,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"96,6 MB","depth":7,"bounds":{"left":0.93650264,"top":0.88747007,"width":0.01861702,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"QT movie","depth":7,"bounds":{"left":0.9581117,"top":0.88747007,"width":0.034906916,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-25.mov","depth":7,"bounds":{"left":0.58776593,"top":0.9034318,"width":0.048537236,"height":0.012769354},"value":"Daily 2026-02-25.mov","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"25 Feb 2026 at 9:59","depth":7,"bounds":{"left":0.8656915,"top":0.9034318,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"503,5 MB","depth":7,"bounds":{"left":0.93351066,"top":0.9034318,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"QT movie","depth":7,"bounds":{"left":0.9581117,"top":0.9034318,"width":0.034906916,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Opportunity-Contacts 2026-02-24.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.9193935,"width":0.08377659,"height":0.012769354},"value":"Opportunity-Contacts 2026-02-24.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"24 Feb 2026 at 12:03","depth":7,"bounds":{"left":0.8656915,"top":0.9193935,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"791,7 MB","depth":7,"bounds":{"left":0.93351066,"top":0.9193935,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.9193935,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-24.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.9353551,"width":0.04920213,"height":0.012769354},"value":"Daily 2026-02-24.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"24 Feb 2026 at 10:02","depth":7,"bounds":{"left":0.8656915,"top":0.9353551,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"520,7 MB","depth":7,"bounds":{"left":0.93351066,"top":0.9353551,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.9353551,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Refinement 2026-02-23.mov","depth":7,"bounds":{"left":0.58776593,"top":0.95131683,"width":0.061835106,"height":0.012769354},"value":"Refinement 2026-02-23.mov","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"23 Feb 2026 at 16:31","depth":7,"bounds":{"left":0.8656915,"top":0.95131683,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2 GB","depth":7,"bounds":{"left":0.94348407,"top":0.95131683,"width":0.011303191,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"QT movie","depth":7,"bounds":{"left":0.9581117,"top":0.95131683,"width":0.034906916,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-20 & Ani.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.96727854,"width":0.061170213,"height":0.012769354},"value":"Daily 2026-02-20 & Ani.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"20 Feb 2026 at 10:53","depth":7,"bounds":{"left":0.8656915,"top":0.96727854,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"2,52 GB","depth":7,"bounds":{"left":0.9368351,"top":0.96727854,"width":0.017952127,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.96727854,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Daily 2026-02-19.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.98324025,"width":0.04886968,"height":0.012769354},"value":"Daily 2026-02-19.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"19 Feb 2026 at 9:54","depth":7,"bounds":{"left":0.8656915,"top":0.98324025,"width":0.056848403,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"234,2 MB","depth":7,"bounds":{"left":0.93351066,"top":0.98324025,"width":0.021276595,"height":0.012769354},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.98324025,"width":0.032912236,"height":0.012769354},"role_description":"text"},{"role":"AXTextField","text":"Review 2026-02-18.mp4","depth":7,"bounds":{"left":0.58776593,"top":0.9992019,"width":0.05319149,"height":0.0007981062},"value":"Review 2026-02-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Feb 2026 at 16:25","depth":7,"bounds":{"left":0.8656915,"top":0.9992019,"width":0.056848403,"height":0.0007981062},"role_description":"text"},{"role":"AXStaticText","text":"925,1 MB","depth":7,"bounds":{"left":0.93351066,"top":0.9992019,"width":0.021276595,"height":0.0007981062},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":0.9992019,"width":0.032912236,"height":0.0007981062},"role_description":"text"},{"role":"AXTextField","text":"Planning 2026-02-18.mp4","depth":7,"bounds":{"left":0.58776593,"top":1.0,"width":0.05618351,"height":-0.01516366},"value":"Planning 2026-02-18.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"18 Feb 2026 at 10:57","depth":7,"bounds":{"left":0.8656915,"top":1.0,"width":0.056848403,"height":-0.01516366},"role_description":"text"},{"role":"AXStaticText","text":"2,04 GB","depth":7,"bounds":{"left":0.9368351,"top":1.0,"width":0.017952127,"height":-0.01516366},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":1.0,"width":0.032912236,"height":-0.01516366},"role_description":"text"},{"role":"AXTextField","text":"Retro 2026-02-17.mp4","depth":7,"bounds":{"left":0.58776593,"top":1.0,"width":0.05618351,"height":-0.031125307},"value":"Retro 2026-02-17.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"17 Feb 2026 at 17:38","depth":7,"bounds":{"left":0.8656915,"top":1.0,"width":0.056848403,"height":-0.031125307},"role_description":"text"},{"role":"AXStaticText","text":"1,31 GB","depth":7,"bounds":{"left":0.9368351,"top":1.0,"width":0.017952127,"height":-0.031125307},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":1.0,"width":0.032912236,"height":-0.031125307},"role_description":"text"},{"role":"AXTextField","text":"Refinement & P1 debugging 2026-02-16.mp4","depth":7,"bounds":{"left":0.58776593,"top":1.0,"width":0.05618351,"height":-0.047086954},"value":"Refinement & P1 debugging 2026-02-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Feb 2026 at 17:36","depth":7,"bounds":{"left":0.8656915,"top":1.0,"width":0.056848403,"height":-0.047086954},"role_description":"text"},{"role":"AXStaticText","text":"4,53 GB","depth":7,"bounds":{"left":0.9368351,"top":1.0,"width":0.017952127,"height":-0.047086954},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":1.0,"width":0.032912236,"height":-0.047086954},"role_description":"text"},{"role":"AXTextField","text":"SyncObjects3 2026-02-16.mp4","depth":7,"bounds":{"left":0.58776593,"top":1.0,"width":0.05618351,"height":-0.06304872},"value":"SyncObjects3 2026-02-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"16 Feb 2026 at 15:10","depth":7,"bounds":{"left":0.8656915,"top":1.0,"width":0.056848403,"height":-0.06304872},"role_description":"text"},{"role":"AXStaticText","text":"1,42 GB","depth":7,"bounds":{"left":0.9368351,"top":1.0,"width":0.017952127,"height":-0.06304872},"role_description":"text"},{"role":"AXStaticText","text":"MPEG-4 movie","depth":7,"bounds":{"left":0.9581117,"top":1.0,"width":0.032912236,"height":-0.06304872},"role_description":"text"},{"role":"AXTextField","text":"SyncObjects2 2026-02-16.mp4","depth":7,"bounds":{"left":0.58776593,"top":1.0,"width":0.066821806,"height":-0.07901037},"value":"SyncObjects2 2026-02-16.mp4","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
8492372441560665975
|
-2910781458352528949
|
idle
|
accessibility
|
NULL
|
Favourites
jiminny
AirDrop
Recents
Applications
Do Favourites
jiminny
AirDrop
Recents
Applications
Documents
Downloads
lukas
iCloud
iCloud Drive
Sync folder
Locations
DXP4800PLUS-B5F
Eject
Network
Tags
CRM
Orange
Red
Yellow
Green
Blue
Purple
All Tags…
Name
Date Modified
Size
Kind
2026
Today at 10:32
--
Folder
CleanShot 2026-04-23 at 09.45.36.mp4
Today at 10:32
1,74 GB
MPEG-4 movie
Daily 2026-04-22.mp4
Yesterday at 10:21
1,36 GB
MPEG-4 movie
Refinement 2026-04-06.mp4
21 Apr 2026 at 11:02
2,41 GB
MPEG-4 movie
Daily 2026-04-21.mp4
21 Apr 2026 at 10:00
567,8 MB
MPEG-4 movie
Refinement 2026-04-20.mp4
20 Apr 2026 at 16:56
4,25 GB
MPEG-4 movie
Daily 2026-04-20.mp4
20 Apr 2026 at 10:06
698,5 MB
MPEG-4 movie
Daily 2026-04-17.mp4
17 Apr 2026 at 10:16
1,16 GB
MPEG-4 movie
Daily 2026-04-16.mp4
16 Apr 2026 at 10:00
513,4 MB
MPEG-4 movie
Planning 2026-04-15.mp4
15 Apr 2026 at 11:14
2,75 GB
MPEG-4 movie
Retro 2026-04-14.mp4
14 Apr 2026 at 17:37
1,44 GB
MPEG-4 movie
Daily 2026-04-14.mp4
14 Apr 2026 at 10:09
924,4 MB
MPEG-4 movie
User pilot (Adi) 2026-04-09.mp4
9 Apr 2026 at 14:47
362,6 MB
MPEG-4 movie
Daily 2026-04-09.mp4
9 Apr 2026 at 10:07
748,8 MB
MPEG-4 movie
Daily 2026-04-08.mp4
8 Apr 2026 at 10:13
1,04 GB
MPEG-4 movie
Daily 2026-04-07.mp4
7 Apr 2026 at 10:01
575,5 MB
MPEG-4 movie
Daily 2026-04-06.mp4
6 Apr 2026 at 10:08
720,5 MB
MPEG-4 movie
Daily 2026-04-03.mp4
3 Apr 2026 at 10:21
1,02 GB
MPEG-4 movie
Planning 2026-04-01 & task split.mp4
1 Apr 2026 at 12:20
4,68 GB
MPEG-4 movie
Retro 2026-03-31.mp4
31 Mar 2026 at 18:29
3,4 GB
MPEG-4 movie
Daily 2026-03-31.mp4
31 Mar 2026 at 10:10
923,6 MB
MPEG-4 movie
Refinement 2026-03-30.mp4
30 Mar 2026 at 17:12
2,77 GB
MPEG-4 movie
Daily 2026-03-30.mp4
30 Mar 2026 at 10:05
641,8 MB
MPEG-4 movie
Daily 2026-03-27.mp4
27 Mar 2026 at 10:09
884,3 MB
MPEG-4 movie
Daily 2026-03-26.mp4
26 Mar 2026 at 9:59
476,6 MB
MPEG-4 movie
Daily 2026-03-24.mp4
24 Mar 2026 at 10:00
550,8 MB
MPEG-4 movie
Refinement 2026-03-23.mp4
23 Mar 2026 at 17:03
3,44 GB
MPEG-4 movie
Daily 2026-03-23.mp4
23 Mar 2026 at 10:00
438,9 MB
MPEG-4 movie
BE chapter 2026-03-20.mp4
20 Mar 2026 at 11:46
1,68 GB
MPEG-4 movie
Daily 2026-03-20.mp4
20 Mar 2026 at 10:06
430,4 MB
MPEG-4 movie
Planing 2026-03-18-converted.mp4
19 Mar 2026 at 12:01
2,38 GB
MPEG-4 movie
Refinement 2026-02-09-converted.mp4
19 Mar 2026 at 11:35
2,26 GB
MPEG-4 movie
Daily 2026-03-19.mp4
19 Mar 2026 at 9:57
386,3 MB
MPEG-4 movie
Review 2026-03-18.mp4
18 Mar 2026 at 16:20
705,8 MB
MPEG-4 movie
Planing 2026-03-18.mp4
18 Mar 2026 at 11:14
2,78 GB
MPEG-4 movie
Retro 2026-03-17.mp4
17 Mar 2026 at 17:40
1,53 GB
MPEG-4 movie
Daily 2026-03-17.mp4
17 Mar 2026 at 10:18
1,2 GB
MPEG-4 movie
Refinement 2026-03-16.mp4
16 Mar 2026 at 16:55
4,19 GB
MPEG-4 movie
Daily 2026-03-16.mp4
16 Mar 2026 at 10:02
592,2 MB
MPEG-4 movie
Daily 2026-03-13.mp4
13 Mar 2026 at 10:12
1,02 GB
MPEG-4 movie
1-1 2026-03-12.mp4
12 Mar 2026 at 18:35
637,6 MB
MPEG-4 movie
Daily 2026-03-12.mp4
12 Mar 2026 at 10:10
978,7 MB
MPEG-4 movie
Daily 2026-03-11.mp4
11 Mar 2026 at 10:06
798,7 MB
MPEG-4 movie
Daily 2026-03-10.mp4
10 Mar 2026 at 9:57
404,6 MB
MPEG-4 movie
Refinement 2026-03-09.mp4
9 Mar 2026 at 17:04
4,16 GB
MPEG-4 movie
Daily 2026-03-09.mp4
9 Mar 2026 at 9:56
319,7 MB
MPEG-4 movie
Daily 2026-03-06.mp4
6 Mar 2026 at 9:57
291,7 MB
MPEG-4 movie
Planning 2026-03-04.mp4
4 Mar 2026 at 11:09
2,62 GB
MPEG-4 movie
Daily 2026-03-02.mp4
2 Mar 2026 at 10:07
768,5 MB
MPEG-4 movie
Daily 2026-02-27.mp4
27 Feb 2026 at 10:02
546,8 MB
MPEG-4 movie
Daily 2026-02-26.mov
26 Feb 2026 at 9:53
96,6 MB
QT movie
Daily 2026-02-25.mov
25 Feb 2026 at 9:59
503,5 MB
QT movie
Opportunity-Contacts 2026-02-24.mp4
24 Feb 2026 at 12:03
791,7 MB
MPEG-4 movie
Daily 2026-02-24.mp4
24 Feb 2026 at 10:02
520,7 MB
MPEG-4 movie
Refinement 2026-02-23.mov
23 Feb 2026 at 16:31
2 GB
QT movie
Daily 2026-02-20 & Ani.mp4
20 Feb 2026 at 10:53
2,52 GB
MPEG-4 movie
Daily 2026-02-19.mp4
19 Feb 2026 at 9:54
234,2 MB
MPEG-4 movie
Review 2026-02-18.mp4
18 Feb 2026 at 16:25
925,1 MB
MPEG-4 movie
Planning 2026-02-18.mp4
18 Feb 2026 at 10:57
2,04 GB
MPEG-4 movie
Retro 2026-02-17.mp4
17 Feb 2026 at 17:38
1,31 GB
MPEG-4 movie
Refinement & P1 debugging 2026-02-16.mp4
16 Feb 2026 at 17:36
4,53 GB
MPEG-4 movie
SyncObjects3 2026-02-16.mp4
16 Feb 2026 at 15:10
1,42 GB
MPEG-4 movie
SyncObjects2 2026-02-16.mp4...
|
73442
|
|
50451
|
1080
|
25
|
2026-04-17T14:52:06.684510+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-17/1776 /Users/lukas/.screenpipe/data/data/2026-04-17/1776437526684_m2.jpg...
|
Finder
|
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FinderFileEditViewWindowHelpscreenlplbe• dataco sc FinderFileEditViewWindowHelpscreenlplbe• dataco sclllescreenpipe.2026-04-16.0.1og• screenpipe.2026-04-17.0.logscreenpipe.2026-04-15.0.10gscreenpipe.2026-04-14.0.log• screenpipe.2026-04-09.0.1ogdb.sqlite-shm• screenpipe.2026-04-11.0.logscreenpipe.2026-04-12.0.loglà screenpipe.2026-04-13.0.logolpesscreenpipe_sync.shconfig.jsonsync.l0odb.sqlite-wal88=llminny® AirDrop•) RecentsA Applications|9 Documents• Desktop( DownloadsA lukasiCloudiCloud Drive283 Sync folderLocations• DXP4800PLUS-B5... ⅔ge NerworeTagsDCKMI• Orange• Red• Yellow• Green• Blue• Purple@ All Tags..Date Modified15 Apr 2026 at 14:53Today at 17:51Yesterday at 20:33To Ap: 207 8 185514 Apr 2026 at 19:319 Apr 2026 at 21:27Today at 16:1111 Apr 2026 at 23:1412 Apr 2026 at 23:5513 Apr 2026 at 19:5015 Apr 2026 at 14:53Today at 17:47Yesterday at 16:49Today at 17:45Today at 17:511 of 16 selected, 25,2 GB availableSearch4,24 GB3.80 G-197 KBWOND1/6 KB162 KB135 Kb9% K=95 KB72 KB13 KB13 KB358 bytesZ3 DYLESZero bytesFolderDocu mentLog FileLoe rlleLog FileLog FileLoc FlleDocumentLog FileLog FileLog Filerolaererminal scriotsJSONLoc FlleDocumentLukas Kovallk s macbook Pro.=llminny• AirDrop•) RecentsA Applications9 Documents• Desktop( DownloadslukasIcloudiCloud Drive283 Sync folderLocationsLJ DXP4800PLUS-B5... €Ga NetworkTagsDCKMI• Orange• Red• Yellow• Green• Blue• Purple• All Tags....screenoloeRecentsNamedo,sqlltedata• archive.dboibesscreenpipe-day.shscreenoloe...• test_writeWorkDate Modified14 Apr 2026 at 20:4914 Apr 2026 at 19:32resteroav ar zoiss11 Apr 2026 at 16:5111 Apr 2026 at 17:0013 Apr 2026 at 17:2111 Apr 2026 at 17:26100% CFri 17 Apr 17:52:06Q SearchscreenpipeSize1,42 GBDocument1,34 GBFolderZ0-.MBDocu ment13 KBFolder3 KBTerminal scriptsZero bytesDocu mentZero bytesUnix Ex...ble File...
|
NULL
|
8492333843967971876
|
NULL
|
click
|
ocr
|
NULL
|
FinderFileEditViewWindowHelpscreenlplbe• dataco sc FinderFileEditViewWindowHelpscreenlplbe• dataco sclllescreenpipe.2026-04-16.0.1og• screenpipe.2026-04-17.0.logscreenpipe.2026-04-15.0.10gscreenpipe.2026-04-14.0.log• screenpipe.2026-04-09.0.1ogdb.sqlite-shm• screenpipe.2026-04-11.0.logscreenpipe.2026-04-12.0.loglà screenpipe.2026-04-13.0.logolpesscreenpipe_sync.shconfig.jsonsync.l0odb.sqlite-wal88=llminny® AirDrop•) RecentsA Applications|9 Documents• Desktop( DownloadsA lukasiCloudiCloud Drive283 Sync folderLocations• DXP4800PLUS-B5... ⅔ge NerworeTagsDCKMI• Orange• Red• Yellow• Green• Blue• Purple@ All Tags..Date Modified15 Apr 2026 at 14:53Today at 17:51Yesterday at 20:33To Ap: 207 8 185514 Apr 2026 at 19:319 Apr 2026 at 21:27Today at 16:1111 Apr 2026 at 23:1412 Apr 2026 at 23:5513 Apr 2026 at 19:5015 Apr 2026 at 14:53Today at 17:47Yesterday at 16:49Today at 17:45Today at 17:511 of 16 selected, 25,2 GB availableSearch4,24 GB3.80 G-197 KBWOND1/6 KB162 KB135 Kb9% K=95 KB72 KB13 KB13 KB358 bytesZ3 DYLESZero bytesFolderDocu mentLog FileLoe rlleLog FileLog FileLoc FlleDocumentLog FileLog FileLog Filerolaererminal scriotsJSONLoc FlleDocumentLukas Kovallk s macbook Pro.=llminny• AirDrop•) RecentsA Applications9 Documents• Desktop( DownloadslukasIcloudiCloud Drive283 Sync folderLocationsLJ DXP4800PLUS-B5... €Ga NetworkTagsDCKMI• Orange• Red• Yellow• Green• Blue• Purple• All Tags....screenoloeRecentsNamedo,sqlltedata• archive.dboibesscreenpipe-day.shscreenoloe...• test_writeWorkDate Modified14 Apr 2026 at 20:4914 Apr 2026 at 19:32resteroav ar zoiss11 Apr 2026 at 16:5111 Apr 2026 at 17:0013 Apr 2026 at 17:2111 Apr 2026 at 17:26100% CFri 17 Apr 17:52:06Q SearchscreenpipeSize1,42 GBDocument1,34 GBFolderZ0-.MBDocu ment13 KBFolder3 KBTerminal scriptsZero bytesDocu mentZero bytesUnix Ex...ble File...
|
NULL
|
|
67422
|
1517
|
16
|
2026-04-21T15:38:10.128995+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-21/1776 /Users/lukas/.screenpipe/data/data/2026-04-21/1776785890128_m2.jpg...
|
Firefox
|
[JY-18909] [Part2] Automated reports with Ask Jimi [JY-18909] [Part2] Automated reports with Ask Jiminny - Jira — Work...
|
True
|
jiminny.atlassian.net/browse/JY-18909
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
Project Phoenix – Figma
Project Phoenix – Figma
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny Mail
Jiminny Mail
[JY-20500] Batch initial sync for Salesforce - Jira
[JY-20500] Batch initial sync for Salesforce - Jira
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Jiminny
Jiminny
Pipelines - jiminny/app
Pipelines - jiminny/app
Formalize
Formalize
[SRD-6793] Les Mills activity types not pulling in - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
Search results: calendar | Jiminny Help Center
Search results: calendar | Jiminny Help Center
Jiminny
Jiminny
Jiminny
Jiminny
Edit - Engineering - Confluence
Edit - Engineering - Confluence
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Skip to:
Sidebar
Sidebar
Top Bar
Top Bar
Main Content
Main Content
Collapse sidebar [
Collapse sidebar [
Switch sites or apps
Switch sites or apps
Go to your Jira homepage
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
6 Notifications
6 Notifications
Help
Help
Settings
Settings
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Apps
Apps
More actions for Apps
More actions for Apps
Spaces
Spaces
Create space
Create space
More actions for spaces
More actions for spaces
Recent
Jiminny (New)
Jiminny (New)
Jiminny (New)
Create board
Create board
More actions for Jiminny (New)
More actions for Jiminny (New)
Platform Team
Platform Team
Board actions
Board actions
SE Kanban
SE Kanban
Board actions
Board actions
Capture Team
Capture Team
Board actions
Board actions
Enterprise Stability Issues 🤕
Enterprise Stability Issues 🤕
Board actions
Board actions
Processing Team
Processing Team
Board actions
Board actions
Service-Desk
Service-Desk
More actions for Service-Desk
More actions for Service-Desk
More spaces
More spaces
Filters
Filters
More actions for Filters
More actions for Filters
Dashboards
Dashboards
Create dashboard
Create dashboard
More actions for Dashboards
More actions for Dashboards
Operations
Operations
More actions for Operations
More actions for Operations
Confluence , (opens new window)
Confluence
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
Customise sidebar
Customise sidebar
Resize side navigation panel
Spaces
Spaces
/
Jiminny (New) Jiminny (New)
Jiminny (New)
/
Epic - Change parent
JY-19240
JY-19240
/
Story - Change work type
JY-18909
JY-18909
Copy link
[Part2] Automated reports with Ask Jiminny- Summary, edit
[Part2] Automated reports with Ask Jiminny
[Part2] Automated reports with Ask Jiminny
Add or create work related to this Story
Add or create work related to this Story
View app actions
View app actions
Collapse Description Description
Collapse Description
Collapse Description
Description
Edit Description, edit
We want to allow our users to automate the execution of their
AJA
prompts in order to save time and have them ready when they need them.
Create the reports:
admins and managers should be able to automate reports based on their Panorama prompts and saved searches
the report should be generated in a pdf - use a lightly branded one this time -
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=7691-61688&t=cLuF7fP7zTl4xBsQ-1
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=7691-61688&t=cLuF7fP7zTl4xBsQ-1
Connect your Figma account
Connect your Figma account
if the customer hasn’t added a brand logo then use the Jiminny logo
once the report is ready it should be shared with the users over email -
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=12208-23064&t=nJK629FloDyaWRYR-1
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=12208-23064&t=nJK629FloDyaWRYR-1
Connect your Figma account
Connect your Figma account
If no one is selected then the report will only be shared with the person who created it
ensure the reports has a proper structure and formatting - headings, bold etc. - take examples from the Exec Reports
ensure the report has links to playback when examples are used
in the beginning of each report have a brief section for ‘Data Srouce’ and ‘Objective’ - take the Exec summary report for example
data source should cover what data has been analysed
objective should be a short paragraph that explains the goal
Show the reports in Jiminny:
show the report in the AI Reports page with a special logo -
Project Phoenix
Project Phoenix
only the creator of the reports and the users it is shared with should be able to see it in the list
users should be able to preview the report and download it
the creator of the report should be able to delete it - deleting it will delete only this specific pdf
'Ask Jiminny Report' should be added as an option to the Report type filter so users can filter the list for such reports
when a report is shared with a user then show who shared it in the ‘Shared’ column -
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=14369-40078&t=We33fyQzIUfHuXVR-4
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=14369-40078&t=We33fyQzIUfHuXVR-4
Connect your Figma account
Connect your Figma account
Collapse Subtasks Subtasks Work item actions Configure columns Create subtask
Collapse Subtasks
Collapse Subtasks
Subtasks
Work item actions
Work item actions
Configure columns
Configure columns
Create subtask
Create subtask
89
% Done
Work
Work
More actions for Work...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0018284575,"top":0.0518755,"width":0.07596409,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"bounds":{"left":0.0,"top":0.09497207,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"bounds":{"left":0.013297873,"top":0.10614525,"width":0.041888297,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":4,"bounds":{"left":0.0,"top":0.12769353,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20372] AI Reports > Empty page design and promotion - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.13886672,"width":0.11319814,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"bounds":{"left":0.0,"top":0.16041501,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"bounds":{"left":0.013297873,"top":0.17158818,"width":0.041888297,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"bounds":{"left":0.0,"top":0.19313647,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"bounds":{"left":0.013297873,"top":0.20430966,"width":0.041888297,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Project Phoenix – Figma","depth":4,"bounds":{"left":0.0,"top":0.22585794,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix – Figma","depth":5,"bounds":{"left":0.013297873,"top":0.23703113,"width":0.041888297,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny MCP Connector - Product - Confluence","depth":4,"bounds":{"left":0.0,"top":0.2585794,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny MCP Connector - Product - Confluence","depth":5,"bounds":{"left":0.013297873,"top":0.2697526,"width":0.08294548,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.29130086,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.30247405,"width":0.02144282,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20500] Batch initial sync for Salesforce - Jira","depth":4,"bounds":{"left":0.0,"top":0.32402235,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20500] Batch initial sync for Salesforce - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.33519554,"width":0.08610372,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.3567438,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.367917,"width":0.042719416,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.38946527,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.013297873,"top":0.40063846,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.42218676,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.43335995,"width":0.039228722,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Formalize","depth":4,"bounds":{"left":0.0,"top":0.45490822,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Formalize","depth":5,"bounds":{"left":0.013297873,"top":0.4660814,"width":0.016788565,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":4,"bounds":{"left":0.0,"top":0.48762968,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6793] Les Mills activity types not pulling in - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.49880287,"width":0.09524601,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Search results: calendar | Jiminny Help Center","depth":4,"bounds":{"left":0.0,"top":0.5203512,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Search results: calendar | Jiminny Help Center","depth":5,"bounds":{"left":0.013297873,"top":0.53152436,"width":0.080119684,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.55307263,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.013297873,"top":0.5642458,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.5857941,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.013297873,"top":0.5969673,"width":0.013131649,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Edit - Engineering - Confluence","depth":4,"bounds":{"left":0.0,"top":0.61851555,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Edit - Engineering - Confluence","depth":5,"bounds":{"left":0.013297873,"top":0.62968874,"width":0.054853722,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira","depth":4,"bounds":{"left":0.0,"top":0.651237,"width":0.07962101,"height":0.032721467},"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.6624102,"width":0.10688165,"height":0.010774142},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.6584198,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.6855547,"width":0.07413564,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Skip to:","depth":9,"bounds":{"left":0.090259306,"top":0.07861133,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Sidebar","depth":10,"bounds":{"left":0.090259306,"top":0.097765364,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Sidebar","depth":11,"bounds":{"left":0.090259306,"top":0.097765364,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Top Bar","depth":10,"bounds":{"left":0.090259306,"top":0.11691939,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Top Bar","depth":11,"bounds":{"left":0.090259306,"top":0.11691939,"width":0.016954787,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Main Content","depth":10,"bounds":{"left":0.090259306,"top":0.13607343,"width":0.029421542,"height":0.01396648},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Main Content","depth":11,"bounds":{"left":0.090259306,"top":0.13607343,"width":0.029421542,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Collapse sidebar [","depth":9,"bounds":{"left":0.08361037,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Collapse sidebar [","depth":11,"bounds":{"left":0.0887633,"top":0.06344773,"width":0.039727394,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Switch sites or apps","depth":10,"bounds":{"left":0.095578454,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Switch sites or apps","depth":12,"bounds":{"left":0.10073138,"top":0.06344773,"width":0.044215426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Go to your Jira homepage","depth":9,"bounds":{"left":0.10887633,"top":0.057861134,"width":0.029421542,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXComboBox","text":"Search, press enter to navigate to advanced search with your text query","depth":11,"bounds":{"left":0.40475398,"top":0.06264964,"width":0.24268617,"height":0.015961692},"help_text":"","placeholder":"Search","role_description":"combo box","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Create","depth":10,"bounds":{"left":0.65575135,"top":0.057861134,"width":0.030086435,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create","depth":12,"bounds":{"left":0.66705453,"top":0.06384677,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Rovo Ask Rovo","depth":12,"bounds":{"left":0.91223407,"top":0.057861134,"width":0.035904255,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Ask Rovo","depth":14,"bounds":{"left":0.92353725,"top":0.06384677,"width":0.020611702,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"6 Notifications","depth":12,"bounds":{"left":0.9494681,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"6 Notifications","depth":14,"bounds":{"left":0.954621,"top":0.06344773,"width":0.031914894,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Help","depth":12,"bounds":{"left":0.96143615,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Help","depth":14,"bounds":{"left":0.9665891,"top":0.06344773,"width":0.010139627,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Settings","depth":12,"bounds":{"left":0.9734042,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Settings","depth":14,"bounds":{"left":0.97855717,"top":0.06344773,"width":0.017952127,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.98537236,"top":0.057861134,"width":0.010638298,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"lukas.kovalik@jiminny.com","depth":14,"bounds":{"left":0.99052525,"top":0.06344773,"width":0.009474754,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"For you","depth":12,"bounds":{"left":0.08361037,"top":0.09976058,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"For you","depth":15,"bounds":{"left":0.09424867,"top":0.10574621,"width":0.01662234,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Recent","depth":12,"bounds":{"left":0.08361037,"top":0.12529927,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Recent","depth":15,"bounds":{"left":0.09424867,"top":0.13128492,"width":0.015458777,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Starred","depth":12,"bounds":{"left":0.08361037,"top":0.15083799,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Starred","depth":15,"bounds":{"left":0.09424867,"top":0.15682362,"width":0.016456118,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Apps","depth":12,"bounds":{"left":0.08361037,"top":0.1763767,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Apps","depth":15,"bounds":{"left":0.09424867,"top":0.18236233,"width":0.011635638,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for Apps","depth":13,"bounds":{"left":0.15309176,"top":0.17956904,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions for Apps","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Spaces","depth":12,"bounds":{"left":0.08361037,"top":0.2019154,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXStaticText","text":"Spaces","depth":15,"bounds":{"left":0.09424867,"top":0.20790103,"width":0.016456118,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create space","depth":13,"bounds":{"left":0.13646941,"top":0.20510775,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create space","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for spaces","depth":13,"bounds":{"left":0.14577793,"top":0.20510775,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions for spaces","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Recent","depth":16,"bounds":{"left":0.08959442,"top":0.23423783,"width":0.013464096,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jiminny (New)","depth":17,"bounds":{"left":0.08759973,"top":0.2529928,"width":0.0674867,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny (New)","depth":20,"bounds":{"left":0.09823803,"top":0.25897846,"width":0.032081116,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Jiminny (New)","depth":18,"bounds":{"left":0.08892952,"top":0.25618514,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":true},{"role":"AXMenuButton","text":"Create board","depth":18,"bounds":{"left":0.13646941,"top":0.25618514,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Create board","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for Jiminny (New)","depth":18,"bounds":{"left":0.14577793,"top":0.25618514,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions for Jiminny (New)","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Platform Team","depth":19,"bounds":{"left":0.09158909,"top":0.27853152,"width":0.06349734,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Team","depth":22,"bounds":{"left":0.1022274,"top":0.28451717,"width":0.032247342,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Board actions","depth":20,"bounds":{"left":0.15309176,"top":0.28172386,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Board actions","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"SE Kanban","depth":19,"bounds":{"left":0.09158909,"top":0.30407023,"width":0.06349734,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"SE Kanban","depth":22,"bounds":{"left":0.1022274,"top":0.31005585,"width":0.024102394,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Board actions","depth":20,"bounds":{"left":0.15309176,"top":0.30726257,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Board actions","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Capture Team","depth":19,"bounds":{"left":0.09158909,"top":0.32960895,"width":0.06349734,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Capture Team","depth":22,"bounds":{"left":0.1022274,"top":0.33559456,"width":0.03125,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Board actions","depth":20,"bounds":{"left":0.15309176,"top":0.33280128,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Board actions","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Enterprise Stability Issues 🤕","depth":19,"bounds":{"left":0.09158909,"top":0.35514766,"width":0.06349734,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enterprise Stability Issues 🤕","depth":22,"bounds":{"left":0.1022274,"top":0.36113328,"width":0.050531916,"height":0.030726258},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Board actions","depth":20,"bounds":{"left":0.15309176,"top":0.35834,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Board actions","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Processing Team","depth":19,"bounds":{"left":0.09158909,"top":0.38068634,"width":0.06349734,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Processing Team","depth":22,"bounds":{"left":0.1022274,"top":0.386672,"width":0.038231384,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Board actions","depth":20,"bounds":{"left":0.15309176,"top":0.38387868,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Board actions","depth":22,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Service-Desk","depth":17,"bounds":{"left":0.08759973,"top":0.40622506,"width":0.0674867,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Service-Desk","depth":20,"bounds":{"left":0.09823803,"top":0.4122107,"width":0.03025266,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for Service-Desk","depth":18,"bounds":{"left":0.15442154,"top":0.4094174,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions for Service-Desk","depth":20,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More spaces","depth":17,"bounds":{"left":0.08759973,"top":0.43176377,"width":0.0674867,"height":0.025538707},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More spaces","depth":20,"bounds":{"left":0.09823803,"top":0.43774942,"width":0.028756648,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Filters","depth":12,"bounds":{"left":0.08361037,"top":0.45730248,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Filters","depth":15,"bounds":{"left":0.09424867,"top":0.4632881,"width":0.013796543,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for Filters","depth":13,"bounds":{"left":0.15309176,"top":0.46049482,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions for Filters","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Dashboards","depth":12,"bounds":{"left":0.08361037,"top":0.4828412,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Dashboards","depth":15,"bounds":{"left":0.09424867,"top":0.4888268,"width":0.026761968,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create dashboard","depth":13,"bounds":{"left":0.15508644,"top":0.48603353,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create dashboard","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for Dashboards","depth":13,"bounds":{"left":0.16240026,"top":0.48603353,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions for Dashboards","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Operations","depth":12,"bounds":{"left":0.08361037,"top":0.5083799,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Operations","depth":15,"bounds":{"left":0.09424867,"top":0.5143655,"width":0.02443484,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for Operations","depth":13,"bounds":{"left":0.15309176,"top":0.51157224,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"More actions for Operations","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Confluence , (opens new window)","depth":13,"bounds":{"left":0.08361037,"top":0.5434956,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Confluence","depth":17,"bounds":{"left":0.09424867,"top":0.5494813,"width":0.025764627,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":15,"bounds":{"left":0.08361037,"top":0.55706304,"width":0.04837101,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Teams , (opens new window)","depth":13,"bounds":{"left":0.08361037,"top":0.56903434,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Teams","depth":17,"bounds":{"left":0.09424867,"top":0.57501996,"width":0.014793883,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", (opens new window)","depth":15,"bounds":{"left":0.08361037,"top":0.5826017,"width":0.04837101,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"open menu","depth":14,"bounds":{"left":0.14378324,"top":0.57222664,"width":0.0039893617,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"open menu","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Customise sidebar","depth":12,"bounds":{"left":0.08361037,"top":0.60415006,"width":0.071476065,"height":0.025538707},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Customise sidebar","depth":15,"bounds":{"left":0.09424867,"top":0.6101357,"width":0.04155585,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Resize side navigation panel","depth":13,"bounds":{"left":0.2109375,"top":0.0981644,"width":0.062333778,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Spaces","depth":15,"bounds":{"left":0.27260637,"top":0.0933759,"width":0.013962766,"height":0.01915403},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Spaces","depth":17,"bounds":{"left":0.27260637,"top":0.09696728,"width":0.013962766,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":15,"bounds":{"left":0.2883976,"top":0.09577015,"width":0.0016622341,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Jiminny (New) Jiminny (New)","depth":15,"bounds":{"left":0.29388297,"top":0.0933759,"width":0.034408245,"height":0.01915403},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny (New)","depth":17,"bounds":{"left":0.3011968,"top":0.09696728,"width":0.027094414,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":15,"bounds":{"left":0.33011967,"top":0.09577015,"width":0.0016622341,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Epic - Change parent","depth":15,"bounds":{"left":0.3336104,"top":0.0933759,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"JY-19240","depth":15,"bounds":{"left":0.3415891,"top":0.0933759,"width":0.018118352,"height":0.01915403},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-19240","depth":17,"bounds":{"left":0.3415891,"top":0.09696728,"width":0.018118352,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":15,"bounds":{"left":0.3615359,"top":0.09577015,"width":0.0016622341,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Story - Change work type","depth":15,"bounds":{"left":0.3650266,"top":0.0933759,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"JY-18909","depth":15,"bounds":{"left":0.37300533,"top":0.0933759,"width":0.017952127,"height":0.01915403},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-18909","depth":17,"bounds":{"left":0.37300533,"top":0.09696728,"width":0.017952127,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy link","depth":16,"bounds":{"left":0.38962767,"top":0.096169196,"width":0.005319149,"height":0.012769354},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"[Part2] Automated reports with Ask Jiminny- Summary, edit","depth":11,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"[Part2] Automated reports with Ask Jiminny","depth":11,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[Part2] Automated reports with Ask Jiminny","depth":12,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Add or create work related to this Story","depth":12,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Add or create work related to this Story","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"View app actions","depth":12,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"View app actions","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Collapse Description Description","depth":11,"bounds":{"left":0.26462767,"top":0.0,"width":0.38314494,"height":0.025538707},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXButton","text":"Collapse Description","depth":13,"bounds":{"left":0.2632979,"top":0.0,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Collapse Description","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Description","depth":14,"bounds":{"left":0.27260637,"top":0.0,"width":0.029587766,"height":0.01556265},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Edit Description, edit","depth":13,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"We want to allow our users to automate the execution of their","depth":14,"bounds":{"left":0.27327126,"top":0.0,"width":0.13597074,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AJA","depth":15,"bounds":{"left":0.40924203,"top":0.0,"width":0.008976064,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"prompts in order to save time and have them ready when they need them.","depth":14,"bounds":{"left":0.41821808,"top":0.0,"width":0.16339761,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Create the reports:","depth":15,"bounds":{"left":0.27327126,"top":0.0,"width":0.04288564,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admins and managers should be able to automate reports based on their Panorama prompts and saved searches","depth":16,"bounds":{"left":0.28125,"top":0.02434158,"width":0.24900267,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"the report should be generated in a pdf - use a lightly branded one this time -","depth":16,"bounds":{"left":0.28125,"top":0.04668795,"width":0.17104389,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=7691-61688&t=cLuF7fP7zTl4xBsQ-1","depth":17,"bounds":{"left":0.28291222,"top":0.04668795,"width":0.3600399,"height":0.03312051},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=7691-61688&t=cLuF7fP7zTl4xBsQ-1","depth":18,"bounds":{"left":0.28291222,"top":0.04668795,"width":0.3600399,"height":0.03312051},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Connect your Figma account","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Connect your Figma account","depth":19,"bounds":{"left":0.37134308,"top":0.06584198,"width":0.063663565,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"if the customer hasn’t added a brand logo then use the Jiminny logo","depth":18,"bounds":{"left":0.28922874,"top":0.08818835,"width":0.14943483,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"once the report is ready it should be shared with the users over email -","depth":16,"bounds":{"left":0.28125,"top":0.11053472,"width":0.15674867,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=12208-23064&t=nJK629FloDyaWRYR-1","depth":17,"bounds":{"left":0.28291222,"top":0.11053472,"width":0.35920876,"height":0.03312051},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=12208-23064&t=nJK629FloDyaWRYR-1","depth":18,"bounds":{"left":0.28291222,"top":0.11053472,"width":0.35920876,"height":0.03312051},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Connect your Figma account","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Connect your Figma account","depth":19,"bounds":{"left":0.36585772,"top":0.12968874,"width":0.06349734,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If no one is selected then the report will only be shared with the person who created it","depth":18,"bounds":{"left":0.28922874,"top":0.15203512,"width":0.18949468,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ensure the reports has a proper structure and formatting - headings, bold etc. - take examples from the Exec Reports","depth":16,"bounds":{"left":0.28125,"top":0.17438148,"width":0.2571476,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"ensure the report has links to playback when examples are used","depth":16,"bounds":{"left":0.28125,"top":0.19672786,"width":0.14245346,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in the beginning of each report have a brief section for ‘Data Srouce’ and ‘Objective’ - take the Exec summary report for example","depth":16,"bounds":{"left":0.28125,"top":0.21907422,"width":0.28208113,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"data source should cover what data has been analysed","depth":18,"bounds":{"left":0.28922874,"top":0.2414206,"width":0.122340426,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"objective should be a short paragraph that explains the goal","depth":18,"bounds":{"left":0.28922874,"top":0.26376694,"width":0.13164894,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Show the reports in Jiminny:","depth":15,"bounds":{"left":0.27327126,"top":0.292498,"width":0.06466091,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"show the report in the AI Reports page with a special logo -","depth":16,"bounds":{"left":0.28125,"top":0.32122904,"width":0.13231383,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Project Phoenix","depth":16,"bounds":{"left":0.41356382,"top":0.32122904,"width":0.034408245,"height":0.01396648},"help_text":"https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=5868-39681&t=nJK629FloDyaWRYR-1","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Project Phoenix","depth":17,"bounds":{"left":0.41356382,"top":0.32122904,"width":0.034408245,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"only the creator of the reports and the users it is shared with should be able to see it in the list","depth":16,"bounds":{"left":0.28125,"top":0.34357542,"width":0.2059508,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"users should be able to preview the report and download it","depth":16,"bounds":{"left":0.28125,"top":0.3659218,"width":0.12898937,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"the creator of the report should be able to delete it - deleting it will delete only this specific pdf","depth":16,"bounds":{"left":0.28125,"top":0.38826814,"width":0.20894282,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'Ask Jiminny Report' should be added as an option to the Report type filter so users can filter the list for such reports","depth":16,"bounds":{"left":0.28125,"top":0.41061452,"width":0.25631648,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"when a report is shared with a user then show who shared it in the ‘Shared’ column -","depth":16,"bounds":{"left":0.28125,"top":0.4329609,"width":0.18716756,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=14369-40078&t=We33fyQzIUfHuXVR-4","depth":17,"bounds":{"left":0.28291222,"top":0.4329609,"width":0.35688165,"height":0.03312051},"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=14369-40078&t=We33fyQzIUfHuXVR-4","depth":18,"bounds":{"left":0.28291222,"top":0.4329609,"width":0.35688165,"height":0.03312051},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Connect your Figma account","depth":17,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Connect your Figma account","depth":19,"bounds":{"left":0.39993352,"top":0.4521149,"width":0.06349734,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Collapse Subtasks Subtasks Work item actions Configure columns Create subtask","depth":11,"bounds":{"left":0.26462767,"top":0.5083799,"width":0.38314494,"height":0.025538707},"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXButton","text":"Collapse Subtasks","depth":13,"bounds":{"left":0.2632979,"top":0.51157224,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Collapse Subtasks","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Subtasks","depth":14,"bounds":{"left":0.27260637,"top":0.51356745,"width":0.023936171,"height":0.01556265},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Work item actions","depth":12,"bounds":{"left":0.62117684,"top":0.51157224,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Work item actions","depth":14,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"Configure columns","depth":14,"bounds":{"left":0.63048536,"top":0.51157224,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Configure columns","depth":16,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Create subtask","depth":13,"bounds":{"left":0.6397939,"top":0.51157224,"width":0.007978723,"height":0.01915403},"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Create subtask","depth":15,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"89","depth":14,"bounds":{"left":0.61918217,"top":0.5351157,"width":0.005817819,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"% Done","depth":13,"bounds":{"left":0.625,"top":0.5351157,"width":0.017453458,"height":0.01396648},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCell","text":"Work","depth":19,"bounds":{"left":0.27293882,"top":0.55706304,"width":0.2613032,"height":0.031923383},"help_text":"","role_description":"cell","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Work","depth":22,"bounds":{"left":0.2755984,"top":0.5666401,"width":0.010305851,"height":0.011971269},"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXMenuButton","text":"More actions for Work","depth":21,"bounds":{"left":0.5305851,"top":0.57102954,"width":0.0003324468,"height":0.01915403},"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8491113002773296207
|
1591227563580277957
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 2 Q2 - Platform Team - Scrum Board Platform Sprint 2 Q2 - Platform Team - Scrum Board - Jira
Project Phoenix – Figma
Project Phoenix – Figma
[JY-20372] AI Reports > Empty page design and promotion - Jira
[JY-20372] AI Reports > Empty page design and promotion - Jira
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Project Phoenix – Figma
Jiminny MCP Connector - Product - Confluence
Jiminny MCP Connector - Product - Confluence
Jiminny Mail
Jiminny Mail
[JY-20500] Batch initial sync for Salesforce - Jira
[JY-20500] Batch initial sync for Salesforce - Jira
Feed — jiminny — Sentry
Feed — jiminny — Sentry
Jiminny
Jiminny
Pipelines - jiminny/app
Pipelines - jiminny/app
Formalize
Formalize
[SRD-6793] Les Mills activity types not pulling in - Jira
[SRD-6793] Les Mills activity types not pulling in - Jira
Search results: calendar | Jiminny Help Center
Search results: calendar | Jiminny Help Center
Jiminny
Jiminny
Jiminny
Jiminny
Edit - Engineering - Confluence
Edit - Engineering - Confluence
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
[JY-18909] [Part2] Automated reports with Ask Jiminny - Jira
Close tab
New Tab
Customize sidebar
Open Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
Skip to:
Sidebar
Sidebar
Top Bar
Top Bar
Main Content
Main Content
Collapse sidebar [
Collapse sidebar [
Switch sites or apps
Switch sites or apps
Go to your Jira homepage
Search, press enter to navigate to advanced search with your text query
Create
Create
Rovo Ask Rovo
Ask Rovo
6 Notifications
6 Notifications
Help
Help
Settings
Settings
[EMAIL]
[EMAIL]
For you
For you
Recent
Recent
Starred
Starred
Apps
Apps
More actions for Apps
More actions for Apps
Spaces
Spaces
Create space
Create space
More actions for spaces
More actions for spaces
Recent
Jiminny (New)
Jiminny (New)
Jiminny (New)
Create board
Create board
More actions for Jiminny (New)
More actions for Jiminny (New)
Platform Team
Platform Team
Board actions
Board actions
SE Kanban
SE Kanban
Board actions
Board actions
Capture Team
Capture Team
Board actions
Board actions
Enterprise Stability Issues 🤕
Enterprise Stability Issues 🤕
Board actions
Board actions
Processing Team
Processing Team
Board actions
Board actions
Service-Desk
Service-Desk
More actions for Service-Desk
More actions for Service-Desk
More spaces
More spaces
Filters
Filters
More actions for Filters
More actions for Filters
Dashboards
Dashboards
Create dashboard
Create dashboard
More actions for Dashboards
More actions for Dashboards
Operations
Operations
More actions for Operations
More actions for Operations
Confluence , (opens new window)
Confluence
, (opens new window)
Teams , (opens new window)
Teams
, (opens new window)
open menu
open menu
Customise sidebar
Customise sidebar
Resize side navigation panel
Spaces
Spaces
/
Jiminny (New) Jiminny (New)
Jiminny (New)
/
Epic - Change parent
JY-19240
JY-19240
/
Story - Change work type
JY-18909
JY-18909
Copy link
[Part2] Automated reports with Ask Jiminny- Summary, edit
[Part2] Automated reports with Ask Jiminny
[Part2] Automated reports with Ask Jiminny
Add or create work related to this Story
Add or create work related to this Story
View app actions
View app actions
Collapse Description Description
Collapse Description
Collapse Description
Description
Edit Description, edit
We want to allow our users to automate the execution of their
AJA
prompts in order to save time and have them ready when they need them.
Create the reports:
admins and managers should be able to automate reports based on their Panorama prompts and saved searches
the report should be generated in a pdf - use a lightly branded one this time -
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=7691-61688&t=cLuF7fP7zTl4xBsQ-1
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=7691-61688&t=cLuF7fP7zTl4xBsQ-1
Connect your Figma account
Connect your Figma account
if the customer hasn’t added a brand logo then use the Jiminny logo
once the report is ready it should be shared with the users over email -
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=12208-23064&t=nJK629FloDyaWRYR-1
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=12208-23064&t=nJK629FloDyaWRYR-1
Connect your Figma account
Connect your Figma account
If no one is selected then the report will only be shared with the person who created it
ensure the reports has a proper structure and formatting - headings, bold etc. - take examples from the Exec Reports
ensure the report has links to playback when examples are used
in the beginning of each report have a brief section for ‘Data Srouce’ and ‘Objective’ - take the Exec summary report for example
data source should cover what data has been analysed
objective should be a short paragraph that explains the goal
Show the reports in Jiminny:
show the report in the AI Reports page with a special logo -
Project Phoenix
Project Phoenix
only the creator of the reports and the users it is shared with should be able to see it in the list
users should be able to preview the report and download it
the creator of the report should be able to delete it - deleting it will delete only this specific pdf
'Ask Jiminny Report' should be added as an option to the Report type filter so users can filter the list for such reports
when a report is shared with a user then show who shared it in the ‘Shared’ column -
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=14369-40078&t=We33fyQzIUfHuXVR-4
https://www.figma.com/design/jXcUe1y9mx5Fiz8KosLAUn/Project-Phoenix?node-id=14369-40078&t=We33fyQzIUfHuXVR-4
Connect your Figma account
Connect your Figma account
Collapse Subtasks Subtasks Work item actions Configure columns Create subtask
Collapse Subtasks
Collapse Subtasks
Subtasks
Work item actions
Work item actions
Configure columns
Configure columns
Create subtask
Create subtask
89
% Done
Work
Work
More actions for Work...
|
NULL
|
|
2481
|
51
|
5
|
2026-04-12T09:57:10.921661+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-12/1775 /Users/lukas/.screenpipe/data/data/2026-04-12/1775987830921_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
{
"name": "speakers"
} {
"name": "speakers"
},
{
"name": "sqlite_sequence"
},
{
"name": "tags"
},
{
"name": "ui_events"
},
{
"name": "ui_events_fts"
},
{
"name": "ui_events_fts_config"
},
{
"name": "ui_events_fts_data"
},
{
"name": "ui_events_fts_docsize"
},
{
"name": "ui_events_fts_idx"
},
{
"name": "video_chunks"
},
{
"name": "vision_tags"
}
]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST [URL_WITH_CREDENTIALS] ~ $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:
curl "[URL_WITH_CREDENTIALS] ~ $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Unable to access screenpipe activity data (claude)
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"{\n \"name\": \"speakers\"\n },\n {\n \"name\": \"sqlite_sequence\"\n },\n {\n \"name\": \"tags\"\n },\n {\n \"name\": \"ui_events\"\n },\n {\n \"name\": \"ui_events_fts\"\n },\n {\n \"name\": \"ui_events_fts_config\"\n },\n {\n \"name\": \"ui_events_fts_data\"\n },\n {\n \"name\": \"ui_events_fts_docsize\"\n },\n {\n \"name\": \"ui_events_fts_idx\"\n },\n {\n \"name\": \"video_chunks\"\n },\n {\n \"name\": \"vision_tags\"\n }\n]\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST http://localhost:3030/raw_sql \\\n -H \"Content-Type: application/json\" \\\n -d '{\"query\": \"PRAGMA table_info(frames) LIMIT 100\"}' | python3 -m json.tool\n{\n \"error\": \"error returned from database: (code: 1) near \\\"LIMIT\\\": syntax error\"\n}\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ clear \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:\ncurl \"http://localhost:3030/elements?\\\napp_name=Firefox&\\\nstart_time=2026-04-12T00:00:00Z&\\\nend_time=2026-04-12T23:59:59Z&\\\nsource=accessibility&\\ # or: ocr\nrole=AXTextField&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# AXButton, AXStaticText, AXLink, AXTextArea, AXRadioButton...\nq=screenpipe&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# text search within elements\nframe_id=2430&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# all elements from one specific frame\nlimit=50&\\\noffset=0\"\nzsh: command not found: #\ncurl: (3) URL rejected: Malformed input to a URL function\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $","depth":4,"value":"{\n \"name\": \"speakers\"\n },\n {\n \"name\": \"sqlite_sequence\"\n },\n {\n \"name\": \"tags\"\n },\n {\n \"name\": \"ui_events\"\n },\n {\n \"name\": \"ui_events_fts\"\n },\n {\n \"name\": \"ui_events_fts_config\"\n },\n {\n \"name\": \"ui_events_fts_data\"\n },\n {\n \"name\": \"ui_events_fts_docsize\"\n },\n {\n \"name\": \"ui_events_fts_idx\"\n },\n {\n \"name\": \"video_chunks\"\n },\n {\n \"name\": \"vision_tags\"\n }\n]\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST http://localhost:3030/raw_sql \\\n -H \"Content-Type: application/json\" \\\n -d '{\"query\": \"PRAGMA table_info(frames) LIMIT 100\"}' | python3 -m json.tool\n{\n \"error\": \"error returned from database: (code: 1) near \\\"LIMIT\\\": syntax error\"\n}\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ clear \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:\ncurl \"http://localhost:3030/elements?\\\napp_name=Firefox&\\\nstart_time=2026-04-12T00:00:00Z&\\\nend_time=2026-04-12T23:59:59Z&\\\nsource=accessibility&\\ # or: ocr\nrole=AXTextField&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# AXButton, AXStaticText, AXLink, AXTextArea, AXRadioButton...\nq=screenpipe&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# text search within elements\nframe_id=2430&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# all elements from one specific frame\nlimit=50&\\\noffset=0\"\nzsh: command not found: #\ncurl: (3) URL rejected: Malformed input to a URL function\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0013888889,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.0055555557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.12604167,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.13020833,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.25069445,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.25486112,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.37534723,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.3795139,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.50416666,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.6246528,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.62881947,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.74930555,"top":0.05888889,"width":0.124305554,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.7534722,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Unable to access screenpipe activity data (claude)","depth":2,"bounds":{"left":0.8736111,"top":0.05888889,"width":0.124305554,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.87777776,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.96944445,"top":0.032222223,"width":0.030555546,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.4965278,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
8491056771369897708
|
1083421503536153457
|
visual_change
|
accessibility
|
NULL
|
{
"name": "speakers"
} {
"name": "speakers"
},
{
"name": "sqlite_sequence"
},
{
"name": "tags"
},
{
"name": "ui_events"
},
{
"name": "ui_events_fts"
},
{
"name": "ui_events_fts_config"
},
{
"name": "ui_events_fts_data"
},
{
"name": "ui_events_fts_docsize"
},
{
"name": "ui_events_fts_idx"
},
{
"name": "video_chunks"
},
{
"name": "vision_tags"
}
]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST [URL_WITH_CREDENTIALS] ~ $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:
curl "[URL_WITH_CREDENTIALS] ~ $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Unable to access screenpipe activity data (claude)
Close Tab
⌥⌘1
-zsh...
|
2480
|
|
2484
|
51
|
8
|
2026-04-12T09:57:21.277250+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-12/1775 /Users/lukas/.screenpipe/data/data/2026-04-12/1775987841277_m1.jpg...
|
iTerm2
|
-zsh
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
{
"name": "speakers"
} {
"name": "speakers"
},
{
"name": "sqlite_sequence"
},
{
"name": "tags"
},
{
"name": "ui_events"
},
{
"name": "ui_events_fts"
},
{
"name": "ui_events_fts_config"
},
{
"name": "ui_events_fts_data"
},
{
"name": "ui_events_fts_docsize"
},
{
"name": "ui_events_fts_idx"
},
{
"name": "video_chunks"
},
{
"name": "vision_tags"
}
]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST [URL_WITH_CREDENTIALS] ~ $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:
curl "[URL_WITH_CREDENTIALS] ~ $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Unable to access screenpipe activity data (claude)
Close Tab
⌥⌘1
-zsh...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"{\n \"name\": \"speakers\"\n },\n {\n \"name\": \"sqlite_sequence\"\n },\n {\n \"name\": \"tags\"\n },\n {\n \"name\": \"ui_events\"\n },\n {\n \"name\": \"ui_events_fts\"\n },\n {\n \"name\": \"ui_events_fts_config\"\n },\n {\n \"name\": \"ui_events_fts_data\"\n },\n {\n \"name\": \"ui_events_fts_docsize\"\n },\n {\n \"name\": \"ui_events_fts_idx\"\n },\n {\n \"name\": \"video_chunks\"\n },\n {\n \"name\": \"vision_tags\"\n }\n]\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST http://localhost:3030/raw_sql \\\n -H \"Content-Type: application/json\" \\\n -d '{\"query\": \"PRAGMA table_info(frames) LIMIT 100\"}' | python3 -m json.tool\n{\n \"error\": \"error returned from database: (code: 1) near \\\"LIMIT\\\": syntax error\"\n}\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ clear \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:\ncurl \"http://localhost:3030/elements?\\\napp_name=Firefox&\\\nstart_time=2026-04-12T00:00:00Z&\\\nend_time=2026-04-12T23:59:59Z&\\\nsource=accessibility&\\ # or: ocr\nrole=AXTextField&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# AXButton, AXStaticText, AXLink, AXTextArea, AXRadioButton...\nq=screenpipe&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# text search within elements\nframe_id=2430&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# all elements from one specific frame\nlimit=50&\\\noffset=0\"\nzsh: command not found: #\ncurl: (3) URL rejected: Malformed input to a URL function\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $","depth":4,"value":"{\n \"name\": \"speakers\"\n },\n {\n \"name\": \"sqlite_sequence\"\n },\n {\n \"name\": \"tags\"\n },\n {\n \"name\": \"ui_events\"\n },\n {\n \"name\": \"ui_events_fts\"\n },\n {\n \"name\": \"ui_events_fts_config\"\n },\n {\n \"name\": \"ui_events_fts_data\"\n },\n {\n \"name\": \"ui_events_fts_docsize\"\n },\n {\n \"name\": \"ui_events_fts_idx\"\n },\n {\n \"name\": \"video_chunks\"\n },\n {\n \"name\": \"vision_tags\"\n }\n]\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST http://localhost:3030/raw_sql \\\n -H \"Content-Type: application/json\" \\\n -d '{\"query\": \"PRAGMA table_info(frames) LIMIT 100\"}' | python3 -m json.tool\n{\n \"error\": \"error returned from database: (code: 1) near \\\"LIMIT\\\": syntax error\"\n}\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ clear \nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:\ncurl \"http://localhost:3030/elements?\\\napp_name=Firefox&\\\nstart_time=2026-04-12T00:00:00Z&\\\nend_time=2026-04-12T23:59:59Z&\\\nsource=accessibility&\\ # or: ocr\nrole=AXTextField&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# AXButton, AXStaticText, AXLink, AXTextArea, AXRadioButton...\nq=screenpipe&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# text search within elements\nframe_id=2430&\\\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000# all elements from one specific frame\nlimit=50&\\\noffset=0\"\nzsh: command not found: #\ncurl: (3) URL rejected: Malformed input to a URL function\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0013888889,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.0055555557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.12604167,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.13020833,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.25069445,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.25486112,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.37534723,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.3795139,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.5,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.50416666,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.6246528,"top":0.05888889,"width":0.12465278,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.62881947,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.74930555,"top":0.05888889,"width":0.124305554,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.7534722,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Unable to access screenpipe activity data (claude)","depth":2,"bounds":{"left":0.8736111,"top":0.05888889,"width":0.124305554,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.87777776,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.96944445,"top":0.032222223,"width":0.030555546,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"-zsh","depth":1,"bounds":{"left":0.4965278,"top":0.033333335,"width":0.022916667,"height":0.017777778},"role_description":"text"}]...
|
8491056771369897708
|
1083421503536153457
|
click
|
accessibility
|
NULL
|
{
"name": "speakers"
} {
"name": "speakers"
},
{
"name": "sqlite_sequence"
},
{
"name": "tags"
},
{
"name": "ui_events"
},
{
"name": "ui_events_fts"
},
{
"name": "ui_events_fts_config"
},
{
"name": "ui_events_fts_data"
},
{
"name": "ui_events_fts_docsize"
},
{
"name": "ui_events_fts_idx"
},
{
"name": "video_chunks"
},
{
"name": "vision_tags"
}
]
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ curl -s -X POST [URL_WITH_CREDENTIALS] ~ $ clear
lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ # All confirmed params:
curl "[URL_WITH_CREDENTIALS] ~ $
DOCKER
Close Tab
DEV (-zsh)
Close Tab
APP (-zsh)
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
-zsh
Close Tab
✳ Unable to access screenpipe activity data (claude)
Close Tab
⌥⌘1
-zsh...
|
NULL
|