proxitor 0.11.0 → 0.13.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/README.md +11 -0
- package/dist/cli.mjs +419 -182
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import process$1, { stdin, stdout } from "node:process";
|
|
|
4
4
|
import os, { homedir } from "node:os";
|
|
5
5
|
import * as tty$1 from "node:tty";
|
|
6
6
|
import tty, { ReadStream } from "node:tty";
|
|
7
|
-
import { formatWithOptions, styleText } from "node:util";
|
|
7
|
+
import { formatWithOptions, isDeepStrictEqual, styleText } from "node:util";
|
|
8
8
|
import * as l$1 from "node:readline";
|
|
9
9
|
import l__default from "node:readline";
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
@@ -16251,6 +16251,7 @@ const modelOverrideSchema = object({
|
|
|
16251
16251
|
headers: record(string$1(), string$1()).optional(),
|
|
16252
16252
|
cacheControl: triStateSchema.optional(),
|
|
16253
16253
|
cacheControlTtl: ttlSchema.optional(),
|
|
16254
|
+
rewriteBlockTtl: triStateSchema.optional(),
|
|
16254
16255
|
sessionId: triStateSchema.optional(),
|
|
16255
16256
|
normalizeVolatileSystem: boolean().optional()
|
|
16256
16257
|
}).strict();
|
|
@@ -16269,6 +16270,7 @@ const proxyConfigSchema = object({
|
|
|
16269
16270
|
headers: record(string$1(), string$1()).optional(),
|
|
16270
16271
|
cacheControl: triStateSchema.default("auto"),
|
|
16271
16272
|
cacheControlTtl: ttlSchema.optional(),
|
|
16273
|
+
rewriteBlockTtl: triStateSchema.default("skip"),
|
|
16272
16274
|
sessionId: triStateSchema.default("auto"),
|
|
16273
16275
|
normalizeVolatileSystem: boolean().default(false),
|
|
16274
16276
|
modelOverrides: record(string$1().min(1), modelOverrideSchema).optional()
|
|
@@ -16398,6 +16400,7 @@ function resolveModelConfig(config, modelName) {
|
|
|
16398
16400
|
headers: config.headers ? { ...config.headers } : void 0,
|
|
16399
16401
|
cacheControl: config.cacheControl,
|
|
16400
16402
|
cacheControlTtl: config.cacheControlTtl,
|
|
16403
|
+
rewriteBlockTtl: config.rewriteBlockTtl,
|
|
16401
16404
|
sessionId: config.sessionId,
|
|
16402
16405
|
normalizeVolatileSystem: config.normalizeVolatileSystem
|
|
16403
16406
|
};
|
|
@@ -16427,6 +16430,7 @@ function applyOverride(result, override) {
|
|
|
16427
16430
|
};
|
|
16428
16431
|
if (override.cacheControl !== void 0) result.cacheControl = override.cacheControl;
|
|
16429
16432
|
if (override.cacheControlTtl !== void 0) result.cacheControlTtl = override.cacheControlTtl;
|
|
16433
|
+
if (override.rewriteBlockTtl !== void 0) result.rewriteBlockTtl = override.rewriteBlockTtl;
|
|
16430
16434
|
if (override.sessionId !== void 0) result.sessionId = override.sessionId;
|
|
16431
16435
|
if (override.normalizeVolatileSystem !== void 0) result.normalizeVolatileSystem = override.normalizeVolatileSystem;
|
|
16432
16436
|
}
|
|
@@ -16491,18 +16495,29 @@ function findConfigFile(explicitPath) {
|
|
|
16491
16495
|
if (found) return found;
|
|
16492
16496
|
throw new MissingConfigError(getConfigSearchPaths());
|
|
16493
16497
|
}
|
|
16494
|
-
function
|
|
16498
|
+
function parseConfigFile(filePath) {
|
|
16495
16499
|
const content = readFileSync(filePath, "utf-8");
|
|
16496
|
-
let raw;
|
|
16497
16500
|
try {
|
|
16498
|
-
|
|
16501
|
+
return filePath.endsWith(".json") ? JSON.parse(content) : (0, import_dist.parse)(content);
|
|
16499
16502
|
} catch (err) {
|
|
16500
16503
|
throw new ConfigParseError(filePath, err instanceof Error ? err : void 0);
|
|
16501
16504
|
}
|
|
16502
|
-
|
|
16505
|
+
}
|
|
16506
|
+
function readConfigFile(filePath) {
|
|
16507
|
+
const result = proxyConfigFileSchema.safeParse(parseConfigFile(filePath));
|
|
16503
16508
|
if (!result.success) throw new ConfigValidationError(filePath, result.error);
|
|
16504
16509
|
return result.data;
|
|
16505
16510
|
}
|
|
16511
|
+
/**
|
|
16512
|
+
* Validated like readConfigFile, but schema defaults are NOT applied, so absent
|
|
16513
|
+
* keys stay undefined — use when "absent" must differ from "explicit default".
|
|
16514
|
+
*/
|
|
16515
|
+
function readConfigFileRaw(filePath) {
|
|
16516
|
+
const raw = parseConfigFile(filePath);
|
|
16517
|
+
const result = proxyConfigFileSchema.safeParse(raw);
|
|
16518
|
+
if (!result.success) throw new ConfigValidationError(filePath, result.error);
|
|
16519
|
+
return raw ?? {};
|
|
16520
|
+
}
|
|
16506
16521
|
//#endregion
|
|
16507
16522
|
//#region src/openrouter/cache.ts
|
|
16508
16523
|
const CACHE_DIR = join(homedir(), ".cache", "proxitor");
|
|
@@ -17917,6 +17932,12 @@ const CACHE_HINTS = {
|
|
|
17917
17932
|
always: "All models",
|
|
17918
17933
|
skip: "Passthrough — leave client cache_control headers as-is"
|
|
17919
17934
|
};
|
|
17935
|
+
/** Shared hint texts for the rewrite-block-ttl tri-state — used in cache-control command + override editor. */
|
|
17936
|
+
const REWRITE_HINTS = {
|
|
17937
|
+
auto: "Anthropic models only — normalize blocks to match root",
|
|
17938
|
+
always: "All models",
|
|
17939
|
+
skip: "Leave client block ttl as-is (may mismatch root)"
|
|
17940
|
+
};
|
|
17920
17941
|
const NORMALIZE_HINTS = {
|
|
17921
17942
|
on: "Rewrite cch → stable prefix cache",
|
|
17922
17943
|
off: "Passthrough — rewrite nothing"
|
|
@@ -17938,7 +17959,7 @@ async function askNormalizeVolatileSystem(message, current, opts) {
|
|
|
17938
17959
|
});
|
|
17939
17960
|
return await select({
|
|
17940
17961
|
message,
|
|
17941
|
-
initialValue: current ?? false,
|
|
17962
|
+
initialValue: current ?? (opts?.removable ? "reset" : false),
|
|
17942
17963
|
options
|
|
17943
17964
|
});
|
|
17944
17965
|
}
|
|
@@ -17967,7 +17988,7 @@ async function askTriState(message, current, hints, opts) {
|
|
|
17967
17988
|
});
|
|
17968
17989
|
return await select({
|
|
17969
17990
|
message,
|
|
17970
|
-
initialValue: current ?? "auto",
|
|
17991
|
+
initialValue: current ?? (opts?.removable ? "reset" : "auto"),
|
|
17971
17992
|
options
|
|
17972
17993
|
});
|
|
17973
17994
|
}
|
|
@@ -18003,7 +18024,7 @@ async function askCacheControlTtl(current, opts) {
|
|
|
18003
18024
|
});
|
|
18004
18025
|
return await select({
|
|
18005
18026
|
message: "Cache TTL",
|
|
18006
|
-
initialValue: current ?? "5m",
|
|
18027
|
+
initialValue: current ?? (opts?.removable ? "reset" : "5m"),
|
|
18007
18028
|
options
|
|
18008
18029
|
});
|
|
18009
18030
|
}
|
|
@@ -18016,14 +18037,17 @@ function applyField(obj, key, field) {
|
|
|
18016
18037
|
else obj[key] = field.value;
|
|
18017
18038
|
}
|
|
18018
18039
|
async function collectSessionTriState(currentSid) {
|
|
18019
|
-
const sid = await askTriState("Session ID mode", currentSid
|
|
18040
|
+
const sid = await askTriState("Session ID mode", currentSid, SESSION_HINTS, { removable: true });
|
|
18020
18041
|
if (typeof sid === "symbol") return null;
|
|
18021
18042
|
if (sid === "reset") return { sessionId: { remove: true } };
|
|
18022
18043
|
return { sessionId: { value: sid } };
|
|
18023
18044
|
}
|
|
18024
|
-
/**
|
|
18025
|
-
|
|
18026
|
-
|
|
18045
|
+
/**
|
|
18046
|
+
* Collects cacheControl mode, TTL, and rewriteBlockTtl.
|
|
18047
|
+
* Mode-cancel aborts; TTL-cancel keeps existing TTL; rewrite-cancel keeps existing rewrite.
|
|
18048
|
+
*/
|
|
18049
|
+
async function collectCacheTriState(currentCc, currentTtl, globalTtl, currentRewrite) {
|
|
18050
|
+
const cc = await askTriState("Cache control mode", currentCc, CACHE_HINTS, { removable: true });
|
|
18027
18051
|
if (typeof cc === "symbol") return null;
|
|
18028
18052
|
let cacheControl;
|
|
18029
18053
|
if (cc === "reset") cacheControl = { remove: true };
|
|
@@ -18032,14 +18056,19 @@ async function collectCacheTriState(currentCc, currentTtl, globalTtl) {
|
|
|
18032
18056
|
removable: true,
|
|
18033
18057
|
globalTtl
|
|
18034
18058
|
});
|
|
18035
|
-
|
|
18036
|
-
if (ttl === "
|
|
18037
|
-
|
|
18038
|
-
|
|
18039
|
-
};
|
|
18059
|
+
let cacheControlTtl;
|
|
18060
|
+
if (typeof ttl === "symbol") cacheControlTtl = void 0;
|
|
18061
|
+
else if (ttl === "reset") cacheControlTtl = { remove: true };
|
|
18062
|
+
else cacheControlTtl = { value: ttl };
|
|
18063
|
+
const rewrite = await askTriState("Rewrite block TTLs", currentRewrite, REWRITE_HINTS, { removable: true });
|
|
18064
|
+
let rewriteBlockTtl;
|
|
18065
|
+
if (typeof rewrite === "symbol") rewriteBlockTtl = void 0;
|
|
18066
|
+
else if (rewrite === "reset") rewriteBlockTtl = { remove: true };
|
|
18067
|
+
else rewriteBlockTtl = { value: rewrite };
|
|
18040
18068
|
return {
|
|
18041
18069
|
cacheControl,
|
|
18042
|
-
cacheControlTtl
|
|
18070
|
+
cacheControlTtl,
|
|
18071
|
+
rewriteBlockTtl
|
|
18043
18072
|
};
|
|
18044
18073
|
}
|
|
18045
18074
|
async function collectNormalizeVolatileSystem(currentNvs) {
|
|
@@ -18318,30 +18347,137 @@ async function browseModelsCommand(client) {
|
|
|
18318
18347
|
});
|
|
18319
18348
|
}
|
|
18320
18349
|
//#endregion
|
|
18321
|
-
//#region src/commands/config/
|
|
18322
|
-
|
|
18323
|
-
|
|
18350
|
+
//#region src/commands/config/cache-control.ts
|
|
18351
|
+
/**
|
|
18352
|
+
* Map a ResolvedField into the `fields` object for `setGlobalConfigFields`:
|
|
18353
|
+
* {value}→assign value, {remove}→assign undefined (delete from disk),
|
|
18354
|
+
* undefined→omit the key (keep existing on disk).
|
|
18355
|
+
*
|
|
18356
|
+
* This is the file-write counterpart of `applyField`. The in-memory `applyField`
|
|
18357
|
+
* can `delete` an existing key, but a fresh `fields` object fed to
|
|
18358
|
+
* `setGlobalConfigFields` needs an explicit `undefined` value to trigger
|
|
18359
|
+
* removal — so keep-existing (undefined field) must be distinguished from
|
|
18360
|
+
* remove ({remove:true}) by omitting vs. assigning the key.
|
|
18361
|
+
*/
|
|
18362
|
+
function assignField(fields, key, field) {
|
|
18363
|
+
if (field === void 0) return;
|
|
18364
|
+
fields[key] = "remove" in field ? void 0 : field.value;
|
|
18365
|
+
}
|
|
18366
|
+
function fieldLabel(field, fallback) {
|
|
18367
|
+
if (field === void 0) return fallback;
|
|
18368
|
+
if ("remove" in field) return "(default)";
|
|
18369
|
+
return String(field.value);
|
|
18370
|
+
}
|
|
18371
|
+
async function cacheControlCommand(opts) {
|
|
18372
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18373
|
+
const cfg = readConfigFileRaw(configPath);
|
|
18374
|
+
const rawCc = cfg.cacheControl;
|
|
18375
|
+
const rawTtl = cfg.cacheControlTtl;
|
|
18376
|
+
const rawRewrite = cfg.rewriteBlockTtl;
|
|
18377
|
+
const effectiveCc = rawCc ?? DEFAULTS.cacheControl;
|
|
18378
|
+
const effectiveRewrite = rawRewrite ?? DEFAULTS.rewriteBlockTtl;
|
|
18379
|
+
log.info(`Current: cacheControl = ${rawCc === void 0 ? `(default -> ${effectiveCc})` : effectiveCc}`);
|
|
18380
|
+
if (rawTtl) log.info(`Current: cacheControlTtl = ${rawTtl}`);
|
|
18381
|
+
log.info(`Current: rewriteBlockTtl = ${rawRewrite === void 0 ? `(default -> ${effectiveRewrite})` : rawRewrite}`);
|
|
18382
|
+
const result = await collectCacheTriState(rawCc, rawTtl, void 0, rawRewrite);
|
|
18383
|
+
if (result === null) return;
|
|
18384
|
+
const fields = {};
|
|
18385
|
+
assignField(fields, "cacheControl", result.cacheControl);
|
|
18386
|
+
assignField(fields, "cacheControlTtl", result.cacheControlTtl);
|
|
18387
|
+
assignField(fields, "rewriteBlockTtl", result.rewriteBlockTtl);
|
|
18388
|
+
setGlobalConfigFields(configPath, fields);
|
|
18389
|
+
log.success(`cacheControl set to ${fieldLabel(result.cacheControl, effectiveCc)}, TTL = ${fieldLabel(result.cacheControlTtl, "(unchanged)")}, rewrite = ${fieldLabel(result.rewriteBlockTtl, effectiveRewrite)}`);
|
|
18390
|
+
}
|
|
18391
|
+
//#endregion
|
|
18392
|
+
//#region src/commands/config/caching-summary.ts
|
|
18393
|
+
/** Friendly TTL label (omit→strip, skip→passthrough). */
|
|
18394
|
+
function describeTtl(value) {
|
|
18395
|
+
if (value === void 0) return "(default)";
|
|
18396
|
+
if (value === "omit") return "strip";
|
|
18397
|
+
if (value === "skip") return "passthrough";
|
|
18398
|
+
return value;
|
|
18399
|
+
}
|
|
18400
|
+
/** Global NVS: distinguish absent (default-origin) from an explicit on/off. */
|
|
18401
|
+
function globalNvsLabel(value) {
|
|
18402
|
+
if (value === void 0) return `(default -> ${DEFAULTS.normalizeVolatileSystem ? "on" : "off"})`;
|
|
18324
18403
|
return value ? "on" : "off";
|
|
18325
18404
|
}
|
|
18326
|
-
|
|
18327
|
-
|
|
18328
|
-
|
|
18329
|
-
if (override.provider) {
|
|
18330
|
-
const keys = Object.keys(override.provider);
|
|
18331
|
-
parts.push(`provider: ${keys.join(", ")}`);
|
|
18332
|
-
}
|
|
18333
|
-
if (override.sessionId) parts.push(`session: ${override.sessionId}`);
|
|
18334
|
-
if (override.cacheControl) parts.push(`cache: ${override.cacheControl}`);
|
|
18335
|
-
if (override.normalizeVolatileSystem !== void 0) parts.push(`normalize: ${nvsHint(override.normalizeVolatileSystem)}`);
|
|
18336
|
-
if (override.headers) parts.push(`${Object.keys(override.headers).length} header(s)`);
|
|
18337
|
-
return parts.join(", ") || "(empty)";
|
|
18405
|
+
/** Global tri-state: distinguish absent (default-origin) from an explicit mode. */
|
|
18406
|
+
function globalTriStateLabel(value, fallback) {
|
|
18407
|
+
return value === void 0 ? `(default -> ${fallback})` : value;
|
|
18338
18408
|
}
|
|
18339
|
-
function
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18409
|
+
function nvsLabel(value, globalValue) {
|
|
18410
|
+
if (value === void 0) return `(inherit -> ${globalValue ? "on" : "off"})`;
|
|
18411
|
+
return value ? "on" : "off";
|
|
18412
|
+
}
|
|
18413
|
+
function perModelTtl(current, globalCfg) {
|
|
18414
|
+
if (current.cacheControlTtl !== void 0) return describeTtl(current.cacheControlTtl);
|
|
18415
|
+
if (globalCfg.cacheControlTtl !== void 0) return `(inherit -> ${describeTtl(globalCfg.cacheControlTtl)})`;
|
|
18416
|
+
return "(inherit)";
|
|
18417
|
+
}
|
|
18418
|
+
function formatGlobalCachingSummary(cfg) {
|
|
18419
|
+
const cc = globalTriStateLabel(cfg.cacheControl, DEFAULTS.cacheControl);
|
|
18420
|
+
const rewrite = globalTriStateLabel(cfg.rewriteBlockTtl, DEFAULTS.rewriteBlockTtl);
|
|
18421
|
+
const sid = globalTriStateLabel(cfg.sessionId, DEFAULTS.sessionId);
|
|
18422
|
+
const nvs = globalNvsLabel(cfg.normalizeVolatileSystem);
|
|
18423
|
+
return [
|
|
18424
|
+
"Three settings shape the request so cache survives.",
|
|
18425
|
+
"",
|
|
18426
|
+
` cacheControl = ${cc} (activate cache)`,
|
|
18427
|
+
` cacheControlTtl = ${describeTtl(cfg.cacheControlTtl)}`,
|
|
18428
|
+
` rewriteBlockTtl = ${rewrite} (normalize block ttl)`,
|
|
18429
|
+
` sessionId = ${sid} (pin provider)`,
|
|
18430
|
+
` normalizeVolatileSystem = ${nvs} (stable prefix)`,
|
|
18431
|
+
"",
|
|
18432
|
+
"Anthropic -> levers 1+2 - non-Anthropic (qwen/glm) -> all 3"
|
|
18433
|
+
].join("\n");
|
|
18434
|
+
}
|
|
18435
|
+
function formatPerModelCachingSummary(modelKey, current, globalCfg) {
|
|
18436
|
+
const gCc = globalCfg.cacheControl ?? DEFAULTS.cacheControl;
|
|
18437
|
+
const gRewrite = globalCfg.rewriteBlockTtl ?? DEFAULTS.rewriteBlockTtl;
|
|
18438
|
+
const gSid = globalCfg.sessionId ?? DEFAULTS.sessionId;
|
|
18439
|
+
const gNvs = globalCfg.normalizeVolatileSystem ?? DEFAULTS.normalizeVolatileSystem;
|
|
18440
|
+
const cc = current.cacheControl ?? `(inherit -> ${gCc})`;
|
|
18441
|
+
const ttl = perModelTtl(current, globalCfg);
|
|
18442
|
+
const rewrite = current.rewriteBlockTtl ?? `(inherit -> ${gRewrite})`;
|
|
18443
|
+
const sid = current.sessionId ?? `(inherit -> ${gSid})`;
|
|
18444
|
+
const nvs = nvsLabel(current.normalizeVolatileSystem, gNvs);
|
|
18445
|
+
return [
|
|
18446
|
+
`Caching for "${modelKey}"`,
|
|
18447
|
+
"",
|
|
18448
|
+
` cacheControl = ${cc}`,
|
|
18449
|
+
` cacheControlTtl = ${ttl}`,
|
|
18450
|
+
` rewriteBlockTtl = ${rewrite}`,
|
|
18451
|
+
` sessionId = ${sid}`,
|
|
18452
|
+
` normalizeVolatileSystem = ${nvs}`
|
|
18453
|
+
].join("\n");
|
|
18454
|
+
}
|
|
18455
|
+
//#endregion
|
|
18456
|
+
//#region src/commands/config/equality.ts
|
|
18457
|
+
/** Deep equality for overrides — gates no-op writes. */
|
|
18458
|
+
function overridesEqual(a, b) {
|
|
18459
|
+
return isDeepStrictEqual(a, b);
|
|
18460
|
+
}
|
|
18461
|
+
//#endregion
|
|
18462
|
+
//#region src/commands/config/normalize-system.ts
|
|
18463
|
+
async function normalizeVolatileSystemCommand(opts) {
|
|
18464
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18465
|
+
const raw = readConfigFileRaw(configPath).normalizeVolatileSystem;
|
|
18466
|
+
const effective = raw ?? DEFAULTS.normalizeVolatileSystem;
|
|
18467
|
+
log.info(`Current: normalizeVolatileSystem = ${raw === void 0 ? `(default -> ${effective ? "on" : "off"})` : effective}`);
|
|
18468
|
+
const choice = await askNormalizeVolatileSystem("Normalize Claude Code's volatile cch hash in the system prompt? Stabilizes the prefix cache for non-Anthropic providers (qwen/glm/etc.).", raw, {
|
|
18469
|
+
removable: true,
|
|
18470
|
+
resetHint: `remove (default: ${DEFAULTS.normalizeVolatileSystem})`
|
|
18471
|
+
});
|
|
18472
|
+
if (typeof choice === "symbol") return;
|
|
18473
|
+
const fields = {};
|
|
18474
|
+
fields.normalizeVolatileSystem = choice === "reset" ? void 0 : choice;
|
|
18475
|
+
setGlobalConfigFields(configPath, fields);
|
|
18476
|
+
const label = choice === "reset" ? `(default: ${DEFAULTS.normalizeVolatileSystem})` : choice;
|
|
18477
|
+
log.success(`normalizeVolatileSystem set to ${label}`);
|
|
18344
18478
|
}
|
|
18479
|
+
//#endregion
|
|
18480
|
+
//#region src/commands/config/override-levers.ts
|
|
18345
18481
|
function readGlobalTtl(configPath) {
|
|
18346
18482
|
if (!configPath) return void 0;
|
|
18347
18483
|
try {
|
|
@@ -18350,32 +18486,6 @@ function readGlobalTtl(configPath) {
|
|
|
18350
18486
|
return;
|
|
18351
18487
|
}
|
|
18352
18488
|
}
|
|
18353
|
-
function showCurrentConfig(modelKey, current) {
|
|
18354
|
-
log.info(`Current config for "${modelKey}":`);
|
|
18355
|
-
if (current.provider) for (const [field, value] of Object.entries(current.provider)) log.info(` provider.${field}: ${JSON.stringify(value)}`);
|
|
18356
|
-
if (current.sessionId) log.info(` sessionId: ${current.sessionId}`);
|
|
18357
|
-
if (current.cacheControl) log.info(` cacheControl: ${current.cacheControl}`);
|
|
18358
|
-
if (current.cacheControlTtl) log.info(` cacheControlTtl: ${current.cacheControlTtl}`);
|
|
18359
|
-
if (current.normalizeVolatileSystem !== void 0) log.info(` normalizeVolatileSystem: ${current.normalizeVolatileSystem}`);
|
|
18360
|
-
if (current.headers) for (const [name, value] of Object.entries(current.headers)) log.info(` headers.${name}: ${value}`);
|
|
18361
|
-
}
|
|
18362
|
-
async function editProvider(modelKey, current, client) {
|
|
18363
|
-
const isPattern = modelKey.includes("*");
|
|
18364
|
-
const mode = await selectRoutingMode("Routing mode");
|
|
18365
|
-
if (isCancel(mode)) return current;
|
|
18366
|
-
if (mode === "skip") {
|
|
18367
|
-
const { provider: _, ...rest } = current;
|
|
18368
|
-
return rest;
|
|
18369
|
-
}
|
|
18370
|
-
const providerOptions = await fetchProvidersForModel(client, modelKey, isPattern);
|
|
18371
|
-
if (!providerOptions) return current;
|
|
18372
|
-
const result = await selectProvidersByMode(mode, providerOptions);
|
|
18373
|
-
if (!result) return current;
|
|
18374
|
-
return {
|
|
18375
|
-
...current,
|
|
18376
|
-
provider: result.provider
|
|
18377
|
-
};
|
|
18378
|
-
}
|
|
18379
18489
|
async function editSessionId(current) {
|
|
18380
18490
|
const result = await collectSessionTriState(current.sessionId);
|
|
18381
18491
|
if (result === null) return current;
|
|
@@ -18383,17 +18493,16 @@ async function editSessionId(current) {
|
|
|
18383
18493
|
applyField(next, "sessionId", result.sessionId);
|
|
18384
18494
|
return next;
|
|
18385
18495
|
}
|
|
18386
|
-
/** @internal */
|
|
18387
18496
|
async function editCacheControl(current, configPath) {
|
|
18388
18497
|
const globalTtl = readGlobalTtl(configPath);
|
|
18389
|
-
const result = await collectCacheTriState(current.cacheControl, current.cacheControlTtl, globalTtl);
|
|
18498
|
+
const result = await collectCacheTriState(current.cacheControl, current.cacheControlTtl, globalTtl, current.rewriteBlockTtl);
|
|
18390
18499
|
if (result === null) return current;
|
|
18391
18500
|
const next = { ...current };
|
|
18392
18501
|
applyField(next, "cacheControl", result.cacheControl);
|
|
18393
18502
|
applyField(next, "cacheControlTtl", result.cacheControlTtl);
|
|
18503
|
+
applyField(next, "rewriteBlockTtl", result.rewriteBlockTtl);
|
|
18394
18504
|
return next;
|
|
18395
18505
|
}
|
|
18396
|
-
/** @internal */
|
|
18397
18506
|
async function editNormalizeVolatileSystem(current) {
|
|
18398
18507
|
const result = await collectNormalizeVolatileSystem(current.normalizeVolatileSystem);
|
|
18399
18508
|
if (result === null) return current;
|
|
@@ -18401,15 +18510,134 @@ async function editNormalizeVolatileSystem(current) {
|
|
|
18401
18510
|
applyField(next, "normalizeVolatileSystem", result.normalizeVolatileSystem);
|
|
18402
18511
|
return next;
|
|
18403
18512
|
}
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18513
|
+
//#endregion
|
|
18514
|
+
//#region src/commands/config/session-routing.ts
|
|
18515
|
+
async function sessionRoutingCommand(opts) {
|
|
18516
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18517
|
+
const raw = readConfigFileRaw(configPath).sessionId;
|
|
18518
|
+
const effective = raw ?? DEFAULTS.sessionId;
|
|
18519
|
+
log.info(`Current: sessionId = ${raw === void 0 ? `(default -> ${effective})` : effective}`);
|
|
18520
|
+
const result = await askTriState("Session routing mode", raw, SESSION_HINTS, { removable: true });
|
|
18521
|
+
if (typeof result === "symbol") return;
|
|
18522
|
+
if (result === "reset") {
|
|
18523
|
+
setGlobalConfigField(configPath, "sessionId", void 0);
|
|
18524
|
+
log.success("sessionId reset to default (auto)");
|
|
18525
|
+
return;
|
|
18526
|
+
}
|
|
18527
|
+
setGlobalConfigField(configPath, "sessionId", result);
|
|
18528
|
+
log.success(`sessionId set to ${result}`);
|
|
18529
|
+
}
|
|
18530
|
+
//#endregion
|
|
18531
|
+
//#region src/commands/config/caching-menu.ts
|
|
18532
|
+
/** Cache levers: option list + dispatch in one table. */
|
|
18533
|
+
const CACHING_LEVERS = [
|
|
18534
|
+
{
|
|
18535
|
+
value: "cacheControl",
|
|
18536
|
+
label: "Activate caching — mode + TTL (cacheControl)",
|
|
18537
|
+
global: (configPath) => cacheControlCommand({ configPath }),
|
|
18538
|
+
perModel: (current, configPath) => editCacheControl(current, configPath)
|
|
18539
|
+
},
|
|
18540
|
+
{
|
|
18541
|
+
value: "sessionId",
|
|
18542
|
+
label: "Pin provider (sessionId)",
|
|
18543
|
+
global: (configPath) => sessionRoutingCommand({ configPath }),
|
|
18544
|
+
perModel: (current) => editSessionId(current)
|
|
18545
|
+
},
|
|
18546
|
+
{
|
|
18547
|
+
value: "normalizeVolatileSystem",
|
|
18548
|
+
label: "Stabilize prefix (normalizeVolatileSystem)",
|
|
18549
|
+
global: (configPath) => normalizeVolatileSystemCommand({ configPath }),
|
|
18550
|
+
perModel: (current) => editNormalizeVolatileSystem(current)
|
|
18551
|
+
}
|
|
18552
|
+
];
|
|
18553
|
+
async function runCachingLeverMenu(opts) {
|
|
18554
|
+
for (;;) {
|
|
18555
|
+
note(opts.renderNote(), opts.noteTitle);
|
|
18556
|
+
const choice = await select({
|
|
18557
|
+
message: "Tune which lever?",
|
|
18558
|
+
options: [...CACHING_LEVERS.map((lever) => ({
|
|
18559
|
+
value: lever.value,
|
|
18560
|
+
label: lever.label
|
|
18561
|
+
})), {
|
|
18562
|
+
value: "back",
|
|
18563
|
+
label: opts.backLabel
|
|
18564
|
+
}]
|
|
18565
|
+
});
|
|
18566
|
+
if (isCancel(choice) || choice === "back") return;
|
|
18567
|
+
const chosen = CACHING_LEVERS.find((l) => l.value === choice);
|
|
18568
|
+
if (chosen) await opts.onLever(chosen);
|
|
18411
18569
|
}
|
|
18412
18570
|
}
|
|
18571
|
+
async function globalCachingMenu(opts) {
|
|
18572
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18573
|
+
await runCachingLeverMenu({
|
|
18574
|
+
noteTitle: "Prompt caching",
|
|
18575
|
+
backLabel: "← Back",
|
|
18576
|
+
renderNote: () => formatGlobalCachingSummary(readConfigFileRaw(configPath)),
|
|
18577
|
+
onLever: (lever) => lever.global(configPath)
|
|
18578
|
+
});
|
|
18579
|
+
}
|
|
18580
|
+
/** Self-persists each lever; returns the latest override. */
|
|
18581
|
+
async function perModelCachingMenu(opts) {
|
|
18582
|
+
let current = opts.current;
|
|
18583
|
+
await runCachingLeverMenu({
|
|
18584
|
+
noteTitle: `Caching for "${opts.modelKey}"`,
|
|
18585
|
+
backLabel: "← Back to override edit",
|
|
18586
|
+
renderNote: () => formatPerModelCachingSummary(opts.modelKey, current, readConfigFile(opts.configPath)),
|
|
18587
|
+
onLever: async (lever) => {
|
|
18588
|
+
const next = await lever.perModel(current, opts.configPath);
|
|
18589
|
+
if (!overridesEqual(next, current)) {
|
|
18590
|
+
setModelOverride(opts.configPath, opts.modelKey, next);
|
|
18591
|
+
current = next;
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
});
|
|
18595
|
+
return current;
|
|
18596
|
+
}
|
|
18597
|
+
async function cachingCommand(opts) {
|
|
18598
|
+
intro("Proxitor · Caching");
|
|
18599
|
+
await globalCachingMenu(opts);
|
|
18600
|
+
outro("Bye!");
|
|
18601
|
+
}
|
|
18602
|
+
//#endregion
|
|
18603
|
+
//#region src/commands/config/edit.ts
|
|
18604
|
+
function nvsHint(value) {
|
|
18605
|
+
if (value === void 0) return "(inherit)";
|
|
18606
|
+
return value ? "on" : "off";
|
|
18607
|
+
}
|
|
18608
|
+
function formatOverrideHint(override) {
|
|
18609
|
+
if (!override) return "(empty)";
|
|
18610
|
+
const parts = [];
|
|
18611
|
+
if (override.provider) {
|
|
18612
|
+
const keys = Object.keys(override.provider);
|
|
18613
|
+
parts.push(`provider: ${keys.join(", ")}`);
|
|
18614
|
+
}
|
|
18615
|
+
if (override.sessionId) parts.push(`session: ${override.sessionId}`);
|
|
18616
|
+
if (override.cacheControl) parts.push(`cache: ${override.cacheControl}`);
|
|
18617
|
+
if (override.normalizeVolatileSystem !== void 0) parts.push(`normalize: ${nvsHint(override.normalizeVolatileSystem)}`);
|
|
18618
|
+
if (override.headers) parts.push(`${Object.keys(override.headers).length} header(s)`);
|
|
18619
|
+
return parts.join(", ") || "(empty)";
|
|
18620
|
+
}
|
|
18621
|
+
function formatCachingHint(current) {
|
|
18622
|
+
return `cc ${current.cacheControl ?? "inherit"} · ttl ${current.cacheControlTtl ? describeTtl(current.cacheControlTtl) : "inherit"} · sid ${current.sessionId ?? "inherit"} · nvs ${nvsHint(current.normalizeVolatileSystem)}`;
|
|
18623
|
+
}
|
|
18624
|
+
async function editProvider(modelKey, current, client) {
|
|
18625
|
+
const isPattern = modelKey.includes("*");
|
|
18626
|
+
const mode = await selectRoutingMode("Routing mode");
|
|
18627
|
+
if (isCancel(mode)) return current;
|
|
18628
|
+
if (mode === "skip") {
|
|
18629
|
+
const { provider: _, ...rest } = current;
|
|
18630
|
+
return rest;
|
|
18631
|
+
}
|
|
18632
|
+
const providerOptions = await fetchProvidersForModel(client, modelKey, isPattern);
|
|
18633
|
+
if (!providerOptions) return current;
|
|
18634
|
+
const result = await selectProvidersByMode(mode, providerOptions);
|
|
18635
|
+
if (!result) return current;
|
|
18636
|
+
return {
|
|
18637
|
+
...current,
|
|
18638
|
+
provider: result.provider
|
|
18639
|
+
};
|
|
18640
|
+
}
|
|
18413
18641
|
/** Run the interactive "Edit model override" flow. */
|
|
18414
18642
|
async function editOverrideCommand(client, configPath) {
|
|
18415
18643
|
intro("Edit Model Override");
|
|
@@ -18432,7 +18660,6 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18432
18660
|
if (isCancel(selected)) return;
|
|
18433
18661
|
const modelKey = selected;
|
|
18434
18662
|
let current = overrides[modelKey] ?? {};
|
|
18435
|
-
showCurrentConfig(modelKey, current);
|
|
18436
18663
|
for (;;) {
|
|
18437
18664
|
const field = await select({
|
|
18438
18665
|
message: "Edit which field?",
|
|
@@ -18443,19 +18670,9 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18443
18670
|
hint: formatOverrideHint({ provider: current.provider })
|
|
18444
18671
|
},
|
|
18445
18672
|
{
|
|
18446
|
-
value: "
|
|
18447
|
-
label: "
|
|
18448
|
-
hint: current
|
|
18449
|
-
},
|
|
18450
|
-
{
|
|
18451
|
-
value: "cacheControl",
|
|
18452
|
-
label: "Cache control",
|
|
18453
|
-
hint: formatCacheHint(current.cacheControl, current.cacheControlTtl)
|
|
18454
|
-
},
|
|
18455
|
-
{
|
|
18456
|
-
value: "normalizeVolatileSystem",
|
|
18457
|
-
label: "Normalize volatile system",
|
|
18458
|
-
hint: nvsHint(current.normalizeVolatileSystem)
|
|
18673
|
+
value: "caching",
|
|
18674
|
+
label: "💾 Caching",
|
|
18675
|
+
hint: formatCachingHint(current)
|
|
18459
18676
|
},
|
|
18460
18677
|
{
|
|
18461
18678
|
value: "done",
|
|
@@ -18464,15 +18681,19 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18464
18681
|
]
|
|
18465
18682
|
});
|
|
18466
18683
|
if (isCancel(field) || field === "done") break;
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18684
|
+
if (field === "caching") {
|
|
18685
|
+
current = await perModelCachingMenu({
|
|
18686
|
+
modelKey,
|
|
18687
|
+
current,
|
|
18688
|
+
configPath: resolvedConfigPath
|
|
18689
|
+
});
|
|
18690
|
+
continue;
|
|
18691
|
+
}
|
|
18692
|
+
const before = current;
|
|
18693
|
+
current = await editProvider(modelKey, current, client);
|
|
18694
|
+
if (!overridesEqual(current, before)) setModelOverride(resolvedConfigPath, modelKey, current);
|
|
18473
18695
|
}
|
|
18474
|
-
|
|
18475
|
-
outro("✓ Override updated");
|
|
18696
|
+
outro("✓ Done");
|
|
18476
18697
|
}
|
|
18477
18698
|
//#endregion
|
|
18478
18699
|
//#region src/commands/config/list.ts
|
|
@@ -18998,31 +19219,6 @@ async function runWizard(opts = {}) {
|
|
|
18998
19219
|
}
|
|
18999
19220
|
}
|
|
19000
19221
|
//#endregion
|
|
19001
|
-
//#region src/commands/config/cache-control.ts
|
|
19002
|
-
async function cacheControlCommand(opts) {
|
|
19003
|
-
const configPath = requireConfigPath(opts?.configPath);
|
|
19004
|
-
const cfg = readConfigFile(configPath);
|
|
19005
|
-
const currentCc = cfg.cacheControl ?? DEFAULTS.cacheControl;
|
|
19006
|
-
const currentTtl = cfg.cacheControlTtl;
|
|
19007
|
-
log.info(`Current: cacheControl = ${currentCc}`);
|
|
19008
|
-
if (currentTtl) log.info(`Current: cacheControlTtl = ${currentTtl}`);
|
|
19009
|
-
const cc = await askTriState("Cache control mode", currentCc, CACHE_HINTS, { removable: true });
|
|
19010
|
-
if (typeof cc === "symbol") return;
|
|
19011
|
-
const fields = {};
|
|
19012
|
-
fields.cacheControl = cc === "reset" ? void 0 : cc;
|
|
19013
|
-
const ttlResult = await askCacheControlTtl(currentTtl, { removable: true });
|
|
19014
|
-
if (typeof ttlResult === "symbol") {
|
|
19015
|
-
setGlobalConfigFields(configPath, fields);
|
|
19016
|
-
log.success(`cacheControl set to ${cc === "reset" ? "(default)" : cc}`);
|
|
19017
|
-
return;
|
|
19018
|
-
}
|
|
19019
|
-
fields.cacheControlTtl = ttlResult === "reset" ? void 0 : ttlResult;
|
|
19020
|
-
setGlobalConfigFields(configPath, fields);
|
|
19021
|
-
const ccLabel = cc === "reset" ? "(default)" : cc;
|
|
19022
|
-
const ttlLabel = ttlResult === "reset" ? "(default)" : ttlResult;
|
|
19023
|
-
log.success(`cacheControl set to ${ccLabel}, TTL = ${ttlLabel}`);
|
|
19024
|
-
}
|
|
19025
|
-
//#endregion
|
|
19026
19222
|
//#region src/commands/config/connection.ts
|
|
19027
19223
|
const FIELD_MAP = {
|
|
19028
19224
|
apiKey: {
|
|
@@ -19096,39 +19292,6 @@ async function connectionMenuCommand(opts) {
|
|
|
19096
19292
|
log.success(`${entry.label} updated`);
|
|
19097
19293
|
}
|
|
19098
19294
|
//#endregion
|
|
19099
|
-
//#region src/commands/config/normalize-system.ts
|
|
19100
|
-
async function normalizeVolatileSystemCommand(opts) {
|
|
19101
|
-
const configPath = requireConfigPath(opts?.configPath);
|
|
19102
|
-
const current = readConfigFile(configPath).normalizeVolatileSystem ?? DEFAULTS.normalizeVolatileSystem;
|
|
19103
|
-
log.info(`Current: normalizeVolatileSystem = ${current}`);
|
|
19104
|
-
const choice = await askNormalizeVolatileSystem("Normalize Claude Code's volatile cch hash in the system prompt? Stabilizes the prefix cache for non-Anthropic providers (qwen/glm/etc.).", current, {
|
|
19105
|
-
removable: true,
|
|
19106
|
-
resetHint: `remove (default: ${DEFAULTS.normalizeVolatileSystem})`
|
|
19107
|
-
});
|
|
19108
|
-
if (typeof choice === "symbol") return;
|
|
19109
|
-
const fields = {};
|
|
19110
|
-
fields.normalizeVolatileSystem = choice === "reset" ? void 0 : choice;
|
|
19111
|
-
setGlobalConfigFields(configPath, fields);
|
|
19112
|
-
const label = choice === "reset" ? `(default: ${DEFAULTS.normalizeVolatileSystem})` : choice;
|
|
19113
|
-
log.success(`normalizeVolatileSystem set to ${label}`);
|
|
19114
|
-
}
|
|
19115
|
-
//#endregion
|
|
19116
|
-
//#region src/commands/config/session-routing.ts
|
|
19117
|
-
async function sessionRoutingCommand(opts) {
|
|
19118
|
-
const configPath = requireConfigPath(opts?.configPath);
|
|
19119
|
-
const current = readConfigFile(configPath).sessionId ?? DEFAULTS.sessionId;
|
|
19120
|
-
log.info(`Current: sessionId = ${current}`);
|
|
19121
|
-
const result = await askTriState("Session routing mode", current, SESSION_HINTS, { removable: true });
|
|
19122
|
-
if (typeof result === "symbol") return;
|
|
19123
|
-
if (result === "reset") {
|
|
19124
|
-
setGlobalConfigField(configPath, "sessionId", void 0);
|
|
19125
|
-
log.success("sessionId reset to default (auto)");
|
|
19126
|
-
return;
|
|
19127
|
-
}
|
|
19128
|
-
setGlobalConfigField(configPath, "sessionId", result);
|
|
19129
|
-
log.success(`sessionId set to ${result}`);
|
|
19130
|
-
}
|
|
19131
|
-
//#endregion
|
|
19132
19295
|
//#region src/commands/config.ts
|
|
19133
19296
|
async function runConfigMenu(client) {
|
|
19134
19297
|
intro("Proxitor Config Manager");
|
|
@@ -19150,16 +19313,8 @@ async function runConfigMenu(client) {
|
|
|
19150
19313
|
label: "🔑 API key & connection"
|
|
19151
19314
|
},
|
|
19152
19315
|
{
|
|
19153
|
-
value: "
|
|
19154
|
-
label: "
|
|
19155
|
-
},
|
|
19156
|
-
{
|
|
19157
|
-
value: "cache",
|
|
19158
|
-
label: "💾 Cache control"
|
|
19159
|
-
},
|
|
19160
|
-
{
|
|
19161
|
-
value: "normalize",
|
|
19162
|
-
label: "🧹 Normalize volatile system"
|
|
19316
|
+
value: "caching",
|
|
19317
|
+
label: "💾 Caching"
|
|
19163
19318
|
},
|
|
19164
19319
|
{
|
|
19165
19320
|
value: "_sep2",
|
|
@@ -19212,14 +19367,8 @@ async function runConfigMenu(client) {
|
|
|
19212
19367
|
case "connection":
|
|
19213
19368
|
await connectionMenuCommand();
|
|
19214
19369
|
break;
|
|
19215
|
-
case "
|
|
19216
|
-
await
|
|
19217
|
-
break;
|
|
19218
|
-
case "cache":
|
|
19219
|
-
await cacheControlCommand();
|
|
19220
|
-
break;
|
|
19221
|
-
case "normalize":
|
|
19222
|
-
await normalizeVolatileSystemCommand();
|
|
19370
|
+
case "caching":
|
|
19371
|
+
await globalCachingMenu();
|
|
19223
19372
|
break;
|
|
19224
19373
|
case "add":
|
|
19225
19374
|
await addOverrideCommand({ client });
|
|
@@ -19244,7 +19393,7 @@ async function runConfigMenu(client) {
|
|
|
19244
19393
|
}
|
|
19245
19394
|
//#endregion
|
|
19246
19395
|
//#region src/version.ts
|
|
19247
|
-
const version = "0.
|
|
19396
|
+
const version = "0.13.0";
|
|
19248
19397
|
//#endregion
|
|
19249
19398
|
//#region src/commands/doctor.ts
|
|
19250
19399
|
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
@@ -19497,11 +19646,15 @@ function fmt(value) {
|
|
|
19497
19646
|
if (value === false) return "off";
|
|
19498
19647
|
return String(value);
|
|
19499
19648
|
}
|
|
19500
|
-
|
|
19649
|
+
/** Cache-lever keys shared by global config + per-model overrides. */
|
|
19650
|
+
const CACHE_LEVER_KEYS = [
|
|
19501
19651
|
"cacheControl",
|
|
19502
19652
|
"cacheControlTtl",
|
|
19503
19653
|
"sessionId",
|
|
19504
|
-
"normalizeVolatileSystem"
|
|
19654
|
+
"normalizeVolatileSystem"
|
|
19655
|
+
];
|
|
19656
|
+
const SCALAR_KEYS = [
|
|
19657
|
+
...CACHE_LEVER_KEYS,
|
|
19505
19658
|
"authType",
|
|
19506
19659
|
"verbose",
|
|
19507
19660
|
"bodyLimit",
|
|
@@ -19511,16 +19664,37 @@ function canonicalEntries(record) {
|
|
|
19511
19664
|
if (!record) return "";
|
|
19512
19665
|
return JSON.stringify(Object.keys(record).sort().map((key) => [key, record[key]]));
|
|
19513
19666
|
}
|
|
19667
|
+
/** Field-level diff for a single model override; '' if nothing changed. */
|
|
19668
|
+
function overrideFieldDiff(prev, next) {
|
|
19669
|
+
const parts = [];
|
|
19670
|
+
for (const key of CACHE_LEVER_KEYS) if (prev?.[key] !== next?.[key]) parts.push(`${key}: ${fmt(prev?.[key])}→${fmt(next?.[key])}`);
|
|
19671
|
+
if (JSON.stringify(buildProviderRouting(prev?.provider)) !== JSON.stringify(buildProviderRouting(next?.provider))) parts.push("provider routing");
|
|
19672
|
+
if (canonicalEntries(prev?.headers) !== canonicalEntries(next?.headers)) parts.push("headers");
|
|
19673
|
+
return parts.join(", ");
|
|
19674
|
+
}
|
|
19675
|
+
/** Per-override diff: +added, -removed, `key (fields)` for modified; '' if unchanged. */
|
|
19676
|
+
function summarizeModelOverridesDiff(prev, next) {
|
|
19677
|
+
const prevKeys = new Set(Object.keys(prev ?? {}));
|
|
19678
|
+
const nextKeys = new Set(Object.keys(next ?? {}));
|
|
19679
|
+
const parts = [];
|
|
19680
|
+
for (const key of [...new Set([...prevKeys, ...nextKeys])].sort()) {
|
|
19681
|
+
const inPrev = prevKeys.has(key);
|
|
19682
|
+
const inNext = nextKeys.has(key);
|
|
19683
|
+
if (inPrev && inNext) {
|
|
19684
|
+
const fields = overrideFieldDiff(prev?.[key], next?.[key]);
|
|
19685
|
+
if (fields) parts.push(`${key} (${fields})`);
|
|
19686
|
+
} else if (inNext) parts.push(`+${key}`);
|
|
19687
|
+
else parts.push(`-${key}`);
|
|
19688
|
+
}
|
|
19689
|
+
return parts.join(", ");
|
|
19690
|
+
}
|
|
19514
19691
|
/** Diff of cache-relevant fields; '' if nothing changed. */
|
|
19515
19692
|
function summarizeChanges(prev, next) {
|
|
19516
19693
|
const parts = [];
|
|
19517
19694
|
for (const key of SCALAR_KEYS) if (prev[key] !== next[key]) parts.push(`${key}: ${fmt(prev[key])}→${fmt(next[key])}`);
|
|
19518
19695
|
if (JSON.stringify(buildProviderRouting(prev.provider)) !== JSON.stringify(buildProviderRouting(next.provider))) parts.push("provider routing");
|
|
19519
|
-
|
|
19520
|
-
|
|
19521
|
-
const nextCount = next.modelOverrides ? Object.keys(next.modelOverrides).length : 0;
|
|
19522
|
-
parts.push(`modelOverrides: ${prevCount}→${nextCount}`);
|
|
19523
|
-
}
|
|
19696
|
+
const overridesDiff = summarizeModelOverridesDiff(prev.modelOverrides, next.modelOverrides);
|
|
19697
|
+
if (overridesDiff) parts.push(`modelOverrides: ${overridesDiff}`);
|
|
19524
19698
|
if (canonicalEntries(prev.headers) !== canonicalEntries(next.headers)) parts.push("headers");
|
|
19525
19699
|
return parts.join(", ");
|
|
19526
19700
|
}
|
|
@@ -22771,6 +22945,56 @@ function buildCacheControl(existing, ttl, isAnthropic) {
|
|
|
22771
22945
|
}
|
|
22772
22946
|
return base;
|
|
22773
22947
|
}
|
|
22948
|
+
/**
|
|
22949
|
+
* Whether to normalize block-level cache_control TTLs. Mirrors the cacheControl
|
|
22950
|
+
* tri-state: `auto` rewrites only when injection is active on an Anthropic-native
|
|
22951
|
+
* endpoint; `always` rewrites everywhere; `skip` never.
|
|
22952
|
+
*/
|
|
22953
|
+
function shouldRewriteBlockTtl(mode, cacheControlMode, modelName, path) {
|
|
22954
|
+
if (mode === "skip") return false;
|
|
22955
|
+
if (mode === "always") return true;
|
|
22956
|
+
return isAnthropicEndpoint(modelName, path) && shouldInjectCacheControl(cacheControlMode, modelName, path);
|
|
22957
|
+
}
|
|
22958
|
+
/** Shallow, order-independent comparison of two cache_control objects. */
|
|
22959
|
+
function sameCacheControl(a, b) {
|
|
22960
|
+
const ka = Object.keys(a);
|
|
22961
|
+
const kb = Object.keys(b);
|
|
22962
|
+
if (ka.length !== kb.length) return false;
|
|
22963
|
+
return ka.every((k) => a[k] === b[k]);
|
|
22964
|
+
}
|
|
22965
|
+
/**
|
|
22966
|
+
* Normalize the TTL on every existing block-level cache_control breakpoint
|
|
22967
|
+
* (system, tools, messages[].content) to match the configured TTL. Reuses
|
|
22968
|
+
* buildCacheControl so block and root stay consistent. Adds no new breakpoints.
|
|
22969
|
+
* Returns whether any block was changed.
|
|
22970
|
+
*/
|
|
22971
|
+
function rewriteBlockTtls(body, ttl, isAnthropic) {
|
|
22972
|
+
let mutated = false;
|
|
22973
|
+
const rewriteNode = (obj) => {
|
|
22974
|
+
const cc = obj.cache_control;
|
|
22975
|
+
if (cc !== null && typeof cc === "object" && !Array.isArray(cc)) {
|
|
22976
|
+
const next = buildCacheControl(cc, ttl, isAnthropic);
|
|
22977
|
+
if (!sameCacheControl(cc, next)) {
|
|
22978
|
+
obj.cache_control = next;
|
|
22979
|
+
mutated = true;
|
|
22980
|
+
}
|
|
22981
|
+
}
|
|
22982
|
+
};
|
|
22983
|
+
const visitBlocks = (arr) => {
|
|
22984
|
+
if (!Array.isArray(arr)) return;
|
|
22985
|
+
for (const item of arr) {
|
|
22986
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
22987
|
+
const obj = item;
|
|
22988
|
+
rewriteNode(obj);
|
|
22989
|
+
visitBlocks(obj.content);
|
|
22990
|
+
}
|
|
22991
|
+
};
|
|
22992
|
+
visitBlocks(body.system);
|
|
22993
|
+
visitBlocks(body.tools);
|
|
22994
|
+
const messages = body.messages;
|
|
22995
|
+
for (const m of messages ?? []) visitBlocks(m?.content);
|
|
22996
|
+
return mutated;
|
|
22997
|
+
}
|
|
22774
22998
|
//#endregion
|
|
22775
22999
|
//#region src/proxy/middleware/inject-cache-control.ts
|
|
22776
23000
|
const injectCacheControl = createMiddleware(async (c, next) => {
|
|
@@ -22780,11 +23004,16 @@ const injectCacheControl = createMiddleware(async (c, next) => {
|
|
|
22780
23004
|
await next();
|
|
22781
23005
|
return;
|
|
22782
23006
|
}
|
|
23007
|
+
const isAnthropic = isAnthropicEndpoint(c.var.modelName, c.req.path);
|
|
23008
|
+
let mutated = false;
|
|
22783
23009
|
if (shouldInjectCacheControl(resolved.cacheControl, c.var.modelName, c.req.path)) {
|
|
22784
|
-
const isAnthropic = isAnthropicEndpoint(c.var.modelName, c.req.path);
|
|
22785
23010
|
parsedBody.cache_control = buildCacheControl(parsedBody.cache_control, resolved.cacheControlTtl, isAnthropic);
|
|
22786
|
-
|
|
23011
|
+
mutated = true;
|
|
22787
23012
|
}
|
|
23013
|
+
if (shouldRewriteBlockTtl(resolved.rewriteBlockTtl, resolved.cacheControl, c.var.modelName, c.req.path)) {
|
|
23014
|
+
if (rewriteBlockTtls(parsedBody, resolved.cacheControlTtl, isAnthropic)) mutated = true;
|
|
23015
|
+
}
|
|
23016
|
+
if (mutated) c.set("bodyMutated", true);
|
|
22788
23017
|
await next();
|
|
22789
23018
|
});
|
|
22790
23019
|
//#endregion
|
|
@@ -23230,6 +23459,14 @@ const configCli = (0, import_cjs.subcommands)({
|
|
|
23230
23459
|
});
|
|
23231
23460
|
}
|
|
23232
23461
|
}),
|
|
23462
|
+
cache: (0, import_cjs.command)({
|
|
23463
|
+
name: "cache",
|
|
23464
|
+
description: "Tune prompt-caching settings (interactive)",
|
|
23465
|
+
args: { ...configArgs },
|
|
23466
|
+
handler: async (args) => {
|
|
23467
|
+
await cachingCommand({ configPath: args.configPath });
|
|
23468
|
+
}
|
|
23469
|
+
}),
|
|
23233
23470
|
edit: (0, import_cjs.command)({
|
|
23234
23471
|
name: "edit",
|
|
23235
23472
|
description: "Edit an existing model override (interactive)",
|