Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/errors/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { AgentCoreCLIError, InputValidationError } from "./errors";
export { AgentCoreCLIError, InputValidationError, type AgentCoreCLIErrorOptions } from "./errors";
export { ERROR_SOURCE } from "./types";
8 changes: 5 additions & 3 deletions src/globalConfig/accessor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
} from "./types";
import type { ReadWriteJson } from "../io";
import type { Logger } from "../logging";
import z from "zod";
import { globalConfigFileSchema } from "./types";
import { DEFAULT_GLOBAL_CONFIG, applyOverrides } from "./config";
import z from "zod";
import { InputValidationError } from "../errors";

type DefaultGlobalConfigAccessorConfig = {
logger: Logger;
Expand Down Expand Up @@ -76,8 +77,9 @@ export class DefaultGlobalConfigAccessor implements GlobalConfigAccessor {
private async writeToConfigFile(data: GlobalConfigFileData): Promise<GlobalConfigFileData> {
const dataParseResult = globalConfigFileSchema.safeParse(data);
if (!dataParseResult.success) {
// TODO: mark this as a client-source error.
throw new TypeError(z.prettifyError(dataParseResult.error));
throw new InputValidationError(z.prettifyError(dataParseResult.error), {
cause: dataParseResult.error,
});
}

await this.json.write(this.filePath, dataParseResult.data);
Expand Down
10 changes: 9 additions & 1 deletion src/tui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import type { AppIO } from "../io";
import type { Core } from "../handlers/types";
import { JsonKey } from "../handlers/keys";
import { AgentCoreCLIError, ERROR_SOURCE, type AgentCoreCLIErrorOptions } from "../errors";

// renderJson pretty-prints a value as indented JSON. It is the output
// counterpart to renderTui: handlers call it to emit machine-readable results
Expand Down Expand Up @@ -43,7 +44,7 @@ export async function renderTuiAt(
io: AppIO,
): Promise<void> {
if (!io.stdin.isTTY || !io.stdout.isTTY) {
throw new TypeError("interactive mode requires a TTY on stdin and stdout");
throw new InvalidEnvironmentError("interactive mode requires a TTY on stdin and stdout");
}

// alternateScreen switches the terminal to its alternate buffer so the TUI
Expand Down Expand Up @@ -74,3 +75,10 @@ export function renderTui(core: Core, io: AppIO): DefaultHandle {
await renderTuiAt(ctx.require(PathKey), ctx, core, io);
};
}

/** Error raised when detecting an invalid environment */
export class InvalidEnvironmentError extends AgentCoreCLIError {
constructor(message?: string, options?: Omit<AgentCoreCLIErrorOptions, "source">) {
super(message, { ...options, source: ERROR_SOURCE.USER });
}
}
6 changes: 2 additions & 4 deletions src/tui/tui.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect, describe } from "bun:test";
import { createRootHandler } from "../handlers";
import { renderJson } from "./index";
import { InvalidEnvironmentError, renderJson } from "./index";
import {
createSilentLogger,
TestCoreClient,
Expand Down Expand Up @@ -89,9 +89,7 @@ describe("TUI stream boundary", () => {
globalConfigAccessor: new TestGlobalConfigAccessor(),
});

await expect(root.route(["node", "agentcore"])).rejects.toThrow(
"interactive mode requires a TTY on stdin and stdout",
);
await expect(root.route(["node", "agentcore"])).rejects.toThrow(InvalidEnvironmentError);
},
);

Expand Down
Loading