fix: bound AgentRun AipodSpec describe output

This commit is contained in:
Codex
2026-07-12 07:12:31 +02:00
parent be9050df55
commit 9b185634de
5 changed files with 454 additions and 2 deletions
+246
View File
@@ -1387,6 +1387,252 @@ export function compactTaskDescriptionPayload(ref: AgentRunResourceRef, task: Re
};
}
export function compactAipodSpecDescriptionPayload(ref: AgentRunResourceRef, data: unknown): Record<string, unknown> {
const root = record(data);
const describedItem = record(root.item);
const item = Object.keys(describedItem).length > 0 ? describedItem : root;
const manifest = record(root.spec);
const manifestMetadata = record(manifest.metadata);
const manifestSpec = record(manifest.spec);
const manifestAipodMetadata = record(manifestSpec.metadata);
const model = firstNonEmptyRecord(item.model, manifestSpec.model);
const workspaceRef = compactAipodSpecWorkspaceRef(firstNonEmptyRecord(item.workspaceRef, manifestSpec.workspaceRef));
const resourceBundleRef = compactAipodSpecResourceBundleRef(item.resourceBundleRef, manifestSpec.resourceBundleRef);
const secretScope = record(record(manifestSpec.executionPolicy).secretScope);
const providerCredentialRefs = compactAipodSpecCredentialRefs(item.providerCredentials, secretScope.providerCredentials, "provider");
const toolCredentialRefs = compactAipodSpecCredentialRefs(item.toolCredentials, secretScope.toolCredentials, "tool");
const imageRef = firstNonEmptyRecord(item.imageRef, manifestSpec.imageRef);
return {
kind: "AipodSpec",
name: ref.name,
state: boundedAipodSpecText(item.state ?? item.status ?? "available", 80),
identity: {
declaredName: boundedAipodSpecText(item.name ?? manifestMetadata.name ?? ref.name, 160),
displayName: boundedAipodSpecText(item.displayName ?? manifestMetadata.displayName, 200),
specHash: boundedAipodSpecText(item.specHash, 160),
valuesPrinted: false,
},
runtime: {
queue: boundedAipodSpecText(item.queue ?? manifestSpec.queue, 120),
lane: boundedAipodSpecText(item.lane ?? manifestSpec.lane, 120),
backendProfile: boundedAipodSpecText(item.backendProfile ?? item.profile ?? manifestSpec.backendProfile, 160),
providerId: boundedAipodSpecText(item.providerId ?? manifestSpec.providerId, 160),
model: {
name: boundedAipodSpecText(model.model, 200),
reasoningEffort: boundedAipodSpecText(model.reasoningEffort, 80),
valuesPrinted: false,
},
valuesPrinted: false,
},
workspaceRef,
resourceBundleRef,
credentialRefs: {
totalCount: providerCredentialRefs.count + toolCredentialRefs.count,
provider: providerCredentialRefs,
tool: toolCredentialRefs,
valuesPrinted: false,
},
provenance: {
source: boundedAipodSpecText(item.source ?? manifestAipodMetadata.source, 320),
apiVersion: boundedAipodSpecText(manifest.apiVersion, 120),
sourceKind: boundedAipodSpecText(manifest.kind, 120),
imageSourceIdentity: boundedAipodSpecText(imageRef.sourceIdentity, 320),
createdAt: boundedAipodSpecText(item.createdAt, 120),
updatedAt: boundedAipodSpecText(item.updatedAt, 120),
valuesPrinted: false,
},
redaction: {
credentialValues: "omitted",
secretRefsOnly: true,
valuesPrinted: false,
},
next: {
full: `bun scripts/cli.ts agentrun describe aipodspec/${ref.name} --full -o json`,
raw: `bun scripts/cli.ts agentrun describe aipodspec/${ref.name} --raw`,
},
valuesPrinted: false,
};
}
export function renderAipodSpecDescription(payload: Record<string, unknown>): string {
const identity = record(payload.identity);
const runtime = record(payload.runtime);
const model = record(runtime.model);
const workspaceRef = record(payload.workspaceRef);
const resourceBundleRef = record(payload.resourceBundleRef);
const sourceIdentity = record(resourceBundleRef.sourceIdentity);
const credentialRefs = record(payload.credentialRefs);
const providerCredentialRefs = record(credentialRefs.provider);
const toolCredentialRefs = record(credentialRefs.tool);
const provenance = record(payload.provenance);
const next = record(payload.next);
const lines = [
`Name: aipodspec/${displayValue(payload.name)}`,
`State: ${displayValue(payload.state)}`,
"",
"Identity:",
` Declared: ${displayValue(identity.declaredName)}`,
` Display: ${displayValue(identity.displayName)}`,
` Hash: ${displayValue(identity.specHash)}`,
"",
"Runtime:",
` Queue: ${displayValue(runtime.queue)}`,
` Lane: ${displayValue(runtime.lane)}`,
` Profile: ${displayValue(runtime.backendProfile)}`,
` Provider: ${displayValue(runtime.providerId)}`,
` Model: ${displayValue(model.name)} reasoning=${displayValue(model.reasoningEffort)}`,
"",
"Workspace:",
` Kind: ${displayValue(workspaceRef.kind)} path=${displayValue(workspaceRef.path)} branch=${displayValue(workspaceRef.branch)}`,
"",
"Primary resource bundle:",
` Source: kind=${displayValue(sourceIdentity.kind)} repo=${displayValue(sourceIdentity.repoUrl)} ref=${displayValue(sourceIdentity.ref)} commit=${displayValue(sourceIdentity.commitId)}`,
` References: bundles=${displayValue(resourceBundleRef.bundleCount)} requiredSkills=${displayValue(resourceBundleRef.requiredSkillCount)} promptRefs=${displayValue(resourceBundleRef.promptRefCount)}`,
` Credentials: provider=${displayValue(providerCredentialRefs.count)} tool=${displayValue(toolCredentialRefs.count)} total=${displayValue(credentialRefs.totalCount)}`,
];
const credentialLines = [
...renderAipodSpecCredentialRefLines(arrayRecords(providerCredentialRefs.items), "provider"),
...renderAipodSpecCredentialRefLines(arrayRecords(toolCredentialRefs.items), "tool"),
];
if (credentialLines.length > 0) lines.push("", "SecretRefs:", ...credentialLines.map((line) => ` ${line}`));
lines.push(
"",
"Provenance:",
` Source: ${displayValue(provenance.source)}`,
` Manifest: ${displayValue(provenance.apiVersion)} ${displayValue(provenance.sourceKind)}`,
` Image source: ${displayValue(provenance.imageSourceIdentity)}`,
` Updated: ${displayValue(provenance.updatedAt)}`,
" Values printed: false",
"",
"Next:",
` ${displayValue(next.full)}`,
` ${displayValue(next.raw)}`,
);
return lines.join("\n");
}
function compactAipodSpecWorkspaceRef(value: Record<string, unknown>): Record<string, unknown> | null {
if (Object.keys(value).length === 0) return null;
return {
kind: boundedAipodSpecText(value.kind, 80),
path: boundedAipodSpecText(value.path, 320),
branch: boundedAipodSpecText(value.branch, 160),
repo: boundedAipodSpecRepoUrl(value.repo ?? value.repoUrl),
commitId: boundedAipodSpecText(value.commitId, 160),
valuesPrinted: false,
};
}
function compactAipodSpecResourceBundleRef(primaryValue: unknown, fallbackValue: unknown): Record<string, unknown> | null {
const value = firstNonEmptyRecord(primaryValue, fallbackValue);
if (Object.keys(value).length === 0) return null;
return {
sourceIdentity: {
kind: boundedAipodSpecText(value.kind, 80),
repoUrl: boundedAipodSpecRepoUrl(value.repoUrl),
ref: boundedAipodSpecText(value.ref, 160),
commitId: boundedAipodSpecText(value.commitId, 160),
valuesPrinted: false,
},
bundleCount: aipodSpecReferenceCount(value.bundles),
requiredSkillCount: aipodSpecReferenceCount(value.requiredSkills),
promptRefCount: aipodSpecReferenceCount(value.promptRefs),
valuesPrinted: false,
};
}
function compactAipodSpecCredentialRefs(primaryValue: unknown, fallbackValue: unknown, kind: "provider" | "tool"): { count: number; items: Record<string, unknown>[]; omittedCount: number; valuesPrinted: false } {
const primary = record(primaryValue);
const primaryItems = arrayRecords(primary.items ?? primaryValue);
const fallbackItems = arrayRecords(fallbackValue);
const sourceItems = primaryItems.length > 0 ? primaryItems : fallbackItems;
const count = Object.keys(primary).length > 0 ? aipodSpecReferenceCount(primaryValue) : sourceItems.length;
const items = sourceItems.slice(0, 6).map((item) => {
const nestedSecretRef = record(item.secretRef);
const projection = record(item.projection);
return {
...(kind === "provider"
? { profile: boundedAipodSpecText(item.profile, 160) }
: {
tool: boundedAipodSpecText(item.tool, 160),
purpose: boundedAipodSpecText(item.purpose, 200),
}),
secretRef: {
namespace: boundedAipodSpecText(item.namespace ?? nestedSecretRef.namespace, 160),
name: boundedAipodSpecText(item.name ?? nestedSecretRef.name, 200),
keys: boundedAipodSpecStrings(item.keys ?? nestedSecretRef.keys, 6, 80),
valuesPrinted: false,
},
...(kind === "tool"
? {
projection: {
kind: boundedAipodSpecText(projection.kind, 80),
envName: boundedAipodSpecText(projection.envName, 160),
secretKey: boundedAipodSpecText(projection.secretKey, 160),
mountPath: boundedAipodSpecText(projection.mountPath, 320),
valuesPrinted: false,
},
}
: {}),
valuesPrinted: false,
};
});
return {
count,
items,
omittedCount: Math.max(0, count - items.length),
valuesPrinted: false,
};
}
function renderAipodSpecCredentialRefLines(items: Record<string, unknown>[], kind: "provider" | "tool"): string[] {
return items.map((item) => {
const secretRef = record(item.secretRef);
const namespace = stringOrNull(secretRef.namespace);
const name = displayValue(secretRef.name);
const reference = namespace === null ? name : `${namespace}/${name}`;
const keys = Array.isArray(secretRef.keys) ? secretRef.keys.map(String).join(",") : "-";
const identity = kind === "provider"
? `profile=${displayValue(item.profile)}`
: `tool=${displayValue(item.tool)} purpose=${displayValue(item.purpose)}`;
return `${kind} ${identity} secret=${reference} keys=${keys}`;
});
}
function firstNonEmptyRecord(...values: unknown[]): Record<string, unknown> {
for (const value of values) {
const candidate = record(value);
if (Object.keys(candidate).length > 0) return candidate;
}
return {};
}
function aipodSpecReferenceCount(value: unknown): number {
if (Array.isArray(value)) return value.length;
const summary = record(value);
const count = summary.count;
if (typeof count === "number" && Number.isFinite(count) && count >= 0) return Math.floor(count);
return arrayRecords(summary.items).length;
}
function boundedAipodSpecStrings(value: unknown, limit: number, maxChars: number): string[] {
if (!Array.isArray(value)) return [];
return value
.slice(0, limit)
.map((item) => boundedAipodSpecText(item, maxChars))
.filter((item): item is string => item !== null);
}
function boundedAipodSpecText(value: unknown, maxChars: number): string | null {
const text = stringOrNull(value);
return text === null ? null : truncateOneLine(text, maxChars);
}
function boundedAipodSpecRepoUrl(value: unknown): string | null {
const sanitized = taskInputRepoUrl(value);
return typeof sanitized === "string" ? truncateOneLine(sanitized, 320) : null;
}
export function taskInputDescriptionPayload(ref: AgentRunResourceRef, task: Record<string, unknown>): Record<string, unknown> {
const metadata = taskInputMetadata(task.metadata);
const aipodImageRef = taskInputImageRef(metadata.aipodImageRef);
+5 -2
View File
@@ -37,7 +37,7 @@ import type { AgentRunResourceOptions, AgentRunResourceRef, AgentRunResourceVerb
import { agentRunDryRunPlan } from "./config";
import { isHelpArg, renderAgentRunHelp } from "./entry";
import { agentRunExplain, arrayRecords, shortId } from "./options";
import { agentRunCancelAuthorityDisclosure, agentRunCancelCascadeScope, agentRunCancelFencingDisclosure, agentRunCancelRunnerAbortDisclosure, compactTaskDescriptionPayload, innerData, normalizeAipodSpecItems, normalizeAttemptResources, normalizeEventItems, normalizeLogItems, normalizeQueueAttemptItems, normalizeRunnerJobItems, normalizeSessionItems, normalizeSingleCommand, normalizeTaskItems, parseResourceKind, parseResourceRef, renderDescribe, renderEventLike, renderGenericDescription, renderMachine, renderMutationSummary, renderQueueAttemptList, renderQueueRetrySummary, renderResourceResult, renderResultSummary, renderTaskDescription, renderedCliResult, requiredContext, rerunWithoutDryRun, resolveAgentRunCancelPolicyTarget, resourceApply, resourceCreate, resourceDispatch, resourceSessionPromptCommand, taskInputDescriptionPayload, taskListState, unwrapTaskDetail } from "./render";
import { agentRunCancelAuthorityDisclosure, agentRunCancelCascadeScope, agentRunCancelFencingDisclosure, agentRunCancelRunnerAbortDisclosure, compactAipodSpecDescriptionPayload, compactTaskDescriptionPayload, innerData, normalizeAipodSpecItems, normalizeAttemptResources, normalizeEventItems, normalizeLogItems, normalizeQueueAttemptItems, normalizeRunnerJobItems, normalizeSessionItems, normalizeSingleCommand, normalizeTaskItems, parseResourceKind, parseResourceRef, renderAipodSpecDescription, renderDescribe, renderEventLike, renderGenericDescription, renderMachine, renderMutationSummary, renderQueueAttemptList, renderQueueRetrySummary, renderResourceResult, renderResultSummary, renderTaskDescription, renderedCliResult, requiredContext, rerunWithoutDryRun, resolveAgentRunCancelPolicyTarget, resourceApply, resourceCreate, resourceDispatch, resourceSessionPromptCommand, taskInputDescriptionPayload, taskListState, unwrapTaskDetail } from "./render";
import { AgentRunRestError, renderAgentRunRestError, resolveAgentRunRestTarget, runAgentRunRestCommand, withAgentRunRestTarget } from "./rest-bridge";
import { runnerJobObservationScript, type RunnerJobObservationIdentity } from "./runner-observation";
import { capture, captureJsonPayload, compactCapture, nonNegativeIntegerOrNull, record, stringOrNull } from "./utils";
@@ -361,7 +361,10 @@ export async function resourceDescribe(config: UniDeskConfig | null, command: st
}
if (ref.kind === "aipodspec") {
const result = await runAgentRunRestCommand(config, "aipod-specs", ["show", ref.name, ...(options.full ? ["--full"] : [])]);
return renderDescribe(command, result, options, ref, renderGenericDescription(ref, innerData(result)));
if (options.raw || options.full) return renderDescribe(command, result, options, ref, renderGenericDescription(ref, innerData(result)));
const payload = compactAipodSpecDescriptionPayload(ref, innerData(result));
if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output, result.ok !== false);
return renderedCliResult(result.ok !== false, command, renderAipodSpecDescription(payload));
}
throw new Error(`unsupported describe kind: ${ref.kind}`);
}