@askalf/dario 6.12.10 → 6.12.11

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
@@ -52,7 +52,7 @@ function readBudgetFlags(args) {
52
52
  return budget;
53
53
  }
54
54
  import { loadAllAccounts as loadAllAccountsForIdentity, regenerateClientIdentity } from './accounts.js';
55
- import { maskEmail, parsePoolHeadroomFloor, configuredPoolRouting, describePoolStrategy } from './pool.js';
55
+ import { maskEmail, parsePoolHeadroomFloor, headroomFloorProblem, headroomFloorFlagProblem, configuredPoolRouting, describePoolStrategy } from './pool.js';
56
56
  import { realpathSync, readFileSync } from 'node:fs';
57
57
  import { join } from 'node:path';
58
58
  import { homedir } from 'node:os';
@@ -541,12 +541,20 @@ async function proxy() {
541
541
  // used rather than ride it into the 429.
542
542
  const poolHeadroomFloorFromFlag = args.find((a) => a.startsWith('--pool-headroom-floor='))?.split('=')[1];
543
543
  if (poolHeadroomFloorFromFlag !== undefined && parsePoolHeadroomFloor(poolHeadroomFloorFromFlag) === null) {
544
- console.error(`[dario] Invalid --pool-headroom-floor "${poolHeadroomFloorFromFlag}". Use a ratio or percent between 2% and 50% (e.g. 0.05 or 5%).`);
544
+ console.error(`[dario] Invalid --pool-headroom-floor: ${headroomFloorFlagProblem(poolHeadroomFloorFromFlag)}`);
545
545
  process.exit(1);
546
546
  }
547
547
  const poolHeadroomFloor = poolHeadroomFloorFromFlag
548
548
  ?? process.env['DARIO_POOL_HEADROOM_FLOOR']
549
549
  ?? fileCfg.pool?.headroomFloor;
550
+ // An invalid flag value aborts startup; an invalid env or config value warns and uses the
551
+ // default floor.
552
+ if (poolHeadroomFloorFromFlag === undefined) {
553
+ const floorSource = process.env['DARIO_POOL_HEADROOM_FLOOR'] !== undefined ? 'DARIO_POOL_HEADROOM_FLOOR' : 'pool.headroomFloor in the config file';
554
+ const floorProblem = headroomFloorProblem(poolHeadroomFloor);
555
+ if (floorProblem)
556
+ console.warn(`[dario] ${floorSource}: ${floorProblem} Using the default 2%.`);
557
+ }
550
558
  // --pool-shared-state — share rate-limit readings and sticky bindings with
551
559
  // the other instances through the refresh-lock service (docs/multi-instance.md).
552
560
  const poolSharedState = args.includes('--pool-shared-state')
@@ -1133,7 +1133,7 @@ export async function runChecks(opts = {}) {
1133
1133
  // (v5.0): it confirms the sole account is eligible, not rejected.
1134
1134
  if (aliases.length >= 1) {
1135
1135
  try {
1136
- const { AccountPool, configuredPoolRouting, describePoolStrategy } = await import('./pool.js');
1136
+ const { AccountPool, configuredPoolRouting, describePoolStrategy, headroomFloorProblem } = await import('./pool.js');
1137
1137
  const { loadConfig } = await import('./config-file.js');
1138
1138
  // The preview must use the strategy the proxy will run, not the
1139
1139
  // default: `new AccountPool()` here always predicted a max-headroom
@@ -1159,6 +1159,16 @@ export async function runChecks(opts = {}) {
1159
1159
  : `no eligible account — all rejected or near-expiry (${ps.exhausted}/${ps.accounts} exhausted)`)
1160
1160
  + ` · ${describePoolStrategy(routing.strategy, routing.headroomFloor)} (env/config; a running proxy's --pool-strategy flag shows in GET /status)`,
1161
1161
  });
1162
+ // Same precedence as configuredPoolRouting: the env var shadows the config file.
1163
+ const floorCandidate = process.env['DARIO_POOL_HEADROOM_FLOOR'] ?? loadConfig().config.pool?.headroomFloor;
1164
+ const floorProblem = headroomFloorProblem(floorCandidate);
1165
+ if (floorProblem) {
1166
+ checks.push({
1167
+ status: 'warn',
1168
+ label: 'Pool headroom floor',
1169
+ detail: `${process.env['DARIO_POOL_HEADROOM_FLOOR'] !== undefined ? 'DARIO_POOL_HEADROOM_FLOOR' : 'pool.headroomFloor'}: ${floorProblem} The default 2% is in effect.`,
1170
+ });
1171
+ }
1162
1172
  }
1163
1173
  catch (err) {
1164
1174
  checks.push({ status: 'warn', label: 'Pool routing', detail: `check failed: ${err.message}` });
package/dist/pool.d.ts CHANGED
@@ -588,12 +588,19 @@ export declare const MAX_POOL_HEADROOM_FLOOR = 0.5;
588
588
  */
589
589
  export declare function parsePoolHeadroomFloor(value: string | number | null | undefined): number | null;
590
590
  /**
591
- * Resolve the pool headroom floor from an explicit value (CLI flag / config
592
- * file, already precedence-merged by the caller) with
593
- * `DARIO_POOL_HEADROOM_FLOOR` as the env fallback. Unparseable or
594
- * out-of-bounds values fall through to the next source and finally to the
595
- * default, matching `resolvePoolStrategy`.
591
+ * Why a configured floor cannot be used, or null when it can (or is unset). The env var and the
592
+ * config file fall back to the default on a bad value, so without this a typo runs silently on 2%.
593
+ * A value between the maximum and 100% most likely names the usage at which to leave a seat
594
+ * rather than the headroom left (dario#1333 set `95` for "leave at 95% used"), so the message
595
+ * names the complement, never below the minimum. Pure.
596
596
  */
597
+ export declare function headroomFloorProblem(value: string | number | null | undefined): string | null;
598
+ /**
599
+ * Why a `--pool-headroom-floor` value was rejected. Unlike the env var and the config file, an
600
+ * empty flag value is an error rather than "unset", so this never returns null. Call it only for
601
+ * a value `parsePoolHeadroomFloor` rejected.
602
+ */
603
+ export declare function headroomFloorFlagProblem(value: string): string;
597
604
  export declare function resolvePoolHeadroomFloor(explicit?: string | number | null, env?: NodeJS.ProcessEnv): number;
598
605
  /**
599
606
  * The 7-day reset `expiring-first` orders by, in epoch ms: the seat's own
package/dist/pool.js CHANGED
@@ -813,6 +813,40 @@ export function parsePoolHeadroomFloor(value) {
813
813
  * out-of-bounds values fall through to the next source and finally to the
814
814
  * default, matching `resolvePoolStrategy`.
815
815
  */
816
+ const floorPct = (r) => `${Math.round(r * 1000) / 10}%`;
817
+ const unusableFloor = (raw) => `"${raw}" is not a usable headroom floor. Use a ratio or percent between ${floorPct(MIN_POOL_HEADROOM_FLOOR)} and ${floorPct(MAX_POOL_HEADROOM_FLOOR)} (e.g. 0.05 or 5%)`;
818
+ /**
819
+ * Why a configured floor cannot be used, or null when it can (or is unset). The env var and the
820
+ * config file fall back to the default on a bad value, so without this a typo runs silently on 2%.
821
+ * A value between the maximum and 100% most likely names the usage at which to leave a seat
822
+ * rather than the headroom left (dario#1333 set `95` for "leave at 95% used"), so the message
823
+ * names the complement, never below the minimum. Pure.
824
+ */
825
+ export function headroomFloorProblem(value) {
826
+ if (value === undefined || value === null)
827
+ return null;
828
+ const raw = String(value).trim();
829
+ if (raw === '' || parsePoolHeadroomFloor(value) !== null)
830
+ return null;
831
+ const base = unusableFloor(raw);
832
+ let n = typeof value === 'number' ? value : Number(raw.endsWith('%') ? raw.slice(0, -1).trim() : raw);
833
+ if (typeof value === 'string' && raw.endsWith('%'))
834
+ n /= 100;
835
+ if (Number.isFinite(n) && n > 1)
836
+ n /= 100;
837
+ if (!Number.isFinite(n) || n <= MAX_POOL_HEADROOM_FLOOR || n >= 1)
838
+ return `${base}.`;
839
+ const left = Math.max(1 - n, MIN_POOL_HEADROOM_FLOOR);
840
+ return `${base}. The floor is the headroom left on a seat, not its usage: to leave a seat at ${floorPct(n)} used, set ${floorPct(left)}.`;
841
+ }
842
+ /**
843
+ * Why a `--pool-headroom-floor` value was rejected. Unlike the env var and the config file, an
844
+ * empty flag value is an error rather than "unset", so this never returns null. Call it only for
845
+ * a value `parsePoolHeadroomFloor` rejected.
846
+ */
847
+ export function headroomFloorFlagProblem(value) {
848
+ return headroomFloorProblem(value) ?? `${unusableFloor(value.trim())}.`;
849
+ }
816
850
  export function resolvePoolHeadroomFloor(explicit, env = process.env) {
817
851
  for (const c of [explicit, env.DARIO_POOL_HEADROOM_FLOOR]) {
818
852
  const n = parsePoolHeadroomFloor(c);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "6.12.10",
3
+ "version": "6.12.11",
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": {