


















@@ -1,5 +1,8 @@
11import crypto from "node:crypto";
2-import { asSafeIntegerInRange } from "../../shared/number-coercion.js";
2+import {
3+isFutureDateTimestampMs,
4+resolveExpiresAtMsFromDurationMs,
5+} from "../../shared/number-coercion.js";
36import { normalizeOptionalString } from "../../shared/string-coerce.js";
4758export const NOVNC_PASSWORD_ENV_KEY = "OPENCLAW_BROWSER_NOVNC_PASSWORD"; // pragma: allowlist secret
@@ -23,12 +26,25 @@ const NO_VNC_OBSERVER_TOKENS = new Map<string, NoVncObserverTokenEntry>();
23262427function pruneExpiredNoVncObserverTokens(now: number) {
2528for (const [token, entry] of NO_VNC_OBSERVER_TOKENS) {
26-if (entry.expiresAt <= now) {
29+if (!isFutureDateTimestampMs(entry.expiresAt, { nowMs: now })) {
2730NO_VNC_OBSERVER_TOKENS.delete(token);
2831}
2932}
3033}
313435+function resolveNoVncObserverTokenExpiresAt(params: { ttlMs?: number; nowMs: number }) {
36+return (
37+resolveExpiresAtMsFromDurationMs(params.ttlMs, {
38+nowMs: params.nowMs,
39+minRemainingMs: 1,
40+}) ??
41+resolveExpiresAtMsFromDurationMs(NOVNC_TOKEN_TTL_MS, {
42+nowMs: params.nowMs,
43+minRemainingMs: 1,
44+})
45+);
46+}
47+3248export function isNoVncEnabled(params: { enableNoVnc: boolean; headless: boolean }) {
3349return params.enableNoVnc && !params.headless;
3450}
@@ -66,13 +82,21 @@ export function issueNoVncObserverToken(params: {
6682const now = params.nowMs ?? Date.now();
6783pruneExpiredNoVncObserverTokens(now);
6884const token = crypto.randomBytes(24).toString("hex");
69-const ttlMs =
70-asSafeIntegerInRange(params.ttlMs, { min: 1, max: MAX_NOVNC_TOKEN_TTL_MS }) ??
71-NOVNC_TOKEN_TTL_MS;
85+const requestedTtlMs =
86+typeof params.ttlMs === "number" && params.ttlMs <= MAX_NOVNC_TOKEN_TTL_MS
87+ ? params.ttlMs
88+ : undefined;
89+const expiresAt = resolveNoVncObserverTokenExpiresAt({
90+ttlMs: requestedTtlMs,
91+nowMs: now,
92+});
93+if (expiresAt === undefined) {
94+return token;
95+}
7296NO_VNC_OBSERVER_TOKENS.set(token, {
7397noVncPort: params.noVncPort,
7498password: normalizeOptionalString(params.password),
75-expiresAt: now + ttlMs,
99+ expiresAt,
76100});
77101return token;
78102}
@@ -92,7 +116,7 @@ export function consumeNoVncObserverToken(
92116return null;
93117}
94118NO_VNC_OBSERVER_TOKENS.delete(normalized);
95-if (entry.expiresAt <= now) {
119+if (!isFutureDateTimestampMs(entry.expiresAt, { nowMs: now })) {
96120return null;
97121}
98122return { noVncPort: entry.noVncPort, password: entry.password };
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。