@@ -38,6 +38,24 @@ function adb(args, opts = {}) {
3838 } ) ;
3939}
4040
41+ function resolveWatchTarget ( ) {
42+ if ( watchTarget ) return watchTarget ;
43+ try {
44+ const out = execFileSync ( 'adb' , [ 'devices' ] , { timeout : 5000 , encoding : 'utf8' } ) ;
45+ const online = out
46+ . split ( '\n' )
47+ . filter ( l => l . includes ( '\tdevice' ) )
48+ . map ( l => l . split ( '\t' ) [ 0 ] . trim ( ) )
49+ . filter ( Boolean ) ;
50+ if ( online . length > 0 ) {
51+ watchTarget = online [ 0 ] ;
52+ saveState ( { watchTarget } ) ;
53+ return watchTarget ;
54+ }
55+ } catch { }
56+ return null ;
57+ }
58+
4159// ── Watch ADB connection ──────────────────────────────
4260
4361app . get ( '/api/watch' , ( req , res ) => {
@@ -80,6 +98,41 @@ app.post('/api/watch/connect', (req, res) => {
8098 }
8199} ) ;
82100
101+ // Trigger urgent alert on the connected watch (for server-initiated alert bridge testing)
102+ app . post ( '/api/watch/alert' , ( req , res ) => {
103+ const requiredKey = process . env . CLAWWATCH_ALERT_KEY ;
104+ const suppliedKey = req . headers [ 'x-clawwatch-alert-key' ] ;
105+ if ( requiredKey && suppliedKey !== requiredKey ) {
106+ return res . status ( 401 ) . json ( { ok : false , error : 'Unauthorized alert bridge call' } ) ;
107+ }
108+
109+ const payload = req . body || { } ;
110+ const eventId = String ( payload . event_id || `evt-${ Date . now ( ) } ` ) ;
111+ const title = String ( payload . title || 'URGENT alert' ) ;
112+ const body = String ( payload . body || payload . prompt || 'Open ClawWatch for details.' ) ;
113+ const room = String ( payload . room || '' ) ;
114+ const prompt = String ( payload . prompt || body ) ;
115+ const target = resolveWatchTarget ( ) ;
116+ if ( ! target ) return res . json ( { ok : false , error : 'Watch target not set. Connect watch first.' } ) ;
117+
118+ try {
119+ execFileSync ( 'adb' , [ '-s' , target , ...[
120+ 'shell' , 'am' , 'start' ,
121+ '-n' , `${ PKG } /.MainActivity` ,
122+ '-a' , 'com.thinkoff.clawwatch.ALERT_OPEN' ,
123+ '--es' , 'alert_event_id' , eventId ,
124+ '--es' , 'alert_title' , title ,
125+ '--es' , 'alert_body' , body ,
126+ '--es' , 'alert_room' , room ,
127+ '--es' , 'alert_prompt' , prompt
128+ ] ] , { timeout : 8000 , encoding : 'utf8' } ) ;
129+
130+ return res . json ( { ok : true , dispatched : true , target, event_id : eventId } ) ;
131+ } catch ( e ) {
132+ return res . json ( { ok : false , error : e . message , target } ) ;
133+ }
134+ } ) ;
135+
83136// ── nullclaw.json config (asset file on Mac) ──────────
84137
85138app . get ( '/api/config' , ( req , res ) => {
0 commit comments