惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
爱范儿
爱范儿
宝玉的分享
宝玉的分享
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - Franky
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 三生石上(FineUI控件)
人人都是产品经理
人人都是产品经理
阮一峰的网络日志
阮一峰的网络日志
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
博客园 - 聂微东
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
博客园 - 叶小钗
WordPress大学
WordPress大学
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
小众软件
小众软件
博客园 - 司徒正美
博客园 - 【当耐特】
IT之家
IT之家

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(ui): ignore invalid reset timestamps · openclaw/openc...
steipete · 2026-05-30 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -1,5 +1,6 @@

11

import { describe, expect, test } from "vitest";

22

import {

3+

asDateTimestampMs,

34

asFiniteNumber,

45

asFiniteNumberInRange,

56

asSafeIntegerInRange,

@@ -119,6 +120,11 @@ describe("number-coercion", () => {

119120

});

120121
121122

test("timestamp ISO helper rejects Date-invalid timestamps", () => {

123+

expect(asDateTimestampMs(0)).toBe(0);

124+

expect(asDateTimestampMs(8_640_000_000_000_000)).toBe(8_640_000_000_000_000);

125+

expect(asDateTimestampMs(8_640_000_000_000_001)).toBeUndefined();

126+

expect(asDateTimestampMs(Number.POSITIVE_INFINITY)).toBeUndefined();

127+

expect(asDateTimestampMs("0")).toBeUndefined();

122128

expect(timestampMsToIsoString(0)).toBe("1970-01-01T00:00:00.000Z");

123129

expect(timestampMsToIsoString(8_640_000_000_000_000)).toBe("+275760-09-13T00:00:00.000Z");

124130

expect(timestampMsToIsoString(8_640_000_000_000_001)).toBeUndefined();

Original file line numberDiff line numberDiff line change

@@ -97,11 +97,15 @@ export const MAX_TIMER_TIMEOUT_MS = 2_147_000_000;

9797

export const MAX_TIMER_TIMEOUT_SECONDS = Math.floor(MAX_TIMER_TIMEOUT_MS / 1000);

9898

export const MAX_DATE_TIMESTAMP_MS = 8_640_000_000_000_000;

9999
100-

export function timestampMsToIsoString(value: unknown): string | undefined {

101-

const timestampMs = asFiniteNumberInRange(value, {

100+

export function asDateTimestampMs(value: unknown): number | undefined {

101+

return asFiniteNumberInRange(value, {

102102

min: -MAX_DATE_TIMESTAMP_MS,

103103

max: MAX_DATE_TIMESTAMP_MS,

104104

});

105+

}

106+
107+

export function timestampMsToIsoString(value: unknown): string | undefined {

108+

const timestampMs = asDateTimestampMs(value);

105109

return timestampMs === undefined ? undefined : new Date(timestampMs).toISOString();

106110

}

107111
Original file line numberDiff line numberDiff line change

@@ -1,5 +1,6 @@

11

import { describe, expect, it } from "vitest";

22

import {

3+

formatMs,

34

formatRelativeTimestamp,

45

formatUnknownText,

56

parseSessionKeyParts,

@@ -37,6 +38,17 @@ describe("formatAgo", () => {

3738

});

3839

});

3940
41+

describe("formatMs", () => {

42+

it("formats epoch timestamps", () => {

43+

expect(formatMs(0)).not.toBe("n/a");

44+

});

45+
46+

it("returns n/a for Date-invalid timestamps", () => {

47+

expect(formatMs(8_640_000_000_000_001)).toBe("n/a");

48+

expect(formatMs(Number.POSITIVE_INFINITY)).toBe("n/a");

49+

});

50+

});

51+
4052

describe("stripThinkingTags", () => {

4153

it("strips <think>…</think> segments", () => {

4254

const input = ["<think>", "secret", "</think>", "", "Hello"].join("\n");

Original file line numberDiff line numberDiff line change

@@ -1,5 +1,6 @@

11

import { formatDurationHuman } from "../../../src/infra/format-time/format-duration.ts";

22

import { formatRelativeTimestamp } from "../../../src/infra/format-time/format-relative.ts";

3+

import { asDateTimestampMs } from "../../../src/shared/number-coercion.js";

34

import { t } from "../i18n/index.ts";

45
56

export { formatRelativeTimestamp, formatDurationHuman };

@@ -37,10 +38,11 @@ export function formatUnknownText(

3738

}

3839
3940

export function formatMs(ms?: number | null): string {

40-

if (!ms && ms !== 0) {

41+

const timestampMs = asDateTimestampMs(ms);

42+

if (timestampMs === undefined) {

4143

return t("common.na");

4244

}

43-

return new Date(ms).toLocaleString();

45+

return new Date(timestampMs).toLocaleString();

4446

}

4547
4648

export function formatList(values?: Array<string | null | undefined>): string {

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,20 @@

1+

import { afterEach, describe, expect, it, vi } from "vitest";

2+

import { formatQuotaReset } from "./provider-quota-summary.ts";

3+
4+

describe("formatQuotaReset", () => {

5+

afterEach(() => {

6+

vi.restoreAllMocks();

7+

});

8+
9+

it("returns compact relative reset windows", () => {

10+

vi.spyOn(Date, "now").mockReturnValue(Date.parse("2026-05-30T12:00:00.000Z"));

11+
12+

expect(formatQuotaReset(Date.now() + 30 * 60_000)).toBe("30m");

13+

expect(formatQuotaReset(Date.now() + 2 * 60 * 60_000 + 15 * 60_000)).toBe("2h 15m");

14+

});

15+
16+

it("ignores Date-invalid reset timestamps", () => {

17+

expect(formatQuotaReset(8_640_000_000_000_001)).toBeNull();

18+

expect(formatQuotaReset(Number.POSITIVE_INFINITY)).toBeNull();

19+

});

20+

});

Original file line numberDiff line numberDiff line change

@@ -1,3 +1,4 @@

1+

import { asDateTimestampMs } from "../../../src/shared/number-coercion.js";

12

import type { ModelAuthStatusProvider, ModelAuthStatusResult } from "./types.ts";

23
34

export type QuotaWindowSummary = {

@@ -8,10 +9,11 @@ export type QuotaWindowSummary = {

89

};

910
1011

export function formatQuotaReset(resetAt?: number): string | null {

11-

if (!resetAt || !Number.isFinite(resetAt)) {

12+

const timestampMs = asDateTimestampMs(resetAt);

13+

if (timestampMs === undefined) {

1214

return null;

1315

}

14-

const diffMs = resetAt - Date.now();

16+

const diffMs = timestampMs - Date.now();

1517

if (diffMs <= 0) {

1618

return "now";

1719

}

@@ -29,7 +31,7 @@ export function formatQuotaReset(resetAt?: number): string | null {

2931

const remainingHours = hours % 24;

3032

return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`;

3133

}

32-

return new Date(resetAt).toLocaleDateString(undefined, { month: "short", day: "numeric" });

34+

return new Date(timestampMs).toLocaleDateString(undefined, { month: "short", day: "numeric" });

3335

}

3436
3537

export function collectQuotaWindows(

Original file line numberDiff line numberDiff line change

@@ -1,5 +1,6 @@

11

import { html, nothing, type TemplateResult } from "lit";

22

import { unsafeHTML } from "lit/directives/unsafe-html.js";

3+

import { asDateTimestampMs } from "../../../../src/shared/number-coercion.js";

34

import { t } from "../../i18n/index.ts";

45

import { resolveCronJobLastRunStatus } from "../cron-status.ts";

56

import { formatCost, formatTokens, formatRelativeTimestamp } from "../format.ts";

@@ -224,14 +225,12 @@ export function renderOverviewCards(props: OverviewCardsProps) {

224225

// Hidden for windows with plenty of headroom to keep the hint readable;

225226

// shown when a window is below 25% to signal urgency.

226227

const formatReset = (resetAt: number | undefined, pctLeft: number): string | null => {

227-

if (!resetAt || !Number.isFinite(resetAt) || pctLeft >= 25) {

228+

const timestampMs = asDateTimestampMs(resetAt);

229+

if (timestampMs === undefined || pctLeft >= 25) {

228230

return null;

229231

}

230-

const d = new Date(resetAt);

231-

if (Number.isNaN(d.getTime())) {

232-

return null;

233-

}

234-

const withinADay = resetAt - Date.now() < 24 * 60 * 60 * 1000;

232+

const d = new Date(timestampMs);

233+

const withinADay = timestampMs - Date.now() < 24 * 60 * 60 * 1000;

235234

return withinADay

236235

? d.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" })

237236

: d.toLocaleDateString(undefined, { month: "short", day: "numeric" });