@bman654/clodex 2.18.6 → 2.18.8
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 +3 -3
- package/dist/cli.js +79 -64
- package/dist/cli.js.map +1 -1
- package/docs/opencode-go.md +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -244,7 +244,7 @@ Manage favorite models (max 20) and short aliases. Favorites feed the endpoint-m
|
|
|
244
244
|
| `--unalias <name>` | Remove a saved short name |
|
|
245
245
|
| `--context <model=stop>` | Choose how much of a model's context window to use: `standard`, `max`, `default` to clear, or a token count such as `500k`. Applies to this run unless `--save` is given |
|
|
246
246
|
| `--save` | With `--context`: store the stop as that model's default |
|
|
247
|
-
| `--json` | Print resolved metadata for saved favorites as JSON (ids, aliases, context stop and windows, output limit, pricing boundary, effort levels). Diagnostics go to stderr so stdout stays parseable |
|
|
247
|
+
| `--json` | Print resolved metadata for saved favorites as JSON (ids, aliases, context stop and windows, output limit, pricing boundary, effort levels). A model saved under several aliases lists them all in `aliases`, in saved order; `alias` is the first. Diagnostics go to stderr so stdout stays parseable |
|
|
248
248
|
| `--help`, `--version` | Help / version |
|
|
249
249
|
|
|
250
250
|
#### Context stops and the pricing boundary
|
|
@@ -324,9 +324,9 @@ and their seeded flags when discovery omits them. Models not using Responses-Lit
|
|
|
324
324
|
this version header and are not hidden by this check. This check does not establish your
|
|
325
325
|
account's model entitlement.
|
|
326
326
|
|
|
327
|
-
Providers supported: `openai` (API key, platform.openai.com), `openai-oauth` (ChatGPT/Codex plan), and `opencode-go` (OpenCode Go API key). OpenCode Go exposes its Anthropic Messages, Chat Completions, and Responses models; entries whose transport has
|
|
327
|
+
Providers supported: `openai` (API key, platform.openai.com), `openai-oauth` (ChatGPT/Codex plan), and `opencode-go` (OpenCode Go API key). OpenCode Go exposes its Anthropic Messages, Chat Completions, and Responses models; only entries whose transport has been verified, live or against OpenCode's per-model endpoint table, are included. See [OpenCode Go provider](docs/opencode-go.md).
|
|
328
328
|
|
|
329
|
-
A **custom OpenAI-compatible server** is any OpenAI-style API, given by its base URL (the part before `/chat/completions`), such as `https://openrouter.ai/api/v1`. `providers add` asks for a name, the base URL and an API key (leave it empty for a local server without auth), lists the server's models to check the connection, and saves the provider as `custom-<name>`. Its models then appear in `clodex models`; once saved as favorites they are addressed as `clodex:custom-<name>:<model>` and can be given a short alias with `clodex models --alias`. The base URL is checked before anything is saved: plain `http://` is accepted only after you confirm it, and only
|
|
329
|
+
A **custom OpenAI-compatible server** is any OpenAI-style API, given by its base URL (the part before `/chat/completions`), such as `https://openrouter.ai/api/v1`. `providers add` asks for a name, the base URL and an API key (leave it empty for a local server without auth), lists the server's models to check the connection, and saves the provider as `custom-<name>`. Its models then appear in `clodex models`; once saved as favorites they are addressed as `clodex:custom-<name>:<model>` and can be given a short alias with `clodex models --alias`. The base URL is checked before anything is saved and again when its models are refreshed: plain `http://` is accepted only after you confirm it, and only if every address the host resolves to is on this machine, your LAN or a private network such as Tailscale (loopback, private, unique-local IPv6 or carrier-grade-NAT `100.64.0.0/10` addresses); `https://` is accepted on any of those networks as well as the public internet; and a link-local (`169.254.0.0/16`, `fe80::/10`) or well-known cloud-metadata address is refused whether or not HTTP was approved. A server on another machine in your tailnet can be added by its `100.x` address, its MagicDNS name, or the HTTPS name `tailscale serve` gives it. A model's id and name are stored with surrounding whitespace trimmed, and a model whose trimmed id or name still contains a control character is skipped, whether the list is fetched by `providers add` or by `providers refresh-models`, and error text from the server is shown with control characters replaced by spaces. Only the OpenAI-compatible kind is offered in the menu.
|
|
330
330
|
|
|
331
331
|
### `clodex install-vscode-launcher`
|
|
332
332
|
|
package/dist/cli.js
CHANGED
|
@@ -346,7 +346,7 @@ import { join } from "path";
|
|
|
346
346
|
// package.json
|
|
347
347
|
var package_default = {
|
|
348
348
|
name: "@bman654/clodex",
|
|
349
|
-
version: "2.18.
|
|
349
|
+
version: "2.18.8",
|
|
350
350
|
publishConfig: {
|
|
351
351
|
access: "public"
|
|
352
352
|
},
|
|
@@ -3486,11 +3486,11 @@ function applyClodexPatches(source, config) {
|
|
|
3486
3486
|
};
|
|
3487
3487
|
for (const [id, value] of Object.entries(MODEL_CONFIG)) {
|
|
3488
3488
|
const spec = value && typeof value === "object" ? value : { alias: value };
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
const a =
|
|
3489
|
+
const aliasNames = spec.alias === void 0 ? [] : [String(spec.alias), ...(spec.moreAliases ?? []).map(String)];
|
|
3490
|
+
for (const name of aliasNames) {
|
|
3491
|
+
const a = name.trim().toLowerCase();
|
|
3492
3492
|
if (!/^[a-z0-9][a-z0-9._-]*(\[1m\])?$/.test(a)) {
|
|
3493
|
-
fail('clodex patch: alias "' +
|
|
3493
|
+
fail('clodex patch: alias "' + name + '" is not a safe lowercase alias');
|
|
3494
3494
|
}
|
|
3495
3495
|
if (isReservedModelAlias(a)) {
|
|
3496
3496
|
fail('clodex patch: reserved alias "' + a + '" cannot be reassigned');
|
|
@@ -3498,25 +3498,24 @@ function applyClodexPatches(source, config) {
|
|
|
3498
3498
|
ALIAS_TO_ID[a] = String(id);
|
|
3499
3499
|
IDENTITIES.push(a);
|
|
3500
3500
|
if (spec.display) DISPLAY_BY_IDENTITY[a] = String(spec.display);
|
|
3501
|
-
|
|
3501
|
+
registerCapabilityKeys(name);
|
|
3502
|
+
}
|
|
3503
|
+
if (aliasNames.length === 0) {
|
|
3502
3504
|
IDENTITIES.push(String(id));
|
|
3503
3505
|
if (spec.display) DISPLAY_BY_IDENTITY[String(id)] = String(spec.display);
|
|
3504
3506
|
}
|
|
3505
|
-
if (spec.alias !== void 0) {
|
|
3506
|
-
registerCapabilityKeys(String(spec.alias));
|
|
3507
|
-
}
|
|
3508
3507
|
registerCapabilityKeys(String(id));
|
|
3509
3508
|
if (spec.context !== void 0) {
|
|
3510
3509
|
const n = Number(spec.context);
|
|
3511
3510
|
if (!Number.isInteger(n) || n <= 0) {
|
|
3512
3511
|
fail('clodex patch: context for "' + id + '" must be a positive integer, got ' + spec.context);
|
|
3513
3512
|
}
|
|
3514
|
-
if (/\[1m\]/i.test(
|
|
3513
|
+
if (aliasNames.some((name) => /\[1m\]/i.test(name)) || /\[1m\]/i.test(id)) {
|
|
3515
3514
|
fail(
|
|
3516
3515
|
'clodex patch: "' + id + '" sets context but keeps the [1m] suffix \u2014 drop the suffix from both the id and the alias'
|
|
3517
3516
|
);
|
|
3518
3517
|
}
|
|
3519
|
-
|
|
3518
|
+
for (const name of aliasNames) CONTEXT_BY_KEY[name.trim().toLowerCase()] = n;
|
|
3520
3519
|
CONTEXT_BY_KEY[String(id).trim().toLowerCase()] = n;
|
|
3521
3520
|
}
|
|
3522
3521
|
if (spec.effort) {
|
|
@@ -3526,8 +3525,8 @@ function applyClodexPatches(source, config) {
|
|
|
3526
3525
|
`clodex patch: effort for "${id}" must include low, medium, and high with a native default`
|
|
3527
3526
|
);
|
|
3528
3527
|
}
|
|
3529
|
-
|
|
3530
|
-
for (const key of capabilityKeys(
|
|
3528
|
+
for (const name of aliasNames) {
|
|
3529
|
+
for (const key of capabilityKeys(name)) {
|
|
3531
3530
|
EFFORT_BY_KEY[key] = effort;
|
|
3532
3531
|
}
|
|
3533
3532
|
}
|
|
@@ -4186,11 +4185,12 @@ function configuredAliases(config) {
|
|
|
4186
4185
|
const aliases = /* @__PURE__ */ new Map();
|
|
4187
4186
|
for (const [id, entry] of Object.entries(config)) {
|
|
4188
4187
|
if (entry.alias === void 0) continue;
|
|
4189
|
-
const
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4188
|
+
for (const name of [entry.alias, ...entry.moreAliases ?? []]) {
|
|
4189
|
+
aliases.set(String(name).trim().toLowerCase(), {
|
|
4190
|
+
id,
|
|
4191
|
+
...entry.display === void 0 ? {} : { display: String(entry.display) }
|
|
4192
|
+
});
|
|
4193
|
+
}
|
|
4194
4194
|
}
|
|
4195
4195
|
return [...aliases].map(([alias, entry]) => ({ alias, ...entry }));
|
|
4196
4196
|
}
|
|
@@ -4902,17 +4902,27 @@ var opencode_go_models_default = [
|
|
|
4902
4902
|
output: 0.6,
|
|
4903
4903
|
cache_read: 3e-3
|
|
4904
4904
|
},
|
|
4905
|
-
modelFormat: "
|
|
4906
|
-
npm: "@ai-sdk/
|
|
4907
|
-
apiUrl: "https://opencode.ai/zen/go",
|
|
4905
|
+
modelFormat: "openai",
|
|
4906
|
+
npm: "@ai-sdk/openai-compatible",
|
|
4907
|
+
apiUrl: "https://opencode.ai/zen/go/v1",
|
|
4908
4908
|
reasoning: true,
|
|
4909
4909
|
modalities: [
|
|
4910
4910
|
"text",
|
|
4911
4911
|
"image"
|
|
4912
4912
|
],
|
|
4913
4913
|
compatibility: {
|
|
4914
|
-
|
|
4915
|
-
|
|
4914
|
+
reasoningEffortMap: {
|
|
4915
|
+
minimal: null,
|
|
4916
|
+
low: null,
|
|
4917
|
+
medium: null,
|
|
4918
|
+
high: "high",
|
|
4919
|
+
max: "max"
|
|
4920
|
+
},
|
|
4921
|
+
supportsStore: false,
|
|
4922
|
+
supportsDeveloperRole: false,
|
|
4923
|
+
maxTokensField: "max_tokens",
|
|
4924
|
+
requiresReasoningContentOnAssistantMessages: true,
|
|
4925
|
+
thinkingFormat: "deepseek"
|
|
4916
4926
|
},
|
|
4917
4927
|
upstreamModelId: "deepseek-v4.1-flash",
|
|
4918
4928
|
family: "deepseek"
|
|
@@ -14408,20 +14418,23 @@ function buildPatchModelConfig(favorites, aliases, modelMetaFor) {
|
|
|
14408
14418
|
alias: source,
|
|
14409
14419
|
reason: "target-not-favorite"
|
|
14410
14420
|
})));
|
|
14411
|
-
const
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
]
|
|
14416
|
-
|
|
14421
|
+
const aliasesByFavorite = /* @__PURE__ */ new Map();
|
|
14422
|
+
for (const alias of normalizedAliases.aliases) {
|
|
14423
|
+
const key = `${alias.providerId}:${alias.modelId}`;
|
|
14424
|
+
if (!favoriteTargets.has(key)) continue;
|
|
14425
|
+
const names = aliasesByFavorite.get(key) ?? [];
|
|
14426
|
+
names.push(alias.name);
|
|
14427
|
+
aliasesByFavorite.set(key, names);
|
|
14428
|
+
}
|
|
14417
14429
|
for (const favorite of favorites) {
|
|
14418
14430
|
const id = stripOneMContextSuffix(httpProxyModelId(favorite.providerId, favorite.modelId));
|
|
14419
14431
|
if (config[id]) continue;
|
|
14420
14432
|
const meta = modelMetaFor(favorite.providerId, favorite.modelId);
|
|
14421
14433
|
const context = meta?.contextWindow;
|
|
14422
|
-
const alias =
|
|
14434
|
+
const [alias, ...moreAliases] = aliasesByFavorite.get(`${favorite.providerId}:${favorite.modelId}`) ?? [];
|
|
14423
14435
|
const entry = {};
|
|
14424
14436
|
if (alias) entry.alias = alias;
|
|
14437
|
+
if (moreAliases.length > 0) entry.moreAliases = moreAliases;
|
|
14425
14438
|
if (context === void 0 || context <= 0) unknownWindows.push(id);
|
|
14426
14439
|
else if (context !== 2e5) entry.context = context;
|
|
14427
14440
|
const display = meta?.displayName?.trim();
|
|
@@ -14461,7 +14474,9 @@ function computePatchConfigHash(config, transformsVersion = PATCH_TRANSFORMS_VER
|
|
|
14461
14474
|
entry.context ?? null,
|
|
14462
14475
|
entry.display ?? null,
|
|
14463
14476
|
entry.effort?.levels ?? null,
|
|
14464
|
-
entry.effort?.defaultLevel ?? null
|
|
14477
|
+
entry.effort?.defaultLevel ?? null,
|
|
14478
|
+
// Appended only when present, so a config with one alias per model keeps its hash.
|
|
14479
|
+
...entry.moreAliases ? [entry.moreAliases] : []
|
|
14465
14480
|
];
|
|
14466
14481
|
});
|
|
14467
14482
|
const payload = [transformsVersion, canonical];
|
|
@@ -14940,7 +14955,7 @@ async function applyPatch(binaryPath, version, desired, configHash, opts) {
|
|
|
14940
14955
|
};
|
|
14941
14956
|
writePatchManifest(manifest);
|
|
14942
14957
|
const modelCount = Object.keys(desired.config).length;
|
|
14943
|
-
const aliasCount = Object.values(desired.config).
|
|
14958
|
+
const aliasCount = Object.values(desired.config).reduce((n, entry) => n + (entry.alias ? 1 : 0) + (entry.moreAliases?.length ?? 0), 0);
|
|
14944
14959
|
const windowCount = Object.values(desired.config).filter((entry) => entry.context).length;
|
|
14945
14960
|
return {
|
|
14946
14961
|
ok: true,
|
|
@@ -15185,14 +15200,14 @@ function effortFor(meta) {
|
|
|
15185
15200
|
...meta.effort ? { providerLevels: meta.effort.levels } : {}
|
|
15186
15201
|
};
|
|
15187
15202
|
}
|
|
15188
|
-
function metadataFor(id, meta,
|
|
15203
|
+
function metadataFor(id, meta, aliases) {
|
|
15189
15204
|
const effective = positive(meta.contextWindow);
|
|
15190
15205
|
const effort = effortFor(meta);
|
|
15191
15206
|
return {
|
|
15192
15207
|
id,
|
|
15193
15208
|
...meta.providerId ? { providerId: meta.providerId } : {},
|
|
15194
15209
|
...meta.modelId ? { modelId: meta.modelId } : {},
|
|
15195
|
-
...
|
|
15210
|
+
...aliases.length > 0 ? { alias: aliases[0], aliases } : {},
|
|
15196
15211
|
...meta.displayName?.trim() ? { displayName: meta.displayName.trim() } : {},
|
|
15197
15212
|
context: {
|
|
15198
15213
|
stop: meta.contextStop ?? "standard",
|
|
@@ -15209,7 +15224,11 @@ function metadataFor(id, meta, alias) {
|
|
|
15209
15224
|
function buildModelMetadata() {
|
|
15210
15225
|
const desired = buildDesiredPatchConfig({ sessionStops: true });
|
|
15211
15226
|
return Object.entries(desired.config).map(
|
|
15212
|
-
([id, entry]) => metadataFor(
|
|
15227
|
+
([id, entry]) => metadataFor(
|
|
15228
|
+
id,
|
|
15229
|
+
desired.metaById[id] ?? {},
|
|
15230
|
+
entry.alias === void 0 ? [] : [entry.alias, ...entry.moreAliases ?? []]
|
|
15231
|
+
)
|
|
15213
15232
|
);
|
|
15214
15233
|
}
|
|
15215
15234
|
function formatModelMetadata(entries) {
|
|
@@ -16009,21 +16028,17 @@ var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
16009
16028
|
"169.254.169.254",
|
|
16010
16029
|
"metadata.google.internal",
|
|
16011
16030
|
"169.254.170.2",
|
|
16012
|
-
"fd00:ec2::254"
|
|
16031
|
+
"fd00:ec2::254",
|
|
16032
|
+
"100.100.100.200",
|
|
16033
|
+
// Alibaba Cloud ECS
|
|
16034
|
+
"fd20:ce::254"
|
|
16035
|
+
// Google Cloud IPv6
|
|
16013
16036
|
]);
|
|
16014
|
-
|
|
16037
|
+
var NON_PUBLIC_RANGES = /* @__PURE__ */ new Set(["loopback", "private", "uniqueLocal", "carrierGradeNat"]);
|
|
16038
|
+
function isBlockedIp(ipStr) {
|
|
16015
16039
|
try {
|
|
16016
16040
|
const ip = ipaddr.process(ipStr);
|
|
16017
|
-
|
|
16018
|
-
if (allowInsecureLocal && (range === "loopback" || range === "private")) {
|
|
16019
|
-
return false;
|
|
16020
|
-
}
|
|
16021
|
-
if (range === "loopback") return true;
|
|
16022
|
-
if (range === "private") return true;
|
|
16023
|
-
if (range === "linkLocal") return true;
|
|
16024
|
-
if (range === "uniqueLocal") return true;
|
|
16025
|
-
if (range === "carrierGradeNat") return true;
|
|
16026
|
-
return false;
|
|
16041
|
+
return ip.range() === "linkLocal" || BLOCKED_HOSTNAMES.has(ip.toString());
|
|
16027
16042
|
} catch {
|
|
16028
16043
|
return true;
|
|
16029
16044
|
}
|
|
@@ -16057,10 +16072,10 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16057
16072
|
return {
|
|
16058
16073
|
ok: false,
|
|
16059
16074
|
error: "Only HTTPS URLs are allowed.",
|
|
16060
|
-
hint: "For
|
|
16075
|
+
hint: "For a server on this machine, your LAN or Tailscale (Ollama, LM Studio, vLLM), allow insecure HTTP when prompted."
|
|
16061
16076
|
};
|
|
16062
16077
|
} else if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
16063
|
-
return { ok: false, error: "URL must use https:// or user-approved http:// for local
|
|
16078
|
+
return { ok: false, error: "URL must use https:// or user-approved http:// for local, LAN or Tailscale servers." };
|
|
16064
16079
|
}
|
|
16065
16080
|
const rawHostname = parsed.hostname.toLowerCase();
|
|
16066
16081
|
const hostname = rawHostname.replace(/^\[(.*)\]$/, "$1");
|
|
@@ -16068,7 +16083,7 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16068
16083
|
return {
|
|
16069
16084
|
ok: false,
|
|
16070
16085
|
error: "This URL points to a blocked internal/metadata host.",
|
|
16071
|
-
hint: "Use
|
|
16086
|
+
hint: "Use your model server's own loopback, LAN, Tailscale or public address."
|
|
16072
16087
|
};
|
|
16073
16088
|
}
|
|
16074
16089
|
const addresses = await resolveHostAddresses(hostname);
|
|
@@ -16085,19 +16100,18 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16085
16100
|
} catch {
|
|
16086
16101
|
continue;
|
|
16087
16102
|
}
|
|
16088
|
-
if (isBlockedIp(addr
|
|
16103
|
+
if (isBlockedIp(addr)) {
|
|
16089
16104
|
return {
|
|
16090
16105
|
ok: false,
|
|
16091
|
-
error: "URL resolves to a
|
|
16092
|
-
hint: "
|
|
16106
|
+
error: "URL resolves to a restricted link-local or cloud metadata address.",
|
|
16107
|
+
hint: "Link-local addresses are not supported. Use the server's loopback, LAN, Tailscale or public address."
|
|
16093
16108
|
};
|
|
16094
16109
|
}
|
|
16095
16110
|
}
|
|
16096
16111
|
if (parsed.protocol === "http:") {
|
|
16097
16112
|
const allResolvedAddressesAreLocal = addresses.every((addr) => {
|
|
16098
16113
|
try {
|
|
16099
|
-
|
|
16100
|
-
return range === "loopback" || range === "private" || range === "uniqueLocal";
|
|
16114
|
+
return NON_PUBLIC_RANGES.has(ipaddr.process(addr).range());
|
|
16101
16115
|
} catch {
|
|
16102
16116
|
return false;
|
|
16103
16117
|
}
|
|
@@ -16105,8 +16119,8 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
16105
16119
|
if (!allowLocal || !allResolvedAddressesAreLocal) {
|
|
16106
16120
|
return {
|
|
16107
16121
|
ok: false,
|
|
16108
|
-
error: "HTTP is only allowed for
|
|
16109
|
-
hint: "Use https
|
|
16122
|
+
error: "HTTP is only allowed for addresses on this machine, your LAN or a private network such as Tailscale.",
|
|
16123
|
+
hint: "Use https:// for a server on the public internet."
|
|
16110
16124
|
};
|
|
16111
16125
|
}
|
|
16112
16126
|
}
|
|
@@ -16354,9 +16368,9 @@ async function runCustomEndpointAddFlow(onResult) {
|
|
|
16354
16368
|
}
|
|
16355
16369
|
let allowInsecureLocal = false;
|
|
16356
16370
|
if (/^http:\/\//i.test(String(baseUrl).trim())) {
|
|
16357
|
-
p3.log.warn("HTTP is not encrypted. Only use it for a trusted
|
|
16371
|
+
p3.log.warn("HTTP is not encrypted. Only use it for a trusted server on this machine, your LAN or Tailscale.");
|
|
16358
16372
|
const allow = await p3.confirm({
|
|
16359
|
-
message: "Allow insecure HTTP for this local
|
|
16373
|
+
message: "Allow insecure HTTP for this local, LAN or Tailscale server?",
|
|
16360
16374
|
initialValue: true
|
|
16361
16375
|
});
|
|
16362
16376
|
if (p3.isCancel(allow)) {
|
|
@@ -16879,8 +16893,9 @@ async function refreshApiListProvider(provider, apiKey) {
|
|
|
16879
16893
|
let safeBaseUrl = baseUrl;
|
|
16880
16894
|
const templateDefault = catalogTemplate?.defaultBaseUrl?.trim();
|
|
16881
16895
|
if (configuredUrl && configuredUrl !== templateDefault) {
|
|
16896
|
+
const customEndpoint = provider.templateId === "custom-openai" || provider.templateId === "custom-anthropic";
|
|
16882
16897
|
const urlCheck = await validateCustomEndpointUrl(baseUrl, {
|
|
16883
|
-
allowInsecureLocal: catalogTemplate?.apiKeyOptional === true
|
|
16898
|
+
allowInsecureLocal: catalogTemplate?.apiKeyOptional === true || customEndpoint
|
|
16884
16899
|
});
|
|
16885
16900
|
if (!urlCheck.ok || !urlCheck.normalizedUrl) {
|
|
16886
16901
|
return { models: [], error: `${urlCheck.error ?? "Invalid API base URL."} ${urlCheck.hint ?? ""}`.trim() };
|
|
@@ -17078,7 +17093,7 @@ async function refreshProviderModels(providerId, apiKey, registry, credentialSna
|
|
|
17078
17093
|
}
|
|
17079
17094
|
} else {
|
|
17080
17095
|
const template = resolveProviderTemplate(provider);
|
|
17081
|
-
const keyOptional = template?.apiKeyOptional === true;
|
|
17096
|
+
const keyOptional = template?.apiKeyOptional === true || isAnonymousProvider(provider);
|
|
17082
17097
|
const effectiveKey = keyOptional && isLikelyPlaceholderKey(apiKey) ? "" : apiKey;
|
|
17083
17098
|
if (!keyOptional && isLikelyPlaceholderKey(effectiveKey)) {
|
|
17084
17099
|
if (cachedModelCount(cacheProvider) > 0) {
|
|
@@ -22645,9 +22660,9 @@ ${pc13.bold("Behavior:")}
|
|
|
22645
22660
|
standard (the provider's tuned default), max (its ceiling), default (clear a
|
|
22646
22661
|
saved choice), or a token count such as 500k. Applies to this run only unless
|
|
22647
22662
|
--save is given. The model is a saved alias or clodex:<provider-id>:<model-id>.
|
|
22648
|
-
--json prints the resolved metadata for saved favorites as JSON: ids,
|
|
22649
|
-
|
|
22650
|
-
levels. Diagnostics go to stderr so stdout stays parseable.
|
|
22663
|
+
--json prints the resolved metadata for saved favorites as JSON: ids, every
|
|
22664
|
+
saved alias, context stop and windows, output limit, pricing boundary, and
|
|
22665
|
+
effort levels. Diagnostics go to stderr so stdout stays parseable.
|
|
22651
22666
|
|
|
22652
22667
|
${pc13.bold("How it works:")}
|
|
22653
22668
|
claude and server use the global favorites list.
|