fix(agentrun): 添加队列任务重试资源命令
This commit is contained in:
@@ -146,6 +146,125 @@ export function renderResourceResult(command: string, raw: Record<string, unknow
|
||||
return renderedCliResult(raw.ok !== false, command, renderResourceTable(items, options.output === "wide"));
|
||||
}
|
||||
|
||||
export function renderQueueAttemptList(command: string, raw: Record<string, unknown>, options: AgentRunResourceOptions, requestedTaskId: string, items: Record<string, unknown>[]): RenderedCliResult {
|
||||
if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false);
|
||||
const data = record(innerData(raw));
|
||||
const taskId = stringOrNull(data.taskId) ?? requestedTaskId;
|
||||
const cursor = typeof data.cursor === "number" ? String(data.cursor) : stringOrNull(data.cursor);
|
||||
const rawItems = arrayRecords(data.items);
|
||||
if (options.full) {
|
||||
return renderMachine(command, {
|
||||
kind: "AttemptList",
|
||||
taskId,
|
||||
count: data.count ?? rawItems.length,
|
||||
cursor,
|
||||
items: rawItems,
|
||||
}, options.output === "yaml" ? "yaml" : "json", raw.ok !== false);
|
||||
}
|
||||
const payload = { kind: "AttemptList", taskId, count: items.length, cursor, items };
|
||||
if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output, raw.ok !== false);
|
||||
if (options.output === "name") return renderedCliResult(raw.ok !== false, command, items.map((item) => `attempt/${String(item.name ?? "")}`).join("\n"));
|
||||
if (items.length === 0) return renderedCliResult(raw.ok !== false, command, `No attempt resources found for task/${taskId}.`);
|
||||
const lines = [
|
||||
`Attempts for task/${taskId}`,
|
||||
renderTable(["NAME", "INDEX", "STATE", "PREVIOUS", "RUN", "CMD", "RJOB", "AGE", "FAILURE", "REASON"], items.map((item) => [
|
||||
resourceName(item),
|
||||
displayValue(item.retryIndex),
|
||||
displayValue(item.state),
|
||||
shortId(stringOrDash(item.previousAttemptId)),
|
||||
shortId(stringOrDash(item.runId)),
|
||||
shortId(stringOrDash(item.commandId)),
|
||||
shortId(stringOrDash(item.runnerJobId)),
|
||||
displayValue(item.age),
|
||||
queueAttemptFailureSummary(item),
|
||||
truncateOneLine(displayValue(item.reason), 48),
|
||||
])),
|
||||
];
|
||||
if (cursor !== null) lines.push(`Next: ${nextQueueAttemptPageCommand(command, cursor, options.limit)}`);
|
||||
return renderedCliResult(raw.ok !== false, command, lines.join("\n"));
|
||||
}
|
||||
|
||||
export function renderQueueRetrySummary(command: string, raw: Record<string, unknown>, options: AgentRunResourceOptions, requestedTaskId: string): RenderedCliResult {
|
||||
if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false);
|
||||
const data = record(innerData(raw));
|
||||
const task = record(data.task);
|
||||
const attempt = record(data.attempt);
|
||||
const previousAttempt = record(data.previousAttempt);
|
||||
const allowedTransition = record(data.allowedTransition);
|
||||
const taskId = stringOrNull(task.id) ?? requestedTaskId;
|
||||
if (options.full) {
|
||||
return renderMachine(command, { kind: "TaskRetry", taskId, ...data }, options.output === "yaml" ? "yaml" : "json", raw.ok !== false);
|
||||
}
|
||||
const normalizedAttempt = compactQueueRetryAttempt(attempt);
|
||||
const normalizedPreviousAttempt = compactQueueRetryAttempt(previousAttempt);
|
||||
const run = record(data.run);
|
||||
const runId = stringOrNull(run.id) ?? stringOrNull(attempt.runId);
|
||||
const commandResource = record(data.command);
|
||||
const commandId = stringOrNull(commandResource.id) ?? stringOrNull(attempt.commandId);
|
||||
const runnerJob = record(data.runnerJob);
|
||||
const runnerJobId = stringOrNull(runnerJob.id) ?? stringOrNull(attempt.runnerJobId);
|
||||
const payload = {
|
||||
kind: "TaskRetry",
|
||||
taskId,
|
||||
dryRun: options.dryRun,
|
||||
mutation: data.mutation === true,
|
||||
idempotentReplay: data.idempotentReplay === true,
|
||||
task: pickCompact(task, ["id", "state", "queue", "lane", "payloadHash", "updatedAt"]),
|
||||
attempt: normalizedAttempt,
|
||||
previousAttempt: normalizedPreviousAttempt,
|
||||
allowedTransition,
|
||||
run: compactQueueRetryIdentity(run, ["id", "status", "terminalStatus", "createdAt", "updatedAt"]),
|
||||
command: compactQueueRetryIdentity(commandResource, ["id", "type", "state", "terminalStatus", "createdAt", "updatedAt"]),
|
||||
runnerJob: compactQueueRetryIdentity(runnerJob, ["id", "state", "phase", "terminalStatus", "attemptId", "createdAt", "updatedAt"]),
|
||||
};
|
||||
if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output, raw.ok !== false);
|
||||
const retryIndex = attempt.retryIndex ?? allowedTransition.retryIndex;
|
||||
const attemptId = stringOrNull(attempt.attemptId);
|
||||
const previousAttemptId = stringOrNull(previousAttempt.attemptId) ?? stringOrNull(allowedTransition.previousAttemptId);
|
||||
const lines = [
|
||||
`${options.dryRun ? "Task retry validated" : "Task retry submitted"}: task/${taskId}`,
|
||||
`OK: ${String(raw.ok !== false)}`,
|
||||
`DryRun: ${String(options.dryRun)}`,
|
||||
`Mutation: ${String(data.mutation === true)}`,
|
||||
`IdempotentReplay: ${String(data.idempotentReplay === true)}`,
|
||||
`Transition: ${displayValue(allowedTransition.from)} -> ${displayValue(allowedTransition.to)} retryIndex=${displayValue(retryIndex)}`,
|
||||
`Attempt: ${attemptId === null ? "planned" : `attempt/${shortId(attemptId)}`} state=${displayValue(attempt.state ?? allowedTransition.to)}`,
|
||||
`PreviousAttempt: ${previousAttemptId === null ? "-" : `attempt/${shortId(previousAttemptId)}`}`,
|
||||
];
|
||||
if (runId !== null) lines.push(`Run: run/${shortId(runId)}`);
|
||||
if (commandId !== null) lines.push(`Command: command/${shortId(commandId)}`);
|
||||
if (runnerJobId !== null) lines.push(`RunnerJob: runnerjob/${shortId(runnerJobId)}`);
|
||||
const next = [
|
||||
`bun scripts/cli.ts agentrun get attempts --task ${taskId}`,
|
||||
`bun scripts/cli.ts agentrun describe task/${taskId}`,
|
||||
runId === null ? null : `bun scripts/cli.ts agentrun events run/${runId} --after-seq 0 --limit 100`,
|
||||
runId === null || commandId === null ? null : `bun scripts/cli.ts agentrun describe command/${commandId} --run ${runId}`,
|
||||
].filter((value): value is string => value !== null);
|
||||
lines.push("", "Next:", ...next.map((value) => ` ${value}`));
|
||||
return renderedCliResult(raw.ok !== false, command, lines.join("\n"));
|
||||
}
|
||||
|
||||
function compactQueueRetryAttempt(value: Record<string, unknown>): Record<string, unknown> | null {
|
||||
if (Object.keys(value).length === 0) return null;
|
||||
const attempt = pickCompact(value, ["attemptId", "taskId", "retryIndex", "state", "idempotencyKey", "reason", "payloadHash", "previousAttemptId", "runId", "commandId", "runnerJobId", "sessionId", "sessionPath", "failureKind", "failureMessage", "createdAt", "updatedAt", "activatedAt", "terminalAt"]);
|
||||
attempt.reason = boundedQueueAttemptText(attempt.reason, 240);
|
||||
attempt.failureMessage = boundedQueueAttemptText(attempt.failureMessage, 320);
|
||||
attempt.sessionPath = boundedQueueAttemptText(attempt.sessionPath, 240);
|
||||
return attempt;
|
||||
}
|
||||
|
||||
function compactQueueRetryIdentity(value: Record<string, unknown>, keys: string[]): Record<string, unknown> | null {
|
||||
return Object.keys(value).length === 0 ? null : pickCompact(value, keys);
|
||||
}
|
||||
|
||||
function nextQueueAttemptPageCommand(command: string, cursor: string, limit: number): string {
|
||||
const base = command
|
||||
.replace(/\s+--cursor(?:=|\s+)\S+/gu, "")
|
||||
.replace(/\s+--limit(?:=|\s+)\S+/gu, "")
|
||||
.trim();
|
||||
return `bun scripts/cli.ts ${base} --cursor ${cursor} --limit ${limit}`;
|
||||
}
|
||||
|
||||
export function renderDescribe(command: string, raw: Record<string, unknown>, options: AgentRunResourceOptions, ref: AgentRunResourceRef, text: string): RenderedCliResult {
|
||||
if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false);
|
||||
if (options.output === "json" || options.output === "yaml") {
|
||||
@@ -355,6 +474,7 @@ export function parseResourceKind(raw: string | undefined): AgentRunResourceKind
|
||||
if (raw === undefined) return null;
|
||||
const value = raw.toLowerCase().replace(/s$/u, "");
|
||||
if (value === "task" || value === "qt" || value === "queue" || value === "queuetask") return "task";
|
||||
if (value === "attempt") return "attempt";
|
||||
if (value === "run") return "run";
|
||||
if (value === "command" || value === "cmd") return "command";
|
||||
if (value === "runnerjob" || value === "runner-job" || value === "rjob" || value === "job") return "runnerjob";
|
||||
@@ -432,6 +552,46 @@ export function normalizeTaskItems(data: unknown, options: AgentRunResourceOptio
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeQueueAttemptItems(data: unknown): Record<string, unknown>[] {
|
||||
const items = arrayRecords(record(data).items ?? data);
|
||||
return items.map((item) => ({
|
||||
kind: "attempt",
|
||||
name: item.attemptId,
|
||||
attemptId: item.attemptId,
|
||||
taskId: item.taskId,
|
||||
retryIndex: item.retryIndex,
|
||||
state: item.state,
|
||||
idempotencyKey: item.idempotencyKey,
|
||||
reason: boundedQueueAttemptText(item.reason, 240),
|
||||
payloadHash: item.payloadHash,
|
||||
previousAttemptId: item.previousAttemptId,
|
||||
runId: item.runId,
|
||||
commandId: item.commandId,
|
||||
runnerJobId: item.runnerJobId,
|
||||
sessionId: item.sessionId,
|
||||
sessionPath: boundedQueueAttemptText(item.sessionPath, 240),
|
||||
failureKind: item.failureKind,
|
||||
failureMessage: boundedQueueAttemptText(item.failureMessage, 320),
|
||||
createdAt: item.createdAt,
|
||||
updatedAt: item.updatedAt,
|
||||
activatedAt: item.activatedAt,
|
||||
terminalAt: item.terminalAt,
|
||||
age: relativeAge(stringOrNull(item.updatedAt) ?? stringOrNull(item.createdAt)),
|
||||
}));
|
||||
}
|
||||
|
||||
function boundedQueueAttemptText(value: unknown, maxChars: number): string | null {
|
||||
const text = stringOrNull(value);
|
||||
return text === null ? null : truncateOneLine(text, maxChars);
|
||||
}
|
||||
|
||||
function queueAttemptFailureSummary(item: Record<string, unknown>): string {
|
||||
const kind = stringOrNull(item.failureKind);
|
||||
const message = stringOrNull(item.failureMessage);
|
||||
if (kind === null && message === null) return "-";
|
||||
return truncateOneLine(kind === null ? message ?? "-" : message === null ? kind : `${kind}: ${message}`, 72);
|
||||
}
|
||||
|
||||
export function taskListState(options: AgentRunResourceOptions): string {
|
||||
const requested = options.state?.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
||||
return requested?.[0] ?? "running";
|
||||
|
||||
Reference in New Issue
Block a user