@devosurf/tesser-server 0.1.0-alpha.2 → 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devosurf/tesser-server",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "Tesser runtime, scheduler, credential broker, git-sync and control-plane.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -20,9 +20,9 @@
20
20
  "esbuild": "^0.28.1",
21
21
  "hono": "^4.12.25",
22
22
  "pg": "^8.21.0",
23
- "@devosurf/tesser-testing": "^0.1.0-alpha.2",
24
- "@devosurf/tesser-brand": "0.1.0-alpha.2",
25
- "@devosurf/tesser-sdk": "0.1.0-alpha.2"
23
+ "@devosurf/tesser-brand": "0.1.0-alpha.4",
24
+ "@devosurf/tesser-sdk": "0.1.0-alpha.4",
25
+ "@devosurf/tesser-testing": "^0.1.0-alpha.4"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/pg": "^8.20.0"
package/src/http/api.ts CHANGED
@@ -234,7 +234,10 @@ export function createApi(deps: ApiDeps): Hono<ApiEnv> {
234
234
  });
235
235
 
236
236
  api.get("/runs", async (c) => {
237
- const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);
237
+ const requestedLimit = Number(c.req.query("limit") ?? 50);
238
+ const requestedOffset = Number(c.req.query("offset") ?? 0);
239
+ const limit = Number.isFinite(requestedLimit) ? Math.min(Math.max(Math.trunc(requestedLimit), 1), 200) : 50;
240
+ const offset = Number.isFinite(requestedOffset) ? Math.max(Math.trunc(requestedOffset), 0) : 0;
238
241
  const params: unknown[] = [c.get("workspaceId")];
239
242
  let filter = "";
240
243
  if (c.req.query("project")) {
@@ -249,16 +252,23 @@ export function createApi(deps: ApiDeps): Hono<ApiEnv> {
249
252
  params.push(c.req.query("status"));
250
253
  filter += ` AND r.status = $${params.length}`;
251
254
  }
252
- params.push(limit);
255
+ const countParams = [...params];
256
+ const total = Number((await deps.db.query<{ count: string }>(
257
+ `SELECT count(*)::text AS count
258
+ FROM runs r JOIN projects p ON p.id = r.project_id
259
+ WHERE p.workspace_id = $1 ${filter}`,
260
+ countParams,
261
+ )).rows[0]?.count ?? "0");
262
+ params.push(limit, offset);
253
263
  const { rows } = await deps.db.query(
254
264
  `SELECT r.id, p.name AS project, r.automation_id, r.env, r.status, r.attempt,
255
265
  r.trigger->>'kind' AS trigger_kind, r.created_at, r.started_at, r.finished_at
256
266
  FROM runs r JOIN projects p ON p.id = r.project_id
257
267
  WHERE p.workspace_id = $1 ${filter}
258
- ORDER BY r.created_at DESC LIMIT $${params.length}`,
268
+ ORDER BY r.created_at DESC LIMIT $${params.length - 1} OFFSET $${params.length}`,
259
269
  params,
260
270
  );
261
- return c.json({ runs: rows });
271
+ return c.json({ runs: rows, total, limit, offset, truncated: offset + rows.length < total });
262
272
  });
263
273
 
264
274
  api.get("/runs/:id", async (c) => {
@@ -273,11 +283,27 @@ export function createApi(deps: ApiDeps): Hono<ApiEnv> {
273
283
  FROM run_steps WHERE run_id = $1 ORDER BY started_at, occurrence`,
274
284
  [c.req.param("id")],
275
285
  );
276
- const logs = await deps.db.query(
277
- `SELECT step, level, msg, meta, created_at FROM run_logs WHERE run_id = $1 ORDER BY id LIMIT 500`,
286
+ const requestedLogLimit = Number(c.req.query("logLimit") ?? 100);
287
+ const requestedLogOffset = Number(c.req.query("logOffset") ?? 0);
288
+ const logLimit = Number.isFinite(requestedLogLimit) ? Math.min(Math.max(Math.trunc(requestedLogLimit), 1), 500) : 100;
289
+ const logOffset = Number.isFinite(requestedLogOffset) ? Math.max(Math.trunc(requestedLogOffset), 0) : 0;
290
+ const logsTotal = Number((await deps.db.query<{ count: string }>(
291
+ `SELECT count(*)::text AS count FROM run_logs WHERE run_id = $1`,
278
292
  [c.req.param("id")],
293
+ )).rows[0]?.count ?? "0");
294
+ const logs = await deps.db.query(
295
+ `SELECT step, level, msg, meta, created_at FROM run_logs WHERE run_id = $1 ORDER BY id LIMIT $2 OFFSET $3`,
296
+ [c.req.param("id"), logLimit, logOffset],
279
297
  );
280
- return c.json({ run: rows[0], steps: steps.rows, logs: logs.rows });
298
+ return c.json({
299
+ run: rows[0],
300
+ steps: steps.rows,
301
+ logs: logs.rows,
302
+ logsTotal,
303
+ logsLimit: logLimit,
304
+ logsOffset: logOffset,
305
+ logsTruncated: logOffset + logs.rows.length < logsTotal,
306
+ });
281
307
  });
282
308
 
283
309
  api.get("/runs/:id/replay", async (c) => {