Skip to content

Commit 332aa8e

Browse files
committed
feat(workflow): query runs by workspace
1 parent 058297b commit 332aa8e

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

src/workflow-store.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,32 @@ try {
135135
assert.equal(store.getRun(run2.id)?.status, "completed");
136136
assert.equal(store.getRun(run2.id)?.resultJson, JSON.stringify({ ok: 1 }));
137137

138+
const otherProjectRun = store.createRun({
139+
name: "other-project",
140+
source: "inline",
141+
scriptPath: join(root, "other.js"),
142+
scriptHash: "other",
143+
workspaceRoot: join(root, "other-project"),
144+
});
145+
assert.deepEqual(
146+
store
147+
.listRunsForWorkspace(join(root, "project"))
148+
.map((entry) => entry.id)
149+
.sort(),
150+
[run.id, run2.id].sort(),
151+
);
152+
assert.deepEqual(
153+
store
154+
.listRunsForWorkspace(join(root, "project"), { statuses: ["completed"] })
155+
.map((entry) => entry.id),
156+
[run2.id],
157+
);
158+
assert.equal(
159+
store.listRunsForWorkspace(join(root, "other-project"))[0]?.id,
160+
otherProjectRun.id,
161+
);
162+
assert.deepEqual(store.listEvents(run.id, 2).map((event) => event.seq), [2, 3]);
163+
138164
// Reap: stale heartbeat + dead pid (force heartbeat via shared sqlite handle)
139165
const run3 = store.createRun({
140166
name: "stale",

src/workflow-store.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,40 @@ export class WorkflowStore {
248248
return rows.map(rowToRun);
249249
}
250250

251+
listRunsForWorkspace(
252+
workspaceRoot: string,
253+
options: {
254+
statuses?: WorkflowRunStatus[];
255+
limit?: number;
256+
} = {},
257+
): WorkflowRunRecord[] {
258+
const root = resolve(workspaceRoot);
259+
const limit = Math.max(1, Math.min(options.limit ?? 50, 500));
260+
const statuses = options.statuses?.filter((status, index, values) =>
261+
values.indexOf(status) === index,
262+
);
263+
264+
if (!statuses?.length) {
265+
const rows = this.database.sqlite
266+
.prepare(
267+
"select * from workflow_runs where workspace_root = ? order by updated_at desc limit ?",
268+
)
269+
.all(root, limit) as WorkflowRunRow[];
270+
return rows.map(rowToRun);
271+
}
272+
273+
const placeholders = statuses.map(() => "?").join(", ");
274+
const rows = this.database.sqlite
275+
.prepare(
276+
`select * from workflow_runs
277+
where workspace_root = ? and status in (${placeholders})
278+
order by updated_at desc
279+
limit ?`,
280+
)
281+
.all(root, ...statuses, limit) as WorkflowRunRow[];
282+
return rows.map(rowToRun);
283+
}
284+
251285
/**
252286
* Atomically claim a starting run for the worker.
253287
* Returns undefined if the run is missing or not claimable.
@@ -570,6 +604,21 @@ export class WorkflowStore {
570604
};
571605
}
572606

607+
listEvents(runId: string, limit = 100): WorkflowEventRecord[] {
608+
const capped = Math.max(1, Math.min(limit, WORKFLOW_LIMITS.eventDrainMax));
609+
const rows = this.database.sqlite
610+
.prepare(
611+
`select * from (
612+
select * from workflow_events
613+
where run_id = ?
614+
order by seq desc
615+
limit ?
616+
) order by seq asc`,
617+
)
618+
.all(runId, capped) as WorkflowEventRow[];
619+
return rows.map(rowToEvent);
620+
}
621+
573622
beginAgentCall(input: BeginAgentCallInput): WorkflowAgentCallRecord {
574623
const now = isoNow();
575624
const isolation: AgentIsolationMode = input.isolation === "worktree" ? "worktree" : "shared";

0 commit comments

Comments
 (0)