mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-25 22:41:11 +00:00
refactor(line): remove dead exports (#107231)
This commit is contained in:
committed by
GitHub
parent
b3239a5f0f
commit
75cd0efb04
@@ -2,14 +2,10 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
resolveLineAccount,
|
||||
resolveDefaultLineAccountId,
|
||||
normalizeAccountId,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
} from "./accounts.js";
|
||||
import { resolveLineAccount, resolveDefaultLineAccountId, normalizeAccountId } from "./accounts.js";
|
||||
|
||||
describe("LINE accounts", () => {
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
@@ -14,8 +14,6 @@ import type {
|
||||
ResolvedLineAccount,
|
||||
} from "./types.js";
|
||||
|
||||
export { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
||||
|
||||
function readFileIfExists(filePath: string | undefined): string | undefined {
|
||||
return tryReadSecretFileSync(filePath, "LINE credential file", { rejectSymlink: true });
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Line tests cover auto reply delivery plugin behavior.
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { LineAutoReplyDeps } from "./auto-reply-delivery.js";
|
||||
import { deliverLineAutoReply } from "./auto-reply-delivery.js";
|
||||
import { sendLineReplyChunks } from "./reply-chunks.js";
|
||||
import { createLineSendReceipt } from "./send-receipt.js";
|
||||
|
||||
type LineAutoReplyDeps = Parameters<typeof deliverLineAutoReply>[0]["deps"];
|
||||
|
||||
const createFlexMessage = (altText: string, contents: unknown) => ({
|
||||
type: "flex" as const,
|
||||
altText,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { buildLineQuickReplyFallbackText } from "./quick-reply-fallback.js";
|
||||
import type { SendLineReplyChunksParams } from "./reply-chunks.js";
|
||||
import type { LineChannelData, LineTemplateMessagePayload } from "./types.js";
|
||||
|
||||
export type LineAutoReplyDeps = {
|
||||
type LineAutoReplyDeps = {
|
||||
buildTemplateMessageFromPayload: (
|
||||
payload: LineTemplateMessagePayload,
|
||||
) => messagingApi.TemplateMessage | null;
|
||||
|
||||
@@ -175,7 +175,6 @@ vi.mock("./bot-message-context.js", () => ({
|
||||
|
||||
let handleLineWebhookEvents: typeof import("./bot-handlers.js").handleLineWebhookEvents;
|
||||
let createLineWebhookReplayCache: typeof import("./bot-handlers.js").createLineWebhookReplayCache;
|
||||
let LineRetryableWebhookError: typeof import("./bot-handlers.js").LineRetryableWebhookError;
|
||||
type LineWebhookContext = Parameters<typeof import("./bot-handlers.js").handleLineWebhookEvents>[1];
|
||||
|
||||
const createRuntime = () => ({ log: vi.fn(), error: vi.fn(), exit: vi.fn() });
|
||||
@@ -315,8 +314,7 @@ async function startInflightReplayDuplicate(params: {
|
||||
|
||||
describe("handleLineWebhookEvents", () => {
|
||||
beforeAll(async () => {
|
||||
({ handleLineWebhookEvents, createLineWebhookReplayCache, LineRetryableWebhookError } =
|
||||
await import("./bot-handlers.js"));
|
||||
({ handleLineWebhookEvents, createLineWebhookReplayCache } = await import("./bot-handlers.js"));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@@ -769,7 +767,7 @@ describe("handleLineWebhookEvents", () => {
|
||||
expect(processMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("mirrors in-flight retryable replay failures so concurrent duplicates also fail", async () => {
|
||||
it("commits in-flight failures so concurrent duplicates do not retry", async () => {
|
||||
let rejectFirst: ((err: Error) => void) | undefined;
|
||||
const firstDone = new Promise<void>((_, reject) => {
|
||||
rejectFirst = reject;
|
||||
@@ -786,10 +784,10 @@ describe("handleLineWebhookEvents", () => {
|
||||
});
|
||||
const { firstRun, secondRun } = await startInflightReplayDuplicate({ event, processMessage });
|
||||
const firstFailure = expect(firstRun).rejects.toThrow("transient inflight failure");
|
||||
const secondFailure = expect(secondRun).rejects.toThrow("transient inflight failure");
|
||||
rejectFirst?.(new LineRetryableWebhookError("transient inflight failure"));
|
||||
rejectFirst?.(new Error("transient inflight failure"));
|
||||
|
||||
await Promise.all([firstFailure, secondFailure]);
|
||||
await firstFailure;
|
||||
await expect(secondRun).resolves.toBeUndefined();
|
||||
expect(processMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -1164,25 +1162,4 @@ describe("handleLineWebhookEvents", () => {
|
||||
"line: event handler failed: Error: transient failure",
|
||||
);
|
||||
});
|
||||
|
||||
it("reopens replay after an explicit retryable event failure", async () => {
|
||||
const processMessage = vi
|
||||
.fn()
|
||||
.mockRejectedValueOnce(new LineRetryableWebhookError("retry me"))
|
||||
.mockResolvedValueOnce(undefined);
|
||||
const event = createReplayMessageEvent({
|
||||
messageId: "m-fail-then-retryable",
|
||||
groupId: "group-retry",
|
||||
userId: "user-retry",
|
||||
webhookEventId: "evt-fail-then-retryable",
|
||||
isRedelivery: false,
|
||||
});
|
||||
const context = createOpenGroupReplayContext(processMessage, createLineWebhookReplayCache());
|
||||
|
||||
await expect(handleLineWebhookEvents([event], context)).rejects.toThrow("retry me");
|
||||
await handleLineWebhookEvents([event], context);
|
||||
|
||||
expect(buildLineMessageContextMock).toHaveBeenCalledTimes(2);
|
||||
expect(processMessage).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,13 +85,6 @@ function normalizeLineIngressEntry(value: string): string | null {
|
||||
return normalizeLineAllowEntry(value) || null;
|
||||
}
|
||||
|
||||
export class LineRetryableWebhookError extends Error {
|
||||
constructor(message: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
this.name = "LineRetryableWebhookError";
|
||||
}
|
||||
}
|
||||
|
||||
export function createLineWebhookReplayCache(): LineWebhookReplayCache {
|
||||
return createClaimableDedupe({
|
||||
ttlMs: LINE_WEBHOOK_REPLAY_WINDOW_MS,
|
||||
@@ -614,11 +607,7 @@ export async function handleLineWebhookEvents(
|
||||
}
|
||||
} catch (err) {
|
||||
if (replayCandidate) {
|
||||
if (err instanceof LineRetryableWebhookError) {
|
||||
replayCandidate.cache.release(replayCandidate.key, { error: err });
|
||||
} else {
|
||||
await replayCandidate.cache.commit(replayCandidate.key);
|
||||
}
|
||||
await replayCandidate.cache.commit(replayCandidate.key);
|
||||
}
|
||||
context.runtime.error?.(danger(`line: event handler failed: ${String(err)}`));
|
||||
firstError ??= err;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Line tests cover message cards plugin behavior.
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { datetimePickerAction, postbackAction, uriAction } from "./actions.js";
|
||||
import { datetimePickerAction, messageAction, postbackAction, uriAction } from "./actions.js";
|
||||
import { registerLineCardCommand } from "./card-command.js";
|
||||
import {
|
||||
createActionCard,
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
createImageCarousel,
|
||||
createImageCarouselColumn,
|
||||
createProductCarousel,
|
||||
messageAction,
|
||||
} from "./template-messages.js";
|
||||
|
||||
const loneHighSurrogate = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])/;
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { createMockIncomingRequest } from "openclaw/plugin-sdk/test-env";
|
||||
import { WEBHOOK_IN_FLIGHT_DEFAULTS } from "openclaw/plugin-sdk/webhook-request-guards";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
type LineNodeWebhookHandler = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
||||
type LineHandleWebhook = (...args: unknown[]) => Promise<void>;
|
||||
@@ -29,8 +29,6 @@ const {
|
||||
}));
|
||||
|
||||
let monitorLineProvider: typeof import("./monitor.js").monitorLineProvider;
|
||||
let getLineRuntimeState: typeof import("./monitor.js").getLineRuntimeState;
|
||||
let clearLineRuntimeStateForTests: typeof import("./monitor.js").clearLineRuntimeStateForTests;
|
||||
let innerLineWebhookHandlerMock: ReturnType<typeof vi.fn<LineNodeWebhookHandler>>;
|
||||
|
||||
type RegisteredRoute = {
|
||||
@@ -142,8 +140,7 @@ vi.mock("./template-messages.js", () => ({
|
||||
|
||||
describe("monitorLineProvider lifecycle", () => {
|
||||
beforeAll(async () => {
|
||||
({ monitorLineProvider, getLineRuntimeState, clearLineRuntimeStateForTests } =
|
||||
await import("./monitor.js"));
|
||||
({ monitorLineProvider } = await import("./monitor.js"));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@@ -161,7 +158,6 @@ describe("monitorLineProvider lifecycle", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
clearLineRuntimeStateForTests();
|
||||
createLineBotMock.mockReset();
|
||||
createLineBotMock.mockImplementation(() => ({
|
||||
account: { accountId: "default" },
|
||||
@@ -200,10 +196,6 @@ describe("monitorLineProvider lifecycle", () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearLineRuntimeStateForTests();
|
||||
});
|
||||
|
||||
const createRouteResponse = () => {
|
||||
const resObj = {
|
||||
statusCode: 0,
|
||||
@@ -289,7 +281,7 @@ describe("monitorLineProvider lifecycle", () => {
|
||||
expect(unregisterHttpMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("records startup state under configured defaultAccount when accountId is omitted", async () => {
|
||||
it("registers the configured defaultAccount when accountId is omitted", async () => {
|
||||
const monitor = await monitorLineProvider({
|
||||
channelAccessToken: "token",
|
||||
channelSecret: "secret", // pragma: allowlist secret
|
||||
@@ -309,13 +301,14 @@ describe("monitorLineProvider lifecycle", () => {
|
||||
runtime: {} as RuntimeEnv,
|
||||
});
|
||||
|
||||
expect(getLineRuntimeState("work")?.running).toBe(true);
|
||||
expect(getLineRuntimeState("default")).toBeUndefined();
|
||||
const registration = requireWebhookRegistration();
|
||||
expect(registration.target.accountId).toBe("work");
|
||||
expect(registration.route.accountId).toBe("work");
|
||||
|
||||
monitor.stop();
|
||||
});
|
||||
|
||||
it("does not record running state when bot startup fails", async () => {
|
||||
it("does not register a webhook when bot startup fails", async () => {
|
||||
createLineBotMock.mockImplementation(() => {
|
||||
throw new Error("line bot startup failed");
|
||||
});
|
||||
@@ -329,7 +322,6 @@ describe("monitorLineProvider lifecycle", () => {
|
||||
}),
|
||||
).rejects.toThrow("line bot startup failed");
|
||||
|
||||
expect(getLineRuntimeState("default")?.running).not.toBe(true);
|
||||
expect(registerWebhookTargetWithPluginRouteMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -63,17 +63,6 @@ interface LineProviderMonitor {
|
||||
stop: () => void;
|
||||
}
|
||||
|
||||
const runtimeState = new Map<
|
||||
string,
|
||||
{
|
||||
running: boolean;
|
||||
lastStartAt: number | null;
|
||||
lastStopAt: number | null;
|
||||
lastError: string | null;
|
||||
lastInboundAt?: number | null;
|
||||
lastOutboundAt?: number | null;
|
||||
}
|
||||
>();
|
||||
const lineWebhookInFlightLimiter = createWebhookInFlightLimiter();
|
||||
const LINE_WEBHOOK_PREAUTH_MAX_BODY_BYTES = 64 * 1024;
|
||||
const LINE_WEBHOOK_PREAUTH_BODY_TIMEOUT_MS = 5_000;
|
||||
@@ -88,36 +77,6 @@ type LineWebhookTarget = {
|
||||
|
||||
const lineWebhookTargets = new Map<string, LineWebhookTarget[]>();
|
||||
|
||||
function recordChannelRuntimeState(params: {
|
||||
channel: string;
|
||||
accountId: string;
|
||||
state: Partial<{
|
||||
running: boolean;
|
||||
lastStartAt: number | null;
|
||||
lastStopAt: number | null;
|
||||
lastError: string | null;
|
||||
lastInboundAt: number | null;
|
||||
lastOutboundAt: number | null;
|
||||
}>;
|
||||
}): void {
|
||||
const key = `${params.channel}:${params.accountId}`;
|
||||
const existing = runtimeState.get(key) ?? {
|
||||
running: false,
|
||||
lastStartAt: null,
|
||||
lastStopAt: null,
|
||||
lastError: null,
|
||||
};
|
||||
runtimeState.set(key, { ...existing, ...params.state });
|
||||
}
|
||||
|
||||
export function getLineRuntimeState(accountId: string) {
|
||||
return runtimeState.get(`line:${accountId}`);
|
||||
}
|
||||
|
||||
export function clearLineRuntimeStateForTests() {
|
||||
runtimeState.clear();
|
||||
}
|
||||
|
||||
function startLineLoadingKeepalive(params: {
|
||||
cfg: OpenClawConfig;
|
||||
userId: string;
|
||||
@@ -188,14 +147,6 @@ export async function monitorLineProvider(
|
||||
|
||||
const { ctxPayload, replyToken, route } = ctx;
|
||||
|
||||
recordChannelRuntimeState({
|
||||
channel: "line",
|
||||
accountId: resolvedAccountId,
|
||||
state: {
|
||||
lastInboundAt: Date.now(),
|
||||
},
|
||||
});
|
||||
|
||||
const shouldShowLoading = Boolean(ctx.userId && !ctx.isGroup);
|
||||
|
||||
const displayNamePromise = ctx.userId
|
||||
@@ -298,14 +249,6 @@ export async function monitorLineProvider(
|
||||
// because this delivery was not a clean success.
|
||||
throw deliveryResult.error;
|
||||
}
|
||||
|
||||
recordChannelRuntimeState({
|
||||
channel: "line",
|
||||
accountId: resolvedAccountId,
|
||||
state: {
|
||||
lastOutboundAt: Date.now(),
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: (err, info) => {
|
||||
runtime.error?.(danger(`line ${info.kind} reply failed: ${String(err)}`));
|
||||
@@ -473,15 +416,6 @@ export async function monitorLineProvider(
|
||||
},
|
||||
});
|
||||
|
||||
recordChannelRuntimeState({
|
||||
channel: "line",
|
||||
accountId: resolvedAccountId,
|
||||
state: {
|
||||
running: true,
|
||||
lastStartAt: Date.now(),
|
||||
},
|
||||
});
|
||||
|
||||
logVerbose(`line: registered webhook handler at ${normalizedPath}`);
|
||||
|
||||
let stopped = false;
|
||||
@@ -492,14 +426,6 @@ export async function monitorLineProvider(
|
||||
stopped = true;
|
||||
logVerbose(`line: stopping provider for account ${resolvedAccountId}`);
|
||||
unregisterHttp();
|
||||
recordChannelRuntimeState({
|
||||
channel: "line",
|
||||
accountId: resolvedAccountId,
|
||||
state: {
|
||||
running: false,
|
||||
lastStopAt: Date.now(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (abortSignal?.aborted) {
|
||||
|
||||
@@ -4,15 +4,12 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { datetimePickerAction, messageAction, postbackAction, uriAction } from "./actions.js";
|
||||
import {
|
||||
createRichMenu,
|
||||
createDefaultMenuConfig,
|
||||
createGridLayout,
|
||||
datetimePickerAction,
|
||||
messageAction,
|
||||
postbackAction,
|
||||
uploadRichMenuImage,
|
||||
uriAction,
|
||||
} from "./rich-menu.js";
|
||||
|
||||
const {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
||||
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media";
|
||||
import { resolveLineAccount } from "./accounts.js";
|
||||
import { datetimePickerAction, messageAction, postbackAction, uriAction } from "./actions.js";
|
||||
import { messageAction } from "./actions.js";
|
||||
import { resolveLineChannelAccessToken } from "./channel-access-token.js";
|
||||
|
||||
type RichMenuRequest = messagingApi.RichMenuRequest;
|
||||
@@ -253,8 +253,6 @@ export function createGridLayout(
|
||||
];
|
||||
}
|
||||
|
||||
export { datetimePickerAction, messageAction, postbackAction, uriAction };
|
||||
|
||||
export function createDefaultMenuConfig(): CreateRichMenuParams {
|
||||
return {
|
||||
size: { width: 2500, height: 843 },
|
||||
|
||||
@@ -22,12 +22,9 @@ type LineRuntime = PluginRuntime & {
|
||||
};
|
||||
};
|
||||
|
||||
const {
|
||||
setRuntime: setLineRuntime,
|
||||
clearRuntime: clearLineRuntime,
|
||||
getRuntime: getLineRuntime,
|
||||
} = createPluginRuntimeStore<LineRuntime>({
|
||||
pluginId: "line",
|
||||
errorMessage: "LINE runtime not initialized - plugin not registered",
|
||||
});
|
||||
export { clearLineRuntime, getLineRuntime, setLineRuntime };
|
||||
const { setRuntime: setLineRuntime, getRuntime: getLineRuntime } =
|
||||
createPluginRuntimeStore<LineRuntime>({
|
||||
pluginId: "line",
|
||||
errorMessage: "LINE runtime not initialized - plugin not registered",
|
||||
});
|
||||
export { getLineRuntime, setLineRuntime };
|
||||
|
||||
@@ -15,9 +15,8 @@ import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "../api.
|
||||
import { linePlugin } from "./channel.js";
|
||||
import { lineGatewayAdapter } from "./gateway.js";
|
||||
import { probeLineBot } from "./probe.js";
|
||||
import { clearLineRuntime, setLineRuntime } from "./runtime.js";
|
||||
import { setLineRuntime } from "./runtime.js";
|
||||
import { lineSetupWizard } from "./setup-surface.js";
|
||||
import { lineStatusAdapter } from "./status.js";
|
||||
|
||||
const { getBotInfoMock, MessagingApiClientMock } = vi.hoisted(() => {
|
||||
const getBotInfoMockLocal = vi.fn();
|
||||
@@ -318,7 +317,6 @@ describe("probeLineBot", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearLineRuntime();
|
||||
vi.useRealTimers();
|
||||
getBotInfoMock.mockClear();
|
||||
});
|
||||
@@ -352,6 +350,8 @@ describe("probeLineBot", () => {
|
||||
|
||||
describe("linePlugin status.probeAccount", () => {
|
||||
it("falls back to the direct probe helper when runtime is not initialized", async () => {
|
||||
vi.resetModules();
|
||||
const { lineStatusAdapter } = await import("./status.js");
|
||||
MessagingApiClientMock.mockReset();
|
||||
MessagingApiClientMock.mockImplementation(function () {
|
||||
return { getBotInfo: getBotInfoMock };
|
||||
@@ -375,8 +375,6 @@ describe("linePlugin status.probeAccount", () => {
|
||||
timeoutMs: 50,
|
||||
};
|
||||
|
||||
clearLineRuntime();
|
||||
|
||||
await expect(lineStatusAdapter.probeAccount!(params)).resolves.toEqual(
|
||||
await probeLineBot("token", 50),
|
||||
);
|
||||
|
||||
@@ -3,8 +3,6 @@ import type { messagingApi } from "@line/bot-sdk";
|
||||
import { messageAction, postbackAction, uriAction, type Action } from "./actions.js";
|
||||
import type { LineTemplateMessagePayload } from "./types.js";
|
||||
|
||||
export { messageAction };
|
||||
|
||||
type TemplateMessage = messagingApi.TemplateMessage;
|
||||
type ConfirmTemplate = messagingApi.ConfirmTemplate;
|
||||
type ButtonsTemplate = messagingApi.ButtonsTemplate;
|
||||
|
||||
@@ -133,17 +133,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
|
||||
"extensions/irc/src/control-chars.ts: isIrcControlChar",
|
||||
"extensions/irc/src/monitor.ts: resolveIrcInboundTarget",
|
||||
"extensions/irc/src/runtime.ts: clearIrcRuntime",
|
||||
"extensions/line/src/accounts.ts: DEFAULT_ACCOUNT_ID",
|
||||
"extensions/line/src/auto-reply-delivery.ts: LineAutoReplyDeps",
|
||||
"extensions/line/src/bot-handlers.ts: LineRetryableWebhookError",
|
||||
"extensions/line/src/monitor.ts: clearLineRuntimeStateForTests",
|
||||
"extensions/line/src/monitor.ts: getLineRuntimeState",
|
||||
"extensions/line/src/rich-menu.ts: datetimePickerAction",
|
||||
"extensions/line/src/rich-menu.ts: messageAction",
|
||||
"extensions/line/src/rich-menu.ts: postbackAction",
|
||||
"extensions/line/src/rich-menu.ts: uriAction",
|
||||
"extensions/line/src/runtime.ts: clearLineRuntime",
|
||||
"extensions/line/src/template-messages.ts: messageAction",
|
||||
"extensions/llama-cpp/src/embedding-provider.ts: createLlamaCppMemoryEmbeddingProvider",
|
||||
"extensions/llama-cpp/src/embedding-provider.ts: DEFAULT_LLAMA_CPP_EMBEDDING_MODEL",
|
||||
"extensions/llama-cpp/src/embedding-provider.ts: formatLlamaCppSetupError",
|
||||
|
||||
Reference in New Issue
Block a user