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

@@ -55,7 +55,7 @@ export function resolveOptionFromCommand<T>(
): T | undefined {
let current: Command | null | undefined = command;
while (current) {
const opts = (current.opts?.() ?? {}) as Record<string, T | undefined>;
const opts = current.opts?.() ?? {};
if (opts[key] !== undefined) return opts[key];
current = current.parent ?? undefined;
}

View File

@@ -59,7 +59,7 @@ function parseValue(raw: string, opts: { json?: boolean }): unknown {
try {
return JSON5.parse(trimmed);
} catch (err) {
throw new Error(`Failed to parse JSON5 value: ${String(err)}`);
throw new Error(`Failed to parse JSON5 value: ${String(err)}`, { cause: err });
}
}

View File

@@ -101,7 +101,7 @@ function formatTokenSummary(tokens: DeviceTokenSummary[] | undefined) {
if (!tokens || tokens.length === 0) return "none";
const parts = tokens
.map((t) => `${t.role}${t.revokedAtMs ? " (revoked)" : ""}`)
.sort((a, b) => a.localeCompare(b));
.toSorted((a, b) => a.localeCompare(b));
return parts.join(", ");
}

View File

@@ -295,7 +295,7 @@ export function registerExecApprovalsCli(program: Command) {
const raw = opts.stdin ? await readStdin() : await fs.readFile(String(opts.file), "utf8");
let file: ExecApprovalsFile;
try {
file = JSON5.parse(raw) as ExecApprovalsFile;
file = JSON5.parse(raw);
} catch (err) {
defaultRuntime.error(`Failed to parse approvals JSON: ${String(err)}`);
defaultRuntime.exit(1);

View File

@@ -288,7 +288,7 @@ export function registerGatewayCli(program: Command) {
async () => await discoverGatewayBeacons({ timeoutMs, wideAreaDomain }),
);
const deduped = dedupeBeacons(beacons).sort((a, b) =>
const deduped = dedupeBeacons(beacons).toSorted((a, b) =>
String(a.displayName || a.instanceName).localeCompare(
String(b.displayName || b.instanceName),
),

View File

@@ -4,9 +4,8 @@ const githubCopilotLoginCommand = vi.fn();
const modelsStatusCommand = vi.fn().mockResolvedValue(undefined);
vi.mock("../commands/models.js", async () => {
const actual = (await vi.importActual<typeof import("../commands/models.js")>(
"../commands/models.js",
)) as typeof import("../commands/models.js");
const actual =
await vi.importActual<typeof import("../commands/models.js")>("../commands/models.js");
return {
...actual,

View File

@@ -28,7 +28,7 @@ export function formatPermissions(raw: unknown) {
const entries = Object.entries(raw as Record<string, unknown>)
.map(([key, value]) => [String(key).trim(), value === true] as const)
.filter(([key]) => key.length > 0)
.sort((a, b) => a[0].localeCompare(b[0]));
.toSorted((a, b) => a[0].localeCompare(b[0]));
if (entries.length === 0) return null;
const parts = entries.map(([key, granted]) => `${key}=${granted ? "yes" : "no"}`);
return `[${parts.join(", ")}]`;

View File

@@ -34,12 +34,12 @@ export function registerNodesCameraCommands(nodes: Command) {
.action(async (opts: NodesRpcOpts) => {
await runNodesCommand("camera list", async () => {
const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
const raw = (await callGatewayCli("node.invoke", opts, {
const raw = await callGatewayCli("node.invoke", opts, {
nodeId,
command: "camera.list",
params: {},
idempotencyKey: randomIdempotencyKey(),
})) as unknown;
});
const res = typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {};
const payload =
@@ -144,7 +144,7 @@ export function registerNodesCameraCommands(nodes: Command) {
invokeParams.timeoutMs = timeoutMs;
}
const raw = (await callGatewayCli("node.invoke", opts, invokeParams)) as unknown;
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
const res =
typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {};
const payload = parseCameraSnapPayload(res.payload);
@@ -213,7 +213,7 @@ export function registerNodesCameraCommands(nodes: Command) {
invokeParams.timeoutMs = timeoutMs;
}
const raw = (await callGatewayCli("node.invoke", opts, invokeParams)) as unknown;
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
const res = typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {};
const payload = parseCameraClipPayload(res.payload);
const filePath = cameraTempPath({

View File

@@ -72,7 +72,7 @@ export function registerNodesCanvasCommands(nodes: Command) {
invokeParams.timeoutMs = timeoutMs;
}
const raw = (await callGatewayCli("node.invoke", opts, invokeParams)) as unknown;
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
const res = typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {};
const payload = parseCanvasSnapshotPayload(res.payload);
const filePath = canvasSnapshotTempPath({

View File

@@ -112,7 +112,7 @@ function resolveExecDefaults(
async function resolveNodePlatform(opts: NodesRpcOpts, nodeId: string): Promise<string | null> {
try {
const res = (await callGatewayCli("node.list", opts, {})) as unknown;
const res = await callGatewayCli("node.list", opts, {});
const nodes = parseNodeList(res);
const match = nodes.find((node) => node.nodeId === nodeId);
return typeof match?.platform === "string" ? match.platform : null;
@@ -324,7 +324,7 @@ export function registerNodesInvokeCommands(nodes: Command) {
invokeParams.timeoutMs = invokeTimeout;
}
const result = (await callGatewayCli("node.invoke", opts, invokeParams)) as unknown;
const result = await callGatewayCli("node.invoke", opts, invokeParams);
if (opts.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;

View File

@@ -53,7 +53,7 @@ export function registerNodesLocationCommands(nodes: Command) {
invokeParams.timeoutMs = invokeTimeoutMs;
}
const raw = (await callGatewayCli("node.invoke", opts, invokeParams)) as unknown;
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
const res = typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {};
const payload =
res.payload && typeof res.payload === "object"

View File

@@ -13,7 +13,7 @@ export function registerNodesPairingCommands(nodes: Command) {
.description("List pending pairing requests")
.action(async (opts: NodesRpcOpts) => {
await runNodesCommand("pending", async () => {
const result = (await callGatewayCli("node.pair.list", opts, {})) as unknown;
const result = await callGatewayCli("node.pair.list", opts, {});
const { pending } = parsePairingList(result);
if (opts.json) {
defaultRuntime.log(JSON.stringify(pending, null, 2));

View File

@@ -54,7 +54,7 @@ export function registerNodesScreenCommands(nodes: Command) {
invokeParams.timeoutMs = timeoutMs;
}
const raw = (await callGatewayCli("node.invoke", opts, invokeParams)) as unknown;
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
const res = typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {};
const parsed = parseScreenRecordPayload(res.payload);
const filePath = opts.out ?? screenRecordTempPath({ ext: parsed.format || "mp4" });

View File

@@ -86,7 +86,7 @@ export function registerNodesStatusCommands(nodes: Command) {
await runNodesCommand("status", async () => {
const connectedOnly = Boolean(opts.connected);
const sinceMs = parseSinceMs(opts.lastConnected, "Invalid --last-connected");
const result = (await callGatewayCli("node.list", opts, {})) as unknown;
const result = await callGatewayCli("node.list", opts, {});
const obj =
typeof result === "object" && result !== null
? (result as Record<string, unknown>)
@@ -146,7 +146,7 @@ export function registerNodesStatusCommands(nodes: Command) {
pathEnv ? `path: ${pathEnv}` : null,
].filter(Boolean) as string[];
const caps = Array.isArray(n.caps)
? n.caps.map(String).filter(Boolean).sort().join(", ")
? n.caps.map(String).filter(Boolean).toSorted().join(", ")
: "?";
const paired = n.paired ? ok("paired") : warn("unpaired");
const connected = n.connected ? ok("connected") : muted("disconnected");
@@ -191,9 +191,9 @@ export function registerNodesStatusCommands(nodes: Command) {
.action(async (opts: NodesRpcOpts) => {
await runNodesCommand("describe", async () => {
const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
const result = (await callGatewayCli("node.describe", opts, {
const result = await callGatewayCli("node.describe", opts, {
nodeId,
})) as unknown;
});
if (opts.json) {
defaultRuntime.log(JSON.stringify(result, null, 2));
return;
@@ -206,9 +206,11 @@ export function registerNodesStatusCommands(nodes: Command) {
const displayName = typeof obj.displayName === "string" ? obj.displayName : nodeId;
const connected = Boolean(obj.connected);
const paired = Boolean(obj.paired);
const caps = Array.isArray(obj.caps) ? obj.caps.map(String).filter(Boolean).sort() : null;
const caps = Array.isArray(obj.caps)
? obj.caps.map(String).filter(Boolean).toSorted()
: null;
const commands = Array.isArray(obj.commands)
? obj.commands.map(String).filter(Boolean).sort()
? obj.commands.map(String).filter(Boolean).toSorted()
: [];
const perms = formatPermissions(obj.permissions);
const family = typeof obj.deviceFamily === "string" ? obj.deviceFamily : null;
@@ -274,7 +276,7 @@ export function registerNodesStatusCommands(nodes: Command) {
await runNodesCommand("list", async () => {
const connectedOnly = Boolean(opts.connected);
const sinceMs = parseSinceMs(opts.lastConnected, "Invalid --last-connected");
const result = (await callGatewayCli("node.pair.list", opts, {})) as unknown;
const result = await callGatewayCli("node.pair.list", opts, {});
const { pending, paired } = parsePairingList(result);
const { heading, muted, warn } = getNodesTheme();
const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);

View File

@@ -61,10 +61,10 @@ export async function resolveNodeId(opts: NodesRpcOpts, query: string) {
let nodes: NodeListNode[] = [];
try {
const res = (await callGatewayCli("node.list", opts, {})) as unknown;
const res = await callGatewayCli("node.list", opts, {});
nodes = parseNodeList(res);
} catch {
const res = (await callGatewayCli("node.pair.list", opts, {})) as unknown;
const res = await callGatewayCli("node.pair.list", opts, {});
const { paired } = parsePairingList(res);
nodes = paired.map((n) => ({
nodeId: n.nodeId,

View File

@@ -29,10 +29,10 @@ function parseChannel(raw: unknown, channels: PairingChannel[]): PairingChannel
const normalized = normalizeChannelId(value);
if (normalized) {
if (!channels.includes(normalized as PairingChannel)) {
if (!channels.includes(normalized)) {
throw new Error(`Channel ${normalized} does not support pairing`);
}
return normalized as PairingChannel;
return normalized;
}
// Allow extension channels: validate format but don't require registry

View File

@@ -40,7 +40,7 @@ export function listPortListeners(port: number): PortProcess[] {
const status = (err as { status?: number }).status;
const code = (err as { code?: string }).code;
if (code === "ENOENT") {
throw new Error("lsof not found; required for --force");
throw new Error("lsof not found; required for --force", { cause: err });
}
if (status === 1) return []; // no listeners
throw err instanceof Error ? err : new Error(String(err));
@@ -55,6 +55,7 @@ export function forceFreePort(port: number): PortProcess[] {
} catch (err) {
throw new Error(
`failed to kill pid ${proc.pid}${proc.command ? ` (${proc.command})` : ""}: ${String(err)}`,
{ cause: err },
);
}
}
@@ -68,6 +69,7 @@ function killPids(listeners: PortProcess[], signal: NodeJS.Signals) {
} catch (err) {
throw new Error(
`failed to kill pid ${proc.pid}${proc.command ? ` (${proc.command})` : ""}: ${String(err)}`,
{ cause: err },
);
}
}

View File

@@ -96,7 +96,7 @@ describe("cli program (nodes media)", () => {
const facings = invokeCalls
.map((call) => (call.params?.params as { facing?: string } | undefined)?.facing)
.filter(Boolean)
.sort((a, b) => a.localeCompare(b));
.toSorted((a, b) => a.localeCompare(b));
expect(facings).toEqual(["back", "front"]);
const out = String(runtime.log.mock.calls[0]?.[0] ?? "");

View File

@@ -77,7 +77,7 @@ export function registerSecurityCli(program: Command) {
} else if (
fixResult.errors.length === 0 &&
fixResult.changes.length === 0 &&
fixResult.actions.every((a) => a.ok === false)
fixResult.actions.every((a) => !a.ok)
) {
lines.push(muted("Fixes: no changes applied"));
} else {

View File

@@ -657,7 +657,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
message: stylePromptMessage(message),
initialValue: false,
});
if (isCancel(ok) || ok === false) {
if (isCancel(ok) || !ok) {
if (!opts.json) {
defaultRuntime.log(theme.muted("Update cancelled."));
}
@@ -1082,7 +1082,7 @@ export async function updateWizardCommand(opts: UpdateWizardOptions = {}): Promi
),
initialValue: true,
});
if (isCancel(ok) || ok === false) {
if (isCancel(ok) || !ok) {
defaultRuntime.log(theme.muted("Update cancelled."));
defaultRuntime.exit(0);
return;