fix: 降低 session 和 queue CLI 默认输出噪声
This commit is contained in:
+248
-14
@@ -15,7 +15,7 @@ import { AgentRunError, errorToJson } from "../../src/common/errors.js";
|
|||||||
import type { RunnerOnceOptions } from "../../src/runner/run-once.js";
|
import type { RunnerOnceOptions } from "../../src/runner/run-once.js";
|
||||||
import { backendProfileSpec, isBackendProfile } from "../../src/common/backend-profiles.js";
|
import { backendProfileSpec, isBackendProfile } from "../../src/common/backend-profiles.js";
|
||||||
import { outputBytesFromPayload, outputTruncatedFromPayload } from "../../src/common/output.js";
|
import { outputBytesFromPayload, outputTruncatedFromPayload } from "../../src/common/output.js";
|
||||||
import { redactText } from "../../src/common/redaction.js";
|
import { redactJson, redactText } from "../../src/common/redaction.js";
|
||||||
|
|
||||||
interface ParsedArgs {
|
interface ParsedArgs {
|
||||||
positional: string[];
|
positional: string[];
|
||||||
@@ -70,7 +70,7 @@ async function dispatch(args: ParsedArgs): Promise<CliResult> {
|
|||||||
if (group === "sessions" && command === "storage" && id) return sessionStorageGet(args, id);
|
if (group === "sessions" && command === "storage" && id) return sessionStorageGet(args, id);
|
||||||
if (group === "sessions" && command === "storage" && !id) throw new AgentRunError("schema-invalid", "sessions storage requires a sessionId", { httpStatus: 2 });
|
if (group === "sessions" && command === "storage" && !id) throw new AgentRunError("schema-invalid", "sessions storage requires a sessionId", { httpStatus: 2 });
|
||||||
if (group === "sessions" && command === "show" && id) return client(args).get(`/api/v1/sessions/${encodeURIComponent(id)}${readerQuery(args)}`);
|
if (group === "sessions" && command === "show" && id) return client(args).get(`/api/v1/sessions/${encodeURIComponent(id)}${readerQuery(args)}`);
|
||||||
if (group === "sessions" && command === "read" && id) return client(args).post(`/api/v1/sessions/${encodeURIComponent(id)}/read`, { readerId: optionalFlag(args, "reader-id") ?? "cli" });
|
if (group === "sessions" && command === "read" && id) return sessionRead(args, id);
|
||||||
if (group === "sessions" && command === "trace" && id) return sessionEvents(args, id, "trace");
|
if (group === "sessions" && command === "trace" && id) return sessionEvents(args, id, "trace");
|
||||||
if (group === "sessions" && command === "output" && id) return sessionEvents(args, id, "output");
|
if (group === "sessions" && command === "output" && id) return sessionEvents(args, id, "output");
|
||||||
if (group === "sessions" && command === "turn") return sessionTurn(args, id ?? null);
|
if (group === "sessions" && command === "turn") return sessionTurn(args, id ?? null);
|
||||||
@@ -181,6 +181,16 @@ interface RunEventSummaryOptions {
|
|||||||
summaryChars: number;
|
summaryChars: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SessionEventSummaryOptions {
|
||||||
|
kind: "trace" | "output";
|
||||||
|
sessionId: string;
|
||||||
|
afterSeq: number;
|
||||||
|
limit: number;
|
||||||
|
runId: string | null;
|
||||||
|
tail: number | null;
|
||||||
|
summaryChars: number;
|
||||||
|
}
|
||||||
|
|
||||||
export function summarizeRunEventPage(page: JsonValue, options: RunEventSummaryOptions): JsonRecord {
|
export function summarizeRunEventPage(page: JsonValue, options: RunEventSummaryOptions): JsonRecord {
|
||||||
const events = eventPageItems(page);
|
const events = eventPageItems(page);
|
||||||
const selected = options.tail === null ? events : events.slice(-options.tail);
|
const selected = options.tail === null ? events : events.slice(-options.tail);
|
||||||
@@ -201,6 +211,69 @@ export function summarizeRunEventPage(page: JsonValue, options: RunEventSummaryO
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function summarizeSessionEventPage(page: JsonValue, options: SessionEventSummaryOptions): JsonRecord {
|
||||||
|
const record = jsonRecordValue(page);
|
||||||
|
if (!record) throw new AgentRunError("schema-invalid", "sessions event response must be an object", { httpStatus: 2 });
|
||||||
|
const events = eventPageItems(page);
|
||||||
|
const selected = options.tail === null ? events : events.slice(-options.tail);
|
||||||
|
const items = selected.map((event) => summarizeRunEvent(event, options.summaryChars));
|
||||||
|
const lastSeq = items.length > 0 ? items[items.length - 1]?.seq ?? null : null;
|
||||||
|
const runId = stringValue(record.runId) ?? options.runId;
|
||||||
|
return {
|
||||||
|
action: `session-${options.kind}-summary`,
|
||||||
|
sessionId: stringValue(record.sessionId) ?? options.sessionId,
|
||||||
|
runId,
|
||||||
|
afterSeq: options.afterSeq,
|
||||||
|
limit: options.limit,
|
||||||
|
tail: options.tail,
|
||||||
|
sourceCount: events.length,
|
||||||
|
count: items.length,
|
||||||
|
cursor: stringValue(record.cursor),
|
||||||
|
lastSeq,
|
||||||
|
nextAfterSeq: lastSeq,
|
||||||
|
valuesPrinted: false,
|
||||||
|
items,
|
||||||
|
drillDownCommands: sessionEventDrillDownCommands(options.kind, options.sessionId, { afterSeq: options.afterSeq, limit: options.limit, runId }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function summarizeQueueDispatchResult(result: JsonValue, taskId: string): JsonRecord {
|
||||||
|
const record = jsonRecordValue(result);
|
||||||
|
if (!record) throw new AgentRunError("schema-invalid", "queue dispatch response must be an object", { httpStatus: 2 });
|
||||||
|
const task = jsonRecordValue(record.task);
|
||||||
|
const run = jsonRecordValue(record.run);
|
||||||
|
const command = jsonRecordValue(record.command);
|
||||||
|
const runnerJob = jsonRecordValue(record.runnerJob);
|
||||||
|
const latestAttempt = jsonRecordValue(record.latestAttempt) ?? jsonRecordValue(task?.latestAttempt);
|
||||||
|
const runId = stringValue(run?.id) ?? stringValue(latestAttempt?.runId);
|
||||||
|
const commandId = stringValue(command?.id) ?? stringValue(latestAttempt?.commandId);
|
||||||
|
const sessionId = stringValue(latestAttempt?.sessionId) ?? stringValue(jsonRecordValue(run?.sessionRef)?.sessionId) ?? stringValue(jsonRecordValue(task?.sessionRef)?.sessionId);
|
||||||
|
return {
|
||||||
|
action: "queue-dispatch-summary",
|
||||||
|
taskId: stringValue(task?.id) ?? taskId,
|
||||||
|
mutation: record.mutation === true,
|
||||||
|
task: summarizeQueueTaskRecord(task, taskId),
|
||||||
|
latestAttempt: summarizeAttemptRecord(latestAttempt),
|
||||||
|
run: summarizeRunRecord(run),
|
||||||
|
command: summarizeCommandRecord(command),
|
||||||
|
runnerJob: summarizeRunnerJobRecord(runnerJob),
|
||||||
|
fullResponseBytes: jsonByteLength(result),
|
||||||
|
valuesPrinted: false,
|
||||||
|
pollCommands: {
|
||||||
|
queue: `./scripts/agentrun queue show ${stringValue(task?.id) ?? taskId}`,
|
||||||
|
...(runId ? { run: `./scripts/agentrun runs show ${runId}` } : {}),
|
||||||
|
...(runId && commandId ? { command: `./scripts/agentrun commands show ${commandId} --run-id ${runId}` } : {}),
|
||||||
|
...(runId ? { events: `./scripts/agentrun runs events ${runId} --after-seq 0 --limit 100 --tail-summary` } : {}),
|
||||||
|
...(sessionId ? { trace: `./scripts/agentrun sessions trace ${sessionId} --after-seq 0 --limit 100`, output: `./scripts/agentrun sessions output ${sessionId} --after-seq 0 --limit 100` } : {}),
|
||||||
|
},
|
||||||
|
expandedOutput: {
|
||||||
|
fullFlag: "--full",
|
||||||
|
rawFlag: "--raw",
|
||||||
|
note: "For mutating commands, request expanded output on the original invocation.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function summarizeRunEvent(event: JsonRecord, summaryChars: number): JsonRecord {
|
function summarizeRunEvent(event: JsonRecord, summaryChars: number): JsonRecord {
|
||||||
const payload = jsonRecordValue(event.payload) ?? {};
|
const payload = jsonRecordValue(event.payload) ?? {};
|
||||||
const command = boundedSummaryString(stringValue(payload.command), summaryChars);
|
const command = boundedSummaryString(stringValue(payload.command), summaryChars);
|
||||||
@@ -209,11 +282,14 @@ function summarizeRunEvent(event: JsonRecord, summaryChars: number): JsonRecord
|
|||||||
const message = boundedSummaryString(stringValue(payload.failureMessage) ?? stringValue(payload.message), summaryChars);
|
const message = boundedSummaryString(stringValue(payload.failureMessage) ?? stringValue(payload.message), summaryChars);
|
||||||
const summary = text ?? command ?? outputSummary ?? message ?? "";
|
const summary = text ?? command ?? outputSummary ?? message ?? "";
|
||||||
const summaryTruncated = [command, text, outputSummary, message].some((value) => value?.endsWith("...") === true);
|
const summaryTruncated = [command, text, outputSummary, message].some((value) => value?.endsWith("...") === true);
|
||||||
|
const runnerTrace = findNestedValue(payload, "runnerTrace");
|
||||||
return {
|
return {
|
||||||
seq: numberValue(event.seq) ?? 0,
|
seq: numberValue(event.seq) ?? 0,
|
||||||
type: stringValue(event.type) ?? "unknown",
|
type: stringValue(event.type) ?? "unknown",
|
||||||
method: stringValue(payload.method),
|
method: stringValue(payload.method),
|
||||||
status: stringValue(payload.status) ?? stringValue(payload.terminalStatus) ?? stringValue(payload.phase),
|
status: stringValue(payload.status) ?? stringValue(payload.terminalStatus) ?? stringValue(payload.phase),
|
||||||
|
phase: stringValue(payload.phase),
|
||||||
|
commandId: stringValue(payload.commandId),
|
||||||
command,
|
command,
|
||||||
text,
|
text,
|
||||||
exitCode: numberValue(payload.exitCode),
|
exitCode: numberValue(payload.exitCode),
|
||||||
@@ -222,6 +298,11 @@ function summarizeRunEvent(event: JsonRecord, summaryChars: number): JsonRecord
|
|||||||
outputBytes: outputBytesFromPayload(payload),
|
outputBytes: outputBytesFromPayload(payload),
|
||||||
outputSummary,
|
outputSummary,
|
||||||
summary,
|
summary,
|
||||||
|
payloadBytes: jsonByteLength(payload),
|
||||||
|
payloadKeys: Object.keys(payload).sort().slice(0, 24),
|
||||||
|
hasRunnerTrace: runnerTrace !== null,
|
||||||
|
runnerTraceBytes: runnerTrace === null ? 0 : jsonByteLength(runnerTrace),
|
||||||
|
hasRawEvent: hasNestedKey(payload, ["raw", "rawEvent", "raw_event", "event"]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,6 +391,140 @@ function isPlainTextOutput(value: CliResult): value is PlainTextOutput {
|
|||||||
return typeof value === "object" && value !== null && plainTextOutputMarker in value;
|
return typeof value === "object" && value !== null && plainTextOutputMarker in value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wantsExpandedOutput(args: ParsedArgs): boolean {
|
||||||
|
return args.flags.get("full") === true || args.flags.get("raw") === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeSessionMutationResult(action: "session-cancel" | "session-read", sessionId: string, result: JsonValue, flags: JsonRecord): JsonRecord {
|
||||||
|
const record = jsonRecordValue(result);
|
||||||
|
return {
|
||||||
|
action,
|
||||||
|
sessionId,
|
||||||
|
mutation: true,
|
||||||
|
...flags,
|
||||||
|
result: summarizeGenericRecord(record),
|
||||||
|
fullResponseBytes: jsonByteLength(result),
|
||||||
|
valuesPrinted: false,
|
||||||
|
drillDownCommands: {
|
||||||
|
show: `./scripts/agentrun sessions show ${sessionId} --reader-id cli`,
|
||||||
|
trace: `./scripts/agentrun sessions trace ${sessionId} --after-seq 0 --limit 100`,
|
||||||
|
output: `./scripts/agentrun sessions output ${sessionId} --after-seq 0 --limit 100`,
|
||||||
|
traceFull: `./scripts/agentrun sessions trace ${sessionId} --after-seq 0 --limit 100 --full`,
|
||||||
|
outputFull: `./scripts/agentrun sessions output ${sessionId} --after-seq 0 --limit 100 --full`,
|
||||||
|
},
|
||||||
|
expandedOutput: {
|
||||||
|
fullFlag: "--full",
|
||||||
|
rawFlag: "--raw",
|
||||||
|
note: "For mutating commands, request expanded output on the original invocation.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function sessionEventDrillDownCommands(kind: "trace" | "output", sessionId: string, options: { afterSeq: number; limit: number; runId: string | null }): JsonRecord {
|
||||||
|
const base = `./scripts/agentrun sessions ${kind} ${sessionId} --after-seq ${options.afterSeq} --limit ${options.limit}${options.runId ? ` --run-id ${options.runId}` : ""}`;
|
||||||
|
return {
|
||||||
|
full: `${base} --full`,
|
||||||
|
raw: `${base} --raw`,
|
||||||
|
next: `./scripts/agentrun sessions ${kind} ${sessionId} --after-seq <nextAfterSeq> --limit ${options.limit}${options.runId ? ` --run-id ${options.runId}` : ""}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeQueueTaskRecord(record: JsonRecord | null, fallbackTaskId: string): JsonRecord {
|
||||||
|
return compactRecord(record, {
|
||||||
|
fallback: { id: fallbackTaskId },
|
||||||
|
keys: ["id", "state", "queue", "lane", "title", "priority", "backendProfile", "providerId", "sessionPath", "version", "updatedAt", "cancelledAt", "cancelReason"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeAttemptRecord(record: JsonRecord | null): JsonRecord | null {
|
||||||
|
if (!record) return null;
|
||||||
|
return compactRecord(record, { keys: ["attemptId", "state", "runId", "commandId", "runnerJobId", "sessionId", "sessionPath"] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeRunRecord(record: JsonRecord | null): JsonRecord | null {
|
||||||
|
if (!record) return null;
|
||||||
|
return compactRecord(record, { keys: ["id", "status", "terminalStatus", "failureKind", "failureMessage", "backendProfile", "providerId", "claimedBy", "leaseExpiresAt", "createdAt", "updatedAt"] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeCommandRecord(record: JsonRecord | null): JsonRecord | null {
|
||||||
|
if (!record) return null;
|
||||||
|
return compactRecord(record, { keys: ["id", "runId", "seq", "type", "state", "createdAt", "updatedAt", "acknowledgedAt"] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeRunnerJobRecord(record: JsonRecord | null): JsonRecord | null {
|
||||||
|
if (!record) return null;
|
||||||
|
const runner = jsonRecordValue(record.runner);
|
||||||
|
const jobIdentity = jsonRecordValue(record.jobIdentity);
|
||||||
|
const kubernetes = jsonRecordValue(record.kubernetes);
|
||||||
|
return {
|
||||||
|
...compactRecord(record, { keys: ["action", "mutation", "runId", "commandId", "attemptId", "runnerId", "namespace", "jobName"] }),
|
||||||
|
logPath: stringValue(runner?.logPath),
|
||||||
|
backendProfile: stringValue(runner?.backendProfile),
|
||||||
|
jobUid: stringValue(jobIdentity?.uid),
|
||||||
|
created: kubernetes?.created === true,
|
||||||
|
warnings: Array.isArray(record.warnings) ? record.warnings.map((item) => boundedSummaryString(typeof item === "string" ? item : JSON.stringify(item), 240)).filter((item): item is string => Boolean(item)) : [],
|
||||||
|
fullResponseBytes: jsonByteLength(record),
|
||||||
|
valuesPrinted: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeGenericRecord(record: JsonRecord | null): JsonRecord | null {
|
||||||
|
if (!record) return null;
|
||||||
|
return compactRecord(record, {
|
||||||
|
keys: ["id", "runId", "commandId", "sessionId", "readerId", "sessionVersion", "taskId", "taskVersion", "status", "state", "terminalStatus", "failureKind", "failureMessage", "updatedAt", "readAt", "cancelledAt", "cancelReason"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactRecord(record: JsonRecord | null, options: { keys: string[]; fallback?: JsonRecord }): JsonRecord {
|
||||||
|
const result: JsonRecord = { ...(options.fallback ?? {}) };
|
||||||
|
if (!record) return result;
|
||||||
|
for (const key of options.keys) {
|
||||||
|
const value = record[key];
|
||||||
|
if (value === undefined) continue;
|
||||||
|
if (typeof value === "string") result[key] = boundedSummaryString(value, key.toLowerCase().includes("message") ? 300 : 900) ?? "";
|
||||||
|
else if (typeof value === "number" || typeof value === "boolean" || value === null) result[key] = value;
|
||||||
|
}
|
||||||
|
result.fullRecordBytes = jsonByteLength(record);
|
||||||
|
result.valuesPrinted = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsonByteLength(value: unknown): number {
|
||||||
|
try {
|
||||||
|
return Buffer.byteLength(JSON.stringify(redactJson(value)) ?? "null", "utf8");
|
||||||
|
} catch {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findNestedValue(value: unknown, key: string, depth = 0): unknown | null {
|
||||||
|
if (depth > 8 || value === null || value === undefined) return null;
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (const item of value) {
|
||||||
|
const found = findNestedValue(item, key, depth + 1);
|
||||||
|
if (found !== null) return found;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeof value !== "object") return null;
|
||||||
|
const record = value as Record<string, unknown>;
|
||||||
|
if (Object.prototype.hasOwnProperty.call(record, key)) return record[key] ?? null;
|
||||||
|
for (const entry of Object.values(record)) {
|
||||||
|
const found = findNestedValue(entry, key, depth + 1);
|
||||||
|
if (found !== null) return found;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasNestedKey(value: unknown, keys: readonly string[], depth = 0): boolean {
|
||||||
|
if (depth > 8 || value === null || value === undefined) return false;
|
||||||
|
if (Array.isArray(value)) return value.some((item) => hasNestedKey(item, keys, depth + 1));
|
||||||
|
if (typeof value !== "object") return false;
|
||||||
|
const record = value as Record<string, unknown>;
|
||||||
|
if (keys.some((key) => Object.prototype.hasOwnProperty.call(record, key))) return true;
|
||||||
|
return Object.values(record).some((entry) => hasNestedKey(entry, keys, depth + 1));
|
||||||
|
}
|
||||||
|
|
||||||
async function listSessions(args: ParsedArgs): Promise<JsonValue> {
|
async function listSessions(args: ParsedArgs): Promise<JsonValue> {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
const state = optionalFlag(args, "state") ?? (args.flags.get("running") === true ? "running" : args.flags.get("unread") === true ? "unread" : args.flags.get("all") === true ? "all" : null);
|
const state = optionalFlag(args, "state") ?? (args.flags.get("running") === true ? "running" : args.flags.get("unread") === true ? "unread" : args.flags.get("all") === true ? "all" : null);
|
||||||
@@ -328,14 +543,24 @@ async function listSessions(args: ParsedArgs): Promise<JsonValue> {
|
|||||||
|
|
||||||
async function sessionEvents(args: ParsedArgs, sessionId: string, kind: "trace" | "output"): Promise<JsonValue> {
|
async function sessionEvents(args: ParsedArgs, sessionId: string, kind: "trace" | "output"): Promise<JsonValue> {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
const afterSeq = optionalFlag(args, "after-seq");
|
const afterSeq = integerFlag(args, "after-seq", 0, { min: 0 });
|
||||||
const limit = optionalFlag(args, "limit");
|
const limit = integerFlag(args, "limit", 100, { min: 1, max: 500 });
|
||||||
const runId = optionalFlag(args, "run-id");
|
const runId = optionalFlag(args, "run-id");
|
||||||
if (afterSeq) params.set("afterSeq", afterSeq);
|
params.set("afterSeq", String(afterSeq));
|
||||||
if (limit) params.set("limit", limit);
|
params.set("limit", String(limit));
|
||||||
if (runId) params.set("runId", runId);
|
if (runId) params.set("runId", runId);
|
||||||
const query = params.toString();
|
const query = params.toString();
|
||||||
return client(args).get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/${kind}${query ? `?${query}` : ""}`);
|
const page = await client(args).get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/${kind}${query ? `?${query}` : ""}`);
|
||||||
|
if (wantsExpandedOutput(args)) return page;
|
||||||
|
return summarizeSessionEventPage(page, {
|
||||||
|
kind,
|
||||||
|
sessionId,
|
||||||
|
afterSeq,
|
||||||
|
limit,
|
||||||
|
runId,
|
||||||
|
tail: tailFlag(args, limit),
|
||||||
|
summaryChars: integerFlag(args, "summary-chars", 900, { min: 1, max: 4_000 }),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sessionCreate(args: ParsedArgs, positionalSessionId: string | null): Promise<JsonRecord> {
|
async function sessionCreate(args: ParsedArgs, positionalSessionId: string | null): Promise<JsonRecord> {
|
||||||
@@ -445,7 +670,14 @@ async function sessionSteer(args: ParsedArgs, sessionId: string): Promise<JsonRe
|
|||||||
|
|
||||||
async function sessionCancel(args: ParsedArgs, sessionId: string): Promise<JsonRecord> {
|
async function sessionCancel(args: ParsedArgs, sessionId: string): Promise<JsonRecord> {
|
||||||
const result = await client(args).post(`/api/v1/sessions/${encodeURIComponent(sessionId)}/control`, { action: "cancel", ...cancelBody(args) });
|
const result = await client(args).post(`/api/v1/sessions/${encodeURIComponent(sessionId)}/control`, { action: "cancel", ...cancelBody(args) });
|
||||||
return { action: "session-cancel", sessionId, result };
|
if (wantsExpandedOutput(args)) return { action: "session-cancel", sessionId, result: result as JsonValue };
|
||||||
|
return summarizeSessionMutationResult("session-cancel", sessionId, result, { cancelled: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sessionRead(args: ParsedArgs, sessionId: string): Promise<JsonValue> {
|
||||||
|
const result = await client(args).post(`/api/v1/sessions/${encodeURIComponent(sessionId)}/read`, { readerId: optionalFlag(args, "reader-id") ?? "cli" });
|
||||||
|
if (wantsExpandedOutput(args)) return result;
|
||||||
|
return summarizeSessionMutationResult("session-read", sessionId, result, { read: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitQueueTask(args: ParsedArgs): Promise<JsonValue> {
|
async function submitQueueTask(args: ParsedArgs): Promise<JsonValue> {
|
||||||
@@ -497,7 +729,9 @@ async function dispatchQueueTask(args: ParsedArgs, taskId: string): Promise<Json
|
|||||||
copy("source-commit", "sourceCommit");
|
copy("source-commit", "sourceCommit");
|
||||||
copyRunnerManagerUrlFlag(args, body);
|
copyRunnerManagerUrlFlag(args, body);
|
||||||
copy("service-account-name", "serviceAccountName");
|
copy("service-account-name", "serviceAccountName");
|
||||||
return client(args).post(`/api/v1/queue/tasks/${encodeURIComponent(taskId)}/dispatch`, body);
|
const result = await client(args).post(`/api/v1/queue/tasks/${encodeURIComponent(taskId)}/dispatch`, body);
|
||||||
|
if (wantsExpandedOutput(args)) return result;
|
||||||
|
return summarizeQueueDispatchResult(result, taskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showRunnerJobStatus(args: ParsedArgs): Promise<JsonValue> {
|
async function showRunnerJobStatus(args: ParsedArgs): Promise<JsonValue> {
|
||||||
@@ -998,10 +1232,10 @@ function help(args: ParsedArgs, group?: string): JsonRecord {
|
|||||||
"sessions show <sessionId> [--reader-id <reader>]",
|
"sessions show <sessionId> [--reader-id <reader>]",
|
||||||
"sessions turn [sessionId] --json-file <run-base.json> --prompt-file <file> [--profile codex|deepseek|minimax-m3|dsflash-go|<dynamic-profile>|M3] [--runner-json-file <job.json>]",
|
"sessions turn [sessionId] --json-file <run-base.json> --prompt-file <file> [--profile codex|deepseek|minimax-m3|dsflash-go|<dynamic-profile>|M3] [--runner-json-file <job.json>]",
|
||||||
"sessions steer <sessionId> --prompt-file <file>",
|
"sessions steer <sessionId> --prompt-file <file>",
|
||||||
"sessions cancel <sessionId> [--reason <text>]",
|
"sessions cancel <sessionId> [--reason <text>] [--full|--raw]",
|
||||||
"sessions trace <sessionId> [--after-seq <n>] [--limit <n>] [--run-id <runId>]",
|
"sessions trace <sessionId> [--after-seq <n>] [--limit <n>] [--run-id <runId>] [--summary-chars <n>] [--full|--raw]",
|
||||||
"sessions output <sessionId> [--after-seq <n>] [--limit <n>] [--run-id <runId>]",
|
"sessions output <sessionId> [--after-seq <n>] [--limit <n>] [--run-id <runId>] [--summary-chars <n>] [--full|--raw]",
|
||||||
"sessions read <sessionId> [--reader-id <reader>]",
|
"sessions read <sessionId> [--reader-id <reader>] [--full|--raw]",
|
||||||
"commands create <runId> --type turn|steer|interrupt --json-file <payload.json>",
|
"commands create <runId> --type turn|steer|interrupt --json-file <payload.json>",
|
||||||
"commands show <commandId> --run-id <runId>",
|
"commands show <commandId> --run-id <runId>",
|
||||||
"commands result <commandId> --run-id <runId>",
|
"commands result <commandId> --run-id <runId>",
|
||||||
@@ -1018,7 +1252,7 @@ function help(args: ParsedArgs, group?: string): JsonRecord {
|
|||||||
"queue commander [--queue <queue>] [--reader-id <reader>]",
|
"queue commander [--queue <queue>] [--reader-id <reader>]",
|
||||||
"queue read <taskId> [--reader-id <reader>]",
|
"queue read <taskId> [--reader-id <reader>]",
|
||||||
"queue cancel <taskId> [--reason <text>]",
|
"queue cancel <taskId> [--reason <text>]",
|
||||||
"queue dispatch <taskId> [--json-file <dispatch.json>] [--idempotency-key <key>] [--image <image>] [--namespace <namespace>]",
|
"queue dispatch <taskId> [--json-file <dispatch.json>] [--idempotency-key <key>] [--image <image>] [--namespace <namespace>] [--full|--raw]",
|
||||||
"queue refresh <taskId>",
|
"queue refresh <taskId>",
|
||||||
"secrets codex render --dry-run [--profile codex|deepseek|minimax-m3|dsflash-go|<dynamic-profile>] [--codex-home <dir>] [--model-catalog-file <file>] [--namespace agentrun-v01] [--secret-name <name>]",
|
"secrets codex render --dry-run [--profile codex|deepseek|minimax-m3|dsflash-go|<dynamic-profile>] [--codex-home <dir>] [--model-catalog-file <file>] [--namespace agentrun-v01] [--secret-name <name>]",
|
||||||
"provider-profiles list",
|
"provider-profiles list",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import type { JsonRecord } from "../../common/types.js";
|
import type { JsonRecord } from "../../common/types.js";
|
||||||
import { assertNoSecretLeak, type SelfTestContext, type SelfTestResult } from "../harness.js";
|
import { assertNoSecretLeak, type SelfTestContext, type SelfTestResult } from "../harness.js";
|
||||||
import { renderRunEventSummaryTsv, summarizeRunEventPage } from "../../../scripts/src/cli.js";
|
import { renderRunEventSummaryTsv, summarizeQueueDispatchResult, summarizeRunEventPage, summarizeSessionEventPage } from "../../../scripts/src/cli.js";
|
||||||
|
|
||||||
export default function selfTest(_context: SelfTestContext): SelfTestResult {
|
export default function selfTest(_context: SelfTestContext): SelfTestResult {
|
||||||
const summary = summarizeRunEventPage({ items: [
|
const summary = summarizeRunEventPage({ items: [
|
||||||
@@ -30,5 +30,49 @@ export default function selfTest(_context: SelfTestContext): SelfTestResult {
|
|||||||
assert.equal(tsv.includes("\n12\ttool_call\tshell\tcompleted\t0\t321\ttrue\t4096"), true);
|
assert.equal(tsv.includes("\n12\ttool_call\tshell\tcompleted\t0\t321\ttrue\t4096"), true);
|
||||||
assert.equal(tsv.includes("test-token-material"), false);
|
assert.equal(tsv.includes("test-token-material"), false);
|
||||||
|
|
||||||
return { name: "15-cli-events-summary", tests: ["runs-events-summary-tail", "runs-events-summary-tsv-redaction"] };
|
const hugeRunnerTrace = "runner trace line with test-token-material\n".repeat(2_000);
|
||||||
|
const sessionSummary = summarizeSessionEventPage({
|
||||||
|
sessionId: "sess_noise",
|
||||||
|
runId: "run_noise",
|
||||||
|
items: [{
|
||||||
|
seq: 266,
|
||||||
|
type: "backend_status",
|
||||||
|
payload: {
|
||||||
|
phase: "turn/cancelled",
|
||||||
|
status: "cancelled",
|
||||||
|
commandId: "cmd_noise",
|
||||||
|
runnerTrace: { raw: hugeRunnerTrace },
|
||||||
|
rawEvent: { nested: hugeRunnerTrace },
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
count: 1,
|
||||||
|
cursor: "266",
|
||||||
|
}, { kind: "trace", sessionId: "sess_noise", afterSeq: 250, limit: 100, runId: null, tail: null, summaryChars: 80 });
|
||||||
|
const sessionItems = sessionSummary.items as JsonRecord[];
|
||||||
|
assert.equal(sessionSummary.action, "session-trace-summary");
|
||||||
|
assert.equal(sessionSummary.nextAfterSeq, 266);
|
||||||
|
assert.equal(sessionItems[0]?.hasRunnerTrace, true);
|
||||||
|
assert.ok(Number(sessionItems[0]?.runnerTraceBytes) > 10_000);
|
||||||
|
assert.equal(JSON.stringify(sessionSummary).includes("runner trace line"), false);
|
||||||
|
assert.equal(String((sessionSummary.drillDownCommands as JsonRecord).full).includes("--full"), true);
|
||||||
|
assert.equal(String((sessionSummary.drillDownCommands as JsonRecord).raw).includes("--raw"), true);
|
||||||
|
assertNoSecretLeak(sessionSummary);
|
||||||
|
|
||||||
|
const queueSummary = summarizeQueueDispatchResult({
|
||||||
|
action: "queue-dispatch",
|
||||||
|
mutation: true,
|
||||||
|
task: { id: "qt_noise", state: "running", queue: "dev", lane: "case", title: "large trace queue dispatch", version: 7, payload: { prompt: hugeRunnerTrace } },
|
||||||
|
run: { id: "run_noise", status: "running", terminalStatus: null, backendProfile: "codex", providerId: "G14" },
|
||||||
|
command: { id: "cmd_noise", runId: "run_noise", seq: 1, type: "turn", state: "acknowledged", payload: { prompt: hugeRunnerTrace } },
|
||||||
|
runnerJob: { action: "create-kubernetes-job", runId: "run_noise", commandId: "cmd_noise", attemptId: "attempt_noise", runnerId: "runner_noise", namespace: "agentrun-v01", jobName: "job-noise", runner: { logPath: "kubectl logs job/job-noise", runnerTrace: hugeRunnerTrace } },
|
||||||
|
latestAttempt: { attemptId: "attempt_noise", state: "running", runId: "run_noise", commandId: "cmd_noise", runnerJobId: "rj_noise", sessionId: "sess_noise", sessionPath: "/api/v1/sessions/sess_noise" },
|
||||||
|
}, "qt_noise");
|
||||||
|
assert.equal(queueSummary.action, "queue-dispatch-summary");
|
||||||
|
assert.equal(((queueSummary.runnerJob as JsonRecord).attemptId), "attempt_noise");
|
||||||
|
assert.equal(JSON.stringify(queueSummary).includes("runner trace line"), false);
|
||||||
|
assert.equal(String(((queueSummary.pollCommands as JsonRecord).events)).includes("--tail-summary"), true);
|
||||||
|
assert.equal(((queueSummary.expandedOutput as JsonRecord).fullFlag), "--full");
|
||||||
|
assertNoSecretLeak(queueSummary);
|
||||||
|
|
||||||
|
return { name: "15-cli-events-summary", tests: ["runs-events-summary-tail", "runs-events-summary-tsv-redaction", "sessions-events-low-noise-summary", "queue-dispatch-low-noise-summary"] };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user