fix: bound AgentRun AipodSpec describe output
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user