fix: reduce infra feedback CLI friction
This commit is contained in:
+106
-6
@@ -4811,7 +4811,12 @@ async function agentRunRestRequest(command: string, method: AgentRunHttpMethod,
|
||||
throw new AgentRunRestError("schema-mismatch", `AgentRun server returned non-JSON response for ${method} ${pathValue}`, { bridge, httpStatus: response.status });
|
||||
}
|
||||
if (response.status === 401 || response.status === 403) throw new AgentRunRestError("auth-failed", stringOrNull(envelope.message) ?? "AgentRun API key was rejected", { bridge, httpStatus: response.status, details: safeAgentRunEnvelope(envelope) });
|
||||
if (!response.ok) throw new AgentRunRestError(response.status === 404 ? "unsupported-version" : "validation-failed", stringOrNull(envelope.message) ?? `AgentRun request failed with HTTP ${response.status}`, { bridge, httpStatus: response.status, details: safeAgentRunEnvelope(envelope) });
|
||||
if (!response.ok) {
|
||||
const details = response.status === 404
|
||||
? addAgentRunNotFoundLookupHint(safeAgentRunEnvelope(envelope), clientConfig, method, pathValue)
|
||||
: safeAgentRunEnvelope(envelope);
|
||||
throw new AgentRunRestError(response.status === 404 ? "not-found" : "validation-failed", stringOrNull(envelope.message) ?? `AgentRun request failed with HTTP ${response.status}`, { bridge, httpStatus: response.status, details });
|
||||
}
|
||||
if (envelope.ok !== true) {
|
||||
const failureKind = normalizeAgentRunFailureKind(stringOrNull(envelope.failureKind), response.status);
|
||||
throw new AgentRunRestError(failureKind, stringOrNull(envelope.message) ?? `AgentRun request failed for ${method} ${pathValue}`, { bridge, httpStatus: response.status, details: safeAgentRunEnvelope(envelope) });
|
||||
@@ -4827,13 +4832,33 @@ async function agentRunRestRequest(command: string, method: AgentRunHttpMethod,
|
||||
function renderAgentRunRestError(command: string, error: AgentRunRestError, options: AgentRunResourceOptions): RenderedCliResult {
|
||||
const payload = error.toPayload(command);
|
||||
if (options.raw || options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output === "yaml" ? "yaml" : "json", false);
|
||||
const laneHint = record(record(payload.agentrun).laneAwareLookup);
|
||||
const currentEndpoint = record(laneHint.currentEndpoint);
|
||||
const nextCommands = Array.isArray(laneHint.nextCommands)
|
||||
? laneHint.nextCommands.filter((value): value is string => typeof value === "string" && value.length > 0).slice(0, 4)
|
||||
: [];
|
||||
const hintLines = Object.keys(laneHint).length === 0
|
||||
? []
|
||||
: [
|
||||
"",
|
||||
"Lane lookup hint:",
|
||||
` Current baseUrl: ${String(currentEndpoint.baseUrl ?? "(unknown)")}`,
|
||||
` Config: ${String(currentEndpoint.configPath ?? "(unknown)")}`,
|
||||
` ${String(laneHint.summary ?? "The resource was not found on the currently configured AgentRun manager endpoint.")}`,
|
||||
];
|
||||
const nextLines = nextCommands.length > 0
|
||||
? nextCommands
|
||||
: [
|
||||
"Check config/agentrun.yaml manager.baseUrl and the AgentRun API route.",
|
||||
"Verify HWLAB_API_KEY is present in env or in the configured auth.file without printing the key.",
|
||||
];
|
||||
return renderedCliResult(false, command, [
|
||||
`Error: ${payload.failureKind}`,
|
||||
String(payload.message ?? ""),
|
||||
...hintLines,
|
||||
"",
|
||||
"Next:",
|
||||
" Check config/agentrun.yaml manager.baseUrl and the AgentRun API route.",
|
||||
" Verify HWLAB_API_KEY is present in env or in the configured auth.file without printing the key.",
|
||||
...nextLines.map((line) => ` ${line}`),
|
||||
].join("\n"));
|
||||
}
|
||||
|
||||
@@ -5059,6 +5084,81 @@ function agentRunRestBridgeMetadata(config: AgentRunClientConfig, auth: AgentRun
|
||||
};
|
||||
}
|
||||
|
||||
function addAgentRunNotFoundLookupHint(details: Record<string, unknown>, config: AgentRunClientConfig, method: AgentRunHttpMethod, pathValue: string): Record<string, unknown> {
|
||||
const resource = agentRunResourceFromPath(pathValue);
|
||||
const laneConfig = readAgentRunLaneLookupConfig(config.sourcePath);
|
||||
const candidateLanes = laneConfig?.lanes ?? [];
|
||||
const nextCommands = candidateLanes
|
||||
.filter((lane) => typeof lane.node === "string" && lane.node.length > 0 && typeof lane.lane === "string" && lane.lane.length > 0)
|
||||
.slice(0, 4)
|
||||
.map((lane) => `bun scripts/cli.ts agentrun control-plane status --node ${lane.node} --lane ${lane.lane}`);
|
||||
return {
|
||||
...details,
|
||||
laneAwareLookup: {
|
||||
kind: "agentrun-resource-not-found-on-current-endpoint",
|
||||
summary: resource === null
|
||||
? "The request returned 404 on the currently configured AgentRun manager endpoint; a resource from another lane will not be visible through this baseUrl."
|
||||
: `${resource.kind}/${resource.id} returned 404 on the currently configured AgentRun manager endpoint; if it was created on another lane, inspect that lane before treating the resource as lost.`,
|
||||
request: {
|
||||
method,
|
||||
path: pathValue,
|
||||
...(resource === null ? {} : { resource }),
|
||||
},
|
||||
currentEndpoint: {
|
||||
transport: "direct-http",
|
||||
baseUrl: config.manager.baseUrl,
|
||||
configPath: config.sourcePath,
|
||||
defaultNode: laneConfig?.defaultNode ?? null,
|
||||
defaultLane: laneConfig?.defaultLane ?? null,
|
||||
},
|
||||
candidateLanes,
|
||||
nextCommands,
|
||||
note: "Resource commands use manager.baseUrl from the client config; --node/--lane currently belongs to control-plane inspection commands.",
|
||||
valuesPrinted: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function agentRunResourceFromPath(pathValue: string): Record<string, string> | null {
|
||||
const run = pathValue.match(/\/runs\/([^/?#]+)/u);
|
||||
if (run?.[1] !== undefined) return { kind: "run", id: decodeURIComponent(run[1]) };
|
||||
const session = pathValue.match(/\/sessions\/([^/?#]+)/u);
|
||||
if (session?.[1] !== undefined) return { kind: "session", id: decodeURIComponent(session[1]) };
|
||||
const command = pathValue.match(/\/commands\/([^/?#]+)/u);
|
||||
if (command?.[1] !== undefined) return { kind: "command", id: decodeURIComponent(command[1]) };
|
||||
const task = pathValue.match(/\/queue\/tasks\/([^/?#]+)/u);
|
||||
if (task?.[1] !== undefined) return { kind: "task", id: decodeURIComponent(task[1]) };
|
||||
return null;
|
||||
}
|
||||
|
||||
function readAgentRunLaneLookupConfig(configPath: string): { defaultNode: string | null; defaultLane: string | null; lanes: Record<string, unknown>[] } | null {
|
||||
try {
|
||||
const raw = readFileSync(configPath, "utf8");
|
||||
const parsed = record(Bun.YAML.parse(raw) as unknown);
|
||||
const controlPlane = record(parsed.controlPlane);
|
||||
const defaultTarget = record(controlPlane.default);
|
||||
const lanes = record(controlPlane.lanes);
|
||||
return {
|
||||
defaultNode: stringOrNull(defaultTarget.node),
|
||||
defaultLane: stringOrNull(defaultTarget.lane),
|
||||
lanes: Object.entries(lanes).map(([laneName, rawLane]) => {
|
||||
const lane = record(rawLane);
|
||||
const runtime = record(lane.runtime);
|
||||
return {
|
||||
lane: laneName,
|
||||
node: stringOrNull(lane.node) ?? stringOrNull(lane.nodeId),
|
||||
version: stringOrNull(lane.version),
|
||||
namespace: stringOrNull(runtime.namespace),
|
||||
internalBaseUrl: stringOrNull(runtime.internalBaseUrl),
|
||||
isDefault: laneName === stringOrNull(defaultTarget.lane),
|
||||
};
|
||||
}),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function agentRunDryRunPlan(action: string, pathValue: string, body: Record<string, unknown>, confirmCommand: string, method: AgentRunHttpMethod = "POST", extra: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
return {
|
||||
ok: true,
|
||||
@@ -5293,9 +5393,9 @@ function safeAgentRunEnvelope(envelope: Record<string, unknown>): Record<string,
|
||||
}
|
||||
|
||||
function normalizeAgentRunFailureKind(raw: string | null, httpStatus: number): AgentRunFailureKind {
|
||||
if (raw === "auth-missing" || raw === "auth-failed" || raw === "agentrun-unreachable" || raw === "schema-mismatch" || raw === "unsupported-version" || raw === "validation-failed") return raw;
|
||||
if (raw === "auth-missing" || raw === "auth-failed" || raw === "agentrun-unreachable" || raw === "schema-mismatch" || raw === "unsupported-version" || raw === "validation-failed" || raw === "not-found") return raw;
|
||||
if (httpStatus === 401 || httpStatus === 403) return "auth-failed";
|
||||
if (httpStatus === 404) return "unsupported-version";
|
||||
if (httpStatus === 404) return "not-found";
|
||||
return raw === "schema-invalid" ? "validation-failed" : "validation-failed";
|
||||
}
|
||||
|
||||
@@ -5383,7 +5483,7 @@ interface AgentRunResolvedAuth {
|
||||
}
|
||||
|
||||
type AgentRunHttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
||||
type AgentRunFailureKind = "auth-missing" | "auth-failed" | "agentrun-unreachable" | "schema-mismatch" | "unsupported-version" | "validation-failed";
|
||||
type AgentRunFailureKind = "auth-missing" | "auth-failed" | "agentrun-unreachable" | "schema-mismatch" | "unsupported-version" | "validation-failed" | "not-found";
|
||||
|
||||
type AgentRunRestCompatGroup = "queue" | "sessions" | "aipod-specs" | "aipods" | "runs" | "commands" | "runner";
|
||||
type AgentRunResourceVerb = "get" | "describe" | "events" | "logs" | "result" | "ack" | "cancel" | "dispatch" | "create" | "apply" | "send" | "explain";
|
||||
|
||||
Reference in New Issue
Block a user