@askalf/dario 6.13.28 → 6.13.30

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.js CHANGED
@@ -64,7 +64,7 @@ import { grantAge, describeGrantAge } from './refresh-grant.js';
64
64
  import { listAccountAliases, loadAllAccounts, addAccountViaOAuth, addAccountViaManualOAuth, addAccountFromKeychain, KeychainImportError, removeAccount, ensureLoginCredentialsInPool, resyncLoginFromCredentialsIfStale, MIGRATED_LOGIN_ALIAS } from './accounts.js';
65
65
  import { listCodexAccountAliases, loadAllCodexAccounts, startAddCodexAccount, completeAddCodexAccount, removeCodexAccount, parseCodexManualPaste } from './codex-accounts.js';
66
66
  import { describeCodexWindow } from './codex-usage.js';
67
- import { listBackends, saveBackend, removeBackend } from './openai-backend.js';
67
+ import { listBackends, saveBackend, removeBackend, defaultBackendBaseUrl } from './openai-backend.js';
68
68
  import { parseOutboundProxy, installOutboundProxyWrapper } from './outbound-proxy.js';
69
69
  // `args` / `command` at module scope — command handlers below close over
70
70
  // `args` to read their own flags. Reading argv is harmless on import; only
@@ -1788,6 +1788,7 @@ async function backend() {
1788
1788
  console.error(' dario backend add openai --key=sk-proj-...');
1789
1789
  console.error(' dario backend add groq --key=gsk_... --base-url=https://api.groq.com/openai/v1');
1790
1790
  console.error(' dario backend add openrouter --key=sk-or-... --base-url=https://openrouter.ai/api/v1');
1791
+ console.error(' dario backend add minimax --key=<api-key> (global; minimax-cn for China)');
1791
1792
  console.error('');
1792
1793
  process.exit(1);
1793
1794
  }
@@ -1798,7 +1799,7 @@ async function backend() {
1798
1799
  const keyArg = args.find(a => a.startsWith('--key='));
1799
1800
  const baseUrlArg = args.find(a => a.startsWith('--base-url='));
1800
1801
  const apiKey = keyArg ? keyArg.split('=').slice(1).join('=') : '';
1801
- const baseUrl = baseUrlArg ? baseUrlArg.split('=').slice(1).join('=') : 'https://api.openai.com/v1';
1802
+ const baseUrl = baseUrlArg ? baseUrlArg.split('=').slice(1).join('=') : defaultBackendBaseUrl(name);
1802
1803
  if (!apiKey) {
1803
1804
  console.error('[dario] --key=<api-key> is required.');
1804
1805
  process.exit(1);
@@ -518,7 +518,7 @@ export declare function detectDrift(t: TemplateData, installedOverride?: string
518
518
  */
519
519
  export declare const SUPPORTED_CC_RANGE: {
520
520
  readonly min: "1.0.0";
521
- readonly maxTested: "2.1.308";
521
+ readonly maxTested: "2.1.309";
522
522
  };
523
523
  /**
524
524
  * Compare two dotted-numeric version strings. Returns negative if `a<b`,
@@ -1238,7 +1238,7 @@ export function detectDrift(t, installedOverride) {
1238
1238
  */
1239
1239
  export const SUPPORTED_CC_RANGE = {
1240
1240
  min: '1.0.0',
1241
- maxTested: '2.1.308',
1241
+ maxTested: '2.1.309',
1242
1242
  };
1243
1243
  /**
1244
1244
  * Compare two dotted-numeric version strings. Returns negative if `a<b`,
@@ -1,4 +1,9 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ export declare const MINIMAX_MODELS: readonly ["MiniMax-M3.1-Flash", "MiniMax-M3"];
3
+ /** Presets use the existing single OpenAI-compatible backend slot. */
4
+ export declare function defaultBackendBaseUrl(name: string): string;
5
+ /** Only direct endpoints imply a MiniMax catalog; custom gateways stay explicit. */
6
+ export declare function isMiniMaxBackend(backend: BackendCredentials | null): boolean;
2
7
  export interface BackendCredentials {
3
8
  provider: string;
4
9
  name: string;
@@ -17,6 +17,20 @@ import { join, basename } from 'node:path';
17
17
  import { homedir } from 'node:os';
18
18
  const DARIO_DIR = join(homedir(), '.dario');
19
19
  const BACKENDS_DIR = join(DARIO_DIR, 'backends');
20
+ export const MINIMAX_MODELS = ['MiniMax-M3.1-Flash', 'MiniMax-M3'];
21
+ /** Presets use the existing single OpenAI-compatible backend slot. */
22
+ export function defaultBackendBaseUrl(name) {
23
+ if (name.toLowerCase() === 'minimax')
24
+ return 'https://api.minimax.io/v1';
25
+ if (name.toLowerCase() === 'minimax-cn')
26
+ return 'https://api.minimax.cn/v1';
27
+ return 'https://api.openai.com/v1';
28
+ }
29
+ /** Only direct endpoints imply a MiniMax catalog; custom gateways stay explicit. */
30
+ export function isMiniMaxBackend(backend) {
31
+ return backend?.provider === 'openai' &&
32
+ ['https://api.minimax.io/v1', 'https://api.minimax.cn/v1', 'https://api.minimaxi.com/v1'].includes(backend.baseUrl.replace(/\/$/, ''));
33
+ }
20
34
  /**
21
35
  * Normalize a caller-supplied backend name into a filesystem-safe leaf.
22
36
  * Strips any directory component and rejects names outside the allowed
package/dist/proxy.d.ts CHANGED
@@ -3,6 +3,7 @@ import { type WriteStream } from 'node:fs';
3
3
  import { getAccessToken, getStatus } from './oauth.js';
4
4
  import { type EffortValue } from './cc-template.js';
5
5
  import { type KeySocket } from './keys.js';
6
+ import { type BackendCredentials } from './openai-backend.js';
6
7
  /**
7
8
  * Assemble the `x-anthropic-billing-header` system-block text, following
8
9
  * Claude Code's format:
@@ -87,6 +88,7 @@ export declare function budgetModelsFor(clientModel: string, ctx: {
87
88
  cliProviderOverride: 'openai' | 'claude' | 'codex' | null;
88
89
  cliModelRaw: string | null | undefined;
89
90
  isOpenAI: boolean;
91
+ compatBackend?: CompatBackendKind;
90
92
  }): string[];
91
93
  /**
92
94
  * Rebuild an OpenAI-shape request body with the model swapped to the pool-
@@ -95,6 +97,16 @@ export declare function budgetModelsFor(clientModel: string, ctx: {
95
97
  * tests; the two call sites are the pool-exhausted dispatch paths.
96
98
  */
97
99
  export declare function buildPoolFallbackBody(body: Buffer, fallbackModel: string): Buffer | null;
100
+ /** Which kind of OpenAI-compatible backend is configured: a direct MiniMax endpoint, any other gateway, or none. */
101
+ export type CompatBackendKind = 'minimax' | 'gateway' | null;
102
+ export declare function compatBackendKind(backend: BackendCredentials | null): CompatBackendKind;
103
+ /**
104
+ * The bare `minimax` model. On a direct MiniMax endpoint it is the shorthand for
105
+ * MiniMax-M3.1-Flash. On a custom gateway it is that gateway's own model id: the request goes
106
+ * to the gateway with the name unchanged (dario#1649 review). With no compatible backend it is
107
+ * left alone. `minimax:<model>` is explicit and always honored by the prefix parser.
108
+ */
109
+ export declare function miniMaxShorthand(model: string, backend: CompatBackendKind): string;
98
110
  export declare function parseProviderPrefix(model: string): {
99
111
  provider: 'openai' | 'claude' | 'codex';
100
112
  model: string;
package/dist/proxy.js CHANGED
@@ -30,7 +30,7 @@ import { resolveSeatPin, SEAT_PIN_HEADER, SEAT_PIN_TOKEN_HEADER } from './seat-p
30
30
  import { loadAllAccounts, loadAccount, saveAccount, refreshAccountToken, resyncLoginFromCredentialsIfStale, ensureLoginCredentialsInPool, mirrorLoginToCredentials, withObservedOrganization } from './accounts.js';
31
31
  import { handleAdminRequest } from './admin-api.js';
32
32
  import { createTokenBucket } from './rate-limit.js';
33
- import { getOpenAIBackend, isOpenAIModel, forwardToOpenAI } from './openai-backend.js';
33
+ import { getOpenAIBackend, isOpenAIModel, forwardToOpenAI, isMiniMaxBackend, MINIMAX_MODELS } from './openai-backend.js';
34
34
  import { forwardToCodex, forwardResponsesToCodex, getCodexModelSlugs, peekCodexModelSlugs, isCodexModel, pickCodexFallback, pickClaudeTarget, CODEX_BACKEND_BASE_URL } from './codex-backend.js';
35
35
  import { effortForCodex } from './effort.js';
36
36
  import { MidstreamGuard, guardFor, loopbackBaseFor, chaosCutFetch, chaosCutState, CONTINUATION_HEADER, CONTINUATION_OF_HEADER, MAX_CONTINUATION_DEPTH, continuationDepth, continuationOfRequest } from './midstream.js';
@@ -378,7 +378,7 @@ export function selectModelOverride(incomingModel, modelOverride, fastModelOverr
378
378
  */
379
379
  export function budgetModelsFor(clientModel, ctx) {
380
380
  const out = new Set();
381
- const aliased = applyModelAlias(clientModel, ctx.modelAliases) ?? clientModel;
381
+ const aliased = miniMaxShorthand(applyModelAlias(clientModel, ctx.modelAliases) ?? clientModel, ctx.compatBackend ?? null);
382
382
  const prefix = parseProviderPrefix(aliased);
383
383
  if (prefix) {
384
384
  out.add(prefix.provider === 'claude' ? resolveClaudeAlias(parseEffortSuffix(prefix.model).model) : prefix.model);
@@ -406,6 +406,7 @@ const PROVIDER_PREFIXES = {
406
406
  openai: 'openai',
407
407
  openrouter: 'openai',
408
408
  groq: 'openai',
409
+ minimax: 'openai',
409
410
  compat: 'openai',
410
411
  local: 'openai',
411
412
  codex: 'codex',
@@ -431,6 +432,26 @@ export function buildPoolFallbackBody(body, fallbackModel) {
431
432
  return null;
432
433
  }
433
434
  }
435
+ export function compatBackendKind(backend) {
436
+ if (!backend)
437
+ return null;
438
+ return isMiniMaxBackend(backend) ? 'minimax' : 'gateway';
439
+ }
440
+ /**
441
+ * The bare `minimax` model. On a direct MiniMax endpoint it is the shorthand for
442
+ * MiniMax-M3.1-Flash. On a custom gateway it is that gateway's own model id: the request goes
443
+ * to the gateway with the name unchanged (dario#1649 review). With no compatible backend it is
444
+ * left alone. `minimax:<model>` is explicit and always honored by the prefix parser.
445
+ */
446
+ export function miniMaxShorthand(model, backend) {
447
+ if (model.toLowerCase() !== 'minimax')
448
+ return model;
449
+ if (backend === 'minimax')
450
+ return `minimax:${MINIMAX_MODELS[0]}`;
451
+ if (backend === 'gateway')
452
+ return `openai:${model}`;
453
+ return model;
454
+ }
434
455
  export function parseProviderPrefix(model) {
435
456
  const idx = model.indexOf(':');
436
457
  if (idx <= 0)
@@ -1915,14 +1936,14 @@ export async function startProxy(opts = {}) {
1915
1936
  // Parse --model once at startup. Supports `<provider>:<model>` to force
1916
1937
  // a backend for every request (e.g. `--model=openai:gpt-4o`). Back-compat:
1917
1938
  // bare names like `opus` resolve via MODEL_ALIASES.
1918
- const modelPrefix = opts.model ? parseProviderPrefix(opts.model) : null;
1939
+ const modelPrefix = opts.model ? parseProviderPrefix(miniMaxShorthand(opts.model, compatBackendKind(openaiBackend))) : null;
1919
1940
  const cliModelRaw = modelPrefix ? modelPrefix.model : opts.model;
1920
1941
  const cliProviderOverride = modelPrefix ? modelPrefix.provider : null;
1921
1942
  const modelOverride = cliModelRaw ? resolveClaudeAlias(cliModelRaw) : null;
1922
1943
  // --fast-model: the model that Haiku-tier (Claude Code sub-agent) requests
1923
1944
  // route to instead of the forced `--model`, so sub-agents stay cheap. Parsed
1924
1945
  // like --model (alias + optional provider prefix). Null = disabled.
1925
- const fastModelPrefix = opts.fastModel ? parseProviderPrefix(opts.fastModel) : null;
1946
+ const fastModelPrefix = opts.fastModel ? parseProviderPrefix(miniMaxShorthand(opts.fastModel, compatBackendKind(openaiBackend))) : null;
1926
1947
  const fastModelRaw = fastModelPrefix ? fastModelPrefix.model : opts.fastModel;
1927
1948
  const fastModelOverride = fastModelRaw ? resolveClaudeAlias(fastModelRaw) : null;
1928
1949
  const identity = loadClaudeIdentity();
@@ -3146,7 +3167,8 @@ export async function startProxy(opts = {}) {
3146
3167
  // User aliases are advertised after the real ids so pickers offer
3147
3168
  // them; already-advertised names aren't duplicated (an alias that
3148
3169
  // shadows a real id still applies at request time).
3149
- const advertised = withLongContextVariants(catalog.bases);
3170
+ const minimaxNames = isMiniMaxBackend(openaiBackend) ? [...MINIMAX_MODELS] : [];
3171
+ const advertised = withLongContextVariants(catalog.bases).concat(minimaxNames);
3150
3172
  const aliasNames = Object.keys(modelAliases).filter((n) => !advertised.includes(n));
3151
3173
  // Codex/ChatGPT-subscription slugs the backend lists for the stored
3152
3174
  // account (dario#1010), so a client's model picker can discover the
@@ -3164,7 +3186,8 @@ export async function startProxy(opts = {}) {
3164
3186
  // Codex slugs are served by the ChatGPT subscription, not Anthropic, so
3165
3187
  // they advertise `owned_by: "openai"` (dario#1137).
3166
3188
  const codexOwners = Object.fromEntries(codexNames.map((s) => [s, 'openai']));
3167
- const body = JSON.stringify(buildOpenAIModelsList(advertised.concat(aliasNames, codexNames), codexOwners));
3189
+ const minimaxOwners = Object.fromEntries(minimaxNames.map((s) => [s, 'minimax']));
3190
+ const body = JSON.stringify(buildOpenAIModelsList(advertised.concat(aliasNames, codexNames), { ...codexOwners, ...minimaxOwners }));
3168
3191
  res.writeHead(200, { ...JSON_HEADERS, 'Access-Control-Allow-Origin': corsOrigin });
3169
3192
  res.end(body);
3170
3193
  return;
@@ -3749,6 +3772,7 @@ export async function startProxy(opts = {}) {
3749
3772
  : resolveMaxTokens(opts.maxTokens, { max_tokens: clientMax });
3750
3773
  const billedAs = budgetModelsFor(typeof pb?.model === 'string' ? pb.model : '', {
3751
3774
  modelAliases, modelOverride, fastModelOverride, cliProviderOverride, cliModelRaw, isOpenAI,
3775
+ compatBackend: compatBackendKind(openaiBackend),
3752
3776
  });
3753
3777
  budgetReserved = requestBudgetReservation(billedAs, body.length, outboundMax, costOfTokensFailClosed, Date.now(), passthrough ? 0 : CC_TEMPLATE_PROMPT_BYTES);
3754
3778
  budgetInflightKey = requestAuth.key.name;
@@ -3852,7 +3876,7 @@ export async function startProxy(opts = {}) {
3852
3876
  }
3853
3877
  }
3854
3878
  const rawModel = parsed.model ?? '';
3855
- const prefix = parseProviderPrefix(rawModel);
3879
+ const prefix = parseProviderPrefix(miniMaxShorthand(rawModel, compatBackendKind(openaiBackend)));
3856
3880
  if (prefix) {
3857
3881
  forcedProvider = prefix.provider;
3858
3882
  // dario#419 — optional effort suffix (model:high / model-high). Claude
@@ -3884,6 +3908,11 @@ export async function startProxy(opts = {}) {
3884
3908
  parsed.model = cliModelRaw;
3885
3909
  body = Buffer.from(JSON.stringify(parsed));
3886
3910
  }
3911
+ else if (isOpenAI && forcedProvider === null && isMiniMaxBackend(openaiBackend) && /^MiniMax-M/i.test(rawModel)) {
3912
+ // Direct MiniMax text IDs, including saved legacy IDs, use the
3913
+ // compatible backend unchanged. Gateway IDs still need a prefix.
3914
+ forcedProvider = 'openai';
3915
+ }
3887
3916
  else if (!isOpenAIModel(rawModel) && forcedProvider !== 'openai') {
3888
3917
  // dario#419 — bare Claude model name carrying an effort suffix with no
3889
3918
  // provider prefix (e.g. Cursor's `claude-opus-4-8-high`). OpenAI-bound
package/docs/routing.md CHANGED
@@ -16,6 +16,31 @@ The tool doesn't know. The backend doesn't know. dario is the seam.
16
16
 
17
17
  **The full Claude lineup, autodetected.** Fable 5, Opus 5.5, Opus 5, Sonnet 5 and Haiku 4.5, plus `[1m]` long-context variants on every family except Haiku, by full id (`claude-opus-5-5`) or shortcut (`fable` / `opus` / `sonnet` / `haiku`; append `1m` for the long-context form; `opus5` / `opus48` / `opus47` / `opus46` / `sonnet46` pin a generation and never float). `GET /v1/models` reads Anthropic's live catalog (TTL-cached, baked fallback offline), so a new model resolves the day it lands with no dario release, and the model-specific request shape is applied automatically. Families pulled upstream are filtered from both the live catalog and the fallback, so `/v1/models` never advertises a model that 404s. A name no provider lists at all, such as a ChatGPT slug your account doesn't have or a typo that belongs to no family, is refused locally with `400` and `x-dario-upstream-rejection: model_unroutable` instead of spending a pool request on an upstream 404. The guard steps aside for `claude-*` names (Anthropic's own 404 stays authoritative there), for requests under a `--model` / `--fast-model` override, for upstream-API-key mode, and for the legacy OpenAI names the built-in map translates.
18
18
 
19
+ ## MiniMax API-key backend
20
+
21
+ `dario backend add minimax --key=<api-key>` uses `https://api.minimax.io/v1`.
22
+ Use the name `minimax-cn` for `https://api.minimax.cn/v1`, or pass `--base-url`
23
+ to keep a custom endpoint. This uses the existing single compatible-backend
24
+ slot; it does not add a second simultaneously active API-key backend. Restart
25
+ the proxy after changing its backend. With no subscription account, start it
26
+ with `dario proxy --no-claude-auth`.
27
+
28
+ On a direct MiniMax endpoint, model `minimax` on `/v1/chat/completions` selects
29
+ `MiniMax-M3.1-Flash`; on a custom gateway, `minimax` is that gateway's own model id and the request goes to the gateway with the name unchanged.
30
+ Direct endpoints advertise `MiniMax-M3.1-Flash` followed by `MiniMax-M3` in
31
+ `/v1/models`; either full ID routes directly. Use `minimax:<model>` for an
32
+ explicit legacy or custom ID, or `--model=minimax:MiniMax-M3.1-Flash` for a server-wide Flash
33
+ override. Custom gateways retain their own IDs and are not assigned a direct
34
+ MiniMax catalog. This API-key route supports Chat Completions, not Messages.
35
+
36
+ Request fields and streamed responses pass through unchanged. Flash defaults
37
+ to adaptive thinking and `reasoning_effort: "max"` upstream; it accepts `low`,
38
+ `medium`, `high`, `xhigh`, and `max`. Do not send `thinking.type: "disabled"`,
39
+ `reasoning_effort: "none"`, `service_tier`, or `reasoning_split: false` to Flash.
40
+ M3 also accepts disabled thinking. The proxy does not rewrite explicit settings
41
+ or remove image/video content parts, tool calls, or reasoning history.
42
+ See the [MiniMax API reference](https://platform.minimax.io/docs/api-reference/text-openai-api).
43
+
19
44
  ---
20
45
 
21
46
  [← README](../README.md) · [all reference docs](../README.md#reference)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "6.13.28",
3
+ "version": "6.13.30",
4
4
  "description": "Use your Claude and ChatGPT subscriptions in Cursor, Cline, Aider, Claude Code and the Agent SDK — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint: either plan answers either wire shape, with automatic failover when one hits its limit.",
5
5
  "type": "module",
6
6
  "bin": {