proxitor 0.11.0 → 0.12.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 +10 -0
- package/dist/cli.mjs +313 -171
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,6 +111,16 @@ While proxitor runs, it logs cache usage from upstream so you can see whether ca
|
|
|
111
111
|
|
|
112
112
|
Quick health poke: `curl http://localhost:8828/health`.
|
|
113
113
|
|
|
114
|
+
### Tuning the cache
|
|
115
|
+
|
|
116
|
+
If the cache hit looks low, three levers fix it — tune them from `proxitor config` → **💾 Caching** (or `proxitor config cache`):
|
|
117
|
+
|
|
118
|
+
- **`cacheControl`** — inject `cache_control` to activate caching (Anthropic-native).
|
|
119
|
+
- **`sessionId`** — inject `session_id` so the provider pins from the first request.
|
|
120
|
+
- **`normalizeVolatileSystem`** — strip Claude Code's volatile `cch` hash so the prefix cache warms on non-Anthropic providers (qwen/glm/…).
|
|
121
|
+
|
|
122
|
+
See the [configuration reference](./docs/configuration.md#prompt-caching) for the full detail.
|
|
123
|
+
|
|
114
124
|
## Commands
|
|
115
125
|
|
|
116
126
|
| Command | Description |
|
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";
|
|
@@ -16491,18 +16491,29 @@ function findConfigFile(explicitPath) {
|
|
|
16491
16491
|
if (found) return found;
|
|
16492
16492
|
throw new MissingConfigError(getConfigSearchPaths());
|
|
16493
16493
|
}
|
|
16494
|
-
function
|
|
16494
|
+
function parseConfigFile(filePath) {
|
|
16495
16495
|
const content = readFileSync(filePath, "utf-8");
|
|
16496
|
-
let raw;
|
|
16497
16496
|
try {
|
|
16498
|
-
|
|
16497
|
+
return filePath.endsWith(".json") ? JSON.parse(content) : (0, import_dist.parse)(content);
|
|
16499
16498
|
} catch (err) {
|
|
16500
16499
|
throw new ConfigParseError(filePath, err instanceof Error ? err : void 0);
|
|
16501
16500
|
}
|
|
16502
|
-
|
|
16501
|
+
}
|
|
16502
|
+
function readConfigFile(filePath) {
|
|
16503
|
+
const result = proxyConfigFileSchema.safeParse(parseConfigFile(filePath));
|
|
16503
16504
|
if (!result.success) throw new ConfigValidationError(filePath, result.error);
|
|
16504
16505
|
return result.data;
|
|
16505
16506
|
}
|
|
16507
|
+
/**
|
|
16508
|
+
* Validated like readConfigFile, but schema defaults are NOT applied, so absent
|
|
16509
|
+
* keys stay undefined — use when "absent" must differ from "explicit default".
|
|
16510
|
+
*/
|
|
16511
|
+
function readConfigFileRaw(filePath) {
|
|
16512
|
+
const raw = parseConfigFile(filePath);
|
|
16513
|
+
const result = proxyConfigFileSchema.safeParse(raw);
|
|
16514
|
+
if (!result.success) throw new ConfigValidationError(filePath, result.error);
|
|
16515
|
+
return raw ?? {};
|
|
16516
|
+
}
|
|
16506
16517
|
//#endregion
|
|
16507
16518
|
//#region src/openrouter/cache.ts
|
|
16508
16519
|
const CACHE_DIR = join(homedir(), ".cache", "proxitor");
|
|
@@ -17938,7 +17949,7 @@ async function askNormalizeVolatileSystem(message, current, opts) {
|
|
|
17938
17949
|
});
|
|
17939
17950
|
return await select({
|
|
17940
17951
|
message,
|
|
17941
|
-
initialValue: current ?? false,
|
|
17952
|
+
initialValue: current ?? (opts?.removable ? "reset" : false),
|
|
17942
17953
|
options
|
|
17943
17954
|
});
|
|
17944
17955
|
}
|
|
@@ -17967,7 +17978,7 @@ async function askTriState(message, current, hints, opts) {
|
|
|
17967
17978
|
});
|
|
17968
17979
|
return await select({
|
|
17969
17980
|
message,
|
|
17970
|
-
initialValue: current ?? "auto",
|
|
17981
|
+
initialValue: current ?? (opts?.removable ? "reset" : "auto"),
|
|
17971
17982
|
options
|
|
17972
17983
|
});
|
|
17973
17984
|
}
|
|
@@ -18003,7 +18014,7 @@ async function askCacheControlTtl(current, opts) {
|
|
|
18003
18014
|
});
|
|
18004
18015
|
return await select({
|
|
18005
18016
|
message: "Cache TTL",
|
|
18006
|
-
initialValue: current ?? "5m",
|
|
18017
|
+
initialValue: current ?? (opts?.removable ? "reset" : "5m"),
|
|
18007
18018
|
options
|
|
18008
18019
|
});
|
|
18009
18020
|
}
|
|
@@ -18016,14 +18027,14 @@ function applyField(obj, key, field) {
|
|
|
18016
18027
|
else obj[key] = field.value;
|
|
18017
18028
|
}
|
|
18018
18029
|
async function collectSessionTriState(currentSid) {
|
|
18019
|
-
const sid = await askTriState("Session ID mode", currentSid
|
|
18030
|
+
const sid = await askTriState("Session ID mode", currentSid, SESSION_HINTS, { removable: true });
|
|
18020
18031
|
if (typeof sid === "symbol") return null;
|
|
18021
18032
|
if (sid === "reset") return { sessionId: { remove: true } };
|
|
18022
18033
|
return { sessionId: { value: sid } };
|
|
18023
18034
|
}
|
|
18024
18035
|
/** TTL is independent of cache mode. Mode-cancel aborts; TTL-cancel keeps existing TTL. */
|
|
18025
18036
|
async function collectCacheTriState(currentCc, currentTtl, globalTtl) {
|
|
18026
|
-
const cc = await askTriState("Cache control mode", currentCc
|
|
18037
|
+
const cc = await askTriState("Cache control mode", currentCc, CACHE_HINTS, { removable: true });
|
|
18027
18038
|
if (typeof cc === "symbol") return null;
|
|
18028
18039
|
let cacheControl;
|
|
18029
18040
|
if (cc === "reset") cacheControl = { remove: true };
|
|
@@ -18318,30 +18329,116 @@ async function browseModelsCommand(client) {
|
|
|
18318
18329
|
});
|
|
18319
18330
|
}
|
|
18320
18331
|
//#endregion
|
|
18321
|
-
//#region src/commands/config/
|
|
18322
|
-
function
|
|
18323
|
-
|
|
18332
|
+
//#region src/commands/config/cache-control.ts
|
|
18333
|
+
async function cacheControlCommand(opts) {
|
|
18334
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18335
|
+
const cfg = readConfigFileRaw(configPath);
|
|
18336
|
+
const rawCc = cfg.cacheControl;
|
|
18337
|
+
const rawTtl = cfg.cacheControlTtl;
|
|
18338
|
+
const effectiveCc = rawCc ?? DEFAULTS.cacheControl;
|
|
18339
|
+
log.info(`Current: cacheControl = ${rawCc === void 0 ? `(default -> ${effectiveCc})` : effectiveCc}`);
|
|
18340
|
+
if (rawTtl) log.info(`Current: cacheControlTtl = ${rawTtl}`);
|
|
18341
|
+
const cc = await askTriState("Cache control mode", rawCc, CACHE_HINTS, { removable: true });
|
|
18342
|
+
if (typeof cc === "symbol") return;
|
|
18343
|
+
const fields = {};
|
|
18344
|
+
fields.cacheControl = cc === "reset" ? void 0 : cc;
|
|
18345
|
+
const ttlResult = await askCacheControlTtl(rawTtl, { removable: true });
|
|
18346
|
+
if (typeof ttlResult === "symbol") {
|
|
18347
|
+
setGlobalConfigFields(configPath, fields);
|
|
18348
|
+
log.success(`cacheControl set to ${cc === "reset" ? "(default)" : cc}`);
|
|
18349
|
+
return;
|
|
18350
|
+
}
|
|
18351
|
+
fields.cacheControlTtl = ttlResult === "reset" ? void 0 : ttlResult;
|
|
18352
|
+
setGlobalConfigFields(configPath, fields);
|
|
18353
|
+
const ccLabel = cc === "reset" ? "(default)" : cc;
|
|
18354
|
+
const ttlLabel = ttlResult === "reset" ? "(default)" : ttlResult;
|
|
18355
|
+
log.success(`cacheControl set to ${ccLabel}, TTL = ${ttlLabel}`);
|
|
18356
|
+
}
|
|
18357
|
+
//#endregion
|
|
18358
|
+
//#region src/commands/config/caching-summary.ts
|
|
18359
|
+
/** Friendly TTL label (omit→strip, skip→passthrough). */
|
|
18360
|
+
function describeTtl(value) {
|
|
18361
|
+
if (value === void 0) return "(default)";
|
|
18362
|
+
if (value === "omit") return "strip";
|
|
18363
|
+
if (value === "skip") return "passthrough";
|
|
18364
|
+
return value;
|
|
18365
|
+
}
|
|
18366
|
+
/** Global NVS: distinguish absent (default-origin) from an explicit on/off. */
|
|
18367
|
+
function globalNvsLabel(value) {
|
|
18368
|
+
if (value === void 0) return `(default -> ${DEFAULTS.normalizeVolatileSystem ? "on" : "off"})`;
|
|
18324
18369
|
return value ? "on" : "off";
|
|
18325
18370
|
}
|
|
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)";
|
|
18371
|
+
/** Global tri-state: distinguish absent (default-origin) from an explicit mode. */
|
|
18372
|
+
function globalTriStateLabel(value, fallback) {
|
|
18373
|
+
return value === void 0 ? `(default -> ${fallback})` : value;
|
|
18338
18374
|
}
|
|
18339
|
-
function
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18375
|
+
function nvsLabel(value, globalValue) {
|
|
18376
|
+
if (value === void 0) return `(inherit -> ${globalValue ? "on" : "off"})`;
|
|
18377
|
+
return value ? "on" : "off";
|
|
18378
|
+
}
|
|
18379
|
+
function perModelTtl(current, globalCfg) {
|
|
18380
|
+
if (current.cacheControlTtl !== void 0) return describeTtl(current.cacheControlTtl);
|
|
18381
|
+
if (globalCfg.cacheControlTtl !== void 0) return `(inherit -> ${describeTtl(globalCfg.cacheControlTtl)})`;
|
|
18382
|
+
return "(inherit)";
|
|
18383
|
+
}
|
|
18384
|
+
function formatGlobalCachingSummary(cfg) {
|
|
18385
|
+
const cc = globalTriStateLabel(cfg.cacheControl, DEFAULTS.cacheControl);
|
|
18386
|
+
const sid = globalTriStateLabel(cfg.sessionId, DEFAULTS.sessionId);
|
|
18387
|
+
const nvs = globalNvsLabel(cfg.normalizeVolatileSystem);
|
|
18388
|
+
return [
|
|
18389
|
+
"Three settings shape the request so cache survives.",
|
|
18390
|
+
"",
|
|
18391
|
+
` cacheControl = ${cc} (activate cache)`,
|
|
18392
|
+
` cacheControlTtl = ${describeTtl(cfg.cacheControlTtl)}`,
|
|
18393
|
+
` sessionId = ${sid} (pin provider)`,
|
|
18394
|
+
` normalizeVolatileSystem = ${nvs} (stable prefix)`,
|
|
18395
|
+
"",
|
|
18396
|
+
"Anthropic -> levers 1+2 - non-Anthropic (qwen/glm) -> all 3"
|
|
18397
|
+
].join("\n");
|
|
18398
|
+
}
|
|
18399
|
+
function formatPerModelCachingSummary(modelKey, current, globalCfg) {
|
|
18400
|
+
const gCc = globalCfg.cacheControl ?? DEFAULTS.cacheControl;
|
|
18401
|
+
const gSid = globalCfg.sessionId ?? DEFAULTS.sessionId;
|
|
18402
|
+
const gNvs = globalCfg.normalizeVolatileSystem ?? DEFAULTS.normalizeVolatileSystem;
|
|
18403
|
+
const cc = current.cacheControl ?? `(inherit -> ${gCc})`;
|
|
18404
|
+
const ttl = perModelTtl(current, globalCfg);
|
|
18405
|
+
const sid = current.sessionId ?? `(inherit -> ${gSid})`;
|
|
18406
|
+
const nvs = nvsLabel(current.normalizeVolatileSystem, gNvs);
|
|
18407
|
+
return [
|
|
18408
|
+
`Caching for "${modelKey}"`,
|
|
18409
|
+
"",
|
|
18410
|
+
` cacheControl = ${cc}`,
|
|
18411
|
+
` cacheControlTtl = ${ttl}`,
|
|
18412
|
+
` sessionId = ${sid}`,
|
|
18413
|
+
` normalizeVolatileSystem = ${nvs}`
|
|
18414
|
+
].join("\n");
|
|
18344
18415
|
}
|
|
18416
|
+
//#endregion
|
|
18417
|
+
//#region src/commands/config/equality.ts
|
|
18418
|
+
/** Deep equality for overrides — gates no-op writes. */
|
|
18419
|
+
function overridesEqual(a, b) {
|
|
18420
|
+
return isDeepStrictEqual(a, b);
|
|
18421
|
+
}
|
|
18422
|
+
//#endregion
|
|
18423
|
+
//#region src/commands/config/normalize-system.ts
|
|
18424
|
+
async function normalizeVolatileSystemCommand(opts) {
|
|
18425
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18426
|
+
const raw = readConfigFileRaw(configPath).normalizeVolatileSystem;
|
|
18427
|
+
const effective = raw ?? DEFAULTS.normalizeVolatileSystem;
|
|
18428
|
+
log.info(`Current: normalizeVolatileSystem = ${raw === void 0 ? `(default -> ${effective ? "on" : "off"})` : effective}`);
|
|
18429
|
+
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, {
|
|
18430
|
+
removable: true,
|
|
18431
|
+
resetHint: `remove (default: ${DEFAULTS.normalizeVolatileSystem})`
|
|
18432
|
+
});
|
|
18433
|
+
if (typeof choice === "symbol") return;
|
|
18434
|
+
const fields = {};
|
|
18435
|
+
fields.normalizeVolatileSystem = choice === "reset" ? void 0 : choice;
|
|
18436
|
+
setGlobalConfigFields(configPath, fields);
|
|
18437
|
+
const label = choice === "reset" ? `(default: ${DEFAULTS.normalizeVolatileSystem})` : choice;
|
|
18438
|
+
log.success(`normalizeVolatileSystem set to ${label}`);
|
|
18439
|
+
}
|
|
18440
|
+
//#endregion
|
|
18441
|
+
//#region src/commands/config/override-levers.ts
|
|
18345
18442
|
function readGlobalTtl(configPath) {
|
|
18346
18443
|
if (!configPath) return void 0;
|
|
18347
18444
|
try {
|
|
@@ -18350,32 +18447,6 @@ function readGlobalTtl(configPath) {
|
|
|
18350
18447
|
return;
|
|
18351
18448
|
}
|
|
18352
18449
|
}
|
|
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
18450
|
async function editSessionId(current) {
|
|
18380
18451
|
const result = await collectSessionTriState(current.sessionId);
|
|
18381
18452
|
if (result === null) return current;
|
|
@@ -18383,7 +18454,6 @@ async function editSessionId(current) {
|
|
|
18383
18454
|
applyField(next, "sessionId", result.sessionId);
|
|
18384
18455
|
return next;
|
|
18385
18456
|
}
|
|
18386
|
-
/** @internal */
|
|
18387
18457
|
async function editCacheControl(current, configPath) {
|
|
18388
18458
|
const globalTtl = readGlobalTtl(configPath);
|
|
18389
18459
|
const result = await collectCacheTriState(current.cacheControl, current.cacheControlTtl, globalTtl);
|
|
@@ -18393,7 +18463,6 @@ async function editCacheControl(current, configPath) {
|
|
|
18393
18463
|
applyField(next, "cacheControlTtl", result.cacheControlTtl);
|
|
18394
18464
|
return next;
|
|
18395
18465
|
}
|
|
18396
|
-
/** @internal */
|
|
18397
18466
|
async function editNormalizeVolatileSystem(current) {
|
|
18398
18467
|
const result = await collectNormalizeVolatileSystem(current.normalizeVolatileSystem);
|
|
18399
18468
|
if (result === null) return current;
|
|
@@ -18401,14 +18470,133 @@ async function editNormalizeVolatileSystem(current) {
|
|
|
18401
18470
|
applyField(next, "normalizeVolatileSystem", result.normalizeVolatileSystem);
|
|
18402
18471
|
return next;
|
|
18403
18472
|
}
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18473
|
+
//#endregion
|
|
18474
|
+
//#region src/commands/config/session-routing.ts
|
|
18475
|
+
async function sessionRoutingCommand(opts) {
|
|
18476
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18477
|
+
const raw = readConfigFileRaw(configPath).sessionId;
|
|
18478
|
+
const effective = raw ?? DEFAULTS.sessionId;
|
|
18479
|
+
log.info(`Current: sessionId = ${raw === void 0 ? `(default -> ${effective})` : effective}`);
|
|
18480
|
+
const result = await askTriState("Session routing mode", raw, SESSION_HINTS, { removable: true });
|
|
18481
|
+
if (typeof result === "symbol") return;
|
|
18482
|
+
if (result === "reset") {
|
|
18483
|
+
setGlobalConfigField(configPath, "sessionId", void 0);
|
|
18484
|
+
log.success("sessionId reset to default (auto)");
|
|
18485
|
+
return;
|
|
18486
|
+
}
|
|
18487
|
+
setGlobalConfigField(configPath, "sessionId", result);
|
|
18488
|
+
log.success(`sessionId set to ${result}`);
|
|
18489
|
+
}
|
|
18490
|
+
//#endregion
|
|
18491
|
+
//#region src/commands/config/caching-menu.ts
|
|
18492
|
+
/** Cache levers: option list + dispatch in one table. */
|
|
18493
|
+
const CACHING_LEVERS = [
|
|
18494
|
+
{
|
|
18495
|
+
value: "cacheControl",
|
|
18496
|
+
label: "Activate caching — mode + TTL (cacheControl)",
|
|
18497
|
+
global: (configPath) => cacheControlCommand({ configPath }),
|
|
18498
|
+
perModel: (current, configPath) => editCacheControl(current, configPath)
|
|
18499
|
+
},
|
|
18500
|
+
{
|
|
18501
|
+
value: "sessionId",
|
|
18502
|
+
label: "Pin provider (sessionId)",
|
|
18503
|
+
global: (configPath) => sessionRoutingCommand({ configPath }),
|
|
18504
|
+
perModel: (current) => editSessionId(current)
|
|
18505
|
+
},
|
|
18506
|
+
{
|
|
18507
|
+
value: "normalizeVolatileSystem",
|
|
18508
|
+
label: "Stabilize prefix (normalizeVolatileSystem)",
|
|
18509
|
+
global: (configPath) => normalizeVolatileSystemCommand({ configPath }),
|
|
18510
|
+
perModel: (current) => editNormalizeVolatileSystem(current)
|
|
18511
|
+
}
|
|
18512
|
+
];
|
|
18513
|
+
async function runCachingLeverMenu(opts) {
|
|
18514
|
+
for (;;) {
|
|
18515
|
+
note(opts.renderNote(), opts.noteTitle);
|
|
18516
|
+
const choice = await select({
|
|
18517
|
+
message: "Tune which lever?",
|
|
18518
|
+
options: [...CACHING_LEVERS.map((lever) => ({
|
|
18519
|
+
value: lever.value,
|
|
18520
|
+
label: lever.label
|
|
18521
|
+
})), {
|
|
18522
|
+
value: "back",
|
|
18523
|
+
label: opts.backLabel
|
|
18524
|
+
}]
|
|
18525
|
+
});
|
|
18526
|
+
if (isCancel(choice) || choice === "back") return;
|
|
18527
|
+
const chosen = CACHING_LEVERS.find((l) => l.value === choice);
|
|
18528
|
+
if (chosen) await opts.onLever(chosen);
|
|
18529
|
+
}
|
|
18530
|
+
}
|
|
18531
|
+
async function globalCachingMenu(opts) {
|
|
18532
|
+
const configPath = requireConfigPath(opts?.configPath);
|
|
18533
|
+
await runCachingLeverMenu({
|
|
18534
|
+
noteTitle: "Prompt caching",
|
|
18535
|
+
backLabel: "← Back",
|
|
18536
|
+
renderNote: () => formatGlobalCachingSummary(readConfigFileRaw(configPath)),
|
|
18537
|
+
onLever: (lever) => lever.global(configPath)
|
|
18538
|
+
});
|
|
18539
|
+
}
|
|
18540
|
+
/** Self-persists each lever; returns the latest override. */
|
|
18541
|
+
async function perModelCachingMenu(opts) {
|
|
18542
|
+
let current = opts.current;
|
|
18543
|
+
await runCachingLeverMenu({
|
|
18544
|
+
noteTitle: `Caching for "${opts.modelKey}"`,
|
|
18545
|
+
backLabel: "← Back to override edit",
|
|
18546
|
+
renderNote: () => formatPerModelCachingSummary(opts.modelKey, current, readConfigFile(opts.configPath)),
|
|
18547
|
+
onLever: async (lever) => {
|
|
18548
|
+
const next = await lever.perModel(current, opts.configPath);
|
|
18549
|
+
if (!overridesEqual(next, current)) {
|
|
18550
|
+
setModelOverride(opts.configPath, opts.modelKey, next);
|
|
18551
|
+
current = next;
|
|
18552
|
+
}
|
|
18553
|
+
}
|
|
18554
|
+
});
|
|
18555
|
+
return current;
|
|
18556
|
+
}
|
|
18557
|
+
async function cachingCommand(opts) {
|
|
18558
|
+
intro("Proxitor · Caching");
|
|
18559
|
+
await globalCachingMenu(opts);
|
|
18560
|
+
outro("Bye!");
|
|
18561
|
+
}
|
|
18562
|
+
//#endregion
|
|
18563
|
+
//#region src/commands/config/edit.ts
|
|
18564
|
+
function nvsHint(value) {
|
|
18565
|
+
if (value === void 0) return "(inherit)";
|
|
18566
|
+
return value ? "on" : "off";
|
|
18567
|
+
}
|
|
18568
|
+
function formatOverrideHint(override) {
|
|
18569
|
+
if (!override) return "(empty)";
|
|
18570
|
+
const parts = [];
|
|
18571
|
+
if (override.provider) {
|
|
18572
|
+
const keys = Object.keys(override.provider);
|
|
18573
|
+
parts.push(`provider: ${keys.join(", ")}`);
|
|
18574
|
+
}
|
|
18575
|
+
if (override.sessionId) parts.push(`session: ${override.sessionId}`);
|
|
18576
|
+
if (override.cacheControl) parts.push(`cache: ${override.cacheControl}`);
|
|
18577
|
+
if (override.normalizeVolatileSystem !== void 0) parts.push(`normalize: ${nvsHint(override.normalizeVolatileSystem)}`);
|
|
18578
|
+
if (override.headers) parts.push(`${Object.keys(override.headers).length} header(s)`);
|
|
18579
|
+
return parts.join(", ") || "(empty)";
|
|
18580
|
+
}
|
|
18581
|
+
function formatCachingHint(current) {
|
|
18582
|
+
return `cc ${current.cacheControl ?? "inherit"} · ttl ${current.cacheControlTtl ? describeTtl(current.cacheControlTtl) : "inherit"} · sid ${current.sessionId ?? "inherit"} · nvs ${nvsHint(current.normalizeVolatileSystem)}`;
|
|
18583
|
+
}
|
|
18584
|
+
async function editProvider(modelKey, current, client) {
|
|
18585
|
+
const isPattern = modelKey.includes("*");
|
|
18586
|
+
const mode = await selectRoutingMode("Routing mode");
|
|
18587
|
+
if (isCancel(mode)) return current;
|
|
18588
|
+
if (mode === "skip") {
|
|
18589
|
+
const { provider: _, ...rest } = current;
|
|
18590
|
+
return rest;
|
|
18411
18591
|
}
|
|
18592
|
+
const providerOptions = await fetchProvidersForModel(client, modelKey, isPattern);
|
|
18593
|
+
if (!providerOptions) return current;
|
|
18594
|
+
const result = await selectProvidersByMode(mode, providerOptions);
|
|
18595
|
+
if (!result) return current;
|
|
18596
|
+
return {
|
|
18597
|
+
...current,
|
|
18598
|
+
provider: result.provider
|
|
18599
|
+
};
|
|
18412
18600
|
}
|
|
18413
18601
|
/** Run the interactive "Edit model override" flow. */
|
|
18414
18602
|
async function editOverrideCommand(client, configPath) {
|
|
@@ -18432,7 +18620,6 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18432
18620
|
if (isCancel(selected)) return;
|
|
18433
18621
|
const modelKey = selected;
|
|
18434
18622
|
let current = overrides[modelKey] ?? {};
|
|
18435
|
-
showCurrentConfig(modelKey, current);
|
|
18436
18623
|
for (;;) {
|
|
18437
18624
|
const field = await select({
|
|
18438
18625
|
message: "Edit which field?",
|
|
@@ -18443,19 +18630,9 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18443
18630
|
hint: formatOverrideHint({ provider: current.provider })
|
|
18444
18631
|
},
|
|
18445
18632
|
{
|
|
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)
|
|
18633
|
+
value: "caching",
|
|
18634
|
+
label: "💾 Caching",
|
|
18635
|
+
hint: formatCachingHint(current)
|
|
18459
18636
|
},
|
|
18460
18637
|
{
|
|
18461
18638
|
value: "done",
|
|
@@ -18464,15 +18641,19 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18464
18641
|
]
|
|
18465
18642
|
});
|
|
18466
18643
|
if (isCancel(field) || field === "done") break;
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18644
|
+
if (field === "caching") {
|
|
18645
|
+
current = await perModelCachingMenu({
|
|
18646
|
+
modelKey,
|
|
18647
|
+
current,
|
|
18648
|
+
configPath: resolvedConfigPath
|
|
18649
|
+
});
|
|
18650
|
+
continue;
|
|
18651
|
+
}
|
|
18652
|
+
const before = current;
|
|
18653
|
+
current = await editProvider(modelKey, current, client);
|
|
18654
|
+
if (!overridesEqual(current, before)) setModelOverride(resolvedConfigPath, modelKey, current);
|
|
18473
18655
|
}
|
|
18474
|
-
|
|
18475
|
-
outro("✓ Override updated");
|
|
18656
|
+
outro("✓ Done");
|
|
18476
18657
|
}
|
|
18477
18658
|
//#endregion
|
|
18478
18659
|
//#region src/commands/config/list.ts
|
|
@@ -18998,31 +19179,6 @@ async function runWizard(opts = {}) {
|
|
|
18998
19179
|
}
|
|
18999
19180
|
}
|
|
19000
19181
|
//#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
19182
|
//#region src/commands/config/connection.ts
|
|
19027
19183
|
const FIELD_MAP = {
|
|
19028
19184
|
apiKey: {
|
|
@@ -19096,39 +19252,6 @@ async function connectionMenuCommand(opts) {
|
|
|
19096
19252
|
log.success(`${entry.label} updated`);
|
|
19097
19253
|
}
|
|
19098
19254
|
//#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
19255
|
//#region src/commands/config.ts
|
|
19133
19256
|
async function runConfigMenu(client) {
|
|
19134
19257
|
intro("Proxitor Config Manager");
|
|
@@ -19150,16 +19273,8 @@ async function runConfigMenu(client) {
|
|
|
19150
19273
|
label: "🔑 API key & connection"
|
|
19151
19274
|
},
|
|
19152
19275
|
{
|
|
19153
|
-
value: "
|
|
19154
|
-
label: "
|
|
19155
|
-
},
|
|
19156
|
-
{
|
|
19157
|
-
value: "cache",
|
|
19158
|
-
label: "💾 Cache control"
|
|
19159
|
-
},
|
|
19160
|
-
{
|
|
19161
|
-
value: "normalize",
|
|
19162
|
-
label: "🧹 Normalize volatile system"
|
|
19276
|
+
value: "caching",
|
|
19277
|
+
label: "💾 Caching"
|
|
19163
19278
|
},
|
|
19164
19279
|
{
|
|
19165
19280
|
value: "_sep2",
|
|
@@ -19212,14 +19327,8 @@ async function runConfigMenu(client) {
|
|
|
19212
19327
|
case "connection":
|
|
19213
19328
|
await connectionMenuCommand();
|
|
19214
19329
|
break;
|
|
19215
|
-
case "
|
|
19216
|
-
await
|
|
19217
|
-
break;
|
|
19218
|
-
case "cache":
|
|
19219
|
-
await cacheControlCommand();
|
|
19220
|
-
break;
|
|
19221
|
-
case "normalize":
|
|
19222
|
-
await normalizeVolatileSystemCommand();
|
|
19330
|
+
case "caching":
|
|
19331
|
+
await globalCachingMenu();
|
|
19223
19332
|
break;
|
|
19224
19333
|
case "add":
|
|
19225
19334
|
await addOverrideCommand({ client });
|
|
@@ -19244,7 +19353,7 @@ async function runConfigMenu(client) {
|
|
|
19244
19353
|
}
|
|
19245
19354
|
//#endregion
|
|
19246
19355
|
//#region src/version.ts
|
|
19247
|
-
const version = "0.
|
|
19356
|
+
const version = "0.12.0";
|
|
19248
19357
|
//#endregion
|
|
19249
19358
|
//#region src/commands/doctor.ts
|
|
19250
19359
|
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
@@ -19497,11 +19606,15 @@ function fmt(value) {
|
|
|
19497
19606
|
if (value === false) return "off";
|
|
19498
19607
|
return String(value);
|
|
19499
19608
|
}
|
|
19500
|
-
|
|
19609
|
+
/** Cache-lever keys shared by global config + per-model overrides. */
|
|
19610
|
+
const CACHE_LEVER_KEYS = [
|
|
19501
19611
|
"cacheControl",
|
|
19502
19612
|
"cacheControlTtl",
|
|
19503
19613
|
"sessionId",
|
|
19504
|
-
"normalizeVolatileSystem"
|
|
19614
|
+
"normalizeVolatileSystem"
|
|
19615
|
+
];
|
|
19616
|
+
const SCALAR_KEYS = [
|
|
19617
|
+
...CACHE_LEVER_KEYS,
|
|
19505
19618
|
"authType",
|
|
19506
19619
|
"verbose",
|
|
19507
19620
|
"bodyLimit",
|
|
@@ -19511,16 +19624,37 @@ function canonicalEntries(record) {
|
|
|
19511
19624
|
if (!record) return "";
|
|
19512
19625
|
return JSON.stringify(Object.keys(record).sort().map((key) => [key, record[key]]));
|
|
19513
19626
|
}
|
|
19627
|
+
/** Field-level diff for a single model override; '' if nothing changed. */
|
|
19628
|
+
function overrideFieldDiff(prev, next) {
|
|
19629
|
+
const parts = [];
|
|
19630
|
+
for (const key of CACHE_LEVER_KEYS) if (prev?.[key] !== next?.[key]) parts.push(`${key}: ${fmt(prev?.[key])}→${fmt(next?.[key])}`);
|
|
19631
|
+
if (JSON.stringify(buildProviderRouting(prev?.provider)) !== JSON.stringify(buildProviderRouting(next?.provider))) parts.push("provider routing");
|
|
19632
|
+
if (canonicalEntries(prev?.headers) !== canonicalEntries(next?.headers)) parts.push("headers");
|
|
19633
|
+
return parts.join(", ");
|
|
19634
|
+
}
|
|
19635
|
+
/** Per-override diff: +added, -removed, `key (fields)` for modified; '' if unchanged. */
|
|
19636
|
+
function summarizeModelOverridesDiff(prev, next) {
|
|
19637
|
+
const prevKeys = new Set(Object.keys(prev ?? {}));
|
|
19638
|
+
const nextKeys = new Set(Object.keys(next ?? {}));
|
|
19639
|
+
const parts = [];
|
|
19640
|
+
for (const key of [...new Set([...prevKeys, ...nextKeys])].sort()) {
|
|
19641
|
+
const inPrev = prevKeys.has(key);
|
|
19642
|
+
const inNext = nextKeys.has(key);
|
|
19643
|
+
if (inPrev && inNext) {
|
|
19644
|
+
const fields = overrideFieldDiff(prev?.[key], next?.[key]);
|
|
19645
|
+
if (fields) parts.push(`${key} (${fields})`);
|
|
19646
|
+
} else if (inNext) parts.push(`+${key}`);
|
|
19647
|
+
else parts.push(`-${key}`);
|
|
19648
|
+
}
|
|
19649
|
+
return parts.join(", ");
|
|
19650
|
+
}
|
|
19514
19651
|
/** Diff of cache-relevant fields; '' if nothing changed. */
|
|
19515
19652
|
function summarizeChanges(prev, next) {
|
|
19516
19653
|
const parts = [];
|
|
19517
19654
|
for (const key of SCALAR_KEYS) if (prev[key] !== next[key]) parts.push(`${key}: ${fmt(prev[key])}→${fmt(next[key])}`);
|
|
19518
19655
|
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
|
-
}
|
|
19656
|
+
const overridesDiff = summarizeModelOverridesDiff(prev.modelOverrides, next.modelOverrides);
|
|
19657
|
+
if (overridesDiff) parts.push(`modelOverrides: ${overridesDiff}`);
|
|
19524
19658
|
if (canonicalEntries(prev.headers) !== canonicalEntries(next.headers)) parts.push("headers");
|
|
19525
19659
|
return parts.join(", ");
|
|
19526
19660
|
}
|
|
@@ -23230,6 +23364,14 @@ const configCli = (0, import_cjs.subcommands)({
|
|
|
23230
23364
|
});
|
|
23231
23365
|
}
|
|
23232
23366
|
}),
|
|
23367
|
+
cache: (0, import_cjs.command)({
|
|
23368
|
+
name: "cache",
|
|
23369
|
+
description: "Tune prompt-caching settings (interactive)",
|
|
23370
|
+
args: { ...configArgs },
|
|
23371
|
+
handler: async (args) => {
|
|
23372
|
+
await cachingCommand({ configPath: args.configPath });
|
|
23373
|
+
}
|
|
23374
|
+
}),
|
|
23233
23375
|
edit: (0, import_cjs.command)({
|
|
23234
23376
|
name: "edit",
|
|
23235
23377
|
description: "Edit an existing model override (interactive)",
|