Skip to content

Commit 1abb3bc

Browse files
committed
add setup tasks pint integration
1 parent d1809be commit 1abb3bc

File tree

2 files changed

+227
-1
lines changed

2 files changed

+227
-1
lines changed

src/PintClient/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { SandboxSession } from "../types";
44
import { Emitter, EmitterSubscription, Event } from "../utils/event";
55
import { Disposable } from "../utils/disposable";
66
import { Client, createClient, createConfig } from "../api-clients/pint/client";
7-
import { PintClientTasks } from "./tasks";
7+
import { PintClientTasks, PintClientSetup } from "./tasks";
88
import {
99
IAgentClient,
1010
IAgentClientPorts,
@@ -748,6 +748,7 @@ export class PintClient implements IAgentClient {
748748
this.shells = new PintShellsClient(apiClient, this.sandboxId);
749749
this.fs = new PintFsClient(apiClient);
750750
this.tasks = new PintClientTasks(apiClient);
751+
this.setup = new PintClientSetup(apiClient);
751752
}
752753

753754
ping(): void {}

src/PintClient/tasks.ts

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

Comments
 (0)