1
1
import { BrowserUse } from "browser-use-sdk" ;
2
- import { SlashCommandBuilder } from "discord.js" ;
2
+ import { SlashCommandBuilder , EmbedBuilder } from "discord.js" ;
3
3
4
4
import { ExhaustiveSwitchCheck } from "../lib/types" ;
5
5
import type { Command } from "./types" ;
@@ -24,169 +24,100 @@ export const run: Command = {
24
24
25
25
const tick : { current : number } = { current : 0 } ;
26
26
27
- const state : { current : BrowserState } = { current : null } ;
27
+ const task = await browseruse . tasks . create ( {
28
+ task : command ,
29
+ agentSettings : {
30
+ llm : "o3" ,
31
+ } ,
32
+ } ) ;
33
+
34
+ const embed = new EmbedBuilder ( )
35
+ . setTitle ( "🤖 Browser Use Task" )
36
+ . setDescription ( `**Command:** ${ command } \n**Task ID:** ${ task . id } ` )
37
+ . setColor ( 0x0099ff )
38
+ . addFields (
39
+ { name : "Status" , value : "🔄 Starting..." , inline : true } ,
40
+ { name : "Live Session" , value : "⏳ Waiting..." , inline : true } ,
41
+ )
42
+ . setTimestamp ( ) ;
43
+
44
+ await interaction . reply ( { embeds : [ embed ] } ) ;
28
45
29
46
poll: do {
30
47
tick . current ++ ;
31
48
32
- let status : BrowserUse . TaskView ;
33
-
34
- // NOTE: We take action on each tick.
35
- if ( state . current == null ) {
36
- status = await browseruse . tasks . create ( {
37
- task : command ,
38
- agentSettings : {
39
- llm : "o3" ,
40
- } ,
41
- } ) ;
42
- } else {
43
- status = ( await browseruse . tasks . retrieve ( state . current . taskId ) ) as BrowserUse . TaskView ;
44
- }
45
-
46
- const [ newState , events ] = reducer ( state . current , { kind : "status" , status } ) ;
47
-
48
- for ( const event of events ) {
49
- switch ( event . kind ) {
50
- case "task_started" :
51
- interaction . reply ( `Browser Use Task started (${ event . taskId } )` ) ;
52
- break ;
53
- case "session_live_url_ready" :
54
- interaction . followUp ( `Watch live Browser Use session at ${ event . liveUrl } ` ) ;
55
- break ;
56
- case "task_step_completed" :
57
- interaction . followUp ( `[${ event . step . url } ] ${ event . step . nextGoal } ` ) ;
58
- break ;
59
- case "task_completed" :
60
- interaction . followUp ( event . output ) ;
61
- break poll;
62
- default :
63
- throw new ExhaustiveSwitchCheck ( event ) ;
64
- }
65
- }
66
-
67
- state . current = newState ;
68
-
69
- // LOGS
70
-
71
- if ( state . current != null ) {
72
- console . log ( `${ state . current . taskId } | [${ tick . current } ] ${ status . status } ` . padEnd ( 100 , "-" ) ) ;
73
- for ( const event of events ) {
74
- console . log ( `${ state . current . taskId } | - ${ event . kind } ` ) ;
75
- }
76
- } else {
77
- // NOTE: This should never happen!
78
- throw new Error ( "Task unexpectedly got negative status update..." ) ;
79
- }
80
-
81
- // TIMER
49
+ const status = ( await browseruse . tasks . retrieve ( task . id ) ) as BrowserUse . TaskView ;
82
50
83
- await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
84
- } while ( true ) ;
51
+ switch ( status . status ) {
52
+ case "started" :
53
+ case "stopped" :
54
+ case "paused" : {
55
+ const liveUrl = status . sessionLiveUrl ?? "⏳ Waiting..." ;
85
56
86
- if ( state . current == null ) {
87
- console . log ( `TASK NOT STARTED` ) ;
88
- } else {
89
- console . log ( `${ state . current . taskId } | [${ tick . current } ] DONE ` . padEnd ( 100 , "-" ) ) ;
90
- }
91
- } ,
92
- } ;
57
+ const description : string [ ] = [ ] ;
93
58
94
- // Event Loop ----------------------------------------------------------------
59
+ description . push ( `**Command:** ${ command } ` ) ;
60
+ description . push ( `**Task ID:** ${ task . id } ` ) ;
95
61
96
- type BrowserState = {
97
- taskId : string ;
98
- sessionId : string ;
62
+ description . push ( "" ) ;
99
63
100
- liveSessionUrl : string | null ;
64
+ if ( status . steps ) {
65
+ for ( const step of status . steps ) {
66
+ description . push ( `- [${ step . url } ] ${ step . nextGoal } ` ) ;
67
+ }
68
+ } else {
69
+ description . push ( "No steps yet" ) ;
70
+ }
101
71
102
- steps : BrowserUse . TaskView . Step [ ] ;
72
+ if ( status . doneOutput ) {
73
+ description . push ( "" ) ;
74
+ description . push ( status . doneOutput ) ;
75
+ }
103
76
104
- output : string | null ;
105
- } | null ;
77
+ const embed = new EmbedBuilder ( )
78
+ . setTitle ( "🤖 Browser Use Task" )
79
+ . setDescription ( description . join ( "\n" ) )
80
+ . setColor ( 0x0099ff )
81
+ . addFields (
82
+ { name : "Status" , value : "🔄 Running..." , inline : true } ,
83
+ { name : "Live Session" , value : liveUrl , inline : true } ,
84
+ )
85
+ . setTimestamp ( ) ;
106
86
107
- type BrowserAction = {
108
- kind : "status" ;
109
- status : BrowserUse . TaskView ;
110
- } ;
87
+ await interaction . editReply ( { embeds : [ embed ] } ) ;
111
88
112
- type ReducerEvent =
113
- | {
114
- kind : "task_started" ;
115
- taskId : string ;
116
- sessionId : string ;
117
- }
118
- | {
119
- kind : "session_live_url_ready" ;
120
- liveUrl : string ;
121
- }
122
- | {
123
- kind : "task_step_completed" ;
124
- step : BrowserUse . TaskView . Step ;
125
- }
126
- | {
127
- kind : "task_completed" ;
128
- output : string ;
129
- } ;
130
-
131
- function reducer ( state : BrowserState , action : BrowserAction ) : [ BrowserState , Array < ReducerEvent > ] {
132
- switch ( action . kind ) {
133
- case "status" : {
134
- if ( state == null ) {
135
- const liveUrl = action . status . sessionLiveUrl ?? null ;
136
-
137
- const state : BrowserState = {
138
- taskId : action . status . id ,
139
- sessionId : action . status . sessionId ,
140
- liveSessionUrl : liveUrl ,
141
- steps : [ ] ,
142
- output : null ,
143
- } ;
144
-
145
- const events : Array < ReducerEvent > = [
146
- { kind : "task_started" , taskId : action . status . id , sessionId : action . status . sessionId } ,
147
- ] ;
148
-
149
- if ( liveUrl != null ) {
150
- events . push ( { kind : "session_live_url_ready" , liveUrl } ) ;
89
+ break ;
151
90
}
152
91
153
- return [ state , events ] ;
154
- }
92
+ case "finished" : {
93
+ const output : string [ ] = [ ] ;
155
94
156
- const events : Array < ReducerEvent > = [ ] ;
95
+ output . push ( `# Browser Use Task - ${ task . id } ✅` ) ;
96
+ output . push ( `## Task` ) ;
97
+ output . push ( command ) ;
157
98
158
- const liveUrl = action . status . sessionLiveUrl ?? null ;
99
+ output . push ( "" ) ;
159
100
160
- if ( state . liveSessionUrl == null && liveUrl != null ) {
161
- events . push ( { kind : "session_live_url_ready" , liveUrl } ) ;
162
- }
101
+ output . push ( `## Output` ) ;
102
+ output . push ( status . doneOutput ) ;
163
103
164
- const steps = state . steps ;
165
- if ( action . status . steps != null ) {
166
- const newSteps = action . status . steps . slice ( state . steps . length ) ;
104
+ await interaction . editReply ( { content : output . join ( "\n" ) , embeds : [ ] } ) ;
167
105
168
- for ( const step of newSteps ) {
169
- steps . push ( step ) ;
170
- events . push ( { kind : "task_step_completed" , step } ) ;
106
+ break poll;
171
107
}
108
+ default :
109
+ throw new ExhaustiveSwitchCheck ( status . status ) ;
172
110
}
173
111
174
- const output = action . status . doneOutput && action . status . doneOutput . length > 0 ? action . status . doneOutput : null ;
112
+ // LOGS
113
+
114
+ console . log ( `[${ status . id } ] (${ tick . current } ) ${ status . status } ` ) ;
175
115
176
- if ( state . output == null && output != null ) {
177
- events . push ( { kind : "task_completed" , output } ) ;
178
- }
116
+ // TIMER
179
117
180
- const newState : BrowserState = {
181
- ...state ,
182
- liveSessionUrl : liveUrl ,
183
- steps : steps ,
184
- output : output ,
185
- } ;
118
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
119
+ } while ( true ) ;
186
120
187
- return [ newState , events ] ;
188
- }
189
- default :
190
- throw new ExhaustiveSwitchCheck ( action . kind ) ;
191
- }
192
- }
121
+ console . log ( "done" ) ;
122
+ } ,
123
+ } ;
0 commit comments