add list runs endpoint

This commit is contained in:
Ramnique Singh 2025-12-09 16:35:27 +05:30
parent 1822deadc1
commit 338cc3d2f9
2 changed files with 91 additions and 2 deletions

View file

@ -22,6 +22,7 @@ export const CreateRunOptions = Run.pick({
export interface IRunsRepo {
create(options: z.infer<typeof CreateRunOptions>): Promise<z.infer<typeof Run>>;
fetch(id: string): Promise<z.infer<typeof Run>>;
list(cursor?: string): Promise<z.infer<typeof ListRunsResponse>>;
appendEvents(runId: string, events: z.infer<typeof RunEvent>[]): Promise<void>;
}
@ -76,4 +77,68 @@ export class FSRunsRepo implements IRunsRepo {
log: events,
};
}
async list(cursor?: string): Promise<z.infer<typeof ListRunsResponse>> {
const runsDir = path.join(WorkDir, 'runs');
const PAGE_SIZE = 20;
let files: string[] = [];
try {
const entries = await fsp.readdir(runsDir, { withFileTypes: true });
files = entries
.filter(e => e.isFile() && e.name.endsWith('.jsonl'))
.map(e => e.name);
} catch (err: any) {
if (err && err.code === 'ENOENT') {
return { runs: [] };
}
throw err;
}
files.sort((a, b) => b.localeCompare(a));
const cursorFile = cursor;
let startIndex = 0;
if (cursorFile) {
const exact = files.indexOf(cursorFile);
if (exact >= 0) {
startIndex = exact + 1;
} else {
const firstOlder = files.findIndex(name => name.localeCompare(cursorFile) < 0);
startIndex = firstOlder === -1 ? files.length : firstOlder;
}
}
const selected = files.slice(startIndex, startIndex + PAGE_SIZE);
const runs: z.infer<typeof ListRunsResponse>['runs'] = [];
for (const name of selected) {
const runId = name.slice(0, -'.jsonl'.length);
try {
const contents = await fsp.readFile(path.join(runsDir, name), 'utf8');
const firstLine = contents.split('\n').find(line => line.trim() !== '');
if (!firstLine) {
continue;
}
const start = StartEvent.parse(JSON.parse(firstLine));
runs.push({
id: runId,
createdAt: start.ts!,
agentId: start.agentName,
});
} catch {
continue;
}
}
const hasMore = startIndex + PAGE_SIZE < files.length;
const nextCursor = hasMore && selected.length > 0
? selected[selected.length - 1]
: undefined;
return {
runs,
...(nextCursor ? { nextCursor } : {}),
};
}
}

View file

@ -12,9 +12,8 @@ import { ModelConfig, Provider } from "./models/models.js";
import { IAgentsRepo } from "./agents/repo.js";
import { Agent } from "./agents/agents.js";
import { AskHumanResponsePayload, authorizePermission, createMessage, createRun, replyToHumanInputRequest, Run, stop, ToolPermissionAuthorizePayload } from './runs/runs.js';
import { IRunsRepo, ListRunsResponse, CreateRunOptions } from './runs/repo.js';
import { IRunsRepo, CreateRunOptions, ListRunsResponse } from './runs/repo.js';
import { IBus } from './application/lib/bus.js';
import { RunEvent } from './entities/run-events.js';
let id = 0;
@ -462,6 +461,31 @@ const routes = new Hono()
return c.json(run);
}
)
.get(
'/runs',
describeRoute({
summary: 'List runs',
description: 'List all runs',
responses: {
200: {
description: 'Runs list',
content: {
'application/json': {
schema: resolver(ListRunsResponse),
},
},
},
},
}),
validator('query', z.object({
cursor: z.string().optional(),
})),
async (c) => {
const repo = container.resolve<IRunsRepo>('runsRepo');
const runs = await repo.list(c.req.valid('query').cursor);
return c.json(runs);
}
)
.post(
'/runs/:runId/messages/new',
describeRoute({