1+ import {
2+ IAgentClientTasks ,
3+ IAgentClientSetup ,
4+ } from "../agent-client-interface" ;
5+ import { Client } from "../api-clients/pint/client" ;
6+ import {
7+ listTasks ,
8+ getTask as getTaskAPI ,
9+ executeTaskAction ,
10+ listSetupTasks ,
11+ } from "../api-clients/pint" ;
12+ import { task , setup } from "../pitcher-protocol" ;
13+ import { Emitter } from "../utils/event" ;
14+ export class PintClientTasks implements IAgentClientTasks {
15+ private onTaskUpdateEmitter = new Emitter < task . TaskDTO > ( ) ;
16+ onTaskUpdate = this . onTaskUpdateEmitter . event ;
17+
18+ constructor ( private apiClient : Client ) { }
19+
20+ async getTasks ( ) : Promise < task . TaskListDTO > {
21+ try {
22+ const response = await listTasks ( {
23+ client : this . apiClient ,
24+ } ) ;
25+
26+ if ( response . data ) {
27+ // Convert API response to TaskListDTO format
28+ const tasks : Record < string , task . TaskDTO > = { } ;
29+
30+ response . data . tasks . forEach ( ( apiTask ) => {
31+ tasks [ apiTask . id ] = {
32+ id : apiTask . id ,
33+ name : apiTask . config . name ,
34+ command : apiTask . config . command ,
35+ runAtStart : apiTask . config . runAtStart ,
36+ preview : {
37+ port : apiTask . config . preview ?. port ,
38+ } ,
39+ shell : null , // TODO: Map exec to shell if needed
40+ ports : [ ] , // TODO: Map ports if available
41+ } ;
42+ } ) ;
43+
44+ return {
45+ tasks,
46+ setupTasks : [ ] , // TODO: Add setup tasks if needed
47+ validationErrors : [ ] ,
48+ } ;
49+ } else {
50+ throw new Error ( response . error ?. message || "Failed to fetch tasks" ) ;
51+ }
52+ } catch ( error ) {
53+ console . error ( "Failed to get tasks:" , error ) ;
54+ return {
55+ tasks : { } ,
56+ setupTasks : [ ] ,
57+ validationErrors : [ error instanceof Error ? error . message : "Unknown error" ] ,
58+ } ;
59+ }
60+ }
61+
62+ async getTask ( taskId : string ) : Promise < task . TaskDTO | undefined > {
63+ try {
64+ const response = await getTaskAPI ( {
65+ client : this . apiClient ,
66+ path : { id : taskId } ,
67+ } ) ;
68+
69+ if ( response . data ) {
70+ const apiTask = response . data . task ;
71+ return {
72+ id : apiTask . id ,
73+ name : apiTask . config . name ,
74+ command : apiTask . config . command ,
75+ runAtStart : apiTask . config . runAtStart ,
76+ preview : {
77+ port : apiTask . config . preview ?. port ,
78+ } ,
79+ shell : null , // TODO: Map exec to shell if needed
80+ ports : [ ] , // TODO: Map ports if available
81+ } ;
82+ } else {
83+ return undefined ;
84+ }
85+ } catch ( error ) {
86+ console . error ( "Failed to get task:" , error ) ;
87+ return undefined ;
88+ }
89+ }
90+
91+ async runTask ( taskId : string ) : Promise < task . TaskDTO > {
92+ try {
93+ const response = await executeTaskAction ( {
94+ client : this . apiClient ,
95+ path : { id : taskId } ,
96+ query : { actionType : "start" } ,
97+ } ) ;
98+
99+ if ( response . data ) {
100+ const taskDTO : task . TaskDTO = {
101+ id : response . data . id ,
102+ name : response . data . name ,
103+ command : response . data . command ,
104+ runAtStart : false , // API doesn't provide this in action response
105+ shell : null , // TODO: Map exec to shell if needed
106+ ports : [ ] , // TODO: Map ports if available
107+ } ;
108+
109+ // Emit task update event
110+ this . onTaskUpdateEmitter . fire ( taskDTO ) ;
111+
112+ return taskDTO ;
113+ } else {
114+ throw new Error ( response . error ?. message || "Failed to run task" ) ;
115+ }
116+ } catch ( error ) {
117+ console . error ( "Failed to run task:" , error ) ;
118+ throw error ;
119+ }
120+ }
121+
122+ async stopTask ( taskId : string ) : Promise < task . TaskDTO | null > {
123+ try {
124+ const response = await executeTaskAction ( {
125+ client : this . apiClient ,
126+ path : { id : taskId } ,
127+ query : { actionType : "stop" } ,
128+ } ) ;
129+
130+ if ( response . data ) {
131+ const taskDTO : task . TaskDTO = {
132+ id : response . data . id ,
133+ name : response . data . name ,
134+ command : response . data . command ,
135+ runAtStart : false , // API doesn't provide this in action response
136+ shell : null , // TODO: Map exec to shell if needed
137+ ports : [ ] , // TODO: Map ports if available
138+ } ;
139+
140+ // Emit task update event
141+ this . onTaskUpdateEmitter . fire ( taskDTO ) ;
142+
143+ return taskDTO ;
144+ } else {
145+ return null ;
146+ }
147+ } catch ( error ) {
148+ console . error ( "Failed to stop task:" , error ) ;
149+ return null ;
150+ }
151+ }
152+ }
153+
154+ export class PintClientSetup implements IAgentClientSetup {
155+ private onSetupProgressUpdateEmitter = new Emitter < setup . SetupProgress > ( ) ;
156+ onSetupProgressUpdate = this . onSetupProgressUpdateEmitter . event ;
157+
158+ constructor ( private apiClient : Client ) { }
159+
160+ async getProgress ( ) : Promise < setup . SetupProgress > {
161+ try {
162+ // Get setup tasks from the API
163+ const response = await listSetupTasks ( {
164+ client : this . apiClient ,
165+ } ) ;
166+
167+ if ( response . data ) {
168+ // Convert API setup tasks to setup progress format
169+ const steps : setup . Step [ ] = response . data . setupTasks . map ( ( setupTask ) => ( {
170+ name : setupTask . name ,
171+ command : setupTask . command ,
172+ shellId : setupTask . execId || null ,
173+ finishStatus : setupTask . status === 'FINISHED' ? 'SUCCEEDED' :
174+ setupTask . status === 'ERROR' ? 'FAILED' : null ,
175+ } ) ) ;
176+
177+ // Determine overall state based on task statuses
178+ let state : setup . SetupProgress [ 'state' ] = 'IDLE' ;
179+ let currentStepIndex = 0 ;
180+
181+ const hasRunningTask = response . data . setupTasks . some ( task => task . status === 'RUNNING' ) ;
182+ const allFinished = response . data . setupTasks . every ( task =>
183+ task . status === 'FINISHED' || task . status === 'ERROR' ) ;
184+
185+ if ( hasRunningTask ) {
186+ state = 'IN_PROGRESS' ;
187+ // Find the first running task
188+ currentStepIndex = response . data . setupTasks . findIndex ( task => task . status === 'RUNNING' ) ;
189+ } else if ( allFinished ) {
190+ state = 'FINISHED' ;
191+ currentStepIndex = steps . length - 1 ;
192+ }
193+
194+ return {
195+ state,
196+ steps,
197+ currentStepIndex : Math . max ( 0 , currentStepIndex ) ,
198+ } ;
199+ } else {
200+ // Return empty setup progress if no data
201+ return {
202+ state : 'IDLE' ,
203+ steps : [ ] ,
204+ currentStepIndex : 0 ,
205+ } ;
206+ }
207+ } catch ( error ) {
208+ console . error ( "Failed to get setup progress:" , error ) ;
209+ return {
210+ state : 'IDLE' ,
211+ steps : [ ] ,
212+ currentStepIndex : 0 ,
213+ } ;
214+ }
215+ }
216+
217+ async init ( ) : Promise < setup . SetupProgress > {
218+ const progress = await this . getProgress ( ) ;
219+
220+ // Emit progress update event
221+ this . onSetupProgressUpdateEmitter . fire ( progress ) ;
222+
223+ return progress ;
224+ }
225+ }
0 commit comments