chore: Enable more lint rules, disable some that trigger a lot. Will clean up later.

This commit is contained in:
cpojer
2026-01-31 16:03:28 +09:00
parent 481f696a87
commit 15792b153f
292 changed files with 643 additions and 699 deletions

View File

@@ -279,7 +279,7 @@ export class GatewayClient {
try {
const parsed = JSON.parse(raw);
if (validateEventFrame(parsed)) {
const evt = parsed as EventFrame;
const evt = parsed;
if (evt.event === "connect.challenge") {
const payload = evt.payload as { nonce?: unknown } | undefined;
const nonce = payload && typeof payload.nonce === "string" ? payload.nonce : null;

View File

@@ -965,7 +965,7 @@ describeLive("gateway live (dev agent, profile keys)", () => {
});
const authStorage = discoverAuthStorage(agentDir);
const modelRegistry = discoverModels(authStorage, agentDir);
const all = modelRegistry.getAll() as Array<Model<Api>>;
const all = modelRegistry.getAll();
const rawModels = process.env.OPENCLAW_LIVE_GATEWAY_MODELS?.trim();
const useModern = !rawModels || rawModels === "modern" || rawModels === "all";

View File

@@ -249,7 +249,7 @@ function mergeAction(
if (!override) {
return validateAction(base);
}
const kind = (override.kind ?? base.kind ?? defaultAction) as "wake" | "agent";
const kind = override.kind ?? base.kind ?? defaultAction;
if (kind === "wake") {
const baseWake = base.kind === "wake" ? base : undefined;
const text = typeof override.text === "string" ? override.text : (baseWake?.text ?? "");

View File

@@ -78,7 +78,7 @@ export function startGatewayMaintenanceTimers(params: {
if (now - v.ts > DEDUPE_TTL_MS) params.dedupe.delete(k);
}
if (params.dedupe.size > DEDUPE_MAX) {
const entries = [...params.dedupe.entries()].sort((a, b) => a[1].ts - b[1].ts);
const entries = [...params.dedupe.entries()].toSorted((a, b) => a[1].ts - b[1].ts);
for (let i = 0; i < params.dedupe.size - DEDUPE_MAX; i++) {
params.dedupe.delete(entries[i][0]);
}

View File

@@ -35,19 +35,15 @@ function ensureAgentRunListener() {
if (evt.stream !== "lifecycle") return;
const phase = evt.data?.phase;
if (phase === "start") {
const startedAt =
typeof evt.data?.startedAt === "number" ? (evt.data.startedAt as number) : undefined;
const startedAt = typeof evt.data?.startedAt === "number" ? evt.data.startedAt : undefined;
agentRunStarts.set(evt.runId, startedAt ?? Date.now());
return;
}
if (phase !== "end" && phase !== "error") return;
const startedAt =
typeof evt.data?.startedAt === "number"
? (evt.data.startedAt as number)
: agentRunStarts.get(evt.runId);
const endedAt =
typeof evt.data?.endedAt === "number" ? (evt.data.endedAt as number) : undefined;
const error = typeof evt.data?.error === "string" ? (evt.data.error as string) : undefined;
typeof evt.data?.startedAt === "number" ? evt.data.startedAt : agentRunStarts.get(evt.runId);
const endedAt = typeof evt.data?.endedAt === "number" ? evt.data.endedAt : undefined;
const error = typeof evt.data?.error === "string" ? evt.data.error : undefined;
agentRunStarts.delete(evt.runId);
recordAgentRunSnapshot({
runId: evt.runId,
@@ -96,11 +92,10 @@ export async function waitForAgentJob(params: {
}
const startedAt =
typeof evt.data?.startedAt === "number"
? (evt.data.startedAt as number)
? evt.data.startedAt
: agentRunStarts.get(evt.runId);
const endedAt =
typeof evt.data?.endedAt === "number" ? (evt.data.endedAt as number) : undefined;
const error = typeof evt.data?.error === "string" ? (evt.data.error as string) : undefined;
const endedAt = typeof evt.data?.endedAt === "number" ? evt.data.endedAt : undefined;
const error = typeof evt.data?.error === "string" ? evt.data.error : undefined;
const snapshot: AgentRunSnapshot = {
runId: evt.runId,
status: phase === "error" ? "error" : "ok",

View File

@@ -44,7 +44,7 @@ import type { GatewayRequestHandlers } from "./types.js";
export const agentHandlers: GatewayRequestHandlers = {
agent: async ({ params, respond, context }) => {
const p = params as Record<string, unknown>;
const p = params;
if (!validateAgentParams(p)) {
respond(
false,
@@ -430,7 +430,7 @@ export const agentHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as AgentIdentityParams;
const p = params;
const agentIdRaw = typeof p.agentId === "string" ? p.agentId.trim() : "";
const sessionKeyRaw = typeof p.sessionKey === "string" ? p.sessionKey.trim() : "";
let agentId = agentIdRaw ? normalizeAgentId(agentIdRaw) : undefined;
@@ -471,7 +471,7 @@ export const agentHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as AgentWaitParams;
const p = params;
const runId = p.runId.trim();
const timeoutMs =
typeof p.timeoutMs === "number" && Number.isFinite(p.timeoutMs)

View File

@@ -89,7 +89,7 @@ export const cronHandlers: GatewayRequestHandlers = {
const normalizedPatch = normalizeCronJobPatch((params as { patch?: unknown } | null)?.patch);
const candidate =
normalizedPatch && typeof params === "object" && params !== null
? { ...(params as Record<string, unknown>), patch: normalizedPatch }
? { ...params, patch: normalizedPatch }
: params;
if (!validateCronUpdateParams(candidate)) {
respond(

View File

@@ -43,7 +43,7 @@ async function resolveLogFile(file: string): Promise<string> {
);
const sorted = candidates
.filter((entry): entry is NonNullable<typeof entry> => Boolean(entry))
.sort((a, b) => b.mtimeMs - a.mtimeMs);
.toSorted((a, b) => b.mtimeMs - a.mtimeMs);
return sorted[0]?.path ?? file;
}

View File

@@ -34,7 +34,7 @@ export function uniqueSortedStrings(values: unknown[]) {
return [...new Set(values.filter((v) => typeof v === "string"))]
.map((v) => v.trim())
.filter(Boolean)
.sort();
.toSorted();
}
export function safeParseJson(value: string | null | undefined): unknown {

View File

@@ -46,7 +46,7 @@ const getInflightMap = (context: GatewayRequestContext) => {
export const sendHandlers: GatewayRequestHandlers = {
send: async ({ params, respond, context }) => {
const p = params as Record<string, unknown>;
const p = params;
if (!validateSendParams(p)) {
respond(
false,
@@ -104,8 +104,8 @@ export const sendHandlers: GatewayRequestHandlers = {
typeof request.accountId === "string" && request.accountId.trim().length
? request.accountId.trim()
: undefined;
const outboundChannel = channel as Exclude<OutboundChannel, "none">;
const plugin = getChannelPlugin(channel as ChannelId);
const outboundChannel = channel;
const plugin = getChannelPlugin(channel);
if (!plugin) {
respond(
false,
@@ -237,7 +237,7 @@ export const sendHandlers: GatewayRequestHandlers = {
}
},
poll: async ({ params, respond, context }) => {
const p = params as Record<string, unknown>;
const p = params;
if (!validatePollParams(p)) {
respond(
false,
@@ -290,7 +290,7 @@ export const sendHandlers: GatewayRequestHandlers = {
? request.accountId.trim()
: undefined;
try {
const plugin = getChannelPlugin(channel as ChannelId);
const plugin = getChannelPlugin(channel);
const outbound = plugin?.outbound;
if (!outbound?.sendPoll) {
respond(
@@ -302,7 +302,7 @@ export const sendHandlers: GatewayRequestHandlers = {
}
const cfg = loadConfig();
const resolved = resolveOutboundTarget({
channel: channel as Exclude<OutboundChannel, "none">,
channel: channel,
to,
cfg,
accountId,

View File

@@ -53,7 +53,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as import("../protocol/index.js").SessionsListParams;
const p = params;
const cfg = loadConfig();
const { storePath, store } = loadCombinedSessionStoreForGateway(cfg);
const result = listSessionsFromStore({
@@ -78,7 +78,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as import("../protocol/index.js").SessionsPreviewParams;
const p = params;
const keysRaw = Array.isArray(p.keys) ? p.keys : [];
const keys = keysRaw
.map((key) => String(key ?? "").trim())
@@ -144,7 +144,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as import("../protocol/index.js").SessionsResolveParams;
const p = params;
const cfg = loadConfig();
const resolved = resolveSessionKeyFromResolveParams({ cfg, p });
@@ -166,7 +166,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as import("../protocol/index.js").SessionsPatchParams;
const p = params;
const key = String(p.key ?? "").trim();
if (!key) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
@@ -215,7 +215,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as import("../protocol/index.js").SessionsResetParams;
const p = params;
const key = String(p.key ?? "").trim();
if (!key) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
@@ -273,7 +273,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as import("../protocol/index.js").SessionsDeleteParams;
const p = params;
const key = String(p.key ?? "").trim();
if (!key) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
@@ -359,7 +359,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
);
return;
}
const p = params as import("../protocol/index.js").SessionsCompactParams;
const p = params;
const key = String(p.key ?? "").trim();
if (!key) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));

View File

@@ -52,7 +52,7 @@ function collectSkillBins(entries: SkillEntry[]): string[] {
}
}
}
return [...bins].sort();
return [...bins].toSorted();
}
export const skillsHandlers: GatewayRequestHandlers = {
@@ -95,7 +95,7 @@ export const skillsHandlers: GatewayRequestHandlers = {
const entries = loadWorkspaceSkillEntries(workspaceDir, { config: cfg });
for (const bin of collectSkillBins(entries)) bins.add(bin);
}
respond(true, { bins: [...bins].sort() }, undefined);
respond(true, { bins: [...bins].toSorted() }, undefined);
},
"skills.install": async ({ params, respond }) => {
if (!validateSkillsInstallParams(params)) {

View File

@@ -54,15 +54,15 @@ export const systemHandlers: GatewayRequestHandlers = {
const reason = typeof params.reason === "string" ? params.reason : undefined;
const roles =
Array.isArray(params.roles) && params.roles.every((t) => typeof t === "string")
? (params.roles as string[])
? params.roles
: undefined;
const scopes =
Array.isArray(params.scopes) && params.scopes.every((t) => typeof t === "string")
? (params.scopes as string[])
? params.scopes
: undefined;
const tags =
Array.isArray(params.tags) && params.tags.every((t) => typeof t === "string")
? (params.tags as string[])
? params.tags
: undefined;
const presenceUpdate = updateSystemPresence({
text,

View File

@@ -33,7 +33,7 @@ export const wizardHandlers: GatewayRequestHandlers = {
}
const sessionId = randomUUID();
const opts = {
mode: params.mode as "local" | "remote" | undefined,
mode: params.mode,
workspace: typeof params.workspace === "string" ? params.workspace : undefined,
};
const session = new WizardSession((prompter) =>
@@ -58,7 +58,7 @@ export const wizardHandlers: GatewayRequestHandlers = {
);
return;
}
const sessionId = params.sessionId as string;
const sessionId = params.sessionId;
const session = context.wizardSessions.get(sessionId);
if (!session) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "wizard not found"));
@@ -95,7 +95,7 @@ export const wizardHandlers: GatewayRequestHandlers = {
);
return;
}
const sessionId = params.sessionId as string;
const sessionId = params.sessionId;
const session = context.wizardSessions.get(sessionId);
if (!session) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "wizard not found"));
@@ -121,7 +121,7 @@ export const wizardHandlers: GatewayRequestHandlers = {
);
return;
}
const sessionId = params.sessionId as string;
const sessionId = params.sessionId;
const session = context.wizardSessions.get(sessionId);
if (!session) {
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "wizard not found"));

View File

@@ -17,7 +17,7 @@ describe("node subscription manager", () => {
manager.sendToSession("main", "chat", { ok: true }, sendEvent);
expect(sent).toHaveLength(2);
expect(sent.map((s) => s.nodeId).sort()).toEqual(["node-a", "node-b"]);
expect(sent.map((s) => s.nodeId).toSorted()).toEqual(["node-a", "node-b"]);
expect(sent[0].event).toBe("chat");
});

View File

@@ -111,9 +111,10 @@ describe("gateway server chat", () => {
idempotencyKey: "idem-route",
});
expect(routeRes.ok).toBe(true);
const stored = JSON.parse(
await fs.readFile(testState.sessionStorePath as string, "utf-8"),
) as Record<string, { lastChannel?: string; lastTo?: string } | undefined>;
const stored = JSON.parse(await fs.readFile(testState.sessionStorePath, "utf-8")) as Record<
string,
{ lastChannel?: string; lastTo?: string } | undefined
>;
expect(stored["agent:main:main"]?.lastChannel).toBe("whatsapp");
expect(stored["agent:main:main"]?.lastTo).toBe("+1555");

View File

@@ -332,7 +332,7 @@ describe("gateway server sessions", () => {
agentId: "home",
});
expect(homeSessions.ok).toBe(true);
expect(homeSessions.payload?.sessions.map((s) => s.key).sort()).toEqual([
expect(homeSessions.payload?.sessions.map((s) => s.key).toSorted()).toEqual([
"agent:home:discord:group:dev",
"agent:home:main",
]);

View File

@@ -115,8 +115,8 @@ const hoisted = vi.hoisted(() => {
const startGatewayConfigReloader = vi.fn(
(opts: { onHotReload: typeof onHotReload; onRestart: typeof onRestart }) => {
onHotReload = opts.onHotReload as typeof onHotReload;
onRestart = opts.onRestart as typeof onRestart;
onHotReload = opts.onHotReload;
onRestart = opts.onRestart;
return { stop: reloaderStop };
},
);

View File

@@ -263,11 +263,11 @@ export function attachGatewayWsMessageHandler(params: {
const isRequestFrame = validateRequestFrame(parsed);
if (
!isRequestFrame ||
(parsed as RequestFrame).method !== "connect" ||
!validateConnectParams((parsed as RequestFrame).params)
parsed.method !== "connect" ||
!validateConnectParams(parsed.params)
) {
const handshakeError = isRequestFrame
? (parsed as RequestFrame).method === "connect"
? parsed.method === "connect"
? `invalid connect params: ${formatValidationErrors(validateConnectParams.errors)}`
: "invalid handshake: first request must be connect"
: "invalid request frame";
@@ -279,7 +279,7 @@ export function attachGatewayWsMessageHandler(params: {
handshakeError,
});
if (isRequestFrame) {
const req = parsed as RequestFrame;
const req = parsed;
send({
type: "res",
id: req.id,
@@ -300,7 +300,7 @@ export function attachGatewayWsMessageHandler(params: {
return;
}
const frame = parsed as RequestFrame;
const frame = parsed;
const connectParams = frame.params as ConnectParams;
const clientLabel = connectParams.client.displayName ?? connectParams.client.id;
@@ -897,7 +897,7 @@ export function attachGatewayWsMessageHandler(params: {
});
return;
}
const req = parsed as RequestFrame;
const req = parsed;
logWs("in", "req", { connId, id: req.id, method: req.method });
const respond = (
ok: boolean,

View File

@@ -359,7 +359,7 @@ function readRecentMessagesFromTranscript(
// skip malformed lines
}
}
return collected.reverse();
return collected.toReversed();
} catch {
return [];
} finally {

View File

@@ -590,7 +590,7 @@ export function listSessionsFromStore(params: {
lastAccountId: deliveryFields.lastAccountId ?? entry?.lastAccountId,
};
})
.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
.toSorted((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
if (search) {
sessions = sessions.filter((s) => {

View File

@@ -414,7 +414,7 @@ vi.mock("../config/config.js", async () => {
: {};
const overrideChannels =
testState.channelsConfig && typeof testState.channelsConfig === "object"
? { ...(testState.channelsConfig as Record<string, unknown>) }
? { ...testState.channelsConfig }
: {};
const mergedChannels = { ...fileChannels, ...overrideChannels };
if (testState.allowFrom !== undefined) {

View File

@@ -145,11 +145,10 @@ export async function handleToolsInvokeHttpRequest(
const action = typeof body.action === "string" ? body.action.trim() : undefined;
const argsRaw = body.args;
const args = (
const args =
argsRaw && typeof argsRaw === "object" && !Array.isArray(argsRaw)
? (argsRaw as Record<string, unknown>)
: {}
) as Record<string, unknown>;
: {};
const rawSessionKey = resolveSessionKeyFromBody(body);
const sessionKey =