Skip to content

Commit bab9005

Browse files
authored
Merge pull request #228 from codesandbox/CSB-703-integrate-with-pint-tasks-apis
chore: add tasks and setup-tasks operations integration
2 parents f4e9cf3 + d588462 commit bab9005

File tree

2 files changed

+242
-3
lines changed

2 files changed

+242
-3
lines changed

src/PintClient/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { SandboxSession } from "../types";
44
import { Disposable } from "../utils/disposable";
55
import { Client, createClient, createConfig } from "../api-clients/pint/client";
66
import {PintFsClient} from "./fs";
7+
import { PintClientTasks, PintClientSetup, PintClientSystem } from "./tasks";
78
import {
89
IAgentClient,
910
IAgentClientPorts,
@@ -117,9 +118,9 @@ export class PintClient implements IAgentClient {
117118
this.ports = new PintPortsClient(apiClient, this.sandboxId);
118119
this.shells = {} as IAgentClientShells; // Not implemented for Pint
119120
this.fs = new PintFsClient(apiClient);
120-
this.tasks = {} as IAgentClientTasks; // Not implemented for Pint
121-
this.setup = {} as IAgentClientSetup; // Not implemented for Pint
122-
this.system = {} as IAgentClientSystem; // Not implemented for Pint
121+
this.tasks = new PintClientTasks(apiClient);
122+
this.setup = new PintClientSetup(apiClient);
123+
this.system = new PintClientSystem(apiClient);
123124
}
124125

125126
ping(): void {}

src/PintClient/tasks.ts

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
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

Comments
 (0)