@bman654/clodex 2.18.7 → 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 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,7 +324,7 @@ 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 not been verified against the live endpoint are left out. See [OpenCode Go provider](docs/opencode-go.md).
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
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
 
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.7",
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
- if (spec.alias !== void 0) {
3490
- const rawAlias = String(spec.alias).trim();
3491
- const a = rawAlias.toLowerCase();
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 "' + spec.alias + '" is not a safe lowercase 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
- } else {
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(String(spec.alias ?? "")) || /\[1m\]/i.test(id)) {
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
- if (spec.alias !== void 0) CONTEXT_BY_KEY[String(spec.alias).trim().toLowerCase()] = n;
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
- if (spec.alias !== void 0) {
3530
- for (const key of capabilityKeys(String(spec.alias))) {
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 alias = String(entry.alias).trim().toLowerCase();
4190
- aliases.set(alias, {
4191
- id,
4192
- ...entry.display === void 0 ? {} : { display: String(entry.display) }
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: "anthropic",
4906
- npm: "@ai-sdk/anthropic",
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
- supportsCountTokens: false,
4915
- supportsReasoningEffort: false
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 aliasByFavorite = new Map(
14412
- normalizedAliases.aliases.filter((alias) => favoriteTargets.has(`${alias.providerId}:${alias.modelId}`)).map((alias) => [
14413
- `${alias.providerId}:${alias.modelId}`,
14414
- alias.name
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 = aliasByFavorite.get(`${favorite.providerId}:${favorite.modelId}`);
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).filter((entry) => entry.alias).length;
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, alias) {
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
- ...alias ? { alias } : {},
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(id, desired.metaById[id] ?? {}, entry.alias)
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) {
@@ -22641,9 +22660,9 @@ ${pc13.bold("Behavior:")}
22641
22660
  standard (the provider's tuned default), max (its ceiling), default (clear a
22642
22661
  saved choice), or a token count such as 500k. Applies to this run only unless
22643
22662
  --save is given. The model is a saved alias or clodex:<provider-id>:<model-id>.
22644
- --json prints the resolved metadata for saved favorites as JSON: ids,
22645
- aliases, context stop and windows, output limit, pricing boundary, and effort
22646
- 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.
22647
22666
 
22648
22667
  ${pc13.bold("How it works:")}
22649
22668
  claude and server use the global favorites list.