claudish 4.6.7 → 4.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +256 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32800,6 +32800,8 @@ var init_model_parser = __esm(() => {
|
|
|
32800
32800
|
lc: "ollamacloud",
|
|
32801
32801
|
meta: "ollamacloud",
|
|
32802
32802
|
poe: "poe",
|
|
32803
|
+
litellm: "litellm",
|
|
32804
|
+
ll: "litellm",
|
|
32803
32805
|
ollama: "ollama",
|
|
32804
32806
|
lms: "lmstudio",
|
|
32805
32807
|
lmstudio: "lmstudio",
|
|
@@ -32820,7 +32822,8 @@ var init_model_parser = __esm(() => {
|
|
|
32820
32822
|
"opencode-zen",
|
|
32821
32823
|
"vertex",
|
|
32822
32824
|
"gemini-codeassist",
|
|
32823
|
-
"poe"
|
|
32825
|
+
"poe",
|
|
32826
|
+
"litellm"
|
|
32824
32827
|
]);
|
|
32825
32828
|
LOCAL_PROVIDERS = new Set(["ollama", "lmstudio", "vllm", "mlx"]);
|
|
32826
32829
|
NATIVE_MODEL_PATTERNS = [
|
|
@@ -33041,7 +33044,8 @@ function resolveRemoteProvider(modelId) {
|
|
|
33041
33044
|
ollamacloud: "ollamacloud",
|
|
33042
33045
|
"opencode-zen": "opencode-zen",
|
|
33043
33046
|
vertex: "vertex",
|
|
33044
|
-
"gemini-codeassist": "gemini-codeassist"
|
|
33047
|
+
"gemini-codeassist": "gemini-codeassist",
|
|
33048
|
+
litellm: "litellm"
|
|
33045
33049
|
};
|
|
33046
33050
|
const mappedProviderName = providerNameMap[parsed.provider];
|
|
33047
33051
|
if (mappedProviderName) {
|
|
@@ -33253,6 +33257,20 @@ var getRemoteProviders = () => [
|
|
|
33253
33257
|
supportsJsonMode: false,
|
|
33254
33258
|
supportsReasoning: true
|
|
33255
33259
|
}
|
|
33260
|
+
},
|
|
33261
|
+
{
|
|
33262
|
+
name: "litellm",
|
|
33263
|
+
baseUrl: process.env.LITELLM_BASE_URL || "",
|
|
33264
|
+
apiPath: "/v1/chat/completions",
|
|
33265
|
+
apiKeyEnvVar: "LITELLM_API_KEY",
|
|
33266
|
+
prefixes: ["litellm/", "ll/"],
|
|
33267
|
+
capabilities: {
|
|
33268
|
+
supportsTools: true,
|
|
33269
|
+
supportsVision: true,
|
|
33270
|
+
supportsStreaming: true,
|
|
33271
|
+
supportsJsonMode: true,
|
|
33272
|
+
supportsReasoning: true
|
|
33273
|
+
}
|
|
33256
33274
|
}
|
|
33257
33275
|
];
|
|
33258
33276
|
var init_remote_provider_registry = __esm(() => {
|
|
@@ -33592,6 +33610,11 @@ var init_provider_resolver = __esm(() => {
|
|
|
33592
33610
|
envVar: "ZAI_API_KEY",
|
|
33593
33611
|
description: "Z.AI API Key",
|
|
33594
33612
|
url: "https://z.ai/"
|
|
33613
|
+
},
|
|
33614
|
+
litellm: {
|
|
33615
|
+
envVar: "LITELLM_API_KEY",
|
|
33616
|
+
description: "LiteLLM API Key",
|
|
33617
|
+
url: "https://docs.litellm.ai/"
|
|
33595
33618
|
}
|
|
33596
33619
|
};
|
|
33597
33620
|
PROVIDER_DISPLAY_NAMES = {
|
|
@@ -33607,7 +33630,8 @@ var init_provider_resolver = __esm(() => {
|
|
|
33607
33630
|
"glm-coding": "GLM Coding",
|
|
33608
33631
|
zai: "Z.AI",
|
|
33609
33632
|
ollamacloud: "OllamaCloud",
|
|
33610
|
-
"opencode-zen": "OpenCode Zen"
|
|
33633
|
+
"opencode-zen": "OpenCode Zen",
|
|
33634
|
+
litellm: "LiteLLM"
|
|
33611
33635
|
};
|
|
33612
33636
|
});
|
|
33613
33637
|
|
|
@@ -34890,7 +34914,7 @@ async function fetchGLMCodingModels2() {
|
|
|
34890
34914
|
return [];
|
|
34891
34915
|
}
|
|
34892
34916
|
}
|
|
34893
|
-
var __filename5, __dirname5, VERSION = "4.6.
|
|
34917
|
+
var __filename5, __dirname5, VERSION = "4.6.8", CACHE_MAX_AGE_DAYS3 = 2, MODELS_JSON_PATH, CLAUDISH_CACHE_DIR3, ALL_MODELS_JSON_PATH2;
|
|
34894
34918
|
var init_cli = __esm(() => {
|
|
34895
34919
|
init_config();
|
|
34896
34920
|
init_model_loader();
|
|
@@ -65417,10 +65441,212 @@ var init_ollamacloud_handler = __esm(() => {
|
|
|
65417
65441
|
init_remote_provider_types();
|
|
65418
65442
|
});
|
|
65419
65443
|
|
|
65420
|
-
// src/
|
|
65421
|
-
import {
|
|
65444
|
+
// src/handlers/shared/remote-provider-handler.ts
|
|
65445
|
+
import { writeFileSync as writeFileSync16, mkdirSync as mkdirSync16 } from "node:fs";
|
|
65422
65446
|
import { homedir as homedir19 } from "node:os";
|
|
65423
65447
|
import { join as join20 } from "node:path";
|
|
65448
|
+
|
|
65449
|
+
class RemoteProviderHandler {
|
|
65450
|
+
targetModel;
|
|
65451
|
+
modelName;
|
|
65452
|
+
apiKey;
|
|
65453
|
+
adapterManager;
|
|
65454
|
+
middlewareManager;
|
|
65455
|
+
port;
|
|
65456
|
+
sessionTotalCost = 0;
|
|
65457
|
+
sessionInputTokens = 0;
|
|
65458
|
+
sessionOutputTokens = 0;
|
|
65459
|
+
contextWindow = 200000;
|
|
65460
|
+
CLAUDE_INTERNAL_CONTEXT_MAX = 200000;
|
|
65461
|
+
constructor(targetModel, modelName, apiKey, port) {
|
|
65462
|
+
this.targetModel = targetModel;
|
|
65463
|
+
this.modelName = modelName;
|
|
65464
|
+
this.apiKey = apiKey;
|
|
65465
|
+
this.port = port;
|
|
65466
|
+
this.adapterManager = new AdapterManager(targetModel);
|
|
65467
|
+
this.middlewareManager = new MiddlewareManager;
|
|
65468
|
+
this.middlewareManager.register(new GeminiThoughtSignatureMiddleware);
|
|
65469
|
+
this.middlewareManager.initialize().catch((err) => log(`[Handler:${targetModel}] Middleware init error: ${err}`));
|
|
65470
|
+
}
|
|
65471
|
+
getAdditionalHeaders() {
|
|
65472
|
+
return {};
|
|
65473
|
+
}
|
|
65474
|
+
getApiEndpoint() {
|
|
65475
|
+
const config3 = this.getProviderConfig();
|
|
65476
|
+
return `${config3.baseUrl}${config3.apiPath}`;
|
|
65477
|
+
}
|
|
65478
|
+
supportsReasoning() {
|
|
65479
|
+
return false;
|
|
65480
|
+
}
|
|
65481
|
+
writeTokenFile(input, output) {
|
|
65482
|
+
try {
|
|
65483
|
+
const total = input + output;
|
|
65484
|
+
const leftPct = this.contextWindow > 0 ? Math.max(0, Math.min(100, Math.round((this.contextWindow - total) / this.contextWindow * 100))) : 100;
|
|
65485
|
+
const pricing = this.getPricing();
|
|
65486
|
+
const data = {
|
|
65487
|
+
input_tokens: input,
|
|
65488
|
+
output_tokens: output,
|
|
65489
|
+
total_tokens: total,
|
|
65490
|
+
total_cost: this.sessionTotalCost,
|
|
65491
|
+
context_window: this.contextWindow,
|
|
65492
|
+
context_left_percent: leftPct,
|
|
65493
|
+
is_free: pricing.isFree || false,
|
|
65494
|
+
is_estimated: pricing.isEstimate || false,
|
|
65495
|
+
provider_name: this.getProviderName(),
|
|
65496
|
+
model_name: this.modelName,
|
|
65497
|
+
updated_at: Date.now()
|
|
65498
|
+
};
|
|
65499
|
+
const claudishDir = join20(homedir19(), ".claudish");
|
|
65500
|
+
mkdirSync16(claudishDir, { recursive: true });
|
|
65501
|
+
writeFileSync16(join20(claudishDir, `tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
|
|
65502
|
+
} catch (e) {
|
|
65503
|
+
log(`[Handler] Error writing token file: ${e}`);
|
|
65504
|
+
}
|
|
65505
|
+
}
|
|
65506
|
+
updateTokenTracking(inputTokens, outputTokens) {
|
|
65507
|
+
this.sessionInputTokens = inputTokens;
|
|
65508
|
+
this.sessionOutputTokens += outputTokens;
|
|
65509
|
+
const pricing = this.getPricing();
|
|
65510
|
+
const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
65511
|
+
this.sessionTotalCost += cost;
|
|
65512
|
+
this.writeTokenFile(inputTokens, this.sessionOutputTokens);
|
|
65513
|
+
}
|
|
65514
|
+
convertMessages(claudeRequest) {
|
|
65515
|
+
return convertMessagesToOpenAI(claudeRequest, this.targetModel, filterIdentity);
|
|
65516
|
+
}
|
|
65517
|
+
convertTools(claudeRequest) {
|
|
65518
|
+
return convertToolsToOpenAI(claudeRequest);
|
|
65519
|
+
}
|
|
65520
|
+
handleStreamingResponse(c, response, adapter, claudeRequest, toolNameMap) {
|
|
65521
|
+
return createStreamingResponseHandler(c, response, adapter, this.targetModel, this.middlewareManager, (input, output) => this.updateTokenTracking(input, output), claudeRequest.tools, toolNameMap);
|
|
65522
|
+
}
|
|
65523
|
+
async handle(c, payload) {
|
|
65524
|
+
const config3 = this.getProviderConfig();
|
|
65525
|
+
const { claudeRequest, droppedParams } = transformOpenAIToClaude(payload);
|
|
65526
|
+
const messages = this.convertMessages(claudeRequest);
|
|
65527
|
+
const tools = this.convertTools(claudeRequest);
|
|
65528
|
+
const systemPromptLength = typeof claudeRequest.system === "string" ? claudeRequest.system.length : 0;
|
|
65529
|
+
logStructured(`${config3.name} Request`, {
|
|
65530
|
+
targetModel: this.targetModel,
|
|
65531
|
+
originalModel: payload.model,
|
|
65532
|
+
messageCount: messages.length,
|
|
65533
|
+
toolCount: tools.length,
|
|
65534
|
+
systemPromptLength,
|
|
65535
|
+
maxTokens: claudeRequest.max_tokens
|
|
65536
|
+
});
|
|
65537
|
+
if (getLogLevel() === "debug") {
|
|
65538
|
+
const lastUserMsg = messages.filter((m) => m.role === "user").pop();
|
|
65539
|
+
if (lastUserMsg) {
|
|
65540
|
+
const content = typeof lastUserMsg.content === "string" ? lastUserMsg.content : JSON.stringify(lastUserMsg.content);
|
|
65541
|
+
log(`[${config3.name}] Last user message: ${truncateContent(content, 500)}`);
|
|
65542
|
+
}
|
|
65543
|
+
if (tools.length > 0) {
|
|
65544
|
+
const toolNames = tools.map((t) => t.function?.name || t.name).join(", ");
|
|
65545
|
+
log(`[${config3.name}] Tools: ${toolNames}`);
|
|
65546
|
+
}
|
|
65547
|
+
}
|
|
65548
|
+
const requestPayload = this.buildRequestPayload(claudeRequest, messages, tools);
|
|
65549
|
+
const adapter = this.adapterManager.getAdapter();
|
|
65550
|
+
if (typeof adapter.reset === "function")
|
|
65551
|
+
adapter.reset();
|
|
65552
|
+
adapter.prepareRequest(requestPayload, claudeRequest);
|
|
65553
|
+
const toolNameMap = adapter.getToolNameMap();
|
|
65554
|
+
await this.middlewareManager.beforeRequest({
|
|
65555
|
+
modelId: this.targetModel,
|
|
65556
|
+
messages,
|
|
65557
|
+
tools,
|
|
65558
|
+
stream: true
|
|
65559
|
+
});
|
|
65560
|
+
const endpoint = this.getApiEndpoint();
|
|
65561
|
+
const headers = {
|
|
65562
|
+
"Content-Type": "application/json",
|
|
65563
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
65564
|
+
...this.getAdditionalHeaders()
|
|
65565
|
+
};
|
|
65566
|
+
log(`[${config3.name}] Calling API: ${endpoint}`);
|
|
65567
|
+
const response = await fetch(endpoint, {
|
|
65568
|
+
method: "POST",
|
|
65569
|
+
headers,
|
|
65570
|
+
body: JSON.stringify(requestPayload)
|
|
65571
|
+
});
|
|
65572
|
+
log(`[${config3.name}] Response status: ${response.status}`);
|
|
65573
|
+
if (!response.ok) {
|
|
65574
|
+
const errorText = await response.text();
|
|
65575
|
+
log(`[${config3.name}] Error: ${errorText}`);
|
|
65576
|
+
return c.json({ error: errorText }, response.status);
|
|
65577
|
+
}
|
|
65578
|
+
if (droppedParams.length > 0) {
|
|
65579
|
+
c.header("X-Dropped-Params", droppedParams.join(", "));
|
|
65580
|
+
}
|
|
65581
|
+
return this.handleStreamingResponse(c, response, adapter, claudeRequest, toolNameMap);
|
|
65582
|
+
}
|
|
65583
|
+
async shutdown() {}
|
|
65584
|
+
}
|
|
65585
|
+
var init_remote_provider_handler = __esm(() => {
|
|
65586
|
+
init_adapter_manager();
|
|
65587
|
+
init_middleware();
|
|
65588
|
+
init_transform();
|
|
65589
|
+
init_logger();
|
|
65590
|
+
init_openai_compat();
|
|
65591
|
+
});
|
|
65592
|
+
|
|
65593
|
+
// src/handlers/litellm-handler.ts
|
|
65594
|
+
var LiteLLMHandler;
|
|
65595
|
+
var init_litellm_handler = __esm(() => {
|
|
65596
|
+
init_remote_provider_handler();
|
|
65597
|
+
LiteLLMHandler = class LiteLLMHandler extends RemoteProviderHandler {
|
|
65598
|
+
baseUrl;
|
|
65599
|
+
constructor(targetModel, modelName, apiKey, port, baseUrl) {
|
|
65600
|
+
super(targetModel, modelName, apiKey, port);
|
|
65601
|
+
this.baseUrl = baseUrl;
|
|
65602
|
+
}
|
|
65603
|
+
getProviderConfig() {
|
|
65604
|
+
return {
|
|
65605
|
+
name: "litellm",
|
|
65606
|
+
baseUrl: this.baseUrl,
|
|
65607
|
+
apiPath: "/v1/chat/completions",
|
|
65608
|
+
apiKeyEnvVar: "LITELLM_API_KEY"
|
|
65609
|
+
};
|
|
65610
|
+
}
|
|
65611
|
+
getPricing() {
|
|
65612
|
+
return {
|
|
65613
|
+
inputCostPer1M: 1,
|
|
65614
|
+
outputCostPer1M: 4,
|
|
65615
|
+
isEstimate: true
|
|
65616
|
+
};
|
|
65617
|
+
}
|
|
65618
|
+
getProviderName() {
|
|
65619
|
+
return "LiteLLM";
|
|
65620
|
+
}
|
|
65621
|
+
buildRequestPayload(claudeRequest, messages, tools) {
|
|
65622
|
+
const payload = {
|
|
65623
|
+
model: this.modelName,
|
|
65624
|
+
messages,
|
|
65625
|
+
temperature: claudeRequest.temperature ?? 1,
|
|
65626
|
+
stream: true,
|
|
65627
|
+
stream_options: { include_usage: true },
|
|
65628
|
+
max_tokens: claudeRequest.max_tokens
|
|
65629
|
+
};
|
|
65630
|
+
if (tools.length > 0) {
|
|
65631
|
+
payload.tools = tools;
|
|
65632
|
+
}
|
|
65633
|
+
if (claudeRequest.tool_choice) {
|
|
65634
|
+
const { type, name } = claudeRequest.tool_choice;
|
|
65635
|
+
if (type === "tool" && name) {
|
|
65636
|
+
payload.tool_choice = { type: "function", function: { name } };
|
|
65637
|
+
} else if (type === "auto" || type === "none") {
|
|
65638
|
+
payload.tool_choice = type;
|
|
65639
|
+
}
|
|
65640
|
+
}
|
|
65641
|
+
return payload;
|
|
65642
|
+
}
|
|
65643
|
+
};
|
|
65644
|
+
});
|
|
65645
|
+
|
|
65646
|
+
// src/services/pricing-cache.ts
|
|
65647
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync17, existsSync as existsSync12, mkdirSync as mkdirSync17, statSync } from "node:fs";
|
|
65648
|
+
import { homedir as homedir20 } from "node:os";
|
|
65649
|
+
import { join as join21 } from "node:path";
|
|
65424
65650
|
function getDynamicPricingSync(provider, modelName) {
|
|
65425
65651
|
if (provider === "openrouter") {
|
|
65426
65652
|
const direct = pricingMap.get(modelName);
|
|
@@ -65502,12 +65728,12 @@ function loadDiskCache() {
|
|
|
65502
65728
|
}
|
|
65503
65729
|
function saveDiskCache() {
|
|
65504
65730
|
try {
|
|
65505
|
-
|
|
65731
|
+
mkdirSync17(CACHE_DIR, { recursive: true });
|
|
65506
65732
|
const data = {};
|
|
65507
65733
|
for (const [key, pricing] of pricingMap) {
|
|
65508
65734
|
data[key] = pricing;
|
|
65509
65735
|
}
|
|
65510
|
-
|
|
65736
|
+
writeFileSync17(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
65511
65737
|
} catch (error46) {
|
|
65512
65738
|
log(`[PricingCache] Error saving disk cache: ${error46}`);
|
|
65513
65739
|
}
|
|
@@ -65537,8 +65763,8 @@ var init_pricing_cache = __esm(() => {
|
|
|
65537
65763
|
init_model_loader();
|
|
65538
65764
|
init_remote_provider_types();
|
|
65539
65765
|
pricingMap = new Map;
|
|
65540
|
-
CACHE_DIR =
|
|
65541
|
-
CACHE_FILE =
|
|
65766
|
+
CACHE_DIR = join21(homedir20(), ".claudish");
|
|
65767
|
+
CACHE_FILE = join21(CACHE_DIR, "pricing-cache.json");
|
|
65542
65768
|
CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
65543
65769
|
PROVIDER_TO_OR_PREFIX = {
|
|
65544
65770
|
openai: ["openai/"],
|
|
@@ -65660,6 +65886,15 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
|
|
|
65660
65886
|
} else if (resolved.provider.name === "ollamacloud") {
|
|
65661
65887
|
handler = new OllamaCloudHandler(resolved.provider, resolved.modelName, apiKey, port);
|
|
65662
65888
|
log(`[Proxy] Created OllamaCloud handler: ${resolved.modelName}`);
|
|
65889
|
+
} else if (resolved.provider.name === "litellm") {
|
|
65890
|
+
if (!resolved.provider.baseUrl) {
|
|
65891
|
+
console.error("Error: LITELLM_BASE_URL or --litellm-url is required for LiteLLM provider.");
|
|
65892
|
+
console.error("Set it with: export LITELLM_BASE_URL='https://your-litellm-instance.com'");
|
|
65893
|
+
console.error("Or use: claudish --litellm-url https://your-instance.com --model litellm@model 'task'");
|
|
65894
|
+
return null;
|
|
65895
|
+
}
|
|
65896
|
+
handler = new LiteLLMHandler(targetModel, resolved.modelName, apiKey, port, resolved.provider.baseUrl);
|
|
65897
|
+
log(`[Proxy] Created LiteLLM handler: ${resolved.modelName} (${resolved.provider.baseUrl})`);
|
|
65663
65898
|
} else if (resolved.provider.name === "vertex") {
|
|
65664
65899
|
const hasApiKey = !!process.env.VERTEX_API_KEY;
|
|
65665
65900
|
const vertexConfig = getVertexConfig();
|
|
@@ -65794,6 +66029,7 @@ var init_proxy_server = __esm(() => {
|
|
|
65794
66029
|
init_vertex_oauth_handler();
|
|
65795
66030
|
init_poe_handler();
|
|
65796
66031
|
init_ollamacloud_handler();
|
|
66032
|
+
init_litellm_handler();
|
|
65797
66033
|
init_provider_registry();
|
|
65798
66034
|
init_model_parser();
|
|
65799
66035
|
init_remote_provider_registry();
|
|
@@ -65811,9 +66047,9 @@ __export(exports_update_checker, {
|
|
|
65811
66047
|
checkForUpdates: () => checkForUpdates
|
|
65812
66048
|
});
|
|
65813
66049
|
import { execSync } from "node:child_process";
|
|
65814
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
65815
|
-
import { homedir as
|
|
65816
|
-
import { join as
|
|
66050
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync18, readFileSync as readFileSync10, unlinkSync as unlinkSync4, writeFileSync as writeFileSync18 } from "node:fs";
|
|
66051
|
+
import { homedir as homedir21, platform as platform2, tmpdir as tmpdir2 } from "node:os";
|
|
66052
|
+
import { join as join22 } from "node:path";
|
|
65817
66053
|
import { createInterface as createInterface2 } from "node:readline";
|
|
65818
66054
|
function getUpdateCommand() {
|
|
65819
66055
|
const scriptPath = process.argv[1] || "";
|
|
@@ -65825,18 +66061,18 @@ function getUpdateCommand() {
|
|
|
65825
66061
|
function getCacheFilePath() {
|
|
65826
66062
|
let cacheDir;
|
|
65827
66063
|
if (isWindows2) {
|
|
65828
|
-
const localAppData = process.env.LOCALAPPDATA ||
|
|
65829
|
-
cacheDir =
|
|
66064
|
+
const localAppData = process.env.LOCALAPPDATA || join22(homedir21(), "AppData", "Local");
|
|
66065
|
+
cacheDir = join22(localAppData, "claudish");
|
|
65830
66066
|
} else {
|
|
65831
|
-
cacheDir =
|
|
66067
|
+
cacheDir = join22(homedir21(), ".cache", "claudish");
|
|
65832
66068
|
}
|
|
65833
66069
|
try {
|
|
65834
66070
|
if (!existsSync13(cacheDir)) {
|
|
65835
|
-
|
|
66071
|
+
mkdirSync18(cacheDir, { recursive: true });
|
|
65836
66072
|
}
|
|
65837
|
-
return
|
|
66073
|
+
return join22(cacheDir, "update-check.json");
|
|
65838
66074
|
} catch {
|
|
65839
|
-
return
|
|
66075
|
+
return join22(tmpdir2(), "claudish-update-check.json");
|
|
65840
66076
|
}
|
|
65841
66077
|
}
|
|
65842
66078
|
function readCache() {
|
|
@@ -65858,7 +66094,7 @@ function writeCache(latestVersion) {
|
|
|
65858
66094
|
lastCheck: Date.now(),
|
|
65859
66095
|
latestVersion
|
|
65860
66096
|
};
|
|
65861
|
-
|
|
66097
|
+
writeFileSync18(cachePath, JSON.stringify(data), "utf-8");
|
|
65862
66098
|
} catch {}
|
|
65863
66099
|
}
|
|
65864
66100
|
function isCacheValid(cache) {
|