feat(ql3): surface bounded console run logs

This commit is contained in:
whyour
2026-08-28 12:27:19 +08:00
parent 920a31fb66
commit 57953ec80b
12 changed files with 391 additions and 14 deletions
+71 -3
View File
@@ -2,6 +2,10 @@ import {
BoundedRunReadProjectionUnavailableError,
executeBoundedRunReadProjection,
} from '@qinglong/runtime-core/bounded-run-read-projection';
import {
RUN_ATTEMPT_STATUSES,
type RunAttemptRecord,
} from '@qinglong/runtime-core/run';
import type { RunRepositoryReader } from '@qinglong/runtime-core/run-repository';
import type { LocalApiResponse } from '../transport/contract';
@@ -22,10 +26,61 @@ function response(
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function latestAttemptProjection(
value: Readonly<RunAttemptRecord>,
runId: string,
): Readonly<Record<string, boolean | string | number>> | null {
const boundedText = (candidate: unknown, maximum: number) =>
typeof candidate === 'string' &&
candidate.length > 0 &&
candidate.length <= maximum &&
!/[\u0000-\u001f\u007f]/u.test(candidate);
const timestamp = (candidate: unknown) =>
Number.isSafeInteger(candidate) && Number(candidate) >= 0;
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
value.runId !== runId ||
!boundedText(value.id, 128) ||
!Number.isSafeInteger(value.attempt) ||
value.attempt < 1 ||
value.attempt > 2_147_483_647 ||
!RUN_ATTEMPT_STATUSES.includes(value.status) ||
!timestamp(value.createdAtMs) ||
(value.startedAtMs !== undefined && !timestamp(value.startedAtMs)) ||
(value.finishedAtMs !== undefined && !timestamp(value.finishedAtMs)) ||
(value.logArtifactId !== undefined &&
!boundedText(value.logArtifactId, 128))
) {
return null;
}
return Object.freeze({
id: value.id,
attempt: value.attempt,
status: value.status,
logAvailable: value.logArtifactId !== undefined,
createdAtMs: value.createdAtMs,
...(value.startedAtMs === undefined
? {}
: { startedAtMs: value.startedAtMs }),
...(value.finishedAtMs === undefined
? {}
: { finishedAtMs: value.finishedAtMs }),
});
}
export function createLocalApiRunReadRoute(
runs: Pick<RunRepositoryReader, 'findRunById'>,
runs: Pick<
RunRepositoryReader,
'findRunById' | 'findLatestAttemptByRunId'
>,
): Readonly<LocalApiRunReadRoute> {
if (!runs || typeof runs.findRunById !== 'function') {
if (
!runs ||
typeof runs.findRunById !== 'function' ||
typeof runs.findLatestAttemptByRunId !== 'function'
) {
throw new TypeError('Local API Run read repository is invalid');
}
return Object.freeze({
@@ -39,9 +94,22 @@ export function createLocalApiRunReadRoute(
if (projection.found !== true) {
return response(404, { code: 'run_not_found' });
}
const latestAttempt = await runs.findLatestAttemptByRunId(
request.runId,
);
const attemptView = latestAttempt
? latestAttemptProjection(latestAttempt, request.runId)
: null;
if (latestAttempt && !attemptView) {
throw new BoundedRunReadProjectionUnavailableError();
}
const { found: _found, ...view } = projection;
return response(200, {
run: Object.freeze({ projectId: request.projectId, ...view }),
run: Object.freeze({
projectId: request.projectId,
...view,
latestAttempt: attemptView,
}),
});
} catch (error) {
if (