diff --git a/src/cli/logs-cli.test.ts b/src/cli/logs-cli.test.ts index b7925bf812b..02bc0b8dab0 100644 --- a/src/cli/logs-cli.test.ts +++ b/src/cli/logs-cli.test.ts @@ -54,7 +54,7 @@ describe("logs cli", () => { expect(stderrWrites.join("")).toContain("Log cursor reset"); }); - it("wires --local-time through CLI parsing and emits local timestamps", async () => { + it("emits local timestamps in plain mode", async () => { callGatewayFromCli.mockResolvedValueOnce({ file: "/tmp/openclaw.log", lines: [ @@ -77,15 +77,17 @@ describe("logs cli", () => { program.exitOverride(); registerLogsCli(program); - await program.parseAsync(["logs", "--local-time", "--plain"], { from: "user" }); + await program.parseAsync(["logs", "--plain"], { from: "user" }); stdoutSpy.mockRestore(); const output = stdoutWrites.join(""); expect(output).toContain("line one"); - const timestamp = output.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z?/u)?.[0]; + // Timestamps should be local ISO format (no trailing Z) + const timestamp = output.match( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{2}:\d{2}/u, + )?.[0]; expect(timestamp).toBeTruthy(); - expect(timestamp?.endsWith("Z")).toBe(false); }); it("warns when the output pipe closes", async () => { @@ -119,35 +121,16 @@ describe("logs cli", () => { }); describe("formatLogTimestamp", () => { - it("formats UTC timestamp in plain mode by default", () => { + it("formats timestamp in local ISO format in plain mode", () => { const result = formatLogTimestamp("2025-01-01T12:00:00.000Z"); - expect(result).toBe("2025-01-01T12:00:00.000Z"); - }); - - it("formats UTC timestamp in pretty mode", () => { - const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "pretty"); - expect(result).toBe("12:00:00"); - }); - - it("formats local time in plain mode when localTime is true", () => { - const utcTime = "2025-01-01T12:00:00.000Z"; - const result = formatLogTimestamp(utcTime, "plain", true); - // Should be local time with explicit timezone offset (not 'Z' suffix). + // Should be local ISO time with timezone offset, no trailing Z + expect(result).not.toContain("Z"); expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{2}:\d{2}$/); - // The exact time depends on timezone, but should be different from UTC - expect(result).not.toBe(utcTime); }); - it("formats local time in pretty mode when localTime is true", () => { - const utcTime = "2025-01-01T12:00:00.000Z"; - const result = formatLogTimestamp(utcTime, "pretty", true); - // Should be HH:MM:SS format + it("formats timestamp in local HH:MM:SS in pretty mode", () => { + const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "pretty"); expect(result).toMatch(/^\d{2}:\d{2}:\d{2}$/); - // Should be different from UTC time (12:00:00) if not in UTC timezone - const tzOffset = new Date(utcTime).getTimezoneOffset(); - if (tzOffset !== 0) { - expect(result).not.toBe("12:00:00"); - } }); it("handles empty or invalid timestamps", () => { diff --git a/src/cli/logs-cli.ts b/src/cli/logs-cli.ts index 14e3df96bd7..b2542a0bed4 100644 --- a/src/cli/logs-cli.ts +++ b/src/cli/logs-cli.ts @@ -27,7 +27,6 @@ type LogsCliOptions = { json?: boolean; plain?: boolean; color?: boolean; - localTime?: boolean; url?: string; token?: string; timeout?: string; @@ -61,11 +60,7 @@ async function fetchLogs( return payload as LogsTailPayload; } -export function formatLogTimestamp( - value?: string, - mode: "pretty" | "plain" = "plain", - localTime = false, -) { +export function formatLogTimestamp(value?: string, mode: "pretty" | "plain" = "plain") { if (!value) { return ""; } @@ -87,7 +82,6 @@ function formatLogLine( opts: { pretty: boolean; rich: boolean; - localTime: boolean; }, ): string { const parsed = parseLogLine(raw); @@ -95,7 +89,7 @@ function formatLogLine( return raw; } const label = parsed.subsystem ?? parsed.module ?? ""; - const time = formatLogTimestamp(parsed.time, opts.pretty ? "pretty" : "plain", opts.localTime); + const time = formatLogTimestamp(parsed.time, opts.pretty ? "pretty" : "plain"); const level = parsed.level ?? ""; const levelLabel = level.padEnd(5).trim(); const message = parsed.message || parsed.raw; @@ -202,7 +196,6 @@ export function registerLogsCli(program: Command) { .option("--json", "Emit JSON log lines", false) .option("--plain", "Plain text output (no ANSI styling)", false) .option("--no-color", "Disable ANSI colors") - .option("--local-time", "Display timestamps in local timezone", false) .addHelpText( "after", () => @@ -219,7 +212,6 @@ export function registerLogsCli(program: Command) { const jsonMode = Boolean(opts.json); const pretty = !jsonMode && Boolean(process.stdout.isTTY) && !opts.plain; const rich = isRich() && opts.color !== false; - const localTime = Boolean(opts.localTime); while (true) { let payload: LogsTailPayload; @@ -291,7 +283,6 @@ export function registerLogsCli(program: Command) { formatLogLine(line, { pretty, rich, - localTime, }), ) ) {