mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
chore(lint): enable unnecessary type parameter rule
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
"typescript/no-meaningless-void-operator": "error",
|
||||
"typescript/no-unnecessary-type-assertion": "error",
|
||||
"typescript/no-unnecessary-type-conversion": "error",
|
||||
"typescript/no-unnecessary-type-parameters": "off",
|
||||
"typescript/no-unnecessary-type-parameters": "error",
|
||||
"typescript/no-unsafe-type-assertion": "off",
|
||||
"typescript/no-useless-default-assignment": "error",
|
||||
"unicorn/consistent-function-scoping": "off",
|
||||
|
||||
@@ -22,6 +22,7 @@ async function normalizeUploadPaths(paths: string[]): Promise<string[]> {
|
||||
return result.paths;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Browser request result type is shared between request and success formatter.
|
||||
async function runBrowserPostAction<T>(params: {
|
||||
parent: BrowserParentOpts;
|
||||
profile: string | undefined;
|
||||
|
||||
@@ -126,6 +126,7 @@ async function readBrowserProxyFile(filePath: string): Promise<BrowserProxyFile
|
||||
return { path: filePath, base64: buffer.toString("base64"), mimeType };
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- CLI JSON params are typed by the invoked method.
|
||||
function decodeParams<T>(raw?: string | null): T {
|
||||
if (!raw) {
|
||||
throw new Error("INVALID_REQUEST: paramsJSON required");
|
||||
|
||||
@@ -118,7 +118,7 @@ export async function handleDiscordMessagingAction(
|
||||
: accountId
|
||||
? { accountId }
|
||||
: undefined;
|
||||
const withReactionRuntimeOptions = <T extends Record<string, unknown>>(extra?: T) => ({
|
||||
const withReactionRuntimeOptions = (extra?: Record<string, unknown>) => ({
|
||||
...(reactionRuntimeOptions ?? cfgOptions),
|
||||
...extra,
|
||||
});
|
||||
|
||||
@@ -311,6 +311,7 @@ export function createMentionRequiredGuildConfig(overrides?: Partial<Config>): C
|
||||
}
|
||||
|
||||
export function captureNextDispatchCtx<
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe captured dispatch context shape.
|
||||
T extends {
|
||||
SessionKey?: string;
|
||||
ParentSessionKey?: string;
|
||||
|
||||
@@ -6,8 +6,8 @@ type ReplyThreadingContext = {
|
||||
ReplyThreading?: ReplyThreadingPolicy;
|
||||
};
|
||||
|
||||
export function applyImplicitReplyBatchGate<T extends object>(
|
||||
ctx: T,
|
||||
export function applyImplicitReplyBatchGate(
|
||||
ctx: object,
|
||||
replyToMode: ReplyToMode,
|
||||
isBatched: boolean,
|
||||
) {
|
||||
@@ -15,5 +15,5 @@ export function applyImplicitReplyBatchGate<T extends object>(
|
||||
if (!replyThreading) {
|
||||
return;
|
||||
}
|
||||
(ctx as T & ReplyThreadingContext).ReplyThreading = replyThreading;
|
||||
(ctx as ReplyThreadingContext).ReplyThreading = replyThreading;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ type DiscordSendModule = typeof import("./send.js");
|
||||
type DiscordSendComponentsModule = typeof import("./send.components.js");
|
||||
type DiscordThreadBindingsModule = typeof import("./monitor/thread-bindings.js");
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves mock call and result types.
|
||||
function invokeMock<TArgs extends unknown[], TResult>(
|
||||
mock: (...args: unknown[]) => unknown,
|
||||
...args: TArgs
|
||||
|
||||
@@ -205,6 +205,7 @@ export function mockResolvedDiscordAccountConfig(overrides: Record<string, unkno
|
||||
}));
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe handler params shape.
|
||||
export function getFirstDiscordMessageHandlerParams<T extends object>() {
|
||||
expect(createDiscordMessageHandlerMock).toHaveBeenCalledTimes(1);
|
||||
const firstCall = createDiscordMessageHandlerMock.mock.calls.at(0) as [T] | undefined;
|
||||
|
||||
@@ -558,7 +558,10 @@ export function registerFeishuBitableTools(api: OpenClawPluginApi) {
|
||||
const getClient = (params: AccountAwareParams | undefined, defaultAccountId?: string) =>
|
||||
createFeishuToolClient({ api, executeParams: params, defaultAccountId });
|
||||
|
||||
const registerBitableTool = <TParams extends AccountAwareParams>(params: {
|
||||
const registerBitableTool = <
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Tool params bind each schema-specific executor to its registered tool.
|
||||
TParams extends AccountAwareParams,
|
||||
>(params: {
|
||||
name: string;
|
||||
label: string;
|
||||
description: string;
|
||||
|
||||
@@ -413,13 +413,13 @@ async function loadMonitorSingleAccount() {
|
||||
return module.monitorSingleAccount;
|
||||
}
|
||||
|
||||
export async function setupFeishuLifecycleHandler<T extends RuntimeEnv>(params: {
|
||||
export async function setupFeishuLifecycleHandler(params: {
|
||||
createEventDispatcherMock: {
|
||||
mockReturnValue: (value: unknown) => unknown;
|
||||
mockReturnValueOnce: (value: unknown) => unknown;
|
||||
};
|
||||
onRegister: (registered: Record<string, (data: unknown) => Promise<void>>) => void;
|
||||
runtime: T;
|
||||
runtime: RuntimeEnv;
|
||||
cfg: ClawdbotConfig;
|
||||
account: ResolvedFeishuAccount;
|
||||
handlerKey: string;
|
||||
|
||||
@@ -32,11 +32,11 @@ type DeviceTokenResponse =
|
||||
error_uri?: string;
|
||||
};
|
||||
|
||||
function parseJsonResponse<T>(value: unknown): T {
|
||||
function parseJsonResponse(value: unknown): Record<string, unknown> {
|
||||
if (!value || typeof value !== "object") {
|
||||
throw new Error("Unexpected response from GitHub");
|
||||
}
|
||||
return value as T;
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
async function requestDeviceCode(params: { scope: string }): Promise<DeviceCodeResponse> {
|
||||
@@ -58,7 +58,7 @@ async function requestDeviceCode(params: { scope: string }): Promise<DeviceCodeR
|
||||
throw new Error(`GitHub device code failed: HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
const json = parseJsonResponse<DeviceCodeResponse>(await res.json());
|
||||
const json = parseJsonResponse(await res.json()) as DeviceCodeResponse;
|
||||
if (!json.device_code || !json.user_code || !json.verification_uri) {
|
||||
throw new Error("GitHub device code response missing fields");
|
||||
}
|
||||
@@ -90,7 +90,7 @@ async function pollForAccessToken(params: {
|
||||
throw new Error(`GitHub device token failed: HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
const json = parseJsonResponse<DeviceTokenResponse>(await res.json());
|
||||
const json = parseJsonResponse(await res.json()) as DeviceTokenResponse;
|
||||
if ("access_token" in json && typeof json.access_token === "string") {
|
||||
return json.access_token;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ vi.mock("openclaw/plugin-sdk/ssrf-runtime", async (importOriginal) => {
|
||||
});
|
||||
|
||||
describe("lmstudio-models", () => {
|
||||
const asFetch = <T>(mock: T) => mock as unknown as typeof fetch;
|
||||
const parseJsonRequestBody = <T>(init: RequestInit | undefined): T => {
|
||||
const asFetch = (mock: unknown) => mock as typeof fetch;
|
||||
const parseJsonRequestBody = (init: RequestInit | undefined): unknown => {
|
||||
if (typeof init?.body !== "string") {
|
||||
throw new Error("Expected request body to be a JSON string");
|
||||
}
|
||||
return JSON.parse(init.body) as T;
|
||||
return JSON.parse(init.body) as unknown;
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
@@ -215,7 +215,7 @@ describe("lmstudio-models", () => {
|
||||
const loadCall = fetchMock.mock.calls.find((call) => String(call[0]).endsWith("/models/load"));
|
||||
expect(loadCall).toBeDefined();
|
||||
const loadInit = loadCall?.[1] as RequestInit;
|
||||
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
|
||||
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
|
||||
expect(loadBody.context_length).toBe(8192);
|
||||
});
|
||||
|
||||
@@ -258,7 +258,7 @@ describe("lmstudio-models", () => {
|
||||
const loadCall = fetchMock.mock.calls.find((call) => String(call[0]).endsWith("/models/load"));
|
||||
expect(loadCall).toBeDefined();
|
||||
const loadInit = loadCall?.[1] as RequestInit;
|
||||
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
|
||||
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
|
||||
expect(loadBody.context_length).toBe(32768);
|
||||
});
|
||||
|
||||
@@ -318,7 +318,7 @@ describe("lmstudio-models", () => {
|
||||
}),
|
||||
});
|
||||
const loadInit = loadCall![1] as RequestInit;
|
||||
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
|
||||
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
|
||||
expect(loadBody.context_length).not.toBe(LMSTUDIO_DEFAULT_LOAD_CONTEXT_LENGTH);
|
||||
});
|
||||
|
||||
@@ -360,7 +360,7 @@ describe("lmstudio-models", () => {
|
||||
const loadCall = fetchMock.mock.calls.find((call) => String(call[0]).endsWith("/models/load"));
|
||||
expect(loadCall).toBeDefined();
|
||||
const loadInit = loadCall?.[1] as unknown as RequestInit;
|
||||
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
|
||||
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
|
||||
expect(loadBody.context_length).toBe(8192);
|
||||
});
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ const previousMatrixEnv = Object.fromEntries(
|
||||
MATRIX_ENV_KEYS.map((key) => [key, process.env[key]]),
|
||||
) as Record<(typeof MATRIX_ENV_KEYS)[number], string | undefined>;
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets callers ascribe plugin runtime shape.
|
||||
function createNonExitingTypedRuntimeEnv<TRuntime>(): TRuntime {
|
||||
return {
|
||||
log: vi.fn(),
|
||||
|
||||
@@ -124,8 +124,6 @@ export async function runMemoryEmbeddingRetryLoop<T>(params: {
|
||||
}
|
||||
}
|
||||
|
||||
export function buildTextEmbeddingInputs<T extends MemoryEmbeddingChunk>(
|
||||
chunks: T[],
|
||||
): MemoryEmbeddingInput[] {
|
||||
export function buildTextEmbeddingInputs(chunks: MemoryEmbeddingChunk[]): MemoryEmbeddingInput[] {
|
||||
return chunks.map((chunk) => chunk.embeddingInput ?? { text: chunk.text });
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from "./api.js";
|
||||
import { contributeMistralResolvedModelCompat } from "./provider-compat.js";
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe provider compat shape.
|
||||
function readCompat<T>(model: unknown): T | undefined {
|
||||
return (model as { compat?: T }).compat;
|
||||
}
|
||||
|
||||
@@ -119,12 +119,12 @@ export function buildMSTeamsGraphMessageUrls(params: {
|
||||
return Array.from(new Set(urls));
|
||||
}
|
||||
|
||||
async function fetchGraphCollection<T>(params: {
|
||||
async function fetchGraphCollection(params: {
|
||||
url: string;
|
||||
accessToken: string;
|
||||
fetchFn?: typeof fetch;
|
||||
ssrfPolicy?: SsrFPolicy;
|
||||
}): Promise<{ status: number; items: T[] }> {
|
||||
}): Promise<{ status: number; items: unknown[] }> {
|
||||
const fetchFn = params.fetchFn ?? fetch;
|
||||
const { response, release } = await fetchWithSsrFGuard({
|
||||
url: params.url,
|
||||
@@ -141,7 +141,7 @@ async function fetchGraphCollection<T>(params: {
|
||||
return { status, items: [] };
|
||||
}
|
||||
try {
|
||||
const data = (await response.json()) as { value?: T[] };
|
||||
const data = (await response.json()) as { value?: unknown[] };
|
||||
return { status, items: Array.isArray(data.value) ? data.value : [] };
|
||||
} catch {
|
||||
return { status, items: [] };
|
||||
@@ -182,12 +182,12 @@ async function downloadGraphHostedContent(params: {
|
||||
ssrfPolicy?: SsrFPolicy;
|
||||
logger?: MSTeamsAttachmentDownloadLogger;
|
||||
}): Promise<{ media: MSTeamsInboundMedia[]; status: number; count: number }> {
|
||||
const hosted = await fetchGraphCollection<GraphHostedContent>({
|
||||
const hosted = (await fetchGraphCollection({
|
||||
url: `${params.messageUrl}/hostedContents`,
|
||||
accessToken: params.accessToken,
|
||||
fetchFn: params.fetchFn,
|
||||
ssrfPolicy: params.ssrfPolicy,
|
||||
});
|
||||
})) as { status: number; items: GraphHostedContent[] };
|
||||
if (hosted.items.length === 0) {
|
||||
return { media: [], status: hosted.status, count: 0 };
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ function graphCollection<T>(...items: T[]) {
|
||||
return { value: items };
|
||||
}
|
||||
|
||||
function mockGraphCollection<T>(...items: T[]) {
|
||||
function mockGraphCollection(...items: unknown[]) {
|
||||
mockJsonFetchResponse(graphCollection(...items));
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ function writeMatrixQaProgress(message: string) {
|
||||
process.stderr.write(`[matrix-qa] ${message}\n`);
|
||||
}
|
||||
|
||||
function countMatrixQaStatuses<T extends { status: "fail" | "pass" | "skip" }>(entries: T[]) {
|
||||
function countMatrixQaStatuses(entries: Array<{ status: "fail" | "pass" | "skip" }>) {
|
||||
return {
|
||||
failed: entries.filter((entry) => entry.status === "fail").length,
|
||||
passed: entries.filter((entry) => entry.status === "pass").length,
|
||||
|
||||
@@ -379,10 +379,8 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = crea
|
||||
}),
|
||||
resolver: {
|
||||
resolveTargets: async ({ cfg, accountId, inputs, kind }) => {
|
||||
const toResolvedTarget = <
|
||||
T extends { input: string; resolved: boolean; id?: string; name?: string },
|
||||
>(
|
||||
entry: T,
|
||||
const toResolvedTarget = (
|
||||
entry: { input: string; resolved: boolean; id?: string; name?: string },
|
||||
note?: string,
|
||||
) => ({
|
||||
input: entry.input,
|
||||
|
||||
@@ -65,7 +65,10 @@ type SlackSocketShutdownClient = {
|
||||
};
|
||||
type Constructor = abstract new (...args: never[]) => unknown;
|
||||
|
||||
function isConstructorFunction<T extends Constructor>(value: unknown): value is T {
|
||||
function isConstructorFunction<
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Constructor guard preserves the requested concrete Slack constructor type.
|
||||
T extends Constructor,
|
||||
>(value: unknown): value is T {
|
||||
return typeof value === "function";
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export type XaiCodeExecutionResult = {
|
||||
export function resolveXaiCodeExecutionConfig(
|
||||
config?: Record<string, unknown>,
|
||||
): XaiCodeExecutionConfig {
|
||||
return coerceXaiToolConfig<XaiCodeExecutionConfig>(config);
|
||||
return coerceXaiToolConfig(config) as XaiCodeExecutionConfig;
|
||||
}
|
||||
|
||||
export function resolveXaiCodeExecutionModel(config?: Record<string, unknown>): string {
|
||||
|
||||
@@ -3,17 +3,17 @@ import { normalizeXaiModelId } from "../model-id.js";
|
||||
|
||||
export { isRecord };
|
||||
|
||||
export function coerceXaiToolConfig<TConfig extends Record<string, unknown>>(
|
||||
export function coerceXaiToolConfig(
|
||||
config: Record<string, unknown> | undefined,
|
||||
): TConfig {
|
||||
return isRecord(config) ? (config as TConfig) : ({} as TConfig);
|
||||
): Record<string, unknown> {
|
||||
return isRecord(config) ? config : {};
|
||||
}
|
||||
|
||||
export function resolveNormalizedXaiToolModel(params: {
|
||||
config?: Record<string, unknown>;
|
||||
defaultModel: string;
|
||||
}): string {
|
||||
const value = coerceXaiToolConfig<{ model?: unknown }>(params.config).model;
|
||||
const value = coerceXaiToolConfig(params.config).model;
|
||||
return typeof value === "string" && value.trim()
|
||||
? normalizeXaiModelId(value.trim())
|
||||
: params.defaultModel;
|
||||
@@ -23,7 +23,7 @@ export function resolvePositiveIntegerToolConfig(
|
||||
config: Record<string, unknown> | undefined,
|
||||
key: string,
|
||||
): number | undefined {
|
||||
const raw = coerceXaiToolConfig<Record<string, unknown>>(config)[key];
|
||||
const raw = coerceXaiToolConfig(config)[key];
|
||||
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export type XaiXSearchResult = {
|
||||
};
|
||||
|
||||
export function resolveXaiXSearchConfig(config?: Record<string, unknown>): XaiXSearchConfig {
|
||||
return coerceXaiToolConfig<XaiXSearchConfig>(config);
|
||||
return coerceXaiToolConfig(config) as XaiXSearchConfig;
|
||||
}
|
||||
|
||||
export function resolveXaiXSearchModel(config?: Record<string, unknown>): string {
|
||||
|
||||
@@ -130,6 +130,7 @@ export type ExtensionPackageBoundaryPackageJson = {
|
||||
devDependencies?: Record<string, string>;
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Boundary helper lets callers ascribe JSON file shape.
|
||||
function readJsonFile<T>(filePath: string): T {
|
||||
return JSON.parse(readFileSync(filePath, "utf8")) as T;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ export type PublishablePluginPackageCandidate<
|
||||
packageJson: TPackageJson;
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Release helper preserves caller-specific package.json shape.
|
||||
function readPluginPackageJson<TPackageJson extends PluginPackageJson = PluginPackageJson>(
|
||||
path: string,
|
||||
): TPackageJson {
|
||||
|
||||
@@ -119,8 +119,8 @@ function isStrictOpenAIJsonSchemaCompatibleRecursive(schema: unknown): boolean {
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveOpenAIStrictToolFlagForInventory<T extends ToolWithParameters>(
|
||||
tools: readonly T[],
|
||||
export function resolveOpenAIStrictToolFlagForInventory(
|
||||
tools: readonly ToolWithParameters[],
|
||||
strict: boolean | null | undefined,
|
||||
): boolean | undefined {
|
||||
if (strict !== true) {
|
||||
|
||||
@@ -555,7 +555,7 @@ vi.mock("../compaction-safety-timeout.js", () => ({
|
||||
vi.mock("../history.js", () => ({
|
||||
getDmHistoryLimitFromSessionKey: (sessionKey: string | undefined, config: unknown) =>
|
||||
hoisted.getDmHistoryLimitFromSessionKeyMock(sessionKey, config),
|
||||
limitHistoryTurns: <T>(messages: T, limit: number | undefined) =>
|
||||
limitHistoryTurns: (messages: unknown, limit: number | undefined) =>
|
||||
hoisted.limitHistoryTurnsMock(messages, limit),
|
||||
}));
|
||||
|
||||
|
||||
@@ -161,10 +161,10 @@ export function resolveSelectedCapabilityProvider<T extends CapabilityProvider>(
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveCapabilityModelCandidatesForTool<T extends CapabilityProvider>(params: {
|
||||
export function resolveCapabilityModelCandidatesForTool(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
agentDir?: string;
|
||||
providers: T[];
|
||||
providers: CapabilityProvider[];
|
||||
}): string[] {
|
||||
const providerDefaults = new Map<string, string>();
|
||||
for (const provider of params.providers) {
|
||||
@@ -206,11 +206,11 @@ export function resolveCapabilityModelCandidatesForTool<T extends CapabilityProv
|
||||
return orderedRefs;
|
||||
}
|
||||
|
||||
export function resolveCapabilityModelConfigForTool<T extends CapabilityProvider>(params: {
|
||||
export function resolveCapabilityModelConfigForTool(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
agentDir?: string;
|
||||
modelConfig?: AgentModelConfig;
|
||||
providers: T[];
|
||||
providers: CapabilityProvider[];
|
||||
}): ToolModelConfig | null {
|
||||
const explicit = coerceToolModelConfig(params.modelConfig);
|
||||
if (hasToolModelConfig(explicit)) {
|
||||
|
||||
@@ -75,7 +75,7 @@ type WebFetchConfig = NonNullable<OpenClawConfig["tools"]>["web"] extends infer
|
||||
: undefined;
|
||||
|
||||
function resolveFetchConfig(cfg?: OpenClawConfig): WebFetchConfig {
|
||||
return resolveWebProviderConfig<"fetch", NonNullable<WebFetchConfig>>(cfg, "fetch");
|
||||
return resolveWebProviderConfig(cfg, "fetch") as NonNullable<WebFetchConfig> | undefined;
|
||||
}
|
||||
|
||||
function resolveFetchEnabled(params: { fetch?: WebFetchConfig; sandboxed?: boolean }): boolean {
|
||||
|
||||
@@ -18,8 +18,8 @@ async function readSessionStore(storePath: string): Promise<Record<string, unkno
|
||||
return JSON.parse(raw) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function pickFirstStoreEntry<T>(store: Record<string, unknown>): T | undefined {
|
||||
const entries = Object.values(store) as T[];
|
||||
function pickFirstStoreEntry(store: Record<string, unknown>): unknown {
|
||||
const entries = Object.values(store);
|
||||
return entries[0];
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ export function registerTriggerHandlingUsageSummaryCases(params: {
|
||||
expect(replyText(r3)).toContain("Usage footer: tokens");
|
||||
|
||||
const finalStore = await readSessionStore(usageStorePath);
|
||||
expect(pickFirstStoreEntry<{ responseUsage?: string }>(finalStore)?.responseUsage).toBe(
|
||||
expect((pickFirstStoreEntry(finalStore) as { responseUsage?: string })?.responseUsage).toBe(
|
||||
"tokens",
|
||||
);
|
||||
expect(runEmbeddedPiAgentMock).not.toHaveBeenCalled();
|
||||
|
||||
@@ -467,14 +467,14 @@ describe("parseAudioTag", () => {
|
||||
});
|
||||
|
||||
describe("resolveResponsePrefixTemplate", () => {
|
||||
function expectResolvedTemplateCases<
|
||||
T extends ReadonlyArray<{
|
||||
function expectResolvedTemplateCases(
|
||||
cases: ReadonlyArray<{
|
||||
name: string;
|
||||
template: string | undefined;
|
||||
values: Parameters<typeof resolveResponsePrefixTemplate>[1];
|
||||
expected: string | undefined;
|
||||
}>,
|
||||
>(cases: T) {
|
||||
) {
|
||||
for (const testCase of cases) {
|
||||
expect(resolveResponsePrefixTemplate(testCase.template, testCase.values), testCase.name).toBe(
|
||||
testCase.expected,
|
||||
|
||||
@@ -176,14 +176,14 @@ export function resolveMergedAccountConfig<TConfig extends Record<string, unknow
|
||||
});
|
||||
}
|
||||
|
||||
export function describeAccountSnapshot<
|
||||
TAccount extends {
|
||||
accountId?: string | null;
|
||||
enabled?: boolean | null;
|
||||
name?: string | null | undefined;
|
||||
},
|
||||
>(params: {
|
||||
account: TAccount;
|
||||
type AccountSnapshotInput = {
|
||||
accountId?: string | null;
|
||||
enabled?: boolean | null;
|
||||
name?: string | null | undefined;
|
||||
};
|
||||
|
||||
export function describeAccountSnapshot(params: {
|
||||
account: AccountSnapshotInput;
|
||||
configured?: boolean | undefined;
|
||||
extra?: Record<string, unknown> | undefined;
|
||||
}): ChannelAccountSnapshot {
|
||||
@@ -196,14 +196,8 @@ export function describeAccountSnapshot<
|
||||
};
|
||||
}
|
||||
|
||||
export function describeWebhookAccountSnapshot<
|
||||
TAccount extends {
|
||||
accountId?: string | null;
|
||||
enabled?: boolean | null;
|
||||
name?: string | null | undefined;
|
||||
},
|
||||
>(params: {
|
||||
account: TAccount;
|
||||
export function describeWebhookAccountSnapshot(params: {
|
||||
account: AccountSnapshotInput;
|
||||
configured?: boolean | undefined;
|
||||
mode?: string | undefined;
|
||||
extra?: Record<string, unknown> | undefined;
|
||||
|
||||
@@ -21,6 +21,7 @@ export type ChannelRuntimeContextRegistry = {
|
||||
abortSignal?: AbortSignal;
|
||||
},
|
||||
) => { dispose: () => void };
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
|
||||
get: <T = unknown>(params: ChannelRuntimeContextKey) => T | undefined;
|
||||
watch: (params: {
|
||||
channelId?: string;
|
||||
|
||||
@@ -29,14 +29,14 @@ export type ChatSenderAllowParams = {
|
||||
chatIdentifier?: string | null;
|
||||
};
|
||||
|
||||
function isAllowedParsedChatSender<TParsed extends ParsedChatAllowTarget>(params: {
|
||||
function isAllowedParsedChatSender(params: {
|
||||
allowFrom: Array<string | number>;
|
||||
sender: string;
|
||||
chatId?: number | null;
|
||||
chatGuid?: string | null;
|
||||
chatIdentifier?: string | null;
|
||||
normalizeSender: (sender: string) => string;
|
||||
parseAllowTarget: (entry: string) => TParsed;
|
||||
parseAllowTarget: (entry: string) => ParsedChatAllowTarget;
|
||||
}): boolean {
|
||||
const allowFrom = normalizeStringEntries(params.allowFrom);
|
||||
if (allowFrom.length === 0) {
|
||||
@@ -224,9 +224,9 @@ export function resolveServicePrefixedOrChatAllowTarget<
|
||||
return null;
|
||||
}
|
||||
|
||||
export function createAllowedChatSenderMatcher<TParsed extends ParsedChatAllowTarget>(params: {
|
||||
export function createAllowedChatSenderMatcher(params: {
|
||||
normalizeSender: (sender: string) => string;
|
||||
parseAllowTarget: (entry: string) => TParsed;
|
||||
parseAllowTarget: (entry: string) => ParsedChatAllowTarget;
|
||||
}): (input: ChatSenderAllowParams) => boolean {
|
||||
return (input) =>
|
||||
isAllowedParsedChatSender({
|
||||
|
||||
@@ -18,9 +18,7 @@ type ExtendableZodObject = ZodTypeAny & {
|
||||
export const AllowFromEntrySchema = z.union([z.string(), z.number()]);
|
||||
export const AllowFromListSchema = z.array(AllowFromEntrySchema).optional();
|
||||
|
||||
export function buildNestedDmConfigSchema<TExtraShape extends ZodRawShape = {}>(
|
||||
extraShape?: TExtraShape,
|
||||
) {
|
||||
export function buildNestedDmConfigSchema(extraShape?: ZodRawShape) {
|
||||
const baseShape = {
|
||||
enabled: z.boolean().optional(),
|
||||
policy: DmPolicySchema.optional(),
|
||||
|
||||
@@ -48,9 +48,9 @@ function listConfigWriteTargetScopes<TChannelId extends string>(
|
||||
return [target.scope];
|
||||
}
|
||||
|
||||
function resolveChannelConfig<TChannelId extends string>(
|
||||
function resolveChannelConfig(
|
||||
cfg: ConfigWritePolicyConfig,
|
||||
channelId?: TChannelId | null,
|
||||
channelId?: string | null,
|
||||
): ChannelConfigWithAccounts | undefined {
|
||||
if (!channelId) {
|
||||
return undefined;
|
||||
@@ -65,9 +65,9 @@ function resolveChannelAccountConfig(
|
||||
return resolveAccountEntry(channelConfig.accounts, normalizeAccountId(accountId));
|
||||
}
|
||||
|
||||
export function resolveChannelConfigWritesShared<TChannelId extends string>(params: {
|
||||
export function resolveChannelConfigWritesShared(params: {
|
||||
cfg: ConfigWritePolicyConfig;
|
||||
channelId?: TChannelId | null;
|
||||
channelId?: string | null;
|
||||
accountId?: string | null;
|
||||
}): boolean {
|
||||
const channelConfig = resolveChannelConfig(params.cfg, params.channelId);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { normalizeChatType } from "../../chat-type.js";
|
||||
import { resolveConversationLabel } from "../../conversation-label.js";
|
||||
import { validateSenderIdentity } from "../../sender-identity.js";
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves channel send mock arg types.
|
||||
export function primeChannelOutboundSendMock<TArgs extends unknown[]>(
|
||||
sendMock: Mock<(...args: TArgs) => Promise<unknown>>,
|
||||
fallbackResult: Record<string, unknown>,
|
||||
|
||||
@@ -48,6 +48,7 @@ export async function runCommandWithRuntime(
|
||||
}
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Commander option values are typed by the caller.
|
||||
export function resolveOptionFromCommand<T>(
|
||||
command: Command | undefined,
|
||||
key: string,
|
||||
|
||||
@@ -17,6 +17,7 @@ function getOptionSource(command: Command, name: string): string | undefined {
|
||||
// Defensive guardrail: allow expected parent/grandparent inheritance without unbounded deep traversal.
|
||||
const MAX_INHERIT_DEPTH = 2;
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Commander option values are typed by the caller.
|
||||
export function inheritOptionFromParent<T = unknown>(
|
||||
command: Command | undefined,
|
||||
name: string,
|
||||
|
||||
@@ -147,6 +147,7 @@ function mockCronEditJobLookup(schedule: unknown): void {
|
||||
);
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets each assertion ascribe expected RPC params.
|
||||
function getGatewayCallParams<T>(method: string): T {
|
||||
const call = callGatewayFromCli.mock.calls.find((entry) => entry[0] === method);
|
||||
return (call?.[2] ?? {}) as T;
|
||||
|
||||
@@ -147,6 +147,7 @@ async function runDaemonCommand(args: string[]) {
|
||||
await daemonProgram.parseAsync(args, { from: "user" });
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe logged JSON shape.
|
||||
function parseFirstJsonRuntimeLine<T>() {
|
||||
const jsonLine = runtimeLogs.find((line) => line.trim().startsWith("{"));
|
||||
return JSON.parse(jsonLine ?? "{}") as T;
|
||||
|
||||
@@ -30,6 +30,7 @@ let runServiceRestart: typeof import("./lifecycle-core.js").runServiceRestart;
|
||||
let runServiceStart: typeof import("./lifecycle-core.js").runServiceStart;
|
||||
let runServiceStop: typeof import("./lifecycle-core.js").runServiceStop;
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe logged JSON shape.
|
||||
function readJsonLog<T extends object>() {
|
||||
const jsonLine = runtimeLogs.find((line) => line.trim().startsWith("{"));
|
||||
return JSON.parse(jsonLine ?? "{}") as T;
|
||||
|
||||
@@ -141,11 +141,11 @@ function parseEnumOption<T extends string>(
|
||||
return (allowed as readonly string[]).includes(raw) ? (raw as T) : null;
|
||||
}
|
||||
|
||||
function formatModeChoices<T extends string>(modes: readonly T[]): string {
|
||||
function formatModeChoices(modes: readonly string[]): string {
|
||||
return modes.map((mode) => `"${mode}"`).join("|");
|
||||
}
|
||||
|
||||
function formatModeErrorList<T extends string>(modes: readonly T[]): string {
|
||||
function formatModeErrorList(modes: readonly string[]): string {
|
||||
const quoted = modes.map((mode) => `"${mode}"`);
|
||||
if (quoted.length === 0) {
|
||||
return "";
|
||||
|
||||
@@ -15,6 +15,7 @@ type ListMarketplacePluginsFn =
|
||||
type ResolveMarketplaceInstallShortcutFn =
|
||||
(typeof import("../plugins/marketplace.js"))["resolveMarketplaceInstallShortcut"];
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves mock call and result types.
|
||||
function invokeMock<TArgs extends unknown[], TResult>(mock: unknown, ...args: TArgs): TResult {
|
||||
return (mock as (...args: TArgs) => TResult)(...args);
|
||||
}
|
||||
|
||||
@@ -10,14 +10,12 @@ export type CommandDescriptorCatalog<TDescriptor extends NamedCommandDescriptor>
|
||||
getCommandsWithSubcommands: () => string[];
|
||||
};
|
||||
|
||||
export function getCommandDescriptorNames<TDescriptor extends CommandDescriptorLike>(
|
||||
descriptors: readonly TDescriptor[],
|
||||
): string[] {
|
||||
export function getCommandDescriptorNames(descriptors: readonly CommandDescriptorLike[]): string[] {
|
||||
return descriptors.map((descriptor) => descriptor.name);
|
||||
}
|
||||
|
||||
export function getCommandsWithSubcommands<TDescriptor extends NamedCommandDescriptor>(
|
||||
descriptors: readonly TDescriptor[],
|
||||
export function getCommandsWithSubcommands(
|
||||
descriptors: readonly NamedCommandDescriptor[],
|
||||
): string[] {
|
||||
return descriptors
|
||||
.filter((descriptor) => descriptor.hasSubcommands)
|
||||
@@ -52,9 +50,9 @@ export function defineCommandDescriptorCatalog<TDescriptor extends NamedCommandD
|
||||
};
|
||||
}
|
||||
|
||||
export function addCommandDescriptorsToProgram<TDescriptor extends CommandDescriptorLike>(
|
||||
export function addCommandDescriptorsToProgram(
|
||||
program: Command,
|
||||
descriptors: readonly TDescriptor[],
|
||||
descriptors: readonly CommandDescriptorLike[],
|
||||
existingCommands: Set<string> = new Set(),
|
||||
): Set<string> {
|
||||
for (const descriptor of descriptors) {
|
||||
|
||||
@@ -46,8 +46,8 @@ export function resolveCommandGroupEntries<TDescriptor extends NamedCommandDescr
|
||||
}));
|
||||
}
|
||||
|
||||
export function buildCommandGroupEntries<TDescriptor extends NamedCommandDescriptor, TRegister>(
|
||||
descriptors: readonly TDescriptor[],
|
||||
export function buildCommandGroupEntries<TRegister>(
|
||||
descriptors: readonly NamedCommandDescriptor[],
|
||||
specs: readonly CommandGroupDescriptorSpec<TRegister>[],
|
||||
mapRegister: (register: TRegister) => CommandGroupEntry["register"],
|
||||
): CommandGroupEntry[] {
|
||||
|
||||
@@ -27,10 +27,10 @@ function getDeclaredCommandJsonMode(command: Command): JsonMode | null {
|
||||
|
||||
function commandSelectedJsonFlag(command: Command, argv: string[]): boolean {
|
||||
const commandWithGlobals = command as Command & {
|
||||
optsWithGlobals?: <T extends Record<string, unknown>>() => T;
|
||||
optsWithGlobals?: () => Record<string, unknown>;
|
||||
};
|
||||
if (typeof commandWithGlobals.optsWithGlobals === "function") {
|
||||
const resolved = commandWithGlobals.optsWithGlobals<Record<string, unknown>>().json;
|
||||
const resolved = commandWithGlobals.optsWithGlobals().json;
|
||||
if (resolved === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ export function spyRuntimeJson(runtime: Pick<OutputRuntimeEnv, "writeJson">) {
|
||||
return vi.spyOn(runtime, "writeJson").mockImplementation(() => {});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets callers ascribe captured JSON shape.
|
||||
export function firstWrittenJsonArg<T>(writeJson: MockCallsWithFirstArg): T | null {
|
||||
return (writeJson.mock.calls[0]?.[0] ?? null) as T | null;
|
||||
}
|
||||
|
||||
@@ -313,6 +313,7 @@ describe("channels command", () => {
|
||||
setMinimalChannelsCommandRegistryForTests();
|
||||
});
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe written config shape.
|
||||
function getWrittenConfig<T>(): T {
|
||||
expect(configMocks.writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
return configMocks.writeConfigFile.mock.calls[0]?.[0] as T;
|
||||
|
||||
@@ -53,6 +53,7 @@ function resolveTestConfigPath() {
|
||||
return path.join(stateDir, "openclaw.json");
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe stored config shape.
|
||||
function readTestConfig<T = OpenClawConfig>(): T {
|
||||
return (testConfigStore.get(resolveTestConfigPath()) ?? {}) as T;
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ export type ContainerItem = {
|
||||
lastUsedAtMs: number;
|
||||
};
|
||||
|
||||
export function countRunning<T extends { running: boolean }>(items: T[]): number {
|
||||
export function countRunning(items: readonly { running: boolean }[]): number {
|
||||
return items.filter((item) => item.running).length;
|
||||
}
|
||||
|
||||
export function countMismatches<T extends { imageMatch: boolean }>(items: T[]): number {
|
||||
export function countMismatches(items: readonly { imageMatch: boolean }[]): number {
|
||||
return items.filter((item) => !item.imageMatch).length;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ type JsonSchemaObject = Record<string, unknown> & {
|
||||
const LEGACY_HIDDEN_PUBLIC_PATHS = ["hooks.internal.handlers"] as const;
|
||||
|
||||
const asJsonSchemaObject = (value: unknown): JsonSchemaObject | null =>
|
||||
asSchemaObject<JsonSchemaObject>(value);
|
||||
asSchemaObject(value) as JsonSchemaObject | null;
|
||||
|
||||
function buildFieldDocumentation(): FieldDocumentation {
|
||||
const titles: Record<string, string> = {};
|
||||
|
||||
@@ -15,11 +15,11 @@ export function cloneSchema<T>(value: T): T {
|
||||
return JSON.parse(JSON.stringify(value)) as T;
|
||||
}
|
||||
|
||||
export function asSchemaObject<T extends object>(value: unknown): T | null {
|
||||
export function asSchemaObject(value: unknown): object | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return null;
|
||||
}
|
||||
return value as T;
|
||||
return value;
|
||||
}
|
||||
|
||||
export function schemaHasChildren(schema: JsonSchemaObject): boolean {
|
||||
|
||||
@@ -28,7 +28,7 @@ type JsonSchemaObject = JsonSchemaNode & {
|
||||
};
|
||||
|
||||
const asJsonSchemaObject = (value: unknown): JsonSchemaObject | null =>
|
||||
asSchemaObject<JsonSchemaObject>(value);
|
||||
asSchemaObject(value) as JsonSchemaObject | null;
|
||||
|
||||
const FORBIDDEN_LOOKUP_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
|
||||
const LOOKUP_SCHEMA_STRING_KEYS = new Set([
|
||||
|
||||
@@ -1033,6 +1033,7 @@ export async function connectWebchatClient(params: {
|
||||
return ws;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Gateway test RPC helper lets callers ascribe response payload shape.
|
||||
export async function rpcReq<T extends Record<string, unknown>>(
|
||||
ws: WebSocket,
|
||||
method: string,
|
||||
|
||||
@@ -25,6 +25,7 @@ export async function importFileModule(params: {
|
||||
return (await import(specifier)) as ModuleNamespace;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic module exports are typed by the caller.
|
||||
export function resolveFunctionModuleExport<T extends GenericFunction>(params: {
|
||||
mod: ModuleNamespace;
|
||||
exportName?: string;
|
||||
|
||||
@@ -48,6 +48,7 @@ export function registerChannelRuntimeContext(
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
|
||||
export function getChannelRuntimeContext<T = unknown>(
|
||||
params: ChannelRuntimeContextKey & {
|
||||
channelRuntime?: ChannelRuntimeSurface;
|
||||
|
||||
@@ -94,7 +94,10 @@ function formatRequestedSource(params: {
|
||||
|
||||
type ExecPolicyField = "security" | "ask" | "askFallback";
|
||||
|
||||
function resolveRequestedField<TValue extends ExecSecurity | ExecAsk>(params: {
|
||||
function resolveRequestedField<
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Field-specific callers narrow the shared requested policy value.
|
||||
TValue extends ExecSecurity | ExecAsk,
|
||||
>(params: {
|
||||
field: ExecPolicyRequestedField;
|
||||
scopeExecConfig?: ExecPolicyConfig;
|
||||
globalExecConfig?: ExecPolicyConfig;
|
||||
|
||||
@@ -96,6 +96,7 @@ function writeTempJsonFile(pathname: string, payload: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- JSON loading helper lets callers ascribe the expected payload type.
|
||||
export function loadJsonFile<T = unknown>(pathname: string): T | undefined {
|
||||
try {
|
||||
const raw = fs.readFileSync(pathname, "utf8");
|
||||
|
||||
@@ -26,6 +26,7 @@ export type ResolveOutboundSendDepOptions = {
|
||||
legacyKeys?: readonly string[];
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Channel-specific dependency lookup returns caller-typed values.
|
||||
export function resolveOutboundSendDep<T>(
|
||||
deps: OutboundSendDeps | null | undefined,
|
||||
channelId: string,
|
||||
|
||||
@@ -560,6 +560,7 @@ export async function handleInvoke(
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- CLI JSON params are typed by the invoked method.
|
||||
function decodeParams<T>(raw?: string | null): T {
|
||||
if (!raw) {
|
||||
throw new Error("INVALID_REQUEST: paramsJSON required");
|
||||
|
||||
@@ -81,14 +81,14 @@ type ParsedChatAllowTarget =
|
||||
| { kind: "handle"; handle: string };
|
||||
|
||||
/** Match chat-aware allowlist entries against sender, chat id, guid, or identifier fields. */
|
||||
export function isAllowedParsedChatSender<TParsed extends ParsedChatAllowTarget>(params: {
|
||||
export function isAllowedParsedChatSender(params: {
|
||||
allowFrom: Array<string | number>;
|
||||
sender: string;
|
||||
chatId?: number | null;
|
||||
chatGuid?: string | null;
|
||||
chatIdentifier?: string | null;
|
||||
normalizeSender: (sender: string) => string;
|
||||
parseAllowTarget: (entry: string) => TParsed;
|
||||
parseAllowTarget: (entry: string) => ParsedChatAllowTarget;
|
||||
}): boolean {
|
||||
const allowFrom = params.allowFrom.map((entry) => String(entry).trim());
|
||||
if (allowFrom.length === 0) {
|
||||
|
||||
@@ -214,6 +214,7 @@ export function adaptScopedAccountAccessor<Result, Config extends OpenClawConfig
|
||||
/** Build the shared allowlist/default target adapter surface for account-scoped channel configs. */
|
||||
export function createScopedAccountConfigAccessors<
|
||||
ResolvedAccount,
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Config preserves caller-specific config subtype for account resolvers.
|
||||
Config extends OpenClawConfig = OpenClawConfig,
|
||||
>(params: {
|
||||
resolveAccount: (params: { cfg: Config; accountId?: string | null }) => ResolvedAccount;
|
||||
|
||||
@@ -335,6 +335,7 @@ function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): u
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic entry export loaders use caller-supplied export types.
|
||||
export function loadBundledEntryExportSync<T>(
|
||||
importMetaUrl: string,
|
||||
reference: BundledEntryModuleRef,
|
||||
|
||||
@@ -56,9 +56,7 @@ export function buildPassiveProbedChannelStatusSummary<TExtra extends object>(
|
||||
};
|
||||
}
|
||||
|
||||
export function buildTrafficStatusSummary<TSnapshot extends TrafficStatusSnapshot>(
|
||||
snapshot?: TSnapshot | null,
|
||||
) {
|
||||
export function buildTrafficStatusSummary(snapshot?: TrafficStatusSnapshot | null) {
|
||||
return {
|
||||
lastInboundAt: snapshot?.lastInboundAt ?? null,
|
||||
lastOutboundAt: snapshot?.lastOutboundAt ?? null,
|
||||
|
||||
@@ -257,6 +257,7 @@ export function loadFacadeModuleAtLocationSync<T extends object>(params: {
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
|
||||
@@ -239,6 +239,7 @@ function buildFacadeActivationCheckParams(
|
||||
};
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(
|
||||
params: BundledPluginPublicSurfaceParams,
|
||||
): T {
|
||||
@@ -269,6 +270,7 @@ export function canLoadActivatedBundledPluginPublicSurface(params: {
|
||||
).allowed;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
|
||||
export function loadActivatedBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
@@ -280,6 +282,7 @@ export function loadActivatedBundledPluginPublicSurfaceModuleSync<T extends obje
|
||||
return loadBundledPluginPublicSurfaceModuleSync<T>(params);
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
|
||||
export function tryLoadActivatedBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
|
||||
@@ -62,6 +62,7 @@ export function loadQaRuntimeModule(): QaRuntimeSurface {
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- QA runtime loader uses caller-supplied test API surface type.
|
||||
export function loadQaRunnerBundledPluginTestApi<T extends object>(pluginId: string): T {
|
||||
const env = resolvePrivateQaBundledPluginsEnv();
|
||||
return loadBundledPluginPublicSurfaceModuleSync<T>({
|
||||
|
||||
@@ -40,6 +40,7 @@ const ALLOWED_PACKAGE_SUFFIXES = [
|
||||
"-media-understanding",
|
||||
] as const;
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe JSON file shape.
|
||||
function readJsonFile<T>(filePath: string): T {
|
||||
return JSON.parse(fs.readFileSync(filePath, "utf8")) as T;
|
||||
}
|
||||
|
||||
@@ -12,25 +12,20 @@ type EnableStateParamsLike = {
|
||||
|
||||
type PluginKindLike = string | readonly string[] | undefined;
|
||||
|
||||
export function toEnableStateResult<TState extends EnableStateLike>(
|
||||
state: TState,
|
||||
): { enabled: boolean; reason?: string } {
|
||||
export function toEnableStateResult(state: EnableStateLike): { enabled: boolean; reason?: string } {
|
||||
return state.enabled ? { enabled: true } : { enabled: false, reason: state.reason };
|
||||
}
|
||||
|
||||
export function resolveEnableStateResult<TParams, TState extends EnableStateLike>(
|
||||
export function resolveEnableStateResult<TParams>(
|
||||
params: TParams,
|
||||
resolveState: (params: TParams) => TState,
|
||||
resolveState: (params: TParams) => EnableStateLike,
|
||||
): { enabled: boolean; reason?: string } {
|
||||
return toEnableStateResult(resolveState(params));
|
||||
}
|
||||
|
||||
export function resolveEnableStateShared<
|
||||
TParams extends EnableStateParamsLike,
|
||||
TState extends EnableStateLike,
|
||||
>(
|
||||
export function resolveEnableStateShared<TParams extends EnableStateParamsLike>(
|
||||
params: TParams,
|
||||
resolveState: (params: TParams) => TState,
|
||||
resolveState: (params: TParams) => EnableStateLike,
|
||||
): { enabled: boolean; reason?: string } {
|
||||
return resolveEnableStateResult(params, resolveState);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ type PackageJson = {
|
||||
devDependencies?: Record<string, string>;
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe JSON file shape.
|
||||
function readJsonFile<T>(relativePath: string): T {
|
||||
return JSON.parse(readFileSync(resolve(REPO_ROOT, relativePath), "utf8")) as T;
|
||||
}
|
||||
|
||||
@@ -455,6 +455,7 @@ export function createHookRunner(
|
||||
|
||||
async function runClaimingHookForPluginOutcome<
|
||||
K extends PluginHookName,
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Targeted hook outcomes preserve caller-specific handled result types.
|
||||
TResult extends { handled: boolean },
|
||||
>(
|
||||
hookName: K,
|
||||
|
||||
@@ -6,6 +6,7 @@ export type LazyPluginServiceHandle = {
|
||||
stop: () => Promise<void>;
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic service exports are typed by the caller.
|
||||
function resolveExport<T>(mod: LazyServiceModule, names: string[]): T | null {
|
||||
for (const name of names) {
|
||||
const value = mod[name];
|
||||
|
||||
@@ -141,6 +141,7 @@ function getSharedBundledPublicSurfaceJiti(modulePath: string, tryNative: boolea
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic public artifact loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginPublicArtifactModuleSync<T extends object>(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
|
||||
@@ -654,14 +654,11 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
});
|
||||
};
|
||||
|
||||
const registerUniqueProviderLike = <
|
||||
T extends { id: string },
|
||||
R extends PluginOwnedProviderRegistration<T>,
|
||||
>(params: {
|
||||
const registerUniqueProviderLike = <T extends { id: string }>(params: {
|
||||
record: PluginRecord;
|
||||
provider: T;
|
||||
kindLabel: string;
|
||||
registrations: R[];
|
||||
registrations: Array<PluginOwnedProviderRegistration<T>>;
|
||||
ownedIds: string[];
|
||||
}) => {
|
||||
const id = params.provider.id.trim();
|
||||
@@ -694,7 +691,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
provider: params.provider,
|
||||
source: record.source,
|
||||
rootDir: record.rootDir,
|
||||
} as R);
|
||||
});
|
||||
};
|
||||
|
||||
const registerSpeechProvider = (record: PluginRecord, provider: SpeechProviderPlugin) => {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
export function defineCachedValue<T extends object, K extends PropertyKey>(
|
||||
target: T,
|
||||
key: K,
|
||||
create: () => unknown,
|
||||
): void {
|
||||
export function defineCachedValue(target: object, key: PropertyKey, create: () => unknown): void {
|
||||
let cached: unknown;
|
||||
let ready = false;
|
||||
Object.defineProperty(target, key, {
|
||||
|
||||
@@ -314,6 +314,7 @@ export function createRuntimeChannel(): PluginRuntime["channel"] {
|
||||
});
|
||||
return { dispose };
|
||||
},
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
|
||||
get: <T = unknown>(params: PluginRuntimeChannelContextKey) => {
|
||||
const normalized = normalizeRuntimeContextKey(params);
|
||||
if (!normalized) {
|
||||
|
||||
@@ -127,6 +127,7 @@ export function getPluginBoundaryJiti(modulePath: string, loaders: PluginJitiLoa
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic plugin boundary loaders use caller-supplied module types.
|
||||
export function loadPluginBoundaryModuleWithJiti<TModule>(
|
||||
modulePath: string,
|
||||
loaders: PluginJitiLoaderCache,
|
||||
@@ -134,6 +135,7 @@ export function loadPluginBoundaryModuleWithJiti<TModule>(
|
||||
return getPluginBoundaryJiti(modulePath, loaders)(modulePath) as TModule;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic plugin boundary loaders use caller-supplied module types.
|
||||
export function createCachedPluginBoundaryModuleLoader<TModule>(
|
||||
params: CachedPluginBoundaryLoaderParams,
|
||||
): () => TModule | null {
|
||||
|
||||
@@ -60,6 +60,7 @@ export type PluginRuntimeChannelContextRegistry = {
|
||||
abortSignal?: AbortSignal;
|
||||
},
|
||||
) => { dispose: () => void };
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
|
||||
get: <T = unknown>(params: PluginRuntimeChannelContextKey) => T | undefined;
|
||||
watch: (params: {
|
||||
channelId?: string;
|
||||
|
||||
@@ -164,11 +164,8 @@ export function buildWebProviderSnapshotCacheKey(params: {
|
||||
});
|
||||
}
|
||||
|
||||
export function mapRegistryProviders<
|
||||
TProvider extends { id: string },
|
||||
TEntry extends { pluginId: string; provider: TProvider },
|
||||
>(params: {
|
||||
entries: readonly TEntry[];
|
||||
export function mapRegistryProviders<TProvider extends { id: string }>(params: {
|
||||
entries: readonly { pluginId: string; provider: TProvider }[];
|
||||
onlyPluginIds?: readonly string[];
|
||||
sortProviders: (
|
||||
providers: Array<TProvider & { pluginId: string }>,
|
||||
|
||||
@@ -97,9 +97,9 @@ export function ensureObject(
|
||||
return next;
|
||||
}
|
||||
|
||||
export function normalizeKnownProvider<TProvider extends { id: string }>(
|
||||
export function normalizeKnownProvider(
|
||||
value: unknown,
|
||||
providers: TProvider[],
|
||||
providers: Array<{ id: string }>,
|
||||
): string | undefined {
|
||||
const normalized = normalizeOptionalLowercaseString(value);
|
||||
if (!normalized) {
|
||||
|
||||
@@ -60,6 +60,7 @@ function serializeJson(value: unknown): string | null {
|
||||
return value === undefined ? null : JSON.stringify(value);
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Persisted JSON columns are typed by the receiving field.
|
||||
function parseJsonValue<T>(raw: string | null): T | undefined {
|
||||
if (!raw?.trim()) {
|
||||
return undefined;
|
||||
|
||||
@@ -78,6 +78,7 @@ function serializeJson(value: unknown): string | null {
|
||||
return value == null ? null : JSON.stringify(value);
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Persisted JSON columns are typed by the receiving field.
|
||||
function parseJsonValue<T>(raw: string | null): T | undefined {
|
||||
if (!raw?.trim()) {
|
||||
return undefined;
|
||||
|
||||
@@ -102,6 +102,7 @@ function resolveWorkspacePackageDir(packageName: string): string {
|
||||
throw new Error(`Unknown workspace package: ${packageName}`);
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginPublicSurfaceSync<T extends object>(params: {
|
||||
pluginId: string;
|
||||
artifactBasename: string;
|
||||
@@ -113,6 +114,7 @@ export function loadBundledPluginPublicSurfaceSync<T extends object>(params: {
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginApiSync<T extends object>(pluginId: string): T {
|
||||
return loadBundledPluginPublicSurfaceSync<T>({
|
||||
pluginId,
|
||||
@@ -120,6 +122,7 @@ export function loadBundledPluginApiSync<T extends object>(pluginId: string): T
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginContractApiSync<T extends object>(pluginId: string): T {
|
||||
return loadBundledPluginPublicSurfaceSync<T>({
|
||||
pluginId,
|
||||
@@ -127,6 +130,7 @@ export function loadBundledPluginContractApiSync<T extends object>(pluginId: str
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginRuntimeApiSync<T extends object>(pluginId: string): T {
|
||||
return loadBundledPluginPublicSurfaceSync<T>({
|
||||
pluginId,
|
||||
@@ -134,6 +138,7 @@ export function loadBundledPluginRuntimeApiSync<T extends object>(pluginId: stri
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
|
||||
export function loadBundledPluginTestApiSync<T extends object>(pluginId: string): T {
|
||||
return loadBundledPluginPublicSurfaceSync<T>({
|
||||
pluginId,
|
||||
|
||||
@@ -46,6 +46,7 @@ export function escapeRegExp(value: string): string {
|
||||
/**
|
||||
* Safely parse JSON, returning null on error instead of throwing.
|
||||
*/
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- JSON parsing helper lets callers ascribe the expected payload type.
|
||||
export function safeParseJson<T>(raw: string): T | null {
|
||||
try {
|
||||
return JSON.parse(raw) as T;
|
||||
|
||||
@@ -42,7 +42,7 @@ export function resolveWebFetchEnabled(params: {
|
||||
}
|
||||
|
||||
function resolveFetchConfig(config: OpenClawConfig | undefined): WebFetchConfig | undefined {
|
||||
return resolveWebProviderConfig<"fetch", NonNullable<WebFetchConfig>>(config, "fetch");
|
||||
return resolveWebProviderConfig(config, "fetch") as NonNullable<WebFetchConfig> | undefined;
|
||||
}
|
||||
|
||||
function hasEntryCredential(
|
||||
@@ -141,10 +141,9 @@ export function resolveWebFetchProviderId(params: {
|
||||
export function resolveWebFetchDefinition(
|
||||
options?: ResolveWebFetchDefinitionParams,
|
||||
): { provider: PluginWebFetchProviderEntry; definition: WebFetchProviderToolDefinition } | null {
|
||||
const fetch = resolveWebProviderConfig<"fetch", NonNullable<WebFetchConfig>>(
|
||||
options?.config,
|
||||
"fetch",
|
||||
);
|
||||
const fetch = resolveWebProviderConfig(options?.config, "fetch") as
|
||||
| NonNullable<WebFetchConfig>
|
||||
| undefined;
|
||||
const runtimeWebFetch = options?.runtimeWebFetch ?? getActiveRuntimeWebToolsMetadata()?.fetch;
|
||||
const providers = sortWebFetchProvidersForAutoDetect(
|
||||
resolvePluginWebFetchProviders({
|
||||
|
||||
@@ -38,7 +38,7 @@ export type {
|
||||
} from "./runtime-types.js";
|
||||
|
||||
function resolveSearchConfig(cfg?: OpenClawConfig): WebSearchConfig {
|
||||
return resolveWebProviderConfig<"search", NonNullable<WebSearchConfig>>(cfg, "search");
|
||||
return resolveWebProviderConfig(cfg, "search") as NonNullable<WebSearchConfig> | undefined;
|
||||
}
|
||||
|
||||
export function resolveWebSearchEnabled(params: {
|
||||
|
||||
@@ -12,10 +12,10 @@ type ProviderWithCredential = {
|
||||
requiresCredential?: boolean;
|
||||
};
|
||||
|
||||
export function resolveWebProviderConfig<
|
||||
TKind extends "search" | "fetch",
|
||||
TConfig extends Record<string, unknown>,
|
||||
>(cfg: OpenClawConfig | undefined, kind: TKind): TConfig | undefined {
|
||||
export function resolveWebProviderConfig(
|
||||
cfg: OpenClawConfig | undefined,
|
||||
kind: "search" | "fetch",
|
||||
): Record<string, unknown> | undefined {
|
||||
const webConfig = cfg?.tools?.web;
|
||||
if (!webConfig || typeof webConfig !== "object") {
|
||||
return undefined;
|
||||
@@ -24,7 +24,7 @@ export function resolveWebProviderConfig<
|
||||
if (!toolConfig || typeof toolConfig !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
return toolConfig as TConfig;
|
||||
return toolConfig as Record<string, unknown>;
|
||||
}
|
||||
|
||||
export function readWebProviderEnvValue(
|
||||
|
||||
@@ -9,8 +9,8 @@ function resolveMockOverrides<TModule extends object>(
|
||||
return typeof factory === "function" ? factory(actual) : factory;
|
||||
}
|
||||
|
||||
function resolveDefaultBase<TModule extends object>(actual: TModule): Record<string, unknown> {
|
||||
const defaultExport = (actual as TModule & { default?: unknown }).default;
|
||||
function resolveDefaultBase(actual: object): Record<string, unknown> {
|
||||
const defaultExport = (actual as { default?: unknown }).default;
|
||||
if (defaultExport && typeof defaultExport === "object") {
|
||||
return defaultExport as Record<string, unknown>;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ type PackageManifestContractParams = {
|
||||
minHostVersionBaseline?: string;
|
||||
};
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe package manifest shape.
|
||||
function readJson<T>(relativePath: string): T {
|
||||
const absolutePath = path.resolve(process.cwd(), relativePath);
|
||||
return JSON.parse(fs.readFileSync(absolutePath, "utf8")) as T;
|
||||
@@ -34,8 +35,8 @@ export function describePackageManifestContract(params: PackageManifestContractP
|
||||
if (params.pluginLocalRuntimeDeps?.length) {
|
||||
for (const dependencyName of params.pluginLocalRuntimeDeps) {
|
||||
it(`keeps ${dependencyName} plugin-local`, () => {
|
||||
const rootManifest = readJson<PackageManifest>("package.json");
|
||||
const pluginManifest = readJson<PackageManifest>(packagePath);
|
||||
const rootManifest = readJson("package.json") as PackageManifest;
|
||||
const pluginManifest = readJson(packagePath) as PackageManifest;
|
||||
const pluginSpec =
|
||||
pluginManifest.dependencies?.[dependencyName] ??
|
||||
pluginManifest.optionalDependencies?.[dependencyName];
|
||||
|
||||
@@ -16,6 +16,7 @@ export function createRuntimeEnv(options?: { throwOnExit?: boolean }): OutputRun
|
||||
};
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets plugin suites ascribe runtime extension shape.
|
||||
export function createTypedRuntimeEnv<TRuntime>(options?: { throwOnExit?: boolean }): TRuntime {
|
||||
return createRuntimeEnv(options) as TRuntime;
|
||||
}
|
||||
@@ -24,6 +25,7 @@ export function createNonExitingRuntimeEnv(): OutputRuntimeEnv {
|
||||
return createRuntimeEnv({ throwOnExit: false });
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets plugin suites ascribe runtime extension shape.
|
||||
export function createNonExitingTypedRuntimeEnv<TRuntime>(): TRuntime {
|
||||
return createTypedRuntimeEnv<TRuntime>({ throwOnExit: false });
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ export function createSetupWizardAdapter(params: SetupWizardAdapterParams) {
|
||||
return buildChannelSetupWizardAdapterFromSetupWizard(params);
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific setup wizard surface type.
|
||||
export function createPluginSetupWizardAdapter<TPlugin extends SetupWizardTestPlugin>(
|
||||
plugin: TPlugin,
|
||||
) {
|
||||
@@ -161,12 +162,14 @@ export function createPluginSetupWizardAdapter<TPlugin extends SetupWizardTestPl
|
||||
});
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific setup wizard surface type.
|
||||
export function createPluginSetupWizardConfigure<TPlugin extends SetupWizardTestPlugin>(
|
||||
plugin: TPlugin,
|
||||
) {
|
||||
return createPluginSetupWizardAdapter(plugin).configure;
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific setup wizard surface type.
|
||||
export function createPluginSetupWizardStatus<TPlugin extends SetupWizardTestPlugin>(
|
||||
plugin: TPlugin,
|
||||
) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific hook API type.
|
||||
export function registerHookHandlersForTest<TApi>(params: {
|
||||
config: Record<string, unknown>;
|
||||
register: (api: TApi) => void;
|
||||
|
||||
@@ -10,8 +10,8 @@ type OxlintTsconfig = {
|
||||
exclude?: string[];
|
||||
};
|
||||
|
||||
function readJson<T>(path: string): T {
|
||||
return JSON.parse(fs.readFileSync(path, "utf8")) as T;
|
||||
function readJson(path: string): unknown {
|
||||
return JSON.parse(fs.readFileSync(path, "utf8")) as unknown;
|
||||
}
|
||||
|
||||
describe("oxlint config", () => {
|
||||
|
||||
Reference in New Issue
Block a user