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