feat(ql3): expose redacted log tails over local mcp

This commit is contained in:
whyour
2026-08-14 18:59:29 +08:00
parent fef0fe2bd1
commit 82adb9ace2
20 changed files with 750 additions and 354 deletions
@@ -50,6 +50,12 @@ import {
BUILTIN_RUN_COMPARE_TOOL_DEFINITION,
executeBuiltInRunCompareTool,
} from '@qinglong/runtime-core/builtin-run-compare-projection';
import {
BUILTIN_RUN_LOG_EXCERPT_TOOL,
BUILTIN_RUN_LOG_EXCERPT_TOOL_DEFINITION,
executeBuiltInRunLogExcerptTool,
type RunAttemptLogReadPort,
} from '@qinglong/runtime-core/builtin-run-log-excerpt-projection';
import {
BUILTIN_TASK_RUN_OUTCOME_COMPARE_TOOL,
BUILTIN_TASK_RUN_OUTCOME_COMPARE_TOOL_DEFINITION,
@@ -102,10 +108,12 @@ export interface AuthenticatedLocalMcpRequest {
export interface QingLongLocalMcpServerDependencies {
readonly projectId: string;
readonly profile: 'edge' | 'standalone';
readonly authenticate: () => Promise<Readonly<AuthenticatedLocalMcpRequest> | null>;
readonly policy: ToolPolicyAuthorizer;
readonly audit: SecurityAuditSink;
readonly runs: LocalMcpRunReader;
readonly runAttemptLogs: RunAttemptLogReadPort;
readonly stepRuns: Pick<StepRunRepository, 'listByRun'>;
readonly taskDefinitions: LocalMcpTaskReader;
readonly triggers: LocalMcpTriggerReader;
@@ -133,7 +141,9 @@ type LocalMcpApprovalReader = Pick<
Pick<ApprovalRequestDetailSource, 'getApprovalRequestDetail'>;
interface LocalMcpReadAuthority {
readonly profile: 'edge' | 'standalone';
readonly runs: LocalMcpRunReader;
readonly runAttemptLogs: RunAttemptLogReadPort;
readonly stepRuns: Pick<StepRunRepository, 'listByRun'>;
readonly taskDefinitions: LocalMcpTaskReader;
readonly triggers: LocalMcpTriggerReader;
@@ -189,6 +199,24 @@ const LOCAL_MCP_READ_TOOLS: readonly LocalMcpReadToolDescriptor[] =
input: ToolJsonValue,
) => executeBuiltInRunReadTool(authority.runs, projectId, input),
}),
Object.freeze({
tool: BUILTIN_RUN_LOG_EXCERPT_TOOL,
definition: BUILTIN_RUN_LOG_EXCERPT_TOOL_DEFINITION,
title: 'QingLong Run Log Tail',
auditReason: 'tool_qinglong_run_log_excerpt',
unavailableCode: 'run_log_excerpt_unavailable',
execute: (
authority: LocalMcpReadAuthority,
projectId: string,
input: ToolJsonValue,
) =>
executeBuiltInRunLogExcerptTool(
authority.runAttemptLogs,
authority.profile,
projectId,
input,
),
}),
Object.freeze({
tool: BUILTIN_RUN_COMPARE_TOOL,
definition: BUILTIN_RUN_COMPARE_TOOL_DEFINITION,
@@ -322,12 +350,15 @@ function validateDependencies(
typeof dependencies !== 'object' ||
Array.isArray(dependencies) ||
typeof dependencies.projectId !== 'string' ||
(dependencies.profile !== 'edge' &&
dependencies.profile !== 'standalone') ||
typeof dependencies.authenticate !== 'function' ||
typeof dependencies.policy?.authorize !== 'function' ||
typeof dependencies.audit?.record !== 'function' ||
typeof dependencies.runs?.listRunsByProject !== 'function' ||
typeof dependencies.runs?.findRunById !== 'function' ||
typeof dependencies.runs?.listEvents !== 'function' ||
typeof dependencies.runAttemptLogs?.read !== 'function' ||
typeof dependencies.stepRuns?.listByRun !== 'function' ||
typeof dependencies.taskDefinitions?.findCurrentTaskDefinition !==
'function' ||