fix(cli): return structured system event errors (#123614)

* fix(cli): return JSON for system event errors

* fix(cli): unify system machine output errors
This commit is contained in:
Peter Steinberger
2026-08-14 04:56:32 -07:00
committed by GitHub
parent c3ae887f46
commit f72001fefb
2 changed files with 62 additions and 2 deletions
+55
View File
@@ -76,15 +76,52 @@ describe("system-cli", () => {
expect(runtimeLogs).toEqual([]);
expect(runtimeErrors[0]).toContain("unwakeable-session-key");
expect(defaultRuntime.writeJson).not.toHaveBeenCalled();
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
});
it("handles invalid wake mode as runtime error", async () => {
await runCli(["system", "event", "--text", "hello", "--mode", "later"]);
expect(callGatewayFromCli).not.toHaveBeenCalled();
expect(runtimeLogs).toEqual([]);
expect(runtimeErrors[0]).toContain("--mode must be now or next-heartbeat");
expect(defaultRuntime.writeJson).not.toHaveBeenCalled();
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
});
it.each([
{
name: "invalid wake mode",
args: ["system", "event", "--text", "hello", "--mode", "later", "--json"],
gatewayResult: undefined,
expectedError: "Error: --mode must be now or next-heartbeat",
gatewayCalls: 0,
},
{
name: "rejected Gateway call",
args: ["system", "event", "--text", "hello", "--json"],
gatewayResult: { ok: false, reason: "unwakeable-session-key" },
expectedError: "Error: unwakeable-session-key",
gatewayCalls: 1,
},
])(
"writes JSON when $name fails",
async ({ args, gatewayResult, expectedError, gatewayCalls }) => {
if (gatewayResult) {
callGatewayFromCli.mockResolvedValueOnce(gatewayResult);
}
await runCli(args);
expect(runtimeLogs).toEqual([JSON.stringify({ error: expectedError }, null, 2)]);
expect(runtimeErrors).toEqual([]);
expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ error: expectedError });
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
expect(callGatewayFromCli).toHaveBeenCalledTimes(gatewayCalls);
},
);
it("forwards --session-key on system event", async () => {
await runCli([
"system",
@@ -123,6 +160,24 @@ describe("system-cli", () => {
expect(params).not.toHaveProperty("sessionKey");
});
it("writes JSON when an implicit machine-output command fails", async () => {
callGatewayFromCli.mockRejectedValueOnce(new Error("Gateway unavailable"));
await runCli(["system", "heartbeat", "last"]);
expect(callGatewayFromCli).toHaveBeenCalledTimes(1);
const [method, gatewayOptions, params, requestOptions] = gatewayCall();
expect(method).toBe("last-heartbeat");
expect(typeof gatewayOptions).toBe("object");
expect(params).toBeUndefined();
expect(requestOptions).toEqual({ expectFinal: false });
const expectedError = "Error: Gateway unavailable";
expect(runtimeLogs).toEqual([JSON.stringify({ error: expectedError }, null, 2)]);
expect(runtimeErrors).toEqual([]);
expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ error: expectedError });
expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
});
it.each([
{ args: ["system", "heartbeat", "last"], method: "last-heartbeat", params: undefined },
{
+7 -2
View File
@@ -35,15 +35,20 @@ async function runSystemGatewayCommand(
action: () => Promise<unknown>,
successText?: string,
): Promise<void> {
const machineOutput = opts.json || successText === undefined;
try {
const result = await action();
if (opts.json || successText === undefined) {
if (machineOutput) {
defaultRuntime.writeJson(result);
} else {
defaultRuntime.log(successText);
}
} catch (err) {
defaultRuntime.error(danger(String(err)));
if (machineOutput) {
defaultRuntime.writeJson({ error: String(err) });
} else {
defaultRuntime.error(danger(String(err)));
}
defaultRuntime.exit(1);
}
}