claudish 7.23.0 → 7.24.0
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 +604 -572
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -651,7 +651,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
651
651
|
});
|
|
652
652
|
|
|
653
653
|
// src/version.ts
|
|
654
|
-
var VERSION = "7.
|
|
654
|
+
var VERSION = "7.24.0";
|
|
655
655
|
|
|
656
656
|
// src/logger.ts
|
|
657
657
|
var exports_logger = {};
|
|
@@ -30418,11 +30418,550 @@ var init_routing_hints = __esm(() => {
|
|
|
30418
30418
|
};
|
|
30419
30419
|
});
|
|
30420
30420
|
|
|
30421
|
+
// src/providers/all-models-cache.ts
|
|
30422
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
30423
|
+
import { homedir as homedir13 } from "os";
|
|
30424
|
+
import { dirname as dirname4, join as join13 } from "path";
|
|
30425
|
+
function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
|
|
30426
|
+
if (!existsSync12(path))
|
|
30427
|
+
return null;
|
|
30428
|
+
let raw;
|
|
30429
|
+
try {
|
|
30430
|
+
raw = JSON.parse(readFileSync9(path, "utf-8"));
|
|
30431
|
+
} catch {
|
|
30432
|
+
return null;
|
|
30433
|
+
}
|
|
30434
|
+
if (!raw || typeof raw !== "object")
|
|
30435
|
+
return null;
|
|
30436
|
+
const data = raw;
|
|
30437
|
+
const lastUpdated = typeof data.lastUpdated === "string" ? data.lastUpdated : new Date(0).toISOString();
|
|
30438
|
+
const models = Array.isArray(data.models) ? data.models : [];
|
|
30439
|
+
const entries = Array.isArray(data.entries) ? data.entries : [];
|
|
30440
|
+
return {
|
|
30441
|
+
version: 2,
|
|
30442
|
+
lastUpdated,
|
|
30443
|
+
entries,
|
|
30444
|
+
models
|
|
30445
|
+
};
|
|
30446
|
+
}
|
|
30447
|
+
function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
|
|
30448
|
+
const existing = readAllModelsCache(path);
|
|
30449
|
+
const merged = {
|
|
30450
|
+
version: 2,
|
|
30451
|
+
lastUpdated: data.lastUpdated ?? new Date().toISOString(),
|
|
30452
|
+
entries: data.entries ?? existing?.entries ?? [],
|
|
30453
|
+
models: data.models ?? existing?.models ?? []
|
|
30454
|
+
};
|
|
30455
|
+
mkdirSync5(dirname4(path), { recursive: true });
|
|
30456
|
+
writeFileSync6(path, JSON.stringify(merged), "utf-8");
|
|
30457
|
+
}
|
|
30458
|
+
var ALL_MODELS_CACHE_PATH;
|
|
30459
|
+
var init_all_models_cache = __esm(() => {
|
|
30460
|
+
ALL_MODELS_CACHE_PATH = join13(homedir13(), ".claudish", "all-models.json");
|
|
30461
|
+
});
|
|
30462
|
+
|
|
30463
|
+
// src/adapters/model-catalog.ts
|
|
30464
|
+
function lookupModel(modelId, cachePath) {
|
|
30465
|
+
const entry = findCacheEntry(modelId, cachePath);
|
|
30466
|
+
if (!entry || entry.contextWindow === undefined)
|
|
30467
|
+
return;
|
|
30468
|
+
return {
|
|
30469
|
+
modelId: entry.modelId,
|
|
30470
|
+
contextWindow: entry.contextWindow,
|
|
30471
|
+
supportsVision: entry.supportsVision
|
|
30472
|
+
};
|
|
30473
|
+
}
|
|
30474
|
+
function lookupModelForProvider(modelId, provider, cachePath) {
|
|
30475
|
+
const entry = findCacheEntry(modelId, cachePath);
|
|
30476
|
+
if (!entry)
|
|
30477
|
+
return;
|
|
30478
|
+
return entry.aggregators?.find((a) => a.provider === provider)?.contextWindow ?? entry.contextWindow;
|
|
30479
|
+
}
|
|
30480
|
+
function resolveSubscriptionRouting(modelId, provider, cachePath) {
|
|
30481
|
+
const entry = findCacheEntry(modelId, cachePath);
|
|
30482
|
+
if (!entry)
|
|
30483
|
+
return { kind: "unknown" };
|
|
30484
|
+
if (entry.subscriptionPlans?.includes(provider)) {
|
|
30485
|
+
const agg = entry.aggregators?.find((a) => a.provider === provider);
|
|
30486
|
+
return agg?.externalId ? { kind: "serves", externalId: agg.externalId } : { kind: "unknown" };
|
|
30487
|
+
}
|
|
30488
|
+
return isSubscriptionPlan(provider, cachePath) ? { kind: "not-served" } : { kind: "unknown" };
|
|
30489
|
+
}
|
|
30490
|
+
function isSubscriptionPlan(provider, cachePath) {
|
|
30491
|
+
const cache = readAllModelsCache(cachePath);
|
|
30492
|
+
if (!cache)
|
|
30493
|
+
return false;
|
|
30494
|
+
return cache.entries.some((e) => e.subscriptionPlans?.includes(provider));
|
|
30495
|
+
}
|
|
30496
|
+
function findCacheEntry(modelId, cachePath) {
|
|
30497
|
+
if (modelId.includes("@")) {
|
|
30498
|
+
throw new Error(`model-catalog lookup received provider-routed ID "${modelId}" \u2014 callers must strip the "@" prefix before calling`);
|
|
30499
|
+
}
|
|
30500
|
+
const cache = readAllModelsCache(cachePath);
|
|
30501
|
+
if (!cache || cache.entries.length === 0)
|
|
30502
|
+
return;
|
|
30503
|
+
const lower = modelId.toLowerCase();
|
|
30504
|
+
const unprefixed = lower.includes("/") ? lower.substring(lower.lastIndexOf("/") + 1) : lower;
|
|
30505
|
+
for (const entry of cache.entries) {
|
|
30506
|
+
const entryId = entry.modelId.toLowerCase();
|
|
30507
|
+
const exactMatch = entryId === unprefixed || entryId === lower;
|
|
30508
|
+
const aliasMatch = entry.aliases?.some((a) => a.toLowerCase() === unprefixed || a.toLowerCase() === lower);
|
|
30509
|
+
if (exactMatch || aliasMatch) {
|
|
30510
|
+
return entry;
|
|
30511
|
+
}
|
|
30512
|
+
}
|
|
30513
|
+
return;
|
|
30514
|
+
}
|
|
30515
|
+
var init_model_catalog = __esm(() => {
|
|
30516
|
+
init_all_models_cache();
|
|
30517
|
+
});
|
|
30518
|
+
|
|
30519
|
+
// src/providers/auto-route.ts
|
|
30520
|
+
var PROVIDER_TO_PREFIX, DISPLAY_NAMES;
|
|
30521
|
+
var init_auto_route = __esm(() => {
|
|
30522
|
+
init_provider_definitions();
|
|
30523
|
+
PROVIDER_TO_PREFIX = (() => {
|
|
30524
|
+
const map3 = {};
|
|
30525
|
+
for (const def of getAllProviders()) {
|
|
30526
|
+
if (def.shortestPrefix) {
|
|
30527
|
+
map3[def.name] = def.shortestPrefix;
|
|
30528
|
+
}
|
|
30529
|
+
}
|
|
30530
|
+
return map3;
|
|
30531
|
+
})();
|
|
30532
|
+
DISPLAY_NAMES = (() => {
|
|
30533
|
+
const map3 = {};
|
|
30534
|
+
for (const def of getAllProviders()) {
|
|
30535
|
+
map3[def.name] = def.displayName;
|
|
30536
|
+
}
|
|
30537
|
+
return map3;
|
|
30538
|
+
})();
|
|
30539
|
+
});
|
|
30540
|
+
|
|
30541
|
+
// src/providers/default-routing-rules.ts
|
|
30542
|
+
function validateRoutingRulesAgainstProviders(rules) {
|
|
30543
|
+
const unknown3 = [];
|
|
30544
|
+
for (const ruleKey of Object.keys(rules)) {
|
|
30545
|
+
const entries = rules[ruleKey] ?? [];
|
|
30546
|
+
for (const entry of entries) {
|
|
30547
|
+
const atIdx = entry.indexOf("@");
|
|
30548
|
+
const providerRaw = atIdx === -1 ? entry : entry.slice(0, atIdx);
|
|
30549
|
+
const canonical = PROVIDER_SHORTCUTS[providerRaw.toLowerCase()] ?? providerRaw.toLowerCase();
|
|
30550
|
+
if (!getProviderByName(canonical)) {
|
|
30551
|
+
unknown3.push({ rule: ruleKey, entry, provider: canonical });
|
|
30552
|
+
}
|
|
30553
|
+
}
|
|
30554
|
+
}
|
|
30555
|
+
if (unknown3.length > 0) {
|
|
30556
|
+
const lines = unknown3.map((u) => ` rule "${u.rule}" \u2192 entry "${u.entry}" \u2192 unknown provider "${u.provider}"`);
|
|
30557
|
+
throw new Error(`[claudish] DEFAULT_ROUTING_RULES references unknown providers:
|
|
30558
|
+
${lines.join(`
|
|
30559
|
+
`)}`);
|
|
30560
|
+
}
|
|
30561
|
+
}
|
|
30562
|
+
function validateDefaultRoutingRules() {
|
|
30563
|
+
validateRoutingRulesAgainstProviders(DEFAULT_ROUTING_RULES);
|
|
30564
|
+
}
|
|
30565
|
+
var DEFAULT_ROUTING_RULES;
|
|
30566
|
+
var init_default_routing_rules = __esm(() => {
|
|
30567
|
+
init_model_parser();
|
|
30568
|
+
init_provider_definitions();
|
|
30569
|
+
DEFAULT_ROUTING_RULES = {
|
|
30570
|
+
"claude-*": ["native-anthropic", "openrouter"],
|
|
30571
|
+
"gpt-*": ["openai-codex", "openai", "openrouter"],
|
|
30572
|
+
"o1-*": ["openai-codex", "openai", "openrouter"],
|
|
30573
|
+
"o3-*": ["openai-codex", "openai", "openrouter"],
|
|
30574
|
+
"gemini-*": ["gemini-codeassist", "google", "openrouter"],
|
|
30575
|
+
"grok-*": ["x-ai", "openrouter"],
|
|
30576
|
+
"kimi-*": ["kimi-coding", "kimi", "openrouter"],
|
|
30577
|
+
"k3*": ["kimi-coding", "kimi", "openrouter"],
|
|
30578
|
+
"minimax-*": ["minimax-coding", "minimax", "openrouter"],
|
|
30579
|
+
"glm-*": ["glm-coding", "glm", "openrouter"],
|
|
30580
|
+
"z-ai-*": ["z-ai", "openrouter"],
|
|
30581
|
+
"deepseek-*": ["deepseek", "openrouter"],
|
|
30582
|
+
fugu: ["sakana-subscription", "sakana"],
|
|
30583
|
+
"fugu-*": ["sakana-subscription", "sakana"],
|
|
30584
|
+
"*-zen": ["opencode-zen"],
|
|
30585
|
+
"*": ["openrouter"]
|
|
30586
|
+
};
|
|
30587
|
+
validateDefaultRoutingRules();
|
|
30588
|
+
});
|
|
30589
|
+
|
|
30590
|
+
// src/providers/catalog-resolvers/openrouter.ts
|
|
30591
|
+
class OpenRouterCatalogResolver {
|
|
30592
|
+
provider = "openrouter";
|
|
30593
|
+
resolveSync(userInput) {
|
|
30594
|
+
const entries = this._getEntries();
|
|
30595
|
+
if (userInput.includes("/")) {
|
|
30596
|
+
if (entries) {
|
|
30597
|
+
for (const entry of entries) {
|
|
30598
|
+
for (const src of Object.values(entry.sources)) {
|
|
30599
|
+
if (src.externalId === userInput)
|
|
30600
|
+
return userInput;
|
|
30601
|
+
}
|
|
30602
|
+
}
|
|
30603
|
+
}
|
|
30604
|
+
return userInput;
|
|
30605
|
+
}
|
|
30606
|
+
if (entries) {
|
|
30607
|
+
const byModelId = entries.find((e) => e.modelId === userInput);
|
|
30608
|
+
if (byModelId) {
|
|
30609
|
+
const orId = this._getOpenRouterExternalId(byModelId);
|
|
30610
|
+
if (orId)
|
|
30611
|
+
return orId;
|
|
30612
|
+
}
|
|
30613
|
+
const byAlias = entries.find((e) => e.aliases.includes(userInput));
|
|
30614
|
+
if (byAlias) {
|
|
30615
|
+
const orId = this._getOpenRouterExternalId(byAlias);
|
|
30616
|
+
if (orId)
|
|
30617
|
+
return orId;
|
|
30618
|
+
}
|
|
30619
|
+
for (const entry of entries) {
|
|
30620
|
+
for (const src of Object.values(entry.sources)) {
|
|
30621
|
+
if (src.externalId === userInput) {
|
|
30622
|
+
const orId = this._getOpenRouterExternalId(entry);
|
|
30623
|
+
if (orId)
|
|
30624
|
+
return orId;
|
|
30625
|
+
}
|
|
30626
|
+
}
|
|
30627
|
+
}
|
|
30628
|
+
const suffix = `/${userInput}`;
|
|
30629
|
+
for (const entry of entries) {
|
|
30630
|
+
const orId = this._getOpenRouterExternalId(entry);
|
|
30631
|
+
if (orId?.endsWith(suffix))
|
|
30632
|
+
return orId;
|
|
30633
|
+
}
|
|
30634
|
+
const lowerSuffix = `/${userInput.toLowerCase()}`;
|
|
30635
|
+
for (const entry of entries) {
|
|
30636
|
+
const orId = this._getOpenRouterExternalId(entry);
|
|
30637
|
+
if (orId?.toLowerCase().endsWith(lowerSuffix))
|
|
30638
|
+
return orId;
|
|
30639
|
+
}
|
|
30640
|
+
}
|
|
30641
|
+
return null;
|
|
30642
|
+
}
|
|
30643
|
+
async warmCache() {
|
|
30644
|
+
if (!_warmPromise) {
|
|
30645
|
+
_warmPromise = this._fetchAndCache();
|
|
30646
|
+
}
|
|
30647
|
+
await _warmPromise;
|
|
30648
|
+
}
|
|
30649
|
+
isCacheWarm() {
|
|
30650
|
+
return _memCache !== null && _memCache.length > 0;
|
|
30651
|
+
}
|
|
30652
|
+
async ensureReady(timeoutMs) {
|
|
30653
|
+
if (this.isCacheWarm())
|
|
30654
|
+
return;
|
|
30655
|
+
if (!_warmPromise) {
|
|
30656
|
+
_warmPromise = this._fetchAndCache();
|
|
30657
|
+
}
|
|
30658
|
+
await Promise.race([
|
|
30659
|
+
_warmPromise,
|
|
30660
|
+
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
30661
|
+
]);
|
|
30662
|
+
}
|
|
30663
|
+
async refreshCatalog(timeoutMs) {
|
|
30664
|
+
let response;
|
|
30665
|
+
try {
|
|
30666
|
+
response = await fetch(FIREBASE_CATALOG_URL, {
|
|
30667
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
30668
|
+
});
|
|
30669
|
+
} catch (err) {
|
|
30670
|
+
const name = err?.name;
|
|
30671
|
+
const reason = name === "TimeoutError" || name === "AbortError" ? "timeout" : "network";
|
|
30672
|
+
return { kind: "fetch_failed", reason };
|
|
30673
|
+
}
|
|
30674
|
+
if (!response.ok) {
|
|
30675
|
+
return { kind: "fetch_failed", reason: "http_error" };
|
|
30676
|
+
}
|
|
30677
|
+
let data;
|
|
30678
|
+
try {
|
|
30679
|
+
data = await response.json();
|
|
30680
|
+
} catch {
|
|
30681
|
+
return { kind: "fetch_failed", reason: "network" };
|
|
30682
|
+
}
|
|
30683
|
+
if (!Array.isArray(data.models) || data.models.length === 0) {
|
|
30684
|
+
return { kind: "fetch_failed", reason: "empty" };
|
|
30685
|
+
}
|
|
30686
|
+
const backwardCompatModels = [];
|
|
30687
|
+
for (const entry of data.models) {
|
|
30688
|
+
const orSource = entry.sources["openrouter-api"];
|
|
30689
|
+
if (orSource?.externalId) {
|
|
30690
|
+
backwardCompatModels.push({ id: orSource.externalId });
|
|
30691
|
+
}
|
|
30692
|
+
}
|
|
30693
|
+
_memCache = data.models;
|
|
30694
|
+
writeAllModelsCache({
|
|
30695
|
+
entries: data.models,
|
|
30696
|
+
models: backwardCompatModels
|
|
30697
|
+
});
|
|
30698
|
+
_warmPromise = Promise.resolve();
|
|
30699
|
+
return { kind: "refreshed", modelCount: data.models.length };
|
|
30700
|
+
}
|
|
30701
|
+
_getOpenRouterExternalId(entry) {
|
|
30702
|
+
const orSource = entry.sources["openrouter-api"];
|
|
30703
|
+
if (orSource?.externalId)
|
|
30704
|
+
return orSource.externalId;
|
|
30705
|
+
for (const src of Object.values(entry.sources)) {
|
|
30706
|
+
if (src.externalId.includes("/"))
|
|
30707
|
+
return src.externalId;
|
|
30708
|
+
}
|
|
30709
|
+
return null;
|
|
30710
|
+
}
|
|
30711
|
+
_getEntries() {
|
|
30712
|
+
if (_memCache)
|
|
30713
|
+
return _memCache;
|
|
30714
|
+
const cache = readAllModelsCache();
|
|
30715
|
+
if (!cache)
|
|
30716
|
+
return null;
|
|
30717
|
+
if (cache.entries.length > 0) {
|
|
30718
|
+
_memCache = cache.entries;
|
|
30719
|
+
return _memCache;
|
|
30720
|
+
}
|
|
30721
|
+
if (cache.models.length > 0) {
|
|
30722
|
+
_memCache = cache.models.map((m) => ({
|
|
30723
|
+
modelId: m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id,
|
|
30724
|
+
aliases: [],
|
|
30725
|
+
sources: { "openrouter-api": { externalId: m.id } }
|
|
30726
|
+
}));
|
|
30727
|
+
return _memCache;
|
|
30728
|
+
}
|
|
30729
|
+
return null;
|
|
30730
|
+
}
|
|
30731
|
+
async _fetchAndCache() {
|
|
30732
|
+
await this.refreshCatalog(8000);
|
|
30733
|
+
}
|
|
30734
|
+
}
|
|
30735
|
+
var FIREBASE_CATALOG_URL, _memCache = null, _warmPromise = null;
|
|
30736
|
+
var init_openrouter = __esm(() => {
|
|
30737
|
+
init_all_models_cache();
|
|
30738
|
+
FIREBASE_CATALOG_URL = process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000";
|
|
30739
|
+
});
|
|
30740
|
+
|
|
30741
|
+
// src/providers/model-catalog-resolver.ts
|
|
30742
|
+
function registerResolver(resolver) {
|
|
30743
|
+
RESOLVER_REGISTRY.set(resolver.provider, resolver);
|
|
30744
|
+
}
|
|
30745
|
+
function getResolver(provider) {
|
|
30746
|
+
return RESOLVER_REGISTRY.get(provider) ?? null;
|
|
30747
|
+
}
|
|
30748
|
+
function resolveModelNameSync(userInput, targetProvider) {
|
|
30749
|
+
if (targetProvider !== "openrouter" && userInput.includes("/")) {
|
|
30750
|
+
return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
|
|
30751
|
+
}
|
|
30752
|
+
const resolver = getResolver(targetProvider);
|
|
30753
|
+
if (!resolver) {
|
|
30754
|
+
return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
|
|
30755
|
+
}
|
|
30756
|
+
const resolved = resolver.resolveSync(userInput);
|
|
30757
|
+
if (!resolved || resolved === userInput) {
|
|
30758
|
+
return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
|
|
30759
|
+
}
|
|
30760
|
+
return {
|
|
30761
|
+
resolvedId: resolved,
|
|
30762
|
+
wasResolved: true,
|
|
30763
|
+
sourceLabel: `${targetProvider} catalog`
|
|
30764
|
+
};
|
|
30765
|
+
}
|
|
30766
|
+
function logResolution(userInput, result, quiet = false) {
|
|
30767
|
+
if (result.wasResolved && !quiet) {
|
|
30768
|
+
process.stderr.write(`[Model] Resolved "${userInput}" \u2192 "${result.resolvedId}" (${result.sourceLabel})
|
|
30769
|
+
`);
|
|
30770
|
+
}
|
|
30771
|
+
}
|
|
30772
|
+
async function ensureCatalogReady(provider, timeoutMs = 5000) {
|
|
30773
|
+
const resolver = getResolver(provider);
|
|
30774
|
+
if (!resolver || resolver.isCacheWarm())
|
|
30775
|
+
return;
|
|
30776
|
+
await resolver.ensureReady(timeoutMs);
|
|
30777
|
+
}
|
|
30778
|
+
async function warmAllCatalogs(providers) {
|
|
30779
|
+
const targets = providers ? [...RESOLVER_REGISTRY.entries()].filter(([k]) => providers.includes(k)) : [...RESOLVER_REGISTRY.entries()];
|
|
30780
|
+
await Promise.allSettled(targets.map(([, r]) => r.warmCache()));
|
|
30781
|
+
}
|
|
30782
|
+
var RESOLVER_REGISTRY;
|
|
30783
|
+
var init_model_catalog_resolver = __esm(() => {
|
|
30784
|
+
init_openrouter();
|
|
30785
|
+
RESOLVER_REGISTRY = new Map;
|
|
30786
|
+
[
|
|
30787
|
+
new OpenRouterCatalogResolver
|
|
30788
|
+
].forEach(registerResolver);
|
|
30789
|
+
});
|
|
30790
|
+
|
|
30791
|
+
// src/providers/routing-rules.ts
|
|
30792
|
+
function mergeRoutingRules(defaults, global_, local) {
|
|
30793
|
+
return { ...defaults, ...global_, ...local };
|
|
30794
|
+
}
|
|
30795
|
+
function loadRoutingRules() {
|
|
30796
|
+
const local = loadLocalConfig()?.routing ?? {};
|
|
30797
|
+
const global_ = loadConfig().routing ?? {};
|
|
30798
|
+
validateRoutingRules(local);
|
|
30799
|
+
validateRoutingRules(global_);
|
|
30800
|
+
return mergeRoutingRules(DEFAULT_ROUTING_RULES, global_, local);
|
|
30801
|
+
}
|
|
30802
|
+
function validateRoutingRules(rules) {
|
|
30803
|
+
const seenLower = new Map;
|
|
30804
|
+
for (const key of Object.keys(rules)) {
|
|
30805
|
+
if (key !== "*" && (key.match(/\*/g) || []).length > 1) {
|
|
30806
|
+
console.error(`[claudish] Warning: routing pattern "${key}" has multiple wildcards \u2014 only single * is supported. This pattern may not match as expected.`);
|
|
30807
|
+
}
|
|
30808
|
+
const lower = key.toLowerCase();
|
|
30809
|
+
const prior = seenLower.get(lower);
|
|
30810
|
+
if (prior !== undefined && prior !== key) {
|
|
30811
|
+
console.error(`[claudish] Warning: routing patterns "${prior}" and "${key}" collide case-insensitively. Matching is case-insensitive, so one will silently shadow the other. Pick one casing and remove the duplicate.`);
|
|
30812
|
+
} else {
|
|
30813
|
+
seenLower.set(lower, key);
|
|
30814
|
+
}
|
|
30815
|
+
}
|
|
30816
|
+
}
|
|
30817
|
+
function matchRoutingRule(modelName, rules) {
|
|
30818
|
+
const lowered = modelName.toLowerCase();
|
|
30819
|
+
for (const [key, entries] of Object.entries(rules)) {
|
|
30820
|
+
if (!key.includes("*") && key.toLowerCase() === lowered)
|
|
30821
|
+
return entries;
|
|
30822
|
+
}
|
|
30823
|
+
const globKeys = Object.keys(rules).filter((k) => k !== "*" && k.includes("*")).sort((a, b) => b.length - a.length);
|
|
30824
|
+
for (const pattern of globKeys) {
|
|
30825
|
+
if (globMatch(pattern, modelName))
|
|
30826
|
+
return rules[pattern];
|
|
30827
|
+
}
|
|
30828
|
+
if (rules["*"] !== undefined)
|
|
30829
|
+
return rules["*"];
|
|
30830
|
+
return null;
|
|
30831
|
+
}
|
|
30832
|
+
function buildRoutingChain(entries, originalModelName, cachePath) {
|
|
30833
|
+
const routes = [];
|
|
30834
|
+
for (const entry of entries) {
|
|
30835
|
+
const atIdx = entry.indexOf("@");
|
|
30836
|
+
let providerRaw;
|
|
30837
|
+
let modelName;
|
|
30838
|
+
if (atIdx !== -1) {
|
|
30839
|
+
providerRaw = entry.slice(0, atIdx);
|
|
30840
|
+
modelName = entry.slice(atIdx + 1);
|
|
30841
|
+
} else {
|
|
30842
|
+
providerRaw = entry;
|
|
30843
|
+
modelName = originalModelName;
|
|
30844
|
+
}
|
|
30845
|
+
const provider = PROVIDER_SHORTCUTS[providerRaw.toLowerCase()] ?? providerRaw.toLowerCase();
|
|
30846
|
+
if (atIdx === -1) {
|
|
30847
|
+
const routing = resolveSubscriptionRouting(modelName, provider, cachePath);
|
|
30848
|
+
if (routing.kind === "not-served")
|
|
30849
|
+
continue;
|
|
30850
|
+
if (routing.kind === "serves")
|
|
30851
|
+
modelName = routing.externalId;
|
|
30852
|
+
}
|
|
30853
|
+
let modelSpec;
|
|
30854
|
+
if (provider === "openrouter") {
|
|
30855
|
+
const resolution = resolveModelNameSync(modelName, "openrouter");
|
|
30856
|
+
modelSpec = resolution.resolvedId;
|
|
30857
|
+
} else {
|
|
30858
|
+
const prefix = PROVIDER_TO_PREFIX[provider] ?? provider;
|
|
30859
|
+
modelSpec = `${prefix}@${modelName}`;
|
|
30860
|
+
}
|
|
30861
|
+
const displayName = DISPLAY_NAMES[provider] ?? provider;
|
|
30862
|
+
routes.push({ provider, modelSpec, displayName });
|
|
30863
|
+
}
|
|
30864
|
+
return routes;
|
|
30865
|
+
}
|
|
30866
|
+
function globMatch(pattern, value) {
|
|
30867
|
+
const star = pattern.indexOf("*");
|
|
30868
|
+
const p = pattern.toLowerCase();
|
|
30869
|
+
const v = value.toLowerCase();
|
|
30870
|
+
if (star === -1)
|
|
30871
|
+
return p === v;
|
|
30872
|
+
const prefix = p.slice(0, star);
|
|
30873
|
+
const suffix = p.slice(star + 1);
|
|
30874
|
+
return v.startsWith(prefix) && v.endsWith(suffix) && v.length >= prefix.length + suffix.length;
|
|
30875
|
+
}
|
|
30876
|
+
async function hasCredentialsForProvider(provider) {
|
|
30877
|
+
return credentials.isAvailable(provider);
|
|
30878
|
+
}
|
|
30879
|
+
async function routeExplicit(modelSpec, model, provider, cachePath) {
|
|
30880
|
+
if (!await hasCredentialsForProvider(provider)) {
|
|
30881
|
+
return {
|
|
30882
|
+
kind: "no-route",
|
|
30883
|
+
reason: `No credentials configured for "${provider}".`,
|
|
30884
|
+
hint: buildCredentialHint(model, [provider]) ?? undefined
|
|
30885
|
+
};
|
|
30886
|
+
}
|
|
30887
|
+
const built = buildRoutingChain([modelSpec], model, cachePath)[0];
|
|
30888
|
+
if (!built) {
|
|
30889
|
+
return {
|
|
30890
|
+
kind: "no-route",
|
|
30891
|
+
reason: `Could not build a route for "${modelSpec}".`
|
|
30892
|
+
};
|
|
30893
|
+
}
|
|
30894
|
+
return { kind: "ok", primary: built, fallbacks: [] };
|
|
30895
|
+
}
|
|
30896
|
+
async function routeBare(model, nativeProvider, rules, defaultProvider, cachePath) {
|
|
30897
|
+
const matched = matchRoutingRule(model, rules) ?? [];
|
|
30898
|
+
const entries = [...matched];
|
|
30899
|
+
if (defaultProvider && defaultProvider.length > 0) {
|
|
30900
|
+
const canonicalDefault = PROVIDER_SHORTCUTS[defaultProvider.toLowerCase()] ?? defaultProvider.toLowerCase();
|
|
30901
|
+
const alreadyPresent = entries.some((e) => {
|
|
30902
|
+
const atIdx = e.indexOf("@");
|
|
30903
|
+
const providerRaw = atIdx === -1 ? e : e.slice(0, atIdx);
|
|
30904
|
+
const canonical = PROVIDER_SHORTCUTS[providerRaw.toLowerCase()] ?? providerRaw.toLowerCase();
|
|
30905
|
+
return canonical === canonicalDefault;
|
|
30906
|
+
});
|
|
30907
|
+
if (!alreadyPresent)
|
|
30908
|
+
entries.push(defaultProvider);
|
|
30909
|
+
}
|
|
30910
|
+
if (entries.length === 0) {
|
|
30911
|
+
return {
|
|
30912
|
+
kind: "no-route",
|
|
30913
|
+
reason: `No routing rule matched "${model}".`,
|
|
30914
|
+
hint: buildCredentialHint(model, [nativeProvider]) ?? undefined
|
|
30915
|
+
};
|
|
30916
|
+
}
|
|
30917
|
+
const candidates = buildRoutingChain(entries, model, cachePath);
|
|
30918
|
+
const credentialed = [];
|
|
30919
|
+
const skipped = [];
|
|
30920
|
+
const checks4 = await Promise.all(candidates.map((candidate) => hasCredentialsForProvider(candidate.provider)));
|
|
30921
|
+
candidates.forEach((candidate, i) => {
|
|
30922
|
+
if (checks4[i]) {
|
|
30923
|
+
credentialed.push(candidate);
|
|
30924
|
+
} else {
|
|
30925
|
+
skipped.push(candidate.provider);
|
|
30926
|
+
}
|
|
30927
|
+
});
|
|
30928
|
+
if (credentialed.length === 0) {
|
|
30929
|
+
return {
|
|
30930
|
+
kind: "no-route",
|
|
30931
|
+
reason: skipped.length > 0 ? `No credentialed providers in chain for "${model}" (tried: ${skipped.join(", ")}).` : `No providers available for "${model}".`,
|
|
30932
|
+
hint: buildCredentialHint(model, skipped) ?? undefined
|
|
30933
|
+
};
|
|
30934
|
+
}
|
|
30935
|
+
const [primary, ...fallbacks] = credentialed;
|
|
30936
|
+
return { kind: "ok", primary, fallbacks };
|
|
30937
|
+
}
|
|
30938
|
+
async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePath) {
|
|
30939
|
+
const parsed = parseModelSpec(modelSpec);
|
|
30940
|
+
if (parsed.isExplicitProvider) {
|
|
30941
|
+
return routeExplicit(modelSpec, parsed.model, parsed.provider, cachePath);
|
|
30942
|
+
}
|
|
30943
|
+
const rules = rulesOverride ?? loadRoutingRules();
|
|
30944
|
+
const defaultProvider = defaultProviderOverride !== undefined ? defaultProviderOverride : rulesOverride !== undefined ? undefined : loadConfig().defaultProvider;
|
|
30945
|
+
return routeBare(parsed.model, parsed.provider, rules, defaultProvider, cachePath);
|
|
30946
|
+
}
|
|
30947
|
+
var init_routing_rules = __esm(() => {
|
|
30948
|
+
init_model_catalog();
|
|
30949
|
+
init_authority();
|
|
30950
|
+
init_profile_config();
|
|
30951
|
+
init_auto_route();
|
|
30952
|
+
init_default_routing_rules();
|
|
30953
|
+
init_model_catalog_resolver();
|
|
30954
|
+
init_model_parser();
|
|
30955
|
+
init_model_parser();
|
|
30956
|
+
init_routing_hints();
|
|
30957
|
+
});
|
|
30958
|
+
|
|
30421
30959
|
// src/providers/provider-resolver.ts
|
|
30422
30960
|
var exports_provider_resolver = {};
|
|
30423
30961
|
__export(exports_provider_resolver, {
|
|
30424
30962
|
validateApiKeysForModels: () => validateApiKeysForModels,
|
|
30425
30963
|
resolveModelProvider: () => resolveModelProvider,
|
|
30964
|
+
rescueRoutableResolutions: () => rescueRoutableResolutions,
|
|
30426
30965
|
requiresOpenRouterKey: () => requiresOpenRouterKey,
|
|
30427
30966
|
isLocalModel: () => isLocalModel,
|
|
30428
30967
|
getMissingKeysError: () => getMissingKeysError,
|
|
@@ -30594,8 +31133,20 @@ async function validateApiKeysForModels(models) {
|
|
|
30594
31133
|
return;
|
|
30595
31134
|
r.apiKeyAvailable = await credentials.isAvailable(r.catalogName);
|
|
30596
31135
|
}));
|
|
31136
|
+
await rescueRoutableResolutions(resolutions);
|
|
30597
31137
|
return resolutions;
|
|
30598
31138
|
}
|
|
31139
|
+
async function rescueRoutableResolutions(resolutions, router = route) {
|
|
31140
|
+
await Promise.all(resolutions.map(async (r) => {
|
|
31141
|
+
if (!r.requiredApiKeyEnvVar || r.apiKeyAvailable)
|
|
31142
|
+
return;
|
|
31143
|
+
try {
|
|
31144
|
+
const plan = await router(r.fullModelId);
|
|
31145
|
+
if (plan.kind === "ok")
|
|
31146
|
+
r.apiKeyAvailable = true;
|
|
31147
|
+
} catch {}
|
|
31148
|
+
}));
|
|
31149
|
+
}
|
|
30599
31150
|
function getMissingKeyResolutions(resolutions) {
|
|
30600
31151
|
return resolutions.filter((r) => r.requiredApiKeyEnvVar && !r.apiKeyAvailable);
|
|
30601
31152
|
}
|
|
@@ -30709,6 +31260,7 @@ var init_provider_resolver = __esm(() => {
|
|
|
30709
31260
|
init_remote_provider_registry();
|
|
30710
31261
|
init_onepassword();
|
|
30711
31262
|
init_routing_hints();
|
|
31263
|
+
init_routing_rules();
|
|
30712
31264
|
API_KEY_INFO = new Proxy({}, {
|
|
30713
31265
|
get(_target, prop) {
|
|
30714
31266
|
return getApiKeyInfoForProvider(prop);
|
|
@@ -30990,9 +31542,9 @@ var init_signal_watcher = __esm(() => {
|
|
|
30990
31542
|
// src/channel/session-manager.ts
|
|
30991
31543
|
import { spawn } from "child_process";
|
|
30992
31544
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
30993
|
-
import { createWriteStream, mkdirSync as
|
|
30994
|
-
import { homedir as
|
|
30995
|
-
import { join as
|
|
31545
|
+
import { createWriteStream, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
31546
|
+
import { homedir as homedir14 } from "os";
|
|
31547
|
+
import { join as join14 } from "path";
|
|
30996
31548
|
|
|
30997
31549
|
class SessionManager {
|
|
30998
31550
|
sessions = new Map;
|
|
@@ -31012,10 +31564,10 @@ class SessionManager {
|
|
|
31012
31564
|
const sessionId = randomUUID2().slice(0, 8);
|
|
31013
31565
|
const timeout = Math.min(opts.timeoutSeconds ?? DEFAULT_TIMEOUT, MAX_TIMEOUT);
|
|
31014
31566
|
const startedAt = new Date().toISOString();
|
|
31015
|
-
const sessionDir =
|
|
31016
|
-
|
|
31567
|
+
const sessionDir = join14(homedir14(), ".claudish", "sessions", sessionId);
|
|
31568
|
+
mkdirSync6(sessionDir, { recursive: true });
|
|
31017
31569
|
if (opts.prompt) {
|
|
31018
|
-
|
|
31570
|
+
writeFileSync7(join14(sessionDir, "prompt.md"), opts.prompt, "utf-8");
|
|
31019
31571
|
}
|
|
31020
31572
|
const args = ["--model", opts.model, "-y", "--stdin", "--quiet", ...opts.claudishFlags ?? []];
|
|
31021
31573
|
const proc = spawn("claudish", args, {
|
|
@@ -31042,7 +31594,7 @@ class SessionManager {
|
|
|
31042
31594
|
});
|
|
31043
31595
|
}
|
|
31044
31596
|
});
|
|
31045
|
-
const outputLogStream = createWriteStream(
|
|
31597
|
+
const outputLogStream = createWriteStream(join14(sessionDir, "output.log"));
|
|
31046
31598
|
const entry = {
|
|
31047
31599
|
info: {
|
|
31048
31600
|
sessionId,
|
|
@@ -31089,9 +31641,9 @@ class SessionManager {
|
|
|
31089
31641
|
watcher.processExited(code);
|
|
31090
31642
|
outputLogStream.end();
|
|
31091
31643
|
if (entry.stderr) {
|
|
31092
|
-
|
|
31644
|
+
writeFileSync7(join14(sessionDir, "stderr.log"), entry.stderr, "utf-8");
|
|
31093
31645
|
}
|
|
31094
|
-
|
|
31646
|
+
writeFileSync7(join14(sessionDir, "meta.json"), JSON.stringify(entry.info, null, 2), "utf-8");
|
|
31095
31647
|
this.cleanupSigint();
|
|
31096
31648
|
});
|
|
31097
31649
|
proc.on("error", (err) => {
|
|
@@ -31264,9 +31816,9 @@ var init_cache_ttl = __esm(() => {
|
|
|
31264
31816
|
});
|
|
31265
31817
|
|
|
31266
31818
|
// src/model-loader.ts
|
|
31267
|
-
import { existsSync as
|
|
31268
|
-
import { homedir as
|
|
31269
|
-
import { join as
|
|
31819
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
31820
|
+
import { homedir as homedir15 } from "os";
|
|
31821
|
+
import { join as join15 } from "path";
|
|
31270
31822
|
function groupRecommendedModels(entries) {
|
|
31271
31823
|
const byId = new Map;
|
|
31272
31824
|
for (const entry of entries) {
|
|
@@ -31362,9 +31914,9 @@ async function getRecommendedModels(opts = {}) {
|
|
|
31362
31914
|
if (!forceRefresh && _cachedRecommendedModels) {
|
|
31363
31915
|
return _cachedRecommendedModels;
|
|
31364
31916
|
}
|
|
31365
|
-
if (!forceRefresh &&
|
|
31917
|
+
if (!forceRefresh && existsSync13(RECOMMENDED_MODELS_CACHE_PATH)) {
|
|
31366
31918
|
try {
|
|
31367
|
-
const cacheData = JSON.parse(
|
|
31919
|
+
const cacheData = JSON.parse(readFileSync10(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
|
|
31368
31920
|
if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
|
|
31369
31921
|
_cachedRecommendedModels = cacheData;
|
|
31370
31922
|
return cacheData;
|
|
@@ -31380,9 +31932,9 @@ async function getRecommendedModels(opts = {}) {
|
|
|
31380
31932
|
if (data.models && data.models.length > 0) {
|
|
31381
31933
|
_cachedRecommendedModels = data;
|
|
31382
31934
|
try {
|
|
31383
|
-
const cacheDir =
|
|
31384
|
-
|
|
31385
|
-
|
|
31935
|
+
const cacheDir = join15(homedir15(), ".claudish");
|
|
31936
|
+
mkdirSync7(cacheDir, { recursive: true });
|
|
31937
|
+
writeFileSync8(RECOMMENDED_MODELS_CACHE_PATH, JSON.stringify(data), "utf-8");
|
|
31386
31938
|
} catch {}
|
|
31387
31939
|
return data;
|
|
31388
31940
|
}
|
|
@@ -31393,9 +31945,9 @@ async function getRecommendedModels(opts = {}) {
|
|
|
31393
31945
|
function getRecommendedModelsSync() {
|
|
31394
31946
|
if (_cachedRecommendedModels)
|
|
31395
31947
|
return _cachedRecommendedModels;
|
|
31396
|
-
if (
|
|
31948
|
+
if (existsSync13(RECOMMENDED_MODELS_CACHE_PATH)) {
|
|
31397
31949
|
try {
|
|
31398
|
-
const cacheData = JSON.parse(
|
|
31950
|
+
const cacheData = JSON.parse(readFileSync10(RECOMMENDED_MODELS_CACHE_PATH, "utf-8"));
|
|
31399
31951
|
if (cacheData.models && cacheData.models.length > 0 && isFreshEnough(cacheData)) {
|
|
31400
31952
|
_cachedRecommendedModels = cacheData;
|
|
31401
31953
|
return cacheData;
|
|
@@ -31519,7 +32071,7 @@ var _cachedModelInfo = null, _cachedModelIds = null, _cachedRecommendedModels =
|
|
|
31519
32071
|
var init_model_loader = __esm(() => {
|
|
31520
32072
|
init_cache_ttl();
|
|
31521
32073
|
FIREBASE_RECOMMENDED_URL = `${FIREBASE_BASE_URL}?catalog=recommended`;
|
|
31522
|
-
RECOMMENDED_MODELS_CACHE_PATH =
|
|
32074
|
+
RECOMMENDED_MODELS_CACHE_PATH = join15(homedir15(), ".claudish", "recommended-models-cache.json");
|
|
31523
32075
|
FIREBASE_SLUG_TO_PROVIDER_NAME = {
|
|
31524
32076
|
openai: "openai",
|
|
31525
32077
|
google: "google",
|
|
@@ -31986,10 +32538,10 @@ var init_request = __esm(() => {
|
|
|
31986
32538
|
return this.#matchResult;
|
|
31987
32539
|
}
|
|
31988
32540
|
get matchedRoutes() {
|
|
31989
|
-
return this.#matchResult[0].map(([[,
|
|
32541
|
+
return this.#matchResult[0].map(([[, route2]]) => route2);
|
|
31990
32542
|
}
|
|
31991
32543
|
get routePath() {
|
|
31992
|
-
return this.#matchResult[0].map(([[,
|
|
32544
|
+
return this.#matchResult[0].map(([[, route2]]) => route2)[this.routeIndex].path;
|
|
31993
32545
|
}
|
|
31994
32546
|
};
|
|
31995
32547
|
});
|
|
@@ -32637,7 +33189,7 @@ function buildMatcherFromPreprocessedRoutes(routes) {
|
|
|
32637
33189
|
if (routes.length === 0) {
|
|
32638
33190
|
return nullMatcher;
|
|
32639
33191
|
}
|
|
32640
|
-
const routesWithStaticPathFlag = routes.map((
|
|
33192
|
+
const routesWithStaticPathFlag = routes.map((route2) => [!/\*|\/:/.test(route2[0]), ...route2]).sort(([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length);
|
|
32641
33193
|
const staticMap = /* @__PURE__ */ Object.create(null);
|
|
32642
33194
|
for (let i = 0, j = -1, len = routesWithStaticPathFlag.length;i < len; i++) {
|
|
32643
33195
|
const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
|
|
@@ -33283,104 +33835,6 @@ var init_remote_provider_types = __esm(() => {
|
|
|
33283
33835
|
};
|
|
33284
33836
|
});
|
|
33285
33837
|
|
|
33286
|
-
// src/providers/all-models-cache.ts
|
|
33287
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
33288
|
-
import { homedir as homedir15 } from "os";
|
|
33289
|
-
import { dirname as dirname4, join as join15 } from "path";
|
|
33290
|
-
function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
|
|
33291
|
-
if (!existsSync13(path))
|
|
33292
|
-
return null;
|
|
33293
|
-
let raw2;
|
|
33294
|
-
try {
|
|
33295
|
-
raw2 = JSON.parse(readFileSync10(path, "utf-8"));
|
|
33296
|
-
} catch {
|
|
33297
|
-
return null;
|
|
33298
|
-
}
|
|
33299
|
-
if (!raw2 || typeof raw2 !== "object")
|
|
33300
|
-
return null;
|
|
33301
|
-
const data = raw2;
|
|
33302
|
-
const lastUpdated = typeof data.lastUpdated === "string" ? data.lastUpdated : new Date(0).toISOString();
|
|
33303
|
-
const models = Array.isArray(data.models) ? data.models : [];
|
|
33304
|
-
const entries = Array.isArray(data.entries) ? data.entries : [];
|
|
33305
|
-
return {
|
|
33306
|
-
version: 2,
|
|
33307
|
-
lastUpdated,
|
|
33308
|
-
entries,
|
|
33309
|
-
models
|
|
33310
|
-
};
|
|
33311
|
-
}
|
|
33312
|
-
function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
|
|
33313
|
-
const existing = readAllModelsCache(path);
|
|
33314
|
-
const merged = {
|
|
33315
|
-
version: 2,
|
|
33316
|
-
lastUpdated: data.lastUpdated ?? new Date().toISOString(),
|
|
33317
|
-
entries: data.entries ?? existing?.entries ?? [],
|
|
33318
|
-
models: data.models ?? existing?.models ?? []
|
|
33319
|
-
};
|
|
33320
|
-
mkdirSync7(dirname4(path), { recursive: true });
|
|
33321
|
-
writeFileSync8(path, JSON.stringify(merged), "utf-8");
|
|
33322
|
-
}
|
|
33323
|
-
var ALL_MODELS_CACHE_PATH;
|
|
33324
|
-
var init_all_models_cache = __esm(() => {
|
|
33325
|
-
ALL_MODELS_CACHE_PATH = join15(homedir15(), ".claudish", "all-models.json");
|
|
33326
|
-
});
|
|
33327
|
-
|
|
33328
|
-
// src/adapters/model-catalog.ts
|
|
33329
|
-
function lookupModel(modelId, cachePath) {
|
|
33330
|
-
const entry = findCacheEntry(modelId, cachePath);
|
|
33331
|
-
if (!entry || entry.contextWindow === undefined)
|
|
33332
|
-
return;
|
|
33333
|
-
return {
|
|
33334
|
-
modelId: entry.modelId,
|
|
33335
|
-
contextWindow: entry.contextWindow,
|
|
33336
|
-
supportsVision: entry.supportsVision
|
|
33337
|
-
};
|
|
33338
|
-
}
|
|
33339
|
-
function lookupModelForProvider(modelId, provider, cachePath) {
|
|
33340
|
-
const entry = findCacheEntry(modelId, cachePath);
|
|
33341
|
-
if (!entry)
|
|
33342
|
-
return;
|
|
33343
|
-
return entry.aggregators?.find((a) => a.provider === provider)?.contextWindow ?? entry.contextWindow;
|
|
33344
|
-
}
|
|
33345
|
-
function resolveSubscriptionRouting(modelId, provider, cachePath) {
|
|
33346
|
-
const entry = findCacheEntry(modelId, cachePath);
|
|
33347
|
-
if (!entry)
|
|
33348
|
-
return { kind: "unknown" };
|
|
33349
|
-
if (entry.subscriptionPlans?.includes(provider)) {
|
|
33350
|
-
const agg = entry.aggregators?.find((a) => a.provider === provider);
|
|
33351
|
-
return agg?.externalId ? { kind: "serves", externalId: agg.externalId } : { kind: "unknown" };
|
|
33352
|
-
}
|
|
33353
|
-
return isSubscriptionPlan(provider, cachePath) ? { kind: "not-served" } : { kind: "unknown" };
|
|
33354
|
-
}
|
|
33355
|
-
function isSubscriptionPlan(provider, cachePath) {
|
|
33356
|
-
const cache = readAllModelsCache(cachePath);
|
|
33357
|
-
if (!cache)
|
|
33358
|
-
return false;
|
|
33359
|
-
return cache.entries.some((e) => e.subscriptionPlans?.includes(provider));
|
|
33360
|
-
}
|
|
33361
|
-
function findCacheEntry(modelId, cachePath) {
|
|
33362
|
-
if (modelId.includes("@")) {
|
|
33363
|
-
throw new Error(`model-catalog lookup received provider-routed ID "${modelId}" \u2014 callers must strip the "@" prefix before calling`);
|
|
33364
|
-
}
|
|
33365
|
-
const cache = readAllModelsCache(cachePath);
|
|
33366
|
-
if (!cache || cache.entries.length === 0)
|
|
33367
|
-
return;
|
|
33368
|
-
const lower = modelId.toLowerCase();
|
|
33369
|
-
const unprefixed = lower.includes("/") ? lower.substring(lower.lastIndexOf("/") + 1) : lower;
|
|
33370
|
-
for (const entry of cache.entries) {
|
|
33371
|
-
const entryId = entry.modelId.toLowerCase();
|
|
33372
|
-
const exactMatch = entryId === unprefixed || entryId === lower;
|
|
33373
|
-
const aliasMatch = entry.aliases?.some((a) => a.toLowerCase() === unprefixed || a.toLowerCase() === lower);
|
|
33374
|
-
if (exactMatch || aliasMatch) {
|
|
33375
|
-
return entry;
|
|
33376
|
-
}
|
|
33377
|
-
}
|
|
33378
|
-
return;
|
|
33379
|
-
}
|
|
33380
|
-
var init_model_catalog = __esm(() => {
|
|
33381
|
-
init_all_models_cache();
|
|
33382
|
-
});
|
|
33383
|
-
|
|
33384
33838
|
// src/adapters/tool-name-utils.ts
|
|
33385
33839
|
function hashToolName(name) {
|
|
33386
33840
|
let h1 = 3735928559;
|
|
@@ -40151,207 +40605,6 @@ var init_fallback_handler = __esm(() => {
|
|
|
40151
40605
|
init_composed_handler();
|
|
40152
40606
|
});
|
|
40153
40607
|
|
|
40154
|
-
// src/providers/catalog-resolvers/openrouter.ts
|
|
40155
|
-
class OpenRouterCatalogResolver {
|
|
40156
|
-
provider = "openrouter";
|
|
40157
|
-
resolveSync(userInput) {
|
|
40158
|
-
const entries = this._getEntries();
|
|
40159
|
-
if (userInput.includes("/")) {
|
|
40160
|
-
if (entries) {
|
|
40161
|
-
for (const entry of entries) {
|
|
40162
|
-
for (const src of Object.values(entry.sources)) {
|
|
40163
|
-
if (src.externalId === userInput)
|
|
40164
|
-
return userInput;
|
|
40165
|
-
}
|
|
40166
|
-
}
|
|
40167
|
-
}
|
|
40168
|
-
return userInput;
|
|
40169
|
-
}
|
|
40170
|
-
if (entries) {
|
|
40171
|
-
const byModelId = entries.find((e) => e.modelId === userInput);
|
|
40172
|
-
if (byModelId) {
|
|
40173
|
-
const orId = this._getOpenRouterExternalId(byModelId);
|
|
40174
|
-
if (orId)
|
|
40175
|
-
return orId;
|
|
40176
|
-
}
|
|
40177
|
-
const byAlias = entries.find((e) => e.aliases.includes(userInput));
|
|
40178
|
-
if (byAlias) {
|
|
40179
|
-
const orId = this._getOpenRouterExternalId(byAlias);
|
|
40180
|
-
if (orId)
|
|
40181
|
-
return orId;
|
|
40182
|
-
}
|
|
40183
|
-
for (const entry of entries) {
|
|
40184
|
-
for (const src of Object.values(entry.sources)) {
|
|
40185
|
-
if (src.externalId === userInput) {
|
|
40186
|
-
const orId = this._getOpenRouterExternalId(entry);
|
|
40187
|
-
if (orId)
|
|
40188
|
-
return orId;
|
|
40189
|
-
}
|
|
40190
|
-
}
|
|
40191
|
-
}
|
|
40192
|
-
const suffix = `/${userInput}`;
|
|
40193
|
-
for (const entry of entries) {
|
|
40194
|
-
const orId = this._getOpenRouterExternalId(entry);
|
|
40195
|
-
if (orId?.endsWith(suffix))
|
|
40196
|
-
return orId;
|
|
40197
|
-
}
|
|
40198
|
-
const lowerSuffix = `/${userInput.toLowerCase()}`;
|
|
40199
|
-
for (const entry of entries) {
|
|
40200
|
-
const orId = this._getOpenRouterExternalId(entry);
|
|
40201
|
-
if (orId?.toLowerCase().endsWith(lowerSuffix))
|
|
40202
|
-
return orId;
|
|
40203
|
-
}
|
|
40204
|
-
}
|
|
40205
|
-
return null;
|
|
40206
|
-
}
|
|
40207
|
-
async warmCache() {
|
|
40208
|
-
if (!_warmPromise) {
|
|
40209
|
-
_warmPromise = this._fetchAndCache();
|
|
40210
|
-
}
|
|
40211
|
-
await _warmPromise;
|
|
40212
|
-
}
|
|
40213
|
-
isCacheWarm() {
|
|
40214
|
-
return _memCache !== null && _memCache.length > 0;
|
|
40215
|
-
}
|
|
40216
|
-
async ensureReady(timeoutMs) {
|
|
40217
|
-
if (this.isCacheWarm())
|
|
40218
|
-
return;
|
|
40219
|
-
if (!_warmPromise) {
|
|
40220
|
-
_warmPromise = this._fetchAndCache();
|
|
40221
|
-
}
|
|
40222
|
-
await Promise.race([
|
|
40223
|
-
_warmPromise,
|
|
40224
|
-
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
40225
|
-
]);
|
|
40226
|
-
}
|
|
40227
|
-
async refreshCatalog(timeoutMs) {
|
|
40228
|
-
let response;
|
|
40229
|
-
try {
|
|
40230
|
-
response = await fetch(FIREBASE_CATALOG_URL, {
|
|
40231
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
40232
|
-
});
|
|
40233
|
-
} catch (err) {
|
|
40234
|
-
const name = err?.name;
|
|
40235
|
-
const reason = name === "TimeoutError" || name === "AbortError" ? "timeout" : "network";
|
|
40236
|
-
return { kind: "fetch_failed", reason };
|
|
40237
|
-
}
|
|
40238
|
-
if (!response.ok) {
|
|
40239
|
-
return { kind: "fetch_failed", reason: "http_error" };
|
|
40240
|
-
}
|
|
40241
|
-
let data;
|
|
40242
|
-
try {
|
|
40243
|
-
data = await response.json();
|
|
40244
|
-
} catch {
|
|
40245
|
-
return { kind: "fetch_failed", reason: "network" };
|
|
40246
|
-
}
|
|
40247
|
-
if (!Array.isArray(data.models) || data.models.length === 0) {
|
|
40248
|
-
return { kind: "fetch_failed", reason: "empty" };
|
|
40249
|
-
}
|
|
40250
|
-
const backwardCompatModels = [];
|
|
40251
|
-
for (const entry of data.models) {
|
|
40252
|
-
const orSource = entry.sources["openrouter-api"];
|
|
40253
|
-
if (orSource?.externalId) {
|
|
40254
|
-
backwardCompatModels.push({ id: orSource.externalId });
|
|
40255
|
-
}
|
|
40256
|
-
}
|
|
40257
|
-
_memCache = data.models;
|
|
40258
|
-
writeAllModelsCache({
|
|
40259
|
-
entries: data.models,
|
|
40260
|
-
models: backwardCompatModels
|
|
40261
|
-
});
|
|
40262
|
-
_warmPromise = Promise.resolve();
|
|
40263
|
-
return { kind: "refreshed", modelCount: data.models.length };
|
|
40264
|
-
}
|
|
40265
|
-
_getOpenRouterExternalId(entry) {
|
|
40266
|
-
const orSource = entry.sources["openrouter-api"];
|
|
40267
|
-
if (orSource?.externalId)
|
|
40268
|
-
return orSource.externalId;
|
|
40269
|
-
for (const src of Object.values(entry.sources)) {
|
|
40270
|
-
if (src.externalId.includes("/"))
|
|
40271
|
-
return src.externalId;
|
|
40272
|
-
}
|
|
40273
|
-
return null;
|
|
40274
|
-
}
|
|
40275
|
-
_getEntries() {
|
|
40276
|
-
if (_memCache)
|
|
40277
|
-
return _memCache;
|
|
40278
|
-
const cache2 = readAllModelsCache();
|
|
40279
|
-
if (!cache2)
|
|
40280
|
-
return null;
|
|
40281
|
-
if (cache2.entries.length > 0) {
|
|
40282
|
-
_memCache = cache2.entries;
|
|
40283
|
-
return _memCache;
|
|
40284
|
-
}
|
|
40285
|
-
if (cache2.models.length > 0) {
|
|
40286
|
-
_memCache = cache2.models.map((m) => ({
|
|
40287
|
-
modelId: m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id,
|
|
40288
|
-
aliases: [],
|
|
40289
|
-
sources: { "openrouter-api": { externalId: m.id } }
|
|
40290
|
-
}));
|
|
40291
|
-
return _memCache;
|
|
40292
|
-
}
|
|
40293
|
-
return null;
|
|
40294
|
-
}
|
|
40295
|
-
async _fetchAndCache() {
|
|
40296
|
-
await this.refreshCatalog(8000);
|
|
40297
|
-
}
|
|
40298
|
-
}
|
|
40299
|
-
var FIREBASE_CATALOG_URL, _memCache = null, _warmPromise = null;
|
|
40300
|
-
var init_openrouter = __esm(() => {
|
|
40301
|
-
init_all_models_cache();
|
|
40302
|
-
FIREBASE_CATALOG_URL = process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000";
|
|
40303
|
-
});
|
|
40304
|
-
|
|
40305
|
-
// src/providers/model-catalog-resolver.ts
|
|
40306
|
-
function registerResolver(resolver) {
|
|
40307
|
-
RESOLVER_REGISTRY.set(resolver.provider, resolver);
|
|
40308
|
-
}
|
|
40309
|
-
function getResolver(provider) {
|
|
40310
|
-
return RESOLVER_REGISTRY.get(provider) ?? null;
|
|
40311
|
-
}
|
|
40312
|
-
function resolveModelNameSync(userInput, targetProvider) {
|
|
40313
|
-
if (targetProvider !== "openrouter" && userInput.includes("/")) {
|
|
40314
|
-
return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
|
|
40315
|
-
}
|
|
40316
|
-
const resolver = getResolver(targetProvider);
|
|
40317
|
-
if (!resolver) {
|
|
40318
|
-
return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
|
|
40319
|
-
}
|
|
40320
|
-
const resolved = resolver.resolveSync(userInput);
|
|
40321
|
-
if (!resolved || resolved === userInput) {
|
|
40322
|
-
return { resolvedId: userInput, wasResolved: false, sourceLabel: "passthrough" };
|
|
40323
|
-
}
|
|
40324
|
-
return {
|
|
40325
|
-
resolvedId: resolved,
|
|
40326
|
-
wasResolved: true,
|
|
40327
|
-
sourceLabel: `${targetProvider} catalog`
|
|
40328
|
-
};
|
|
40329
|
-
}
|
|
40330
|
-
function logResolution(userInput, result, quiet = false) {
|
|
40331
|
-
if (result.wasResolved && !quiet) {
|
|
40332
|
-
process.stderr.write(`[Model] Resolved "${userInput}" \u2192 "${result.resolvedId}" (${result.sourceLabel})
|
|
40333
|
-
`);
|
|
40334
|
-
}
|
|
40335
|
-
}
|
|
40336
|
-
async function ensureCatalogReady(provider, timeoutMs = 5000) {
|
|
40337
|
-
const resolver = getResolver(provider);
|
|
40338
|
-
if (!resolver || resolver.isCacheWarm())
|
|
40339
|
-
return;
|
|
40340
|
-
await resolver.ensureReady(timeoutMs);
|
|
40341
|
-
}
|
|
40342
|
-
async function warmAllCatalogs(providers) {
|
|
40343
|
-
const targets = providers ? [...RESOLVER_REGISTRY.entries()].filter(([k]) => providers.includes(k)) : [...RESOLVER_REGISTRY.entries()];
|
|
40344
|
-
await Promise.allSettled(targets.map(([, r]) => r.warmCache()));
|
|
40345
|
-
}
|
|
40346
|
-
var RESOLVER_REGISTRY;
|
|
40347
|
-
var init_model_catalog_resolver = __esm(() => {
|
|
40348
|
-
init_openrouter();
|
|
40349
|
-
RESOLVER_REGISTRY = new Map;
|
|
40350
|
-
[
|
|
40351
|
-
new OpenRouterCatalogResolver
|
|
40352
|
-
].forEach(registerResolver);
|
|
40353
|
-
});
|
|
40354
|
-
|
|
40355
40608
|
// src/handlers/native-handler-advisor.ts
|
|
40356
40609
|
import { appendFileSync as appendFileSync3 } from "fs";
|
|
40357
40610
|
function loadAdvisorSwapConfig(cliModels, cliCollector) {
|
|
@@ -42958,245 +43211,6 @@ var init_provider_profiles = __esm(() => {
|
|
|
42958
43211
|
};
|
|
42959
43212
|
});
|
|
42960
43213
|
|
|
42961
|
-
// src/providers/auto-route.ts
|
|
42962
|
-
var PROVIDER_TO_PREFIX, DISPLAY_NAMES;
|
|
42963
|
-
var init_auto_route = __esm(() => {
|
|
42964
|
-
init_provider_definitions();
|
|
42965
|
-
PROVIDER_TO_PREFIX = (() => {
|
|
42966
|
-
const map3 = {};
|
|
42967
|
-
for (const def of getAllProviders()) {
|
|
42968
|
-
if (def.shortestPrefix) {
|
|
42969
|
-
map3[def.name] = def.shortestPrefix;
|
|
42970
|
-
}
|
|
42971
|
-
}
|
|
42972
|
-
return map3;
|
|
42973
|
-
})();
|
|
42974
|
-
DISPLAY_NAMES = (() => {
|
|
42975
|
-
const map3 = {};
|
|
42976
|
-
for (const def of getAllProviders()) {
|
|
42977
|
-
map3[def.name] = def.displayName;
|
|
42978
|
-
}
|
|
42979
|
-
return map3;
|
|
42980
|
-
})();
|
|
42981
|
-
});
|
|
42982
|
-
|
|
42983
|
-
// src/providers/default-routing-rules.ts
|
|
42984
|
-
function validateRoutingRulesAgainstProviders(rules) {
|
|
42985
|
-
const unknown3 = [];
|
|
42986
|
-
for (const ruleKey of Object.keys(rules)) {
|
|
42987
|
-
const entries = rules[ruleKey] ?? [];
|
|
42988
|
-
for (const entry of entries) {
|
|
42989
|
-
const atIdx = entry.indexOf("@");
|
|
42990
|
-
const providerRaw = atIdx === -1 ? entry : entry.slice(0, atIdx);
|
|
42991
|
-
const canonical = PROVIDER_SHORTCUTS[providerRaw.toLowerCase()] ?? providerRaw.toLowerCase();
|
|
42992
|
-
if (!getProviderByName(canonical)) {
|
|
42993
|
-
unknown3.push({ rule: ruleKey, entry, provider: canonical });
|
|
42994
|
-
}
|
|
42995
|
-
}
|
|
42996
|
-
}
|
|
42997
|
-
if (unknown3.length > 0) {
|
|
42998
|
-
const lines = unknown3.map((u) => ` rule "${u.rule}" \u2192 entry "${u.entry}" \u2192 unknown provider "${u.provider}"`);
|
|
42999
|
-
throw new Error(`[claudish] DEFAULT_ROUTING_RULES references unknown providers:
|
|
43000
|
-
${lines.join(`
|
|
43001
|
-
`)}`);
|
|
43002
|
-
}
|
|
43003
|
-
}
|
|
43004
|
-
function validateDefaultRoutingRules() {
|
|
43005
|
-
validateRoutingRulesAgainstProviders(DEFAULT_ROUTING_RULES);
|
|
43006
|
-
}
|
|
43007
|
-
var DEFAULT_ROUTING_RULES;
|
|
43008
|
-
var init_default_routing_rules = __esm(() => {
|
|
43009
|
-
init_model_parser();
|
|
43010
|
-
init_provider_definitions();
|
|
43011
|
-
DEFAULT_ROUTING_RULES = {
|
|
43012
|
-
"claude-*": ["native-anthropic", "openrouter"],
|
|
43013
|
-
"gpt-*": ["openai-codex", "openai", "openrouter"],
|
|
43014
|
-
"o1-*": ["openai-codex", "openai", "openrouter"],
|
|
43015
|
-
"o3-*": ["openai-codex", "openai", "openrouter"],
|
|
43016
|
-
"gemini-*": ["gemini-codeassist", "google", "openrouter"],
|
|
43017
|
-
"grok-*": ["x-ai", "openrouter"],
|
|
43018
|
-
"kimi-*": ["kimi-coding", "kimi", "openrouter"],
|
|
43019
|
-
"k3*": ["kimi-coding", "kimi", "openrouter"],
|
|
43020
|
-
"minimax-*": ["minimax-coding", "minimax", "openrouter"],
|
|
43021
|
-
"glm-*": ["glm-coding", "glm", "openrouter"],
|
|
43022
|
-
"z-ai-*": ["z-ai", "openrouter"],
|
|
43023
|
-
"deepseek-*": ["deepseek", "openrouter"],
|
|
43024
|
-
fugu: ["sakana-subscription", "sakana"],
|
|
43025
|
-
"fugu-*": ["sakana-subscription", "sakana"],
|
|
43026
|
-
"*-zen": ["opencode-zen"],
|
|
43027
|
-
"*": ["openrouter"]
|
|
43028
|
-
};
|
|
43029
|
-
validateDefaultRoutingRules();
|
|
43030
|
-
});
|
|
43031
|
-
|
|
43032
|
-
// src/providers/routing-rules.ts
|
|
43033
|
-
function mergeRoutingRules(defaults, global_, local) {
|
|
43034
|
-
return { ...defaults, ...global_, ...local };
|
|
43035
|
-
}
|
|
43036
|
-
function loadRoutingRules() {
|
|
43037
|
-
const local = loadLocalConfig()?.routing ?? {};
|
|
43038
|
-
const global_ = loadConfig().routing ?? {};
|
|
43039
|
-
validateRoutingRules(local);
|
|
43040
|
-
validateRoutingRules(global_);
|
|
43041
|
-
return mergeRoutingRules(DEFAULT_ROUTING_RULES, global_, local);
|
|
43042
|
-
}
|
|
43043
|
-
function validateRoutingRules(rules) {
|
|
43044
|
-
const seenLower = new Map;
|
|
43045
|
-
for (const key of Object.keys(rules)) {
|
|
43046
|
-
if (key !== "*" && (key.match(/\*/g) || []).length > 1) {
|
|
43047
|
-
console.error(`[claudish] Warning: routing pattern "${key}" has multiple wildcards \u2014 only single * is supported. This pattern may not match as expected.`);
|
|
43048
|
-
}
|
|
43049
|
-
const lower = key.toLowerCase();
|
|
43050
|
-
const prior = seenLower.get(lower);
|
|
43051
|
-
if (prior !== undefined && prior !== key) {
|
|
43052
|
-
console.error(`[claudish] Warning: routing patterns "${prior}" and "${key}" collide case-insensitively. Matching is case-insensitive, so one will silently shadow the other. Pick one casing and remove the duplicate.`);
|
|
43053
|
-
} else {
|
|
43054
|
-
seenLower.set(lower, key);
|
|
43055
|
-
}
|
|
43056
|
-
}
|
|
43057
|
-
}
|
|
43058
|
-
function matchRoutingRule(modelName, rules) {
|
|
43059
|
-
const lowered = modelName.toLowerCase();
|
|
43060
|
-
for (const [key, entries] of Object.entries(rules)) {
|
|
43061
|
-
if (!key.includes("*") && key.toLowerCase() === lowered)
|
|
43062
|
-
return entries;
|
|
43063
|
-
}
|
|
43064
|
-
const globKeys = Object.keys(rules).filter((k) => k !== "*" && k.includes("*")).sort((a, b) => b.length - a.length);
|
|
43065
|
-
for (const pattern of globKeys) {
|
|
43066
|
-
if (globMatch(pattern, modelName))
|
|
43067
|
-
return rules[pattern];
|
|
43068
|
-
}
|
|
43069
|
-
if (rules["*"] !== undefined)
|
|
43070
|
-
return rules["*"];
|
|
43071
|
-
return null;
|
|
43072
|
-
}
|
|
43073
|
-
function buildRoutingChain(entries, originalModelName, cachePath) {
|
|
43074
|
-
const routes = [];
|
|
43075
|
-
for (const entry of entries) {
|
|
43076
|
-
const atIdx = entry.indexOf("@");
|
|
43077
|
-
let providerRaw;
|
|
43078
|
-
let modelName;
|
|
43079
|
-
if (atIdx !== -1) {
|
|
43080
|
-
providerRaw = entry.slice(0, atIdx);
|
|
43081
|
-
modelName = entry.slice(atIdx + 1);
|
|
43082
|
-
} else {
|
|
43083
|
-
providerRaw = entry;
|
|
43084
|
-
modelName = originalModelName;
|
|
43085
|
-
}
|
|
43086
|
-
const provider = PROVIDER_SHORTCUTS[providerRaw.toLowerCase()] ?? providerRaw.toLowerCase();
|
|
43087
|
-
if (atIdx === -1) {
|
|
43088
|
-
const routing = resolveSubscriptionRouting(modelName, provider, cachePath);
|
|
43089
|
-
if (routing.kind === "not-served")
|
|
43090
|
-
continue;
|
|
43091
|
-
if (routing.kind === "serves")
|
|
43092
|
-
modelName = routing.externalId;
|
|
43093
|
-
}
|
|
43094
|
-
let modelSpec;
|
|
43095
|
-
if (provider === "openrouter") {
|
|
43096
|
-
const resolution = resolveModelNameSync(modelName, "openrouter");
|
|
43097
|
-
modelSpec = resolution.resolvedId;
|
|
43098
|
-
} else {
|
|
43099
|
-
const prefix = PROVIDER_TO_PREFIX[provider] ?? provider;
|
|
43100
|
-
modelSpec = `${prefix}@${modelName}`;
|
|
43101
|
-
}
|
|
43102
|
-
const displayName = DISPLAY_NAMES[provider] ?? provider;
|
|
43103
|
-
routes.push({ provider, modelSpec, displayName });
|
|
43104
|
-
}
|
|
43105
|
-
return routes;
|
|
43106
|
-
}
|
|
43107
|
-
function globMatch(pattern, value) {
|
|
43108
|
-
const star = pattern.indexOf("*");
|
|
43109
|
-
const p = pattern.toLowerCase();
|
|
43110
|
-
const v = value.toLowerCase();
|
|
43111
|
-
if (star === -1)
|
|
43112
|
-
return p === v;
|
|
43113
|
-
const prefix = p.slice(0, star);
|
|
43114
|
-
const suffix = p.slice(star + 1);
|
|
43115
|
-
return v.startsWith(prefix) && v.endsWith(suffix) && v.length >= prefix.length + suffix.length;
|
|
43116
|
-
}
|
|
43117
|
-
async function hasCredentialsForProvider(provider) {
|
|
43118
|
-
return credentials.isAvailable(provider);
|
|
43119
|
-
}
|
|
43120
|
-
async function routeExplicit(modelSpec, model, provider, cachePath) {
|
|
43121
|
-
if (!await hasCredentialsForProvider(provider)) {
|
|
43122
|
-
return {
|
|
43123
|
-
kind: "no-route",
|
|
43124
|
-
reason: `No credentials configured for "${provider}".`,
|
|
43125
|
-
hint: buildCredentialHint(model, [provider]) ?? undefined
|
|
43126
|
-
};
|
|
43127
|
-
}
|
|
43128
|
-
const built = buildRoutingChain([modelSpec], model, cachePath)[0];
|
|
43129
|
-
if (!built) {
|
|
43130
|
-
return {
|
|
43131
|
-
kind: "no-route",
|
|
43132
|
-
reason: `Could not build a route for "${modelSpec}".`
|
|
43133
|
-
};
|
|
43134
|
-
}
|
|
43135
|
-
return { kind: "ok", primary: built, fallbacks: [] };
|
|
43136
|
-
}
|
|
43137
|
-
async function routeBare(model, nativeProvider, rules, defaultProvider, cachePath) {
|
|
43138
|
-
const matched = matchRoutingRule(model, rules) ?? [];
|
|
43139
|
-
const entries = [...matched];
|
|
43140
|
-
if (defaultProvider && defaultProvider.length > 0) {
|
|
43141
|
-
const canonicalDefault = PROVIDER_SHORTCUTS[defaultProvider.toLowerCase()] ?? defaultProvider.toLowerCase();
|
|
43142
|
-
const alreadyPresent = entries.some((e) => {
|
|
43143
|
-
const atIdx = e.indexOf("@");
|
|
43144
|
-
const providerRaw = atIdx === -1 ? e : e.slice(0, atIdx);
|
|
43145
|
-
const canonical = PROVIDER_SHORTCUTS[providerRaw.toLowerCase()] ?? providerRaw.toLowerCase();
|
|
43146
|
-
return canonical === canonicalDefault;
|
|
43147
|
-
});
|
|
43148
|
-
if (!alreadyPresent)
|
|
43149
|
-
entries.push(defaultProvider);
|
|
43150
|
-
}
|
|
43151
|
-
if (entries.length === 0) {
|
|
43152
|
-
return {
|
|
43153
|
-
kind: "no-route",
|
|
43154
|
-
reason: `No routing rule matched "${model}".`,
|
|
43155
|
-
hint: buildCredentialHint(model, [nativeProvider]) ?? undefined
|
|
43156
|
-
};
|
|
43157
|
-
}
|
|
43158
|
-
const candidates = buildRoutingChain(entries, model, cachePath);
|
|
43159
|
-
const credentialed = [];
|
|
43160
|
-
const skipped = [];
|
|
43161
|
-
const checks4 = await Promise.all(candidates.map((candidate) => hasCredentialsForProvider(candidate.provider)));
|
|
43162
|
-
candidates.forEach((candidate, i) => {
|
|
43163
|
-
if (checks4[i]) {
|
|
43164
|
-
credentialed.push(candidate);
|
|
43165
|
-
} else {
|
|
43166
|
-
skipped.push(candidate.provider);
|
|
43167
|
-
}
|
|
43168
|
-
});
|
|
43169
|
-
if (credentialed.length === 0) {
|
|
43170
|
-
return {
|
|
43171
|
-
kind: "no-route",
|
|
43172
|
-
reason: skipped.length > 0 ? `No credentialed providers in chain for "${model}" (tried: ${skipped.join(", ")}).` : `No providers available for "${model}".`,
|
|
43173
|
-
hint: buildCredentialHint(model, skipped) ?? undefined
|
|
43174
|
-
};
|
|
43175
|
-
}
|
|
43176
|
-
const [primary, ...fallbacks] = credentialed;
|
|
43177
|
-
return { kind: "ok", primary, fallbacks };
|
|
43178
|
-
}
|
|
43179
|
-
async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePath) {
|
|
43180
|
-
const parsed = parseModelSpec(modelSpec);
|
|
43181
|
-
if (parsed.isExplicitProvider) {
|
|
43182
|
-
return routeExplicit(modelSpec, parsed.model, parsed.provider, cachePath);
|
|
43183
|
-
}
|
|
43184
|
-
const rules = rulesOverride ?? loadRoutingRules();
|
|
43185
|
-
const defaultProvider = defaultProviderOverride !== undefined ? defaultProviderOverride : rulesOverride !== undefined ? undefined : loadConfig().defaultProvider;
|
|
43186
|
-
return routeBare(parsed.model, parsed.provider, rules, defaultProvider, cachePath);
|
|
43187
|
-
}
|
|
43188
|
-
var init_routing_rules = __esm(() => {
|
|
43189
|
-
init_model_catalog();
|
|
43190
|
-
init_authority();
|
|
43191
|
-
init_profile_config();
|
|
43192
|
-
init_auto_route();
|
|
43193
|
-
init_default_routing_rules();
|
|
43194
|
-
init_model_catalog_resolver();
|
|
43195
|
-
init_model_parser();
|
|
43196
|
-
init_model_parser();
|
|
43197
|
-
init_routing_hints();
|
|
43198
|
-
});
|
|
43199
|
-
|
|
43200
43214
|
// src/handlers/shared/local-queue.ts
|
|
43201
43215
|
class LocalModelQueue {
|
|
43202
43216
|
static instance = null;
|
|
@@ -58871,6 +58885,7 @@ var init_config = __esm(() => {
|
|
|
58871
58885
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
58872
58886
|
CLAUDE_CODE_SUBAGENT_MODEL: "CLAUDE_CODE_SUBAGENT_MODEL",
|
|
58873
58887
|
CLAUDE_CODE_AUTO_COMPACT_WINDOW: "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
|
|
58888
|
+
CLAUDE_CODE_MAX_CONTEXT_TOKENS: "CLAUDE_CODE_MAX_CONTEXT_TOKENS",
|
|
58874
58889
|
OLLAMA_BASE_URL: "OLLAMA_BASE_URL",
|
|
58875
58890
|
OLLAMA_HOST: "OLLAMA_HOST",
|
|
58876
58891
|
LMSTUDIO_BASE_URL: "LMSTUDIO_BASE_URL",
|
|
@@ -71758,6 +71773,7 @@ var init_terminal_isolation = __esm(() => {
|
|
|
71758
71773
|
var exports_claude_runner = {};
|
|
71759
71774
|
__export(exports_claude_runner, {
|
|
71760
71775
|
runClaudeWithProxy: () => runClaudeWithProxy,
|
|
71776
|
+
resolveContextWindowEnv: () => resolveContextWindowEnv,
|
|
71761
71777
|
managedSettingsForcesClaudeAi: () => managedSettingsForcesClaudeAi,
|
|
71762
71778
|
isProxyAuthMode: () => isProxyAuthMode,
|
|
71763
71779
|
computeMainThreadContextWindow: () => computeMainThreadContextWindow,
|
|
@@ -72021,6 +72037,27 @@ async function computeMainThreadContextWindow(config3, cachePath) {
|
|
|
72021
72037
|
}
|
|
72022
72038
|
return Number.isFinite(min) ? min : 0;
|
|
72023
72039
|
}
|
|
72040
|
+
function resolveContextWindowEnv(realWindow, processEnv = process.env) {
|
|
72041
|
+
const vars = {};
|
|
72042
|
+
if (!(realWindow > 0))
|
|
72043
|
+
return { vars };
|
|
72044
|
+
if (!processEnv[ENV.CLAUDE_CODE_MAX_CONTEXT_TOKENS]) {
|
|
72045
|
+
vars[ENV.CLAUDE_CODE_MAX_CONTEXT_TOKENS] = String(realWindow);
|
|
72046
|
+
}
|
|
72047
|
+
if (processEnv[ENV.CLAUDE_CODE_AUTO_COMPACT_WINDOW])
|
|
72048
|
+
return { vars };
|
|
72049
|
+
if (realWindow >= MIN_AUTO_COMPACT_WINDOW) {
|
|
72050
|
+
vars[ENV.CLAUDE_CODE_AUTO_COMPACT_WINDOW] = String(realWindow);
|
|
72051
|
+
return {
|
|
72052
|
+
vars,
|
|
72053
|
+
notice: `[claudish] Auto-compact window: ${realWindow.toLocaleString()} tokens ` + "(Claude Code compacts before the backend's real limit)"
|
|
72054
|
+
};
|
|
72055
|
+
}
|
|
72056
|
+
return {
|
|
72057
|
+
vars,
|
|
72058
|
+
notice: `[claudish] Model's real context window (${realWindow.toLocaleString()}) is below ` + `Claude Code's ${MIN_AUTO_COMPACT_WINDOW.toLocaleString()}-token auto-compact floor \u2014 ` + "leaving CLAUDE_CODE_AUTO_COMPACT_WINDOW unset so native auto-compaction stays on."
|
|
72059
|
+
};
|
|
72060
|
+
}
|
|
72024
72061
|
async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
|
|
72025
72062
|
const hasProfileMappings = config3.modelOpus || config3.modelSonnet || config3.modelHaiku || config3.modelSubagent;
|
|
72026
72063
|
const modelId = config3.model || (hasProfileMappings || config3.monitor ? undefined : "unknown");
|
|
@@ -72091,16 +72128,11 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
|
|
|
72091
72128
|
} else {
|
|
72092
72129
|
env.ANTHROPIC_API_KEY = "sk-ant-api03-placeholder-not-used-proxy-handles-auth-with-openrouter-key-xxxxxxxxxxxxxxxxxxxxx";
|
|
72093
72130
|
env.ANTHROPIC_AUTH_TOKEN = "placeholder-token-not-used-proxy-handles-auth";
|
|
72094
|
-
|
|
72095
|
-
|
|
72096
|
-
|
|
72097
|
-
|
|
72098
|
-
|
|
72099
|
-
console.error(`[claudish] Auto-compact window: ${autoCompactWindow.toLocaleString()} tokens ` + "(Claude Code compacts before the backend's real limit)");
|
|
72100
|
-
}
|
|
72101
|
-
} else if (autoCompactWindow > 0 && !config3.quiet) {
|
|
72102
|
-
console.error(`[claudish] Model's real context window (${autoCompactWindow.toLocaleString()}) is below ` + `Claude Code's ${MIN_AUTO_COMPACT_WINDOW.toLocaleString()}-token auto-compact floor \u2014 ` + "leaving CLAUDE_CODE_AUTO_COMPACT_WINDOW unset so native auto-compaction stays on.");
|
|
72103
|
-
}
|
|
72131
|
+
const realWindow = await computeMainThreadContextWindow(config3);
|
|
72132
|
+
const contextEnv = resolveContextWindowEnv(realWindow, process.env);
|
|
72133
|
+
Object.assign(env, contextEnv.vars);
|
|
72134
|
+
if (contextEnv.notice && !config3.quiet) {
|
|
72135
|
+
console.error(contextEnv.notice);
|
|
72104
72136
|
}
|
|
72105
72137
|
}
|
|
72106
72138
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudish",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.24.0",
|
|
4
4
|
"description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"ai"
|
|
61
61
|
],
|
|
62
62
|
"optionalDependencies": {
|
|
63
|
-
"@claudish/magmux-darwin-arm64": "7.
|
|
64
|
-
"@claudish/magmux-darwin-x64": "7.
|
|
65
|
-
"@claudish/magmux-linux-arm64": "7.
|
|
66
|
-
"@claudish/magmux-linux-x64": "7.
|
|
63
|
+
"@claudish/magmux-darwin-arm64": "7.24.0",
|
|
64
|
+
"@claudish/magmux-darwin-x64": "7.24.0",
|
|
65
|
+
"@claudish/magmux-linux-arm64": "7.24.0",
|
|
66
|
+
"@claudish/magmux-linux-x64": "7.24.0"
|
|
67
67
|
},
|
|
68
68
|
"author": "Jack Rudenko <i@madappgang.com>",
|
|
69
69
|
"license": "MIT",
|