mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 03:52:48 +05:30
feat(llm): Added Anthropic Claude provider
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
OllamaEmbeddingProvider,
|
||||
ProviderManager,
|
||||
OpenRouterProvider,
|
||||
AnthropicProvider,
|
||||
GeminiEmbeddingProvider,
|
||||
MockEmbeddingProvider,
|
||||
} from "@omnia/llm";
|
||||
@@ -67,6 +68,13 @@ function buildLLMProvider(inst: ModelProviderInstance): ILLMProvider {
|
||||
inst.name,
|
||||
inst.maxContext,
|
||||
);
|
||||
} else if (inst.providerName === "anthropic") {
|
||||
return new AnthropicProvider(
|
||||
inst.apiKey,
|
||||
inst.modelName,
|
||||
inst.name,
|
||||
inst.maxContext,
|
||||
);
|
||||
}
|
||||
return new MockLLMProvider([]);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@langchain/anthropic": "^0.3.11",
|
||||
"@langchain/google-genai": "^2.2.0",
|
||||
"@langchain/ollama": "^0.2.3",
|
||||
"@langchain/openrouter": "^0.4.3",
|
||||
|
||||
@@ -258,6 +258,27 @@ When no active row is found for the requested type:
|
||||
|
||||
Also exports `GeminiEmbeddingProvider` (implements `IEmbeddingProvider`) using the same key resolution pattern but querying for the `"embedding"` type.
|
||||
|
||||
### Anthropic Claude — `AnthropicProvider`
|
||||
|
||||
| Property | Value |
|
||||
| --------------------------- | ------------------------------------------------------ |
|
||||
| **File** | [`providers/anthropic.ts`](src/providers/anthropic.ts) |
|
||||
| **Provider ID** | `anthropic` |
|
||||
| **SDK** | `@langchain/anthropic` (`ChatAnthropic`) |
|
||||
| **Default Model** | `claude-3-5-sonnet-latest` |
|
||||
| **Default Embedding Model** | _(none)_ |
|
||||
| **Default Max Context** | `200000` |
|
||||
| **Type** | Generative only (no embedding provider) |
|
||||
|
||||
**Key resolution** in the constructor follows this cascade:
|
||||
|
||||
```
|
||||
1. Explicit apiKey argument → use it
|
||||
2. ProviderManager.getActive() → if providerName matches "anthropic"
|
||||
3. ANTHROPIC_API_KEY env var → final fallback
|
||||
4. None found → throw Error
|
||||
```
|
||||
|
||||
### OpenRouter — `OpenRouterProvider`
|
||||
|
||||
| Property | Value |
|
||||
@@ -284,21 +305,21 @@ Same three-step key resolution as Gemini (`explicit → ProviderManager → env
|
||||
| **Default Max Context** | `32768` |
|
||||
| **Type** | Generative + Embedding |
|
||||
|
||||
Ollama runs **locally** — no API key is required. The `apiKey` field in `ModelProviderInstance` is repurposed to store the Ollama server base URL (default: `http://localhost:11434`).
|
||||
Ollama runs **locally** — no API key is required. The `endpointUrl` field in `ModelProviderInstance` stores the Ollama server base URL (default: `http://localhost:11434`).
|
||||
|
||||
**Key resolution** in the constructor:
|
||||
|
||||
```
|
||||
1. Explicit baseUrl argument → use it
|
||||
2. ProviderManager.getActive() → if providerName matches "ollama"
|
||||
(apiKey field = base URL)
|
||||
(endpointUrl field = base URL)
|
||||
3. Default → http://localhost:11434
|
||||
```
|
||||
|
||||
Also exports `OllamaEmbeddingProvider` (implements `IEmbeddingProvider`), which uses the same resolution pattern against the `"embedding"` type instance. The default embedding model is `nomic-embed-text`.
|
||||
|
||||
> [!TIP]
|
||||
> To get started: `ollama pull llama3.1` and `ollama pull nomic-embed-text`. Then create a provider instance with `apiKey` = `http://localhost:11434`.
|
||||
> To get started: `ollama pull llama3.1` and `ollama pull nomic-embed-text`. Then create a provider instance with `endpointUrl` = `http://localhost:11434`.
|
||||
|
||||
### Mock — `MockLLMProvider`
|
||||
|
||||
@@ -358,6 +379,7 @@ The `buildLLMProvider()` and `buildEmbeddingProvider()` functions perform the fi
|
||||
| `"google-genai"` | `GeminiProvider` | `GeminiEmbeddingProvider` |
|
||||
| `"openrouter"` | `OpenRouterProvider` | _(falls through to mock)_ |
|
||||
| `"ollama"` | `OllamaProvider` | `OllamaEmbeddingProvider` |
|
||||
| `"anthropic"` | `AnthropicProvider` | _(falls through to mock)_ |
|
||||
| _(anything else)_ | `MockLLMProvider` | `MockEmbeddingProvider` |
|
||||
|
||||
## Structured Output
|
||||
@@ -380,10 +402,11 @@ This sends the Zod schema to the model as a structured output constraint. The re
|
||||
|
||||
[`config.ts`](src/config.ts) parses environment variables using Zod:
|
||||
|
||||
| Variable | Required | Description |
|
||||
| -------------------- | -------- | --------------------- |
|
||||
| `GOOGLE_API_KEY` | No | Google Gemini API key |
|
||||
| `OPENROUTER_API_KEY` | No | OpenRouter API key |
|
||||
| Variable | Required | Description |
|
||||
| -------------------- | -------- | ------------------------ |
|
||||
| `GOOGLE_API_KEY` | No | Google Gemini API key |
|
||||
| `OPENROUTER_API_KEY` | No | OpenRouter API key |
|
||||
| `ANTHROPIC_API_KEY` | No | Anthropic Claude API key |
|
||||
|
||||
Both are optional because providers can also be configured through the database via the GUI settings page.
|
||||
|
||||
@@ -400,6 +423,7 @@ packages/llm/
|
||||
│ ├── google-genai.ts # GeminiProvider + GeminiEmbeddingProvider
|
||||
│ ├── ollama.ts # OllamaProvider + OllamaEmbeddingProvider
|
||||
│ ├── openrouter.ts # OpenRouterProvider
|
||||
│ ├── anthropic.ts # AnthropicProvider
|
||||
│ └── mock.ts # MockLLMProvider + MockEmbeddingProvider
|
||||
├── tests/
|
||||
│ ├── mock.test.ts
|
||||
|
||||
@@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
const LLMConfigSchema = z.object({
|
||||
GOOGLE_API_KEY: z.string().optional(),
|
||||
OPENROUTER_API_KEY: z.string().optional(),
|
||||
ANTHROPIC_API_KEY: z.string().optional(),
|
||||
});
|
||||
|
||||
export const llmConfig = LLMConfigSchema.parse(process.env);
|
||||
|
||||
@@ -4,4 +4,5 @@ export * from "./providers/google-genai.js";
|
||||
export * from "./providers/mock.js";
|
||||
export * from "./providers/ollama.js";
|
||||
export * from "./providers/openrouter.js";
|
||||
export * from "./providers/anthropic.js";
|
||||
export * from "./provider-manager.js";
|
||||
|
||||
@@ -76,6 +76,13 @@ export const AVAILABLE_PROVIDERS: ModelProviderMeta[] = [
|
||||
defaultModel: "gemini-2.5-flash",
|
||||
defaultEmbeddingModel: "gemini-embedding-001",
|
||||
},
|
||||
{
|
||||
id: "anthropic",
|
||||
displayName: "Anthropic Claude",
|
||||
description: "Official Claude integration using @langchain/anthropic SDK",
|
||||
defaultModel: "claude-3-5-sonnet-latest",
|
||||
defaultEmbeddingModel: "",
|
||||
},
|
||||
{
|
||||
id: "openrouter",
|
||||
displayName: "OpenRouter",
|
||||
|
||||
@@ -99,6 +99,7 @@ function getSettingsDb() {
|
||||
if (totalCount.count === 0) {
|
||||
const googleKey = process.env.GOOGLE_API_KEY;
|
||||
const openRouterKey = process.env.OPENROUTER_API_KEY;
|
||||
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
||||
let hasInsertedGenerative = false;
|
||||
|
||||
if (googleKey && googleKey.trim()) {
|
||||
@@ -138,6 +139,29 @@ function getSettingsDb() {
|
||||
);
|
||||
}
|
||||
|
||||
if (anthropicKey && anthropicKey.trim()) {
|
||||
const id = "provider-default-anthropic";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO provider_instances (id, name, providerName, apiKey, isActive, modelName, type, maxContext)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
).run(
|
||||
id,
|
||||
"Anthropic (Env)",
|
||||
"anthropic",
|
||||
anthropicKey.trim(),
|
||||
isActive,
|
||||
"claude-3-5-sonnet-latest",
|
||||
"generative",
|
||||
200000,
|
||||
);
|
||||
if (isActive === 1) {
|
||||
hasInsertedGenerative = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (openRouterKey && openRouterKey.trim()) {
|
||||
const id = "provider-default-openrouter";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
@@ -391,6 +415,7 @@ export class ProviderManager {
|
||||
if (totalCount.count === 0) {
|
||||
const googleKey = process.env.GOOGLE_API_KEY;
|
||||
const openRouterKey = process.env.OPENROUTER_API_KEY;
|
||||
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
||||
let hasInsertedGenerative = false;
|
||||
|
||||
if (googleKey && googleKey.trim()) {
|
||||
@@ -430,6 +455,29 @@ export class ProviderManager {
|
||||
);
|
||||
}
|
||||
|
||||
if (anthropicKey && anthropicKey.trim()) {
|
||||
const id = "provider-default-anthropic";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO provider_instances (id, name, providerName, apiKey, isActive, modelName, type, maxContext)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
).run(
|
||||
id,
|
||||
"Anthropic (Env)",
|
||||
"anthropic",
|
||||
anthropicKey.trim(),
|
||||
isActive,
|
||||
"claude-3-5-sonnet-latest",
|
||||
"generative",
|
||||
200000,
|
||||
);
|
||||
if (isActive === 1) {
|
||||
hasInsertedGenerative = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (openRouterKey && openRouterKey.trim()) {
|
||||
const id = "provider-default-openrouter";
|
||||
const isActive = hasInsertedGenerative ? 0 : 1;
|
||||
@@ -576,6 +624,19 @@ export class ProviderManager {
|
||||
maxContext: 32768,
|
||||
};
|
||||
}
|
||||
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
||||
if (anthropicKey && anthropicKey.trim()) {
|
||||
return {
|
||||
id: "provider-default-env-fallback",
|
||||
name: "Anthropic (Env Fallback)",
|
||||
providerName: "anthropic",
|
||||
apiKey: anthropicKey.trim(),
|
||||
isActive: true,
|
||||
modelName: "claude-3-5-sonnet-latest",
|
||||
type: "generative",
|
||||
maxContext: 200000,
|
||||
};
|
||||
}
|
||||
const openRouterKey = process.env.OPENROUTER_API_KEY;
|
||||
if (openRouterKey && openRouterKey.trim()) {
|
||||
return {
|
||||
|
||||
114
packages/llm/src/providers/anthropic.ts
Normal file
114
packages/llm/src/providers/anthropic.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { z } from "zod";
|
||||
import { ChatAnthropic } from "@langchain/anthropic";
|
||||
import {
|
||||
ILLMProvider,
|
||||
LLMRequest,
|
||||
LLMResponse,
|
||||
LLMCallRecord,
|
||||
} from "../llm.js";
|
||||
import { llmConfig } from "../config.js";
|
||||
import { ProviderManager } from "../provider-manager.js";
|
||||
|
||||
export class AnthropicProvider implements ILLMProvider {
|
||||
static readonly providerId = "anthropic";
|
||||
static readonly displayName = "Anthropic Claude";
|
||||
static readonly description =
|
||||
"Official Claude integration using @langchain/anthropic SDK";
|
||||
static readonly defaultModel = "claude-3-5-sonnet-latest";
|
||||
|
||||
providerName = "Anthropic";
|
||||
private model: ChatAnthropic;
|
||||
private modelNameUsed: string;
|
||||
private providerInstanceName?: string;
|
||||
private maxContextUsed?: number;
|
||||
lastCalls: LLMCallRecord[] = [];
|
||||
|
||||
constructor(
|
||||
apiKey?: string,
|
||||
modelName?: string,
|
||||
providerInstanceName?: string,
|
||||
maxContext?: number,
|
||||
) {
|
||||
let key = apiKey;
|
||||
let model = modelName;
|
||||
this.providerInstanceName = providerInstanceName;
|
||||
this.maxContextUsed = maxContext;
|
||||
|
||||
if (!key) {
|
||||
const active = ProviderManager.getActive("generative");
|
||||
if (active && active.providerName === AnthropicProvider.providerId) {
|
||||
key = active.apiKey;
|
||||
if (!model) {
|
||||
model = active.modelName;
|
||||
}
|
||||
if (!this.providerInstanceName) {
|
||||
this.providerInstanceName = active.name;
|
||||
}
|
||||
if (this.maxContextUsed === undefined) {
|
||||
this.maxContextUsed = active.maxContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
key = llmConfig.ANTHROPIC_API_KEY;
|
||||
if (!this.providerInstanceName && key) {
|
||||
this.providerInstanceName = "Environment Variable";
|
||||
}
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new Error(
|
||||
"ANTHROPIC_API_KEY is required to initialize AnthropicProvider",
|
||||
);
|
||||
}
|
||||
|
||||
this.modelNameUsed = model || AnthropicProvider.defaultModel;
|
||||
this.model = new ChatAnthropic({
|
||||
apiKey: key,
|
||||
model: this.modelNameUsed,
|
||||
});
|
||||
}
|
||||
|
||||
async generateStructuredResponse<T extends z.ZodTypeAny>(
|
||||
request: LLMRequest<T>,
|
||||
): Promise<LLMResponse<z.infer<T>>> {
|
||||
const structuredModel = this.model.withStructuredOutput(request.schema, {
|
||||
includeRaw: true,
|
||||
});
|
||||
const result = (await structuredModel.invoke([
|
||||
{ role: "system", content: request.systemPrompt },
|
||||
{ role: "user", content: request.userContext },
|
||||
])) as unknown as {
|
||||
parsed?: z.infer<T>;
|
||||
raw?: {
|
||||
usage_metadata?: {
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
total_tokens?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const parsed = result?.parsed;
|
||||
const raw = result?.raw;
|
||||
|
||||
const usage = {
|
||||
inputTokens: raw?.usage_metadata?.input_tokens || 0,
|
||||
outputTokens: raw?.usage_metadata?.output_tokens || 0,
|
||||
totalTokens: raw?.usage_metadata?.total_tokens || 0,
|
||||
modelName: this.modelNameUsed,
|
||||
providerInstanceName: this.providerInstanceName || "Default",
|
||||
maxContext:
|
||||
this.maxContextUsed !== undefined ? this.maxContextUsed : 200000,
|
||||
};
|
||||
|
||||
this.lastCalls.push({
|
||||
systemPrompt: request.systemPrompt,
|
||||
userContext: request.userContext,
|
||||
usage,
|
||||
});
|
||||
|
||||
return { success: true, data: parsed, usage };
|
||||
}
|
||||
}
|
||||
76
pnpm-lock.yaml
generated
76
pnpm-lock.yaml
generated
@@ -260,6 +260,9 @@ settings:
|
||||
importers:
|
||||
.:
|
||||
dependencies:
|
||||
"@langchain/anthropic":
|
||||
specifier: ^0.3.11
|
||||
version: 0.3.34(zod@4.4.3)
|
||||
"@langchain/google-genai":
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0(@langchain/core@1.2.1(openai@6.45.0(ws@8.21.0)(zod@4.4.3))(ws@8.21.0))
|
||||
@@ -564,6 +567,18 @@ packages:
|
||||
integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==,
|
||||
}
|
||||
|
||||
"@anthropic-ai/sdk@0.65.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-zIdPOcrCVEI8t3Di40nH4z9EoeyGZfXbYSvWdDLsB/KkaSYMnEgC7gmcgWu83g2NTn1ZTpbMvpdttWDGGIk6zw==,
|
||||
}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
zod: ^3.25.0 || ^4.0.0
|
||||
peerDependenciesMeta:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
"@astrojs/compiler-binding-darwin-arm64@0.3.0":
|
||||
resolution:
|
||||
{
|
||||
@@ -2052,6 +2067,15 @@ packages:
|
||||
integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==,
|
||||
}
|
||||
|
||||
"@langchain/anthropic@0.3.34":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-8bOW1A2VHRCjbzdYElrjxutKNs9NSIxYRGtR+OJWVzluMqoKKh2NmmFrpPizEyqCUEG2tTq5xt6XA1lwfqMJRA==,
|
||||
}
|
||||
engines: { node: ">=18" }
|
||||
peerDependencies:
|
||||
"@langchain/core": ">=0.3.58 <0.4.0"
|
||||
|
||||
"@langchain/core@1.2.1":
|
||||
resolution:
|
||||
{
|
||||
@@ -5697,6 +5721,13 @@ packages:
|
||||
integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==,
|
||||
}
|
||||
|
||||
fast-xml-parser@4.5.7:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-a6Qh1RMCNbSrU1+sAyAAZH3rTe+OaWJbNZIq0S+ifZciUUOQtlVxBJwoTUE2bYhysmG/RYyI5WJFIKdBahJdrQ==,
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
fastq@1.20.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -6516,6 +6547,13 @@ packages:
|
||||
integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==,
|
||||
}
|
||||
|
||||
json-schema-to-ts@3.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==,
|
||||
}
|
||||
engines: { node: ">=16" }
|
||||
|
||||
json-schema-traverse@0.4.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -8642,6 +8680,12 @@ packages:
|
||||
}
|
||||
engines: { node: ">=0.10.0" }
|
||||
|
||||
strnum@1.1.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==,
|
||||
}
|
||||
|
||||
style-to-js@1.1.21:
|
||||
resolution:
|
||||
{
|
||||
@@ -8812,6 +8856,12 @@ packages:
|
||||
integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==,
|
||||
}
|
||||
|
||||
ts-algebra@2.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==,
|
||||
}
|
||||
|
||||
ts-api-utils@2.5.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -9487,6 +9537,12 @@ snapshots:
|
||||
package-manager-detector: 1.7.0
|
||||
tinyexec: 1.2.4
|
||||
|
||||
"@anthropic-ai/sdk@0.65.0(zod@4.4.3)":
|
||||
dependencies:
|
||||
json-schema-to-ts: 3.1.1
|
||||
optionalDependencies:
|
||||
zod: 4.4.3
|
||||
|
||||
"@astrojs/compiler-binding-darwin-arm64@0.3.0":
|
||||
optional: true
|
||||
|
||||
@@ -10367,6 +10423,13 @@ snapshots:
|
||||
"@jridgewell/resolve-uri": 3.1.2
|
||||
"@jridgewell/sourcemap-codec": 1.5.5
|
||||
|
||||
"@langchain/anthropic@0.3.34(zod@4.4.3)":
|
||||
dependencies:
|
||||
"@anthropic-ai/sdk": 0.65.0(zod@4.4.3)
|
||||
fast-xml-parser: 4.5.7
|
||||
transitivePeerDependencies:
|
||||
- zod
|
||||
|
||||
"@langchain/core@1.2.1(openai@6.45.0(ws@8.21.0)(zod@4.4.3))(ws@8.21.0)":
|
||||
dependencies:
|
||||
"@cfworker/json-schema": 4.1.1
|
||||
@@ -12869,6 +12932,10 @@ snapshots:
|
||||
dependencies:
|
||||
fast-string-width: 3.0.2
|
||||
|
||||
fast-xml-parser@4.5.7:
|
||||
dependencies:
|
||||
strnum: 1.1.2
|
||||
|
||||
fastq@1.20.1:
|
||||
dependencies:
|
||||
reusify: 1.1.0
|
||||
@@ -13365,6 +13432,11 @@ snapshots:
|
||||
|
||||
json-parse-even-better-errors@2.3.1: {}
|
||||
|
||||
json-schema-to-ts@3.1.1:
|
||||
dependencies:
|
||||
"@babel/runtime": 7.29.7
|
||||
ts-algebra: 2.0.0
|
||||
|
||||
json-schema-traverse@0.4.1: {}
|
||||
|
||||
json-schema-traverse@1.0.0: {}
|
||||
@@ -15052,6 +15124,8 @@ snapshots:
|
||||
|
||||
strip-json-comments@2.0.1: {}
|
||||
|
||||
strnum@1.1.2: {}
|
||||
|
||||
style-to-js@1.1.21:
|
||||
dependencies:
|
||||
style-to-object: 1.0.14
|
||||
@@ -15135,6 +15209,8 @@ snapshots:
|
||||
|
||||
trough@2.2.0: {}
|
||||
|
||||
ts-algebra@2.0.0: {}
|
||||
|
||||
ts-api-utils@2.5.0(typescript@6.0.3):
|
||||
dependencies:
|
||||
typescript: 6.0.3
|
||||
|
||||
Reference in New Issue
Block a user