llm-relay 0.44.0 → 0.45.1
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 +1 -0
- package/dist/authEnv.d.ts +20 -12
- package/dist/authEnv.js +76 -36
- package/dist/authEnv.js.map +1 -1
- package/dist/candidates.d.ts +2 -0
- package/dist/candidates.js +2 -1
- package/dist/candidates.js.map +1 -1
- package/dist/circuit-breaker.d.ts +2 -0
- package/dist/circuit-breaker.js +17 -0
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +193 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -3
- package/dist/config.js +87 -7
- package/dist/config.js.map +1 -1
- package/dist/cooldown-clear.d.ts +2 -0
- package/dist/cooldown-clear.js +20 -4
- package/dist/cooldown-clear.js.map +1 -1
- package/dist/credential-fleet.d.ts +5 -4
- package/dist/credential-fleet.js +9 -9
- package/dist/credential-fleet.js.map +1 -1
- package/dist/dotenv.d.ts +2 -0
- package/dist/dotenv.js +9 -0
- package/dist/dotenv.js.map +1 -1
- package/dist/key-checker.d.ts +1 -0
- package/dist/key-checker.js +3 -2
- package/dist/key-checker.js.map +1 -1
- package/dist/key-import.d.ts +7 -0
- package/dist/key-import.js +52 -0
- package/dist/key-import.js.map +1 -1
- package/dist/keys-cli.d.ts +48 -0
- package/dist/keys-cli.js +693 -0
- package/dist/keys-cli.js.map +1 -0
- package/dist/keystore.d.ts +130 -0
- package/dist/keystore.js +1195 -0
- package/dist/keystore.js.map +1 -0
- package/dist/os-keyring.d.ts +88 -0
- package/dist/os-keyring.js +476 -0
- package/dist/os-keyring.js.map +1 -0
- package/dist/registry.d.ts +2 -1
- package/dist/registry.js +1 -0
- package/dist/registry.js.map +1 -1
- package/dist/resolved-attempt.d.ts +2 -1
- package/dist/resolved-attempt.js +3 -2
- package/dist/resolved-attempt.js.map +1 -1
- package/dist/routes/admin.js +8 -1
- package/dist/routes/admin.js.map +1 -1
- package/dist/secret-file-acl.d.ts +47 -12
- package/dist/secret-file-acl.js +151 -16
- package/dist/secret-file-acl.js.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/target-facts.d.ts +5 -0
- package/dist/target-facts.js +10 -3
- package/dist/target-facts.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,6 +33,7 @@ import { DASHBOARD_MEDIA_TYPE, isDashboardUtcTimestamp } from "./dashboard-contr
|
|
|
33
33
|
import { DASHBOARD_BOOTSTRAP_SCHEMA, DASHBOARD_BOOTSTRAP_REQUEST_SCHEMA } from "./dashboard-routes.js";
|
|
34
34
|
import { flushRuntimeTelemetry } from "./ping/runtime-telemetry.js";
|
|
35
35
|
import { flushProbeCache } from "./ping/probe-cache.js";
|
|
36
|
+
import { runKeysAddLocal, runKeysDisableLocal, runKeysEnableLocal, runKeysExportLocal, runKeysImportLocal, runKeysListLocal, runKeysRemoveLocal, runKeysRevokeLocal, runKeysRotateLocal, runKeysUnlockLocal, } from "./keys-cli.js";
|
|
36
37
|
const VALUE_FLAGS = new Set([
|
|
37
38
|
"--config", "-config", "-c",
|
|
38
39
|
"--provider", "-provider", "-p",
|
|
@@ -63,6 +64,9 @@ const VALUE_FLAGS = new Set([
|
|
|
63
64
|
"--reset-field", "-reset-field",
|
|
64
65
|
"--reset-ms", "-reset-ms",
|
|
65
66
|
"--import", "-import",
|
|
67
|
+
"--label", "-label",
|
|
68
|
+
"--env-name", "-env-name",
|
|
69
|
+
"--out", "-out",
|
|
66
70
|
]);
|
|
67
71
|
let cachedArgv;
|
|
68
72
|
let cachedParsedArgs;
|
|
@@ -158,7 +162,8 @@ ${formatTextTable([
|
|
|
158
162
|
["llm-relay [options]", "Start proxy."],
|
|
159
163
|
["llm-relay onboard [--import <file>] [--force]", "Set up or import provider keys."],
|
|
160
164
|
["llm-relay setup [target]", "target: claude-cli | claude-desktop."],
|
|
161
|
-
["llm-relay keys | check-keys", "Check every configured credential slot."],
|
|
165
|
+
["llm-relay keys | keys check | check-keys", "Check every configured credential slot."],
|
|
166
|
+
["llm-relay keys <action> ...", "action: add|list|rotate|revoke|remove|disable|enable|export|import|unlock."],
|
|
162
167
|
["llm-relay pools [--probe]", "List members; --probe tests each deployment once."],
|
|
163
168
|
["llm-relay pools <action> <name> [<spec>...]", "action: set|add|remove|delete."],
|
|
164
169
|
["llm-relay routing <action> ...", "action: show|get|default|tier|subagent|sort|benchmark|set|unset."],
|
|
@@ -669,6 +674,43 @@ export async function runCheckKeys() {
|
|
|
669
674
|
];
|
|
670
675
|
process.stdout.write(formatTextTable(rows) + "\n");
|
|
671
676
|
}
|
|
677
|
+
/** `llm-relay keys add` — CLI-local credential insertion; secret input is never argv. */
|
|
678
|
+
export async function runKeysAdd(provider = getPositionalArgs(process.argv)[2], deps = {}) {
|
|
679
|
+
await runKeysAddLocal(deps.config ?? loadOrExit(), provider, {
|
|
680
|
+
...(argValue("--label", "-label") === undefined
|
|
681
|
+
? {} : { label: argValue("--label", "-label") }),
|
|
682
|
+
...(argValue("--env-name", "-env-name") === undefined
|
|
683
|
+
? {} : { envName: argValue("--env-name", "-env-name") }),
|
|
684
|
+
check: hasFlag("--check", "-check"),
|
|
685
|
+
}, deps);
|
|
686
|
+
}
|
|
687
|
+
export async function runKeysList(deps = {}) {
|
|
688
|
+
runKeysListLocal(deps.config ?? loadOrExit(), deps);
|
|
689
|
+
}
|
|
690
|
+
export async function runKeysRotate(credential = getPositionalArgs(process.argv)[2], deps = {}) {
|
|
691
|
+
await runKeysRotateLocal(deps.config ?? loadOrExit(), credential, deps);
|
|
692
|
+
}
|
|
693
|
+
export async function runKeysRevoke(credential = getPositionalArgs(process.argv)[2], deps = {}) {
|
|
694
|
+
runKeysRevokeLocal(credential, deps);
|
|
695
|
+
}
|
|
696
|
+
export async function runKeysRemove(credential = getPositionalArgs(process.argv)[2], deps = {}) {
|
|
697
|
+
runKeysRemoveLocal(credential, hasFlag("--purge", "-purge"), deps);
|
|
698
|
+
}
|
|
699
|
+
export async function runKeysDisable(credential = getPositionalArgs(process.argv)[2], deps = {}) {
|
|
700
|
+
runKeysDisableLocal(credential, deps);
|
|
701
|
+
}
|
|
702
|
+
export async function runKeysEnable(credential = getPositionalArgs(process.argv)[2], deps = {}) {
|
|
703
|
+
runKeysEnableLocal(credential, deps);
|
|
704
|
+
}
|
|
705
|
+
export async function runKeysExport(deps = {}) {
|
|
706
|
+
await runKeysExportLocal(argValue("--out", "-out"), deps);
|
|
707
|
+
}
|
|
708
|
+
export async function runKeysImport(importPath = getPositionalArgs(process.argv)[2], deps = {}) {
|
|
709
|
+
await runKeysImportLocal(deps.config ?? loadOrExit(), importPath, deps);
|
|
710
|
+
}
|
|
711
|
+
export async function runKeysUnlock(deps = {}) {
|
|
712
|
+
await runKeysUnlockLocal(deps);
|
|
713
|
+
}
|
|
672
714
|
/**
|
|
673
715
|
* Talk to a RUNNING proxy if there is one. The offload switch has to reach the live process to
|
|
674
716
|
* take effect without a restart, and `candidates` gets better data from it (warm ping history,
|
|
@@ -2716,12 +2758,98 @@ import { importKeysFromFile } from "./key-import.js";
|
|
|
2716
2758
|
import { setupClaudeCli, setupClaudeDesktop } from "./setup-claude.js";
|
|
2717
2759
|
import { getTelemetryReport } from "./telemetry.js";
|
|
2718
2760
|
import { globalCircuitBreaker } from "./circuit-breaker.js";
|
|
2761
|
+
const KEYS_SUBCOMMANDS = [
|
|
2762
|
+
"add", "list", "rotate", "revoke", "remove", "disable", "enable", "export", "import",
|
|
2763
|
+
"unlock", "check",
|
|
2764
|
+
];
|
|
2765
|
+
const KEYS_GLOBAL_FLAGS = ["--config", "-config", "-c"];
|
|
2766
|
+
const KEYS_FLAGS = {
|
|
2767
|
+
add: ["--label", "-label", "--env-name", "-env-name", "--check", "-check"],
|
|
2768
|
+
list: [],
|
|
2769
|
+
rotate: [],
|
|
2770
|
+
revoke: [],
|
|
2771
|
+
remove: ["--purge", "-purge"],
|
|
2772
|
+
disable: [],
|
|
2773
|
+
enable: [],
|
|
2774
|
+
export: ["--out", "-out"],
|
|
2775
|
+
import: [],
|
|
2776
|
+
unlock: [],
|
|
2777
|
+
check: [],
|
|
2778
|
+
};
|
|
2779
|
+
const KEYS_POSITIONAL_COUNT = {
|
|
2780
|
+
add: 3,
|
|
2781
|
+
list: 2,
|
|
2782
|
+
rotate: 3,
|
|
2783
|
+
revoke: 3,
|
|
2784
|
+
remove: 3,
|
|
2785
|
+
disable: 3,
|
|
2786
|
+
enable: 3,
|
|
2787
|
+
export: 2,
|
|
2788
|
+
import: 3,
|
|
2789
|
+
unlock: 2,
|
|
2790
|
+
check: 2,
|
|
2791
|
+
};
|
|
2792
|
+
/** Strict, secret-safe keys parser. Diagnostics deliberately never echo rejected argv tokens. */
|
|
2793
|
+
export function validateKeysCommandArgs(argv) {
|
|
2794
|
+
const positionals = getPositionalArgs(argv);
|
|
2795
|
+
const candidate = positionals[1];
|
|
2796
|
+
if (candidate !== undefined && !KEYS_SUBCOMMANDS.includes(candidate)) {
|
|
2797
|
+
return `unknown subcommand; valid subcommands: ${KEYS_SUBCOMMANDS.join(", ")}`;
|
|
2798
|
+
}
|
|
2799
|
+
const subcommand = candidate;
|
|
2800
|
+
const allowed = new Set(KEYS_GLOBAL_FLAGS);
|
|
2801
|
+
if (subcommand !== undefined) {
|
|
2802
|
+
for (const flag of KEYS_FLAGS[subcommand])
|
|
2803
|
+
allowed.add(flag);
|
|
2804
|
+
}
|
|
2805
|
+
for (let index = 2; index < argv.length; index += 1) {
|
|
2806
|
+
const arg = argv[index];
|
|
2807
|
+
if (arg === undefined || !arg.startsWith("-"))
|
|
2808
|
+
continue;
|
|
2809
|
+
const equalsAt = arg.indexOf("=");
|
|
2810
|
+
const flag = equalsAt === -1 ? arg : arg.slice(0, equalsAt);
|
|
2811
|
+
if (!allowed.has(flag))
|
|
2812
|
+
return "unsupported option";
|
|
2813
|
+
if (VALUE_FLAGS.has(flag)) {
|
|
2814
|
+
if (equalsAt !== -1) {
|
|
2815
|
+
if (arg.slice(equalsAt + 1).length === 0)
|
|
2816
|
+
return "invalid arguments";
|
|
2817
|
+
}
|
|
2818
|
+
else {
|
|
2819
|
+
const value = argv[index + 1];
|
|
2820
|
+
if (value === undefined || value.startsWith("-"))
|
|
2821
|
+
return "invalid arguments";
|
|
2822
|
+
index += 1;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
else if (equalsAt !== -1) {
|
|
2826
|
+
return "invalid arguments";
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
const expected = subcommand === undefined ? 1 : KEYS_POSITIONAL_COUNT[subcommand];
|
|
2830
|
+
return positionals.length === expected ? null : "invalid arguments";
|
|
2831
|
+
}
|
|
2832
|
+
function reportKeysCommandError(error) {
|
|
2833
|
+
process.stderr.write(`llm-relay keys: ${error.message}\n`);
|
|
2834
|
+
process.exit(1);
|
|
2835
|
+
}
|
|
2836
|
+
function runKeysPromise(operation) {
|
|
2837
|
+
operation.catch(reportKeysCommandError);
|
|
2838
|
+
}
|
|
2719
2839
|
export function main() {
|
|
2720
2840
|
const rawCommand = rawCliCommand(process.argv);
|
|
2721
2841
|
const positionals = getPositionalArgs(process.argv);
|
|
2722
2842
|
const arg2 = positionals[0];
|
|
2723
2843
|
const arg3 = positionals[1];
|
|
2724
2844
|
const arg4 = positionals[2];
|
|
2845
|
+
if (arg2 === "keys") {
|
|
2846
|
+
const validationError = validateKeysCommandArgs(process.argv);
|
|
2847
|
+
if (validationError !== null) {
|
|
2848
|
+
process.stderr.write(`llm-relay keys: ${validationError}\n`);
|
|
2849
|
+
process.exit(1);
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2725
2853
|
// Mutation parsing owns its raw argv so help/version-shaped typos cannot bypass fail-closed
|
|
2726
2854
|
// validation and turn a scoped clear into a wider request.
|
|
2727
2855
|
if (rawCommand === "cooldowns" || arg2 === "cooldowns") {
|
|
@@ -2779,13 +2907,57 @@ export function main() {
|
|
|
2779
2907
|
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
2780
2908
|
return;
|
|
2781
2909
|
}
|
|
2782
|
-
if (arg2 === "
|
|
2910
|
+
if (arg2 === "check-keys") {
|
|
2783
2911
|
runCheckKeys().catch((e) => {
|
|
2784
2912
|
process.stderr.write(`llm-relay check-keys: ${e.message}\n`);
|
|
2785
2913
|
process.exit(1);
|
|
2786
2914
|
});
|
|
2787
2915
|
return;
|
|
2788
2916
|
}
|
|
2917
|
+
if (arg2 === "keys") {
|
|
2918
|
+
switch (arg3) {
|
|
2919
|
+
case undefined:
|
|
2920
|
+
case "check":
|
|
2921
|
+
// Bare `keys` remains byte-for-byte the same status command as `check-keys`.
|
|
2922
|
+
runCheckKeys().catch((e) => {
|
|
2923
|
+
process.stderr.write(`llm-relay check-keys: ${e.message}\n`);
|
|
2924
|
+
process.exit(1);
|
|
2925
|
+
});
|
|
2926
|
+
return;
|
|
2927
|
+
case "add":
|
|
2928
|
+
runKeysPromise(runKeysAdd(arg4));
|
|
2929
|
+
return;
|
|
2930
|
+
case "list":
|
|
2931
|
+
runKeysPromise(runKeysList());
|
|
2932
|
+
return;
|
|
2933
|
+
case "rotate":
|
|
2934
|
+
runKeysPromise(runKeysRotate(arg4));
|
|
2935
|
+
return;
|
|
2936
|
+
case "revoke":
|
|
2937
|
+
runKeysPromise(runKeysRevoke(arg4));
|
|
2938
|
+
return;
|
|
2939
|
+
case "remove":
|
|
2940
|
+
runKeysPromise(runKeysRemove(arg4));
|
|
2941
|
+
return;
|
|
2942
|
+
case "disable":
|
|
2943
|
+
runKeysPromise(runKeysDisable(arg4));
|
|
2944
|
+
return;
|
|
2945
|
+
case "enable":
|
|
2946
|
+
runKeysPromise(runKeysEnable(arg4));
|
|
2947
|
+
return;
|
|
2948
|
+
case "export":
|
|
2949
|
+
runKeysPromise(runKeysExport());
|
|
2950
|
+
return;
|
|
2951
|
+
case "import":
|
|
2952
|
+
runKeysPromise(runKeysImport(arg4));
|
|
2953
|
+
return;
|
|
2954
|
+
case "unlock":
|
|
2955
|
+
runKeysPromise(runKeysUnlock());
|
|
2956
|
+
return;
|
|
2957
|
+
default:
|
|
2958
|
+
throw new Error("unreachable keys subcommand after validation");
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2789
2961
|
if (arg2 === "models") {
|
|
2790
2962
|
runModels().catch((e) => {
|
|
2791
2963
|
process.stderr.write(`llm-relay: ${e.message}\n`);
|
|
@@ -2934,6 +3106,12 @@ export function classifyCommand(argv) {
|
|
|
2934
3106
|
// Clears process-local relay state through the authenticated mutation plane.
|
|
2935
3107
|
case "cooldowns":
|
|
2936
3108
|
return arg3 === "clear" ? "mutating" : "read-only";
|
|
3109
|
+
case "keys":
|
|
3110
|
+
return arg3 === "add" || arg3 === "rotate" || arg3 === "revoke" ||
|
|
3111
|
+
arg3 === "remove" || arg3 === "disable" || arg3 === "enable" ||
|
|
3112
|
+
arg3 === "export" || arg3 === "import"
|
|
3113
|
+
? "mutating"
|
|
3114
|
+
: "read-only";
|
|
2937
3115
|
case "config":
|
|
2938
3116
|
return arg3 === "set" || arg3 === "unset" ? "mutating" : "read-only";
|
|
2939
3117
|
case "routing":
|
|
@@ -2952,7 +3130,7 @@ export function classifyCommand(argv) {
|
|
|
2952
3130
|
return arg3 === "accept" || arg3 === "reject" || arg3 === "propose" ? "mutating" : "read-only";
|
|
2953
3131
|
case "dashboard":
|
|
2954
3132
|
return "read-only";
|
|
2955
|
-
//
|
|
3133
|
+
// check-keys, models, telemetry, dispatch, candidates, pools, ping, help, version —
|
|
2956
3134
|
// and anything not yet listed. `dispatch -x` is included on purpose: it reports spend to a
|
|
2957
3135
|
// running proxy's in-memory cooldowns and changes nothing on this machine.
|
|
2958
3136
|
default:
|
|
@@ -2964,9 +3142,19 @@ export function classifyCommand(argv) {
|
|
|
2964
3142
|
* and re-exec), then the command itself. `main` stays synchronous so its exit paths are direct.
|
|
2965
3143
|
*/
|
|
2966
3144
|
export async function run() {
|
|
2967
|
-
// Reject malformed mutation argv before the self-update gate can perform any network request
|
|
2968
|
-
|
|
3145
|
+
// Reject malformed mutation argv before the self-update gate can perform any network request
|
|
3146
|
+
// or replace the installed CLI. Keys diagnostics stay generic so rejected argv is never echoed.
|
|
3147
|
+
const command = rawCliCommand(process.argv);
|
|
3148
|
+
if (command === "cooldowns")
|
|
2969
3149
|
parseCooldownClearArgs(process.argv);
|
|
3150
|
+
if (command === "keys") {
|
|
3151
|
+
const validationError = validateKeysCommandArgs(process.argv);
|
|
3152
|
+
if (validationError !== null) {
|
|
3153
|
+
process.stderr.write(`llm-relay keys: ${validationError}\n`);
|
|
3154
|
+
process.exit(1);
|
|
3155
|
+
return;
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
2970
3158
|
if (shouldCheckUpdates(process.argv, process.env, classifyCommand(process.argv))) {
|
|
2971
3159
|
try {
|
|
2972
3160
|
await ensureUpToDate();
|