proxitor 0.10.0 → 0.11.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/cli.mjs +247 -55
- package/dist/cli.mjs.map +1 -1
- package/dist/prompt.mjs +3 -3
- package/dist/prompt.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -6,8 +6,8 @@ import * as tty$1 from "node:tty";
|
|
|
6
6
|
import tty, { ReadStream } from "node:tty";
|
|
7
7
|
import { formatWithOptions, styleText } from "node:util";
|
|
8
8
|
import * as l$1 from "node:readline";
|
|
9
|
-
import
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
9
|
+
import l__default from "node:readline";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
11
11
|
import { dirname, join, resolve, sep } from "node:path";
|
|
12
12
|
import { createServer } from "node:net";
|
|
13
13
|
import { STATUS_CODES, createServer as createServer$1 } from "node:http";
|
|
@@ -4603,7 +4603,7 @@ var V = class {
|
|
|
4603
4603
|
this.state = "cancel", this.close();
|
|
4604
4604
|
}, { once: true });
|
|
4605
4605
|
}
|
|
4606
|
-
this.rl =
|
|
4606
|
+
this.rl = l__default.createInterface({
|
|
4607
4607
|
input: this.input,
|
|
4608
4608
|
tabSize: 2,
|
|
4609
4609
|
prompt: "",
|
|
@@ -17220,7 +17220,7 @@ const r = Object.create(null), i = (e) => globalThis.process?.env || import.meta
|
|
|
17220
17220
|
const e = i(true);
|
|
17221
17221
|
return Object.keys(e);
|
|
17222
17222
|
}
|
|
17223
|
-
}), t = typeof process < "u" && process.env && process.env.NODE_ENV || "", f
|
|
17223
|
+
}), t = typeof process < "u" && process.env && process.env.NODE_ENV || "", f = [
|
|
17224
17224
|
["APPVEYOR"],
|
|
17225
17225
|
[
|
|
17226
17226
|
"AWS_AMPLIFY",
|
|
@@ -17312,7 +17312,7 @@ const r = Object.create(null), i = (e) => globalThis.process?.env || import.meta
|
|
|
17312
17312
|
]
|
|
17313
17313
|
];
|
|
17314
17314
|
function b() {
|
|
17315
|
-
if (globalThis.process?.env) for (const e of f
|
|
17315
|
+
if (globalThis.process?.env) for (const e of f) {
|
|
17316
17316
|
const s = e[1] || e[0];
|
|
17317
17317
|
if (globalThis.process?.env[s]) return {
|
|
17318
17318
|
name: e[0].toLowerCase(),
|
|
@@ -17917,6 +17917,31 @@ const CACHE_HINTS = {
|
|
|
17917
17917
|
always: "All models",
|
|
17918
17918
|
skip: "Passthrough — leave client cache_control headers as-is"
|
|
17919
17919
|
};
|
|
17920
|
+
const NORMALIZE_HINTS = {
|
|
17921
|
+
on: "Rewrite cch → stable prefix cache",
|
|
17922
|
+
off: "Passthrough — rewrite nothing"
|
|
17923
|
+
};
|
|
17924
|
+
async function askNormalizeVolatileSystem(message, current, opts) {
|
|
17925
|
+
const options = [{
|
|
17926
|
+
value: true,
|
|
17927
|
+
label: "On",
|
|
17928
|
+
hint: NORMALIZE_HINTS.on
|
|
17929
|
+
}, {
|
|
17930
|
+
value: false,
|
|
17931
|
+
label: "Off",
|
|
17932
|
+
hint: NORMALIZE_HINTS.off
|
|
17933
|
+
}];
|
|
17934
|
+
if (opts?.removable) options.push({
|
|
17935
|
+
value: "reset",
|
|
17936
|
+
label: "Reset / inherit",
|
|
17937
|
+
hint: opts.resetHint ?? "Remove override"
|
|
17938
|
+
});
|
|
17939
|
+
return await select({
|
|
17940
|
+
message,
|
|
17941
|
+
initialValue: current ?? false,
|
|
17942
|
+
options
|
|
17943
|
+
});
|
|
17944
|
+
}
|
|
17920
17945
|
async function askTriState(message, current, hints, opts) {
|
|
17921
17946
|
const options = [
|
|
17922
17947
|
{
|
|
@@ -18017,6 +18042,12 @@ async function collectCacheTriState(currentCc, currentTtl, globalTtl) {
|
|
|
18017
18042
|
cacheControlTtl: { value: ttl }
|
|
18018
18043
|
};
|
|
18019
18044
|
}
|
|
18045
|
+
async function collectNormalizeVolatileSystem(currentNvs) {
|
|
18046
|
+
const nvs = await askNormalizeVolatileSystem("Normalize volatile system (cch hash)", currentNvs, { removable: true });
|
|
18047
|
+
if (typeof nvs === "symbol") return null;
|
|
18048
|
+
if (nvs === "reset") return { normalizeVolatileSystem: { remove: true } };
|
|
18049
|
+
return { normalizeVolatileSystem: { value: nvs } };
|
|
18050
|
+
}
|
|
18020
18051
|
//#endregion
|
|
18021
18052
|
//#region src/commands/config/add.ts
|
|
18022
18053
|
const CUSTOM_PATTERN_VALUE = "__proxitor_custom_pattern__";
|
|
@@ -18119,13 +18150,24 @@ async function configureProviderAndSave(configPath, client, modelKey, isPattern)
|
|
|
18119
18150
|
const providerResult = await selectProvidersByMode(mode, providerOptions);
|
|
18120
18151
|
if (!providerResult) return;
|
|
18121
18152
|
override = providerResult;
|
|
18122
|
-
override = await
|
|
18153
|
+
override = await collectOptionalOverrides(override);
|
|
18123
18154
|
if (!await confirmAndSave(configPath, modelKey, override, client)) return;
|
|
18124
18155
|
outro("✓ Model override saved");
|
|
18125
18156
|
}
|
|
18126
|
-
async function
|
|
18157
|
+
async function collectOptionalOverrides(override) {
|
|
18127
18158
|
override = await collectSession(override);
|
|
18128
18159
|
override = await collectCache(override);
|
|
18160
|
+
override = await collectNormalize(override);
|
|
18161
|
+
return override;
|
|
18162
|
+
}
|
|
18163
|
+
async function collectNormalize(override) {
|
|
18164
|
+
const want = await confirm({
|
|
18165
|
+
message: "Configure normalizeVolatileSystem for this model?",
|
|
18166
|
+
initialValue: false
|
|
18167
|
+
});
|
|
18168
|
+
if (isCancel(want) || !want) return override;
|
|
18169
|
+
const result = await collectNormalizeVolatileSystem(override.normalizeVolatileSystem);
|
|
18170
|
+
if (result && !("remove" in result.normalizeVolatileSystem)) override.normalizeVolatileSystem = result.normalizeVolatileSystem.value;
|
|
18129
18171
|
return override;
|
|
18130
18172
|
}
|
|
18131
18173
|
async function collectSession(override) {
|
|
@@ -18201,6 +18243,7 @@ function formatOverrideYaml(override) {
|
|
|
18201
18243
|
if (override.sessionId) parts.push(`sessionId: ${override.sessionId}`);
|
|
18202
18244
|
if (override.cacheControl) parts.push(`cacheControl: ${override.cacheControl}`);
|
|
18203
18245
|
if (override.cacheControlTtl) parts.push(`cacheControlTtl: ${override.cacheControlTtl}`);
|
|
18246
|
+
if (override.normalizeVolatileSystem !== void 0) parts.push(`normalizeVolatileSystem: ${override.normalizeVolatileSystem}`);
|
|
18204
18247
|
return parts.join("\n ") || "(empty)";
|
|
18205
18248
|
}
|
|
18206
18249
|
//#endregion
|
|
@@ -18276,6 +18319,10 @@ async function browseModelsCommand(client) {
|
|
|
18276
18319
|
}
|
|
18277
18320
|
//#endregion
|
|
18278
18321
|
//#region src/commands/config/edit.ts
|
|
18322
|
+
function nvsHint(value) {
|
|
18323
|
+
if (value === void 0) return "(inherit)";
|
|
18324
|
+
return value ? "on" : "off";
|
|
18325
|
+
}
|
|
18279
18326
|
function formatOverrideHint(override) {
|
|
18280
18327
|
if (!override) return "(empty)";
|
|
18281
18328
|
const parts = [];
|
|
@@ -18285,6 +18332,7 @@ function formatOverrideHint(override) {
|
|
|
18285
18332
|
}
|
|
18286
18333
|
if (override.sessionId) parts.push(`session: ${override.sessionId}`);
|
|
18287
18334
|
if (override.cacheControl) parts.push(`cache: ${override.cacheControl}`);
|
|
18335
|
+
if (override.normalizeVolatileSystem !== void 0) parts.push(`normalize: ${nvsHint(override.normalizeVolatileSystem)}`);
|
|
18288
18336
|
if (override.headers) parts.push(`${Object.keys(override.headers).length} header(s)`);
|
|
18289
18337
|
return parts.join(", ") || "(empty)";
|
|
18290
18338
|
}
|
|
@@ -18308,6 +18356,7 @@ function showCurrentConfig(modelKey, current) {
|
|
|
18308
18356
|
if (current.sessionId) log.info(` sessionId: ${current.sessionId}`);
|
|
18309
18357
|
if (current.cacheControl) log.info(` cacheControl: ${current.cacheControl}`);
|
|
18310
18358
|
if (current.cacheControlTtl) log.info(` cacheControlTtl: ${current.cacheControlTtl}`);
|
|
18359
|
+
if (current.normalizeVolatileSystem !== void 0) log.info(` normalizeVolatileSystem: ${current.normalizeVolatileSystem}`);
|
|
18311
18360
|
if (current.headers) for (const [name, value] of Object.entries(current.headers)) log.info(` headers.${name}: ${value}`);
|
|
18312
18361
|
}
|
|
18313
18362
|
async function editProvider(modelKey, current, client) {
|
|
@@ -18344,6 +18393,23 @@ async function editCacheControl(current, configPath) {
|
|
|
18344
18393
|
applyField(next, "cacheControlTtl", result.cacheControlTtl);
|
|
18345
18394
|
return next;
|
|
18346
18395
|
}
|
|
18396
|
+
/** @internal */
|
|
18397
|
+
async function editNormalizeVolatileSystem(current) {
|
|
18398
|
+
const result = await collectNormalizeVolatileSystem(current.normalizeVolatileSystem);
|
|
18399
|
+
if (result === null) return current;
|
|
18400
|
+
const next = { ...current };
|
|
18401
|
+
applyField(next, "normalizeVolatileSystem", result.normalizeVolatileSystem);
|
|
18402
|
+
return next;
|
|
18403
|
+
}
|
|
18404
|
+
async function applyFieldEdit(field, modelKey, current, client, configPath) {
|
|
18405
|
+
switch (field) {
|
|
18406
|
+
case "provider": return editProvider(modelKey, current, client);
|
|
18407
|
+
case "sessionId": return editSessionId(current);
|
|
18408
|
+
case "cacheControl": return editCacheControl(current, configPath);
|
|
18409
|
+
case "normalizeVolatileSystem": return editNormalizeVolatileSystem(current);
|
|
18410
|
+
default: return current;
|
|
18411
|
+
}
|
|
18412
|
+
}
|
|
18347
18413
|
/** Run the interactive "Edit model override" flow. */
|
|
18348
18414
|
async function editOverrideCommand(client, configPath) {
|
|
18349
18415
|
intro("Edit Model Override");
|
|
@@ -18386,6 +18452,11 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18386
18452
|
label: "Cache control",
|
|
18387
18453
|
hint: formatCacheHint(current.cacheControl, current.cacheControlTtl)
|
|
18388
18454
|
},
|
|
18455
|
+
{
|
|
18456
|
+
value: "normalizeVolatileSystem",
|
|
18457
|
+
label: "Normalize volatile system",
|
|
18458
|
+
hint: nvsHint(current.normalizeVolatileSystem)
|
|
18459
|
+
},
|
|
18389
18460
|
{
|
|
18390
18461
|
value: "done",
|
|
18391
18462
|
label: "✓ Done"
|
|
@@ -18393,17 +18464,7 @@ async function editOverrideCommand(client, configPath) {
|
|
|
18393
18464
|
]
|
|
18394
18465
|
});
|
|
18395
18466
|
if (isCancel(field) || field === "done") break;
|
|
18396
|
-
|
|
18397
|
-
case "provider":
|
|
18398
|
-
current = await editProvider(modelKey, current, client);
|
|
18399
|
-
break;
|
|
18400
|
-
case "sessionId":
|
|
18401
|
-
current = await editSessionId(current);
|
|
18402
|
-
break;
|
|
18403
|
-
case "cacheControl":
|
|
18404
|
-
current = await editCacheControl(current, resolvedConfigPath);
|
|
18405
|
-
break;
|
|
18406
|
-
}
|
|
18467
|
+
current = await applyFieldEdit(field, modelKey, current, client, resolvedConfigPath);
|
|
18407
18468
|
}
|
|
18408
18469
|
const save = await confirm({ message: "Save changes?" });
|
|
18409
18470
|
if (isCancel(save) || !save) {
|
|
@@ -19040,26 +19101,9 @@ async function normalizeVolatileSystemCommand(opts) {
|
|
|
19040
19101
|
const configPath = requireConfigPath(opts?.configPath);
|
|
19041
19102
|
const current = readConfigFile(configPath).normalizeVolatileSystem ?? DEFAULTS.normalizeVolatileSystem;
|
|
19042
19103
|
log.info(`Current: normalizeVolatileSystem = ${current}`);
|
|
19043
|
-
const choice = await
|
|
19044
|
-
|
|
19045
|
-
|
|
19046
|
-
{
|
|
19047
|
-
value: true,
|
|
19048
|
-
label: "On",
|
|
19049
|
-
hint: "rewrite cch → stable prefix"
|
|
19050
|
-
},
|
|
19051
|
-
{
|
|
19052
|
-
value: false,
|
|
19053
|
-
label: "Off",
|
|
19054
|
-
hint: "passthrough"
|
|
19055
|
-
},
|
|
19056
|
-
{
|
|
19057
|
-
value: "reset",
|
|
19058
|
-
label: "Reset",
|
|
19059
|
-
hint: `remove (default: ${DEFAULTS.normalizeVolatileSystem})`
|
|
19060
|
-
}
|
|
19061
|
-
],
|
|
19062
|
-
initialValue: 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})`
|
|
19063
19107
|
});
|
|
19064
19108
|
if (typeof choice === "symbol") return;
|
|
19065
19109
|
const fields = {};
|
|
@@ -19200,7 +19244,7 @@ async function runConfigMenu(client) {
|
|
|
19200
19244
|
}
|
|
19201
19245
|
//#endregion
|
|
19202
19246
|
//#region src/version.ts
|
|
19203
|
-
const version = "0.
|
|
19247
|
+
const version = "0.11.0";
|
|
19204
19248
|
//#endregion
|
|
19205
19249
|
//#region src/commands/doctor.ts
|
|
19206
19250
|
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
@@ -19438,6 +19482,142 @@ async function doctorCommand(opts = {}) {
|
|
|
19438
19482
|
return exitCode;
|
|
19439
19483
|
}
|
|
19440
19484
|
//#endregion
|
|
19485
|
+
//#region src/config-source.ts
|
|
19486
|
+
/** Default watcher: fs.watchFile polling; returns a stop function. */
|
|
19487
|
+
const watchStat = (filename, pollIntervalMs, onChange) => {
|
|
19488
|
+
watchFile(filename, {
|
|
19489
|
+
interval: pollIntervalMs,
|
|
19490
|
+
persistent: false
|
|
19491
|
+
}, onChange);
|
|
19492
|
+
return () => unwatchFile(filename);
|
|
19493
|
+
};
|
|
19494
|
+
function fmt(value) {
|
|
19495
|
+
if (value === void 0) return "unset";
|
|
19496
|
+
if (value === true) return "on";
|
|
19497
|
+
if (value === false) return "off";
|
|
19498
|
+
return String(value);
|
|
19499
|
+
}
|
|
19500
|
+
const SCALAR_KEYS = [
|
|
19501
|
+
"cacheControl",
|
|
19502
|
+
"cacheControlTtl",
|
|
19503
|
+
"sessionId",
|
|
19504
|
+
"normalizeVolatileSystem",
|
|
19505
|
+
"authType",
|
|
19506
|
+
"verbose",
|
|
19507
|
+
"bodyLimit",
|
|
19508
|
+
"openrouterBaseUrl"
|
|
19509
|
+
];
|
|
19510
|
+
function canonicalEntries(record) {
|
|
19511
|
+
if (!record) return "";
|
|
19512
|
+
return JSON.stringify(Object.keys(record).sort().map((key) => [key, record[key]]));
|
|
19513
|
+
}
|
|
19514
|
+
/** Diff of cache-relevant fields; '' if nothing changed. */
|
|
19515
|
+
function summarizeChanges(prev, next) {
|
|
19516
|
+
const parts = [];
|
|
19517
|
+
for (const key of SCALAR_KEYS) if (prev[key] !== next[key]) parts.push(`${key}: ${fmt(prev[key])}→${fmt(next[key])}`);
|
|
19518
|
+
if (JSON.stringify(buildProviderRouting(prev.provider)) !== JSON.stringify(buildProviderRouting(next.provider))) parts.push("provider routing");
|
|
19519
|
+
if (canonicalEntries(prev.modelOverrides) !== canonicalEntries(next.modelOverrides)) {
|
|
19520
|
+
const prevCount = prev.modelOverrides ? Object.keys(prev.modelOverrides).length : 0;
|
|
19521
|
+
const nextCount = next.modelOverrides ? Object.keys(next.modelOverrides).length : 0;
|
|
19522
|
+
parts.push(`modelOverrides: ${prevCount}→${nextCount}`);
|
|
19523
|
+
}
|
|
19524
|
+
if (canonicalEntries(prev.headers) !== canonicalEntries(next.headers)) parts.push("headers");
|
|
19525
|
+
return parts.join(", ");
|
|
19526
|
+
}
|
|
19527
|
+
function createConfigSource(options) {
|
|
19528
|
+
return new FileWatchingConfigSource(options);
|
|
19529
|
+
}
|
|
19530
|
+
var FileWatchingConfigSource = class {
|
|
19531
|
+
current;
|
|
19532
|
+
loadOptions;
|
|
19533
|
+
load;
|
|
19534
|
+
pollIntervalMs;
|
|
19535
|
+
watch;
|
|
19536
|
+
boundHost;
|
|
19537
|
+
boundPort;
|
|
19538
|
+
resolvedPath;
|
|
19539
|
+
stopWatch;
|
|
19540
|
+
loading = false;
|
|
19541
|
+
pending = false;
|
|
19542
|
+
watching = false;
|
|
19543
|
+
constructor(options) {
|
|
19544
|
+
this.current = options.initial;
|
|
19545
|
+
this.loadOptions = options.loadOptions;
|
|
19546
|
+
this.load = options.load ?? loadConfig;
|
|
19547
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 1e3;
|
|
19548
|
+
this.watch = options.watch ?? watchStat;
|
|
19549
|
+
this.boundHost = options.initial.host;
|
|
19550
|
+
this.boundPort = options.initial.port;
|
|
19551
|
+
this.resolvedPath = options.loadOptions.noConfig ? null : tryFindConfigFile(options.loadOptions.configPath);
|
|
19552
|
+
}
|
|
19553
|
+
get() {
|
|
19554
|
+
return this.current;
|
|
19555
|
+
}
|
|
19556
|
+
async reload() {
|
|
19557
|
+
if (this.loading) {
|
|
19558
|
+
this.pending = true;
|
|
19559
|
+
return { ok: true };
|
|
19560
|
+
}
|
|
19561
|
+
this.loading = true;
|
|
19562
|
+
try {
|
|
19563
|
+
const next = await this.load(this.loadOptions);
|
|
19564
|
+
const restartNeeded = next.host !== this.boundHost || next.port !== this.boundPort;
|
|
19565
|
+
let diff = "";
|
|
19566
|
+
try {
|
|
19567
|
+
diff = summarizeChanges(this.current, next);
|
|
19568
|
+
} catch {
|
|
19569
|
+
diff = "";
|
|
19570
|
+
}
|
|
19571
|
+
this.current = next;
|
|
19572
|
+
if (restartNeeded) logger.warn("host/port changed — restart proxitor to apply (live reload does not re-bind the socket)");
|
|
19573
|
+
logger.info(`Config reloaded${diff ? ` — ${diff}` : " (no material changes)"}`);
|
|
19574
|
+
return { ok: true };
|
|
19575
|
+
} catch (error) {
|
|
19576
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
19577
|
+
logger.error(`Config reload failed — keeping previous config: ${msg}`);
|
|
19578
|
+
return {
|
|
19579
|
+
ok: false,
|
|
19580
|
+
error: msg
|
|
19581
|
+
};
|
|
19582
|
+
} finally {
|
|
19583
|
+
this.loading = false;
|
|
19584
|
+
if (this.pending) {
|
|
19585
|
+
this.pending = false;
|
|
19586
|
+
this.reload();
|
|
19587
|
+
}
|
|
19588
|
+
}
|
|
19589
|
+
}
|
|
19590
|
+
start() {
|
|
19591
|
+
if (this.watching) return;
|
|
19592
|
+
if (!this.resolvedPath) {
|
|
19593
|
+
logger.info("Live config reload disabled (no config file)");
|
|
19594
|
+
return;
|
|
19595
|
+
}
|
|
19596
|
+
this.watching = true;
|
|
19597
|
+
const path = this.resolvedPath;
|
|
19598
|
+
this.stopWatch = this.watch(path, this.pollIntervalMs, (curr, prev) => {
|
|
19599
|
+
try {
|
|
19600
|
+
this.onStat(path, curr, prev);
|
|
19601
|
+
} catch {}
|
|
19602
|
+
});
|
|
19603
|
+
}
|
|
19604
|
+
onStat(path, curr, prev) {
|
|
19605
|
+
if (curr.nlink === 0) {
|
|
19606
|
+
logger.warn(`config file disappeared — keeping current config (${path})`);
|
|
19607
|
+
return;
|
|
19608
|
+
}
|
|
19609
|
+
if (curr.mtimeMs === prev.mtimeMs) return;
|
|
19610
|
+
this.reload();
|
|
19611
|
+
}
|
|
19612
|
+
stop() {
|
|
19613
|
+
if (this.watching) {
|
|
19614
|
+
this.stopWatch?.();
|
|
19615
|
+
this.stopWatch = void 0;
|
|
19616
|
+
this.watching = false;
|
|
19617
|
+
}
|
|
19618
|
+
}
|
|
19619
|
+
};
|
|
19620
|
+
//#endregion
|
|
19441
19621
|
//#region node_modules/.pnpm/@hono+node-server@2.0.4_hono@4.12.25/node_modules/@hono/node-server/dist/index.mjs
|
|
19442
19622
|
var RequestError = class extends Error {
|
|
19443
19623
|
constructor(message, options) {
|
|
@@ -22832,20 +23012,23 @@ const injectChain = [
|
|
|
22832
23012
|
normalizeVolatileSystemMiddleware,
|
|
22833
23013
|
injectSessionId
|
|
22834
23014
|
];
|
|
22835
|
-
function createProxyServer(
|
|
23015
|
+
function createProxyServer(source, onReady) {
|
|
22836
23016
|
const app = new Hono();
|
|
22837
|
-
const globalRouting = buildProviderRouting(config.provider);
|
|
22838
|
-
const modelOverrideKeys = Object.keys(config.modelOverrides ?? []);
|
|
22839
23017
|
app.use("*", async (c, next) => {
|
|
22840
|
-
c.set("config",
|
|
23018
|
+
c.set("config", source.get());
|
|
22841
23019
|
await next();
|
|
22842
23020
|
});
|
|
22843
|
-
app.get("/health", (c) =>
|
|
22844
|
-
|
|
22845
|
-
|
|
22846
|
-
|
|
22847
|
-
|
|
22848
|
-
|
|
23021
|
+
app.get("/health", (c) => {
|
|
23022
|
+
const config = source.get();
|
|
23023
|
+
const globalRouting = buildProviderRouting(config.provider);
|
|
23024
|
+
const modelOverrideKeys = Object.keys(config.modelOverrides ?? []);
|
|
23025
|
+
return c.json({
|
|
23026
|
+
ok: true,
|
|
23027
|
+
upstream: config.openrouterBaseUrl,
|
|
23028
|
+
provider: globalRouting ?? "not configured",
|
|
23029
|
+
modelOverrides: modelOverrideKeys
|
|
23030
|
+
});
|
|
23031
|
+
});
|
|
22849
23032
|
for (const path of INJECT_PATHS) app.post(path, setupRequest, readBody, ...injectChain, buildUpstreamReq, forwardRequest);
|
|
22850
23033
|
app.all("*", setupRequest, readBody, resolveConfig, buildUpstreamReq, forwardRequest);
|
|
22851
23034
|
app.onError((err, c) => {
|
|
@@ -22856,20 +23039,22 @@ function createProxyServer(config, onReady) {
|
|
|
22856
23039
|
type: "proxy_internal_error"
|
|
22857
23040
|
} }, { status: 500 });
|
|
22858
23041
|
});
|
|
23042
|
+
const initial = source.get();
|
|
22859
23043
|
return serve({
|
|
22860
23044
|
fetch: app.fetch,
|
|
22861
|
-
port:
|
|
22862
|
-
hostname:
|
|
23045
|
+
port: initial.port,
|
|
23046
|
+
hostname: initial.host
|
|
22863
23047
|
}, onReady);
|
|
22864
23048
|
}
|
|
22865
23049
|
const SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
22866
|
-
function startProxyServer(
|
|
22867
|
-
const server = createProxyServer(
|
|
23050
|
+
function startProxyServer(source, onReady) {
|
|
23051
|
+
const server = createProxyServer(source, onReady);
|
|
22868
23052
|
let shuttingDown = false;
|
|
22869
23053
|
function shutdown(signal) {
|
|
22870
23054
|
if (shuttingDown) return;
|
|
22871
23055
|
shuttingDown = true;
|
|
22872
23056
|
logger.info(`${signal} received — draining active connections…`);
|
|
23057
|
+
source.stop();
|
|
22873
23058
|
const timer = setTimeout(() => {
|
|
22874
23059
|
logger.warn("Forcing shutdown — drain timeout exceeded");
|
|
22875
23060
|
process.exit(1);
|
|
@@ -22987,17 +23172,24 @@ const startCommand = (0, import_cjs.command)({
|
|
|
22987
23172
|
},
|
|
22988
23173
|
handler: async ({ configPath, port, host, noConfig, openrouterKey, verbose }) => {
|
|
22989
23174
|
try {
|
|
22990
|
-
const
|
|
23175
|
+
const loadOptions = {
|
|
22991
23176
|
configPath,
|
|
22992
23177
|
noConfig,
|
|
22993
23178
|
port,
|
|
22994
23179
|
host,
|
|
22995
23180
|
openrouterKey,
|
|
22996
23181
|
verbose
|
|
23182
|
+
};
|
|
23183
|
+
const cfg = await loadConfig(loadOptions);
|
|
23184
|
+
const source = createConfigSource({
|
|
23185
|
+
loadOptions,
|
|
23186
|
+
initial: cfg
|
|
22997
23187
|
});
|
|
22998
|
-
|
|
23188
|
+
source.start();
|
|
23189
|
+
startProxyServer(source, () => {
|
|
22999
23190
|
logger.ready(`Proxitor proxy listening on ${cfg.host}:${cfg.port}`);
|
|
23000
23191
|
logger.info("Routing requests to OpenRouter");
|
|
23192
|
+
if (source.resolvedPath) logger.info(`Watching ${source.resolvedPath} for changes (live reload)`);
|
|
23001
23193
|
});
|
|
23002
23194
|
} catch (error) {
|
|
23003
23195
|
logger.error("Failed to start proxy:", error);
|