🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@askalf/dario

Package Overview
Dependencies
Maintainers
1
Versions
384
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@askalf/dario - npm Package Compare versions

Comparing version
5.2.7
to
5.2.8
+20
-0
dist/doctor.js

@@ -808,2 +808,22 @@ /**

}
// ---- Live session state (only observable from a running proxy)
// Session/sticky counts live in the proxy's memory, so — unlike the
// local-state checks above — this reads them off /health (internal-disclosure
// caller, so it must reach dario on loopback). Silent skip when no proxy is
// up: the counts don't exist, and doctor is often run because it's down.
try {
const darioBase = process.env.DARIO_TEST_URL || 'http://127.0.0.1:3456';
const healthRes = await fetch(`${darioBase}/health`, { signal: AbortSignal.timeout(800) });
if (healthRes.ok) {
const health = (await healthRes.json().catch(() => null));
const sx = health?.sessions;
if (sx && typeof sx.mode === 'string') {
const detail = sx.mode === 'pool'
? `${sx.stickyBindings ?? 0} sticky binding${sx.stickyBindings === 1 ? '' : 's'} — conversation→account affinity, lazily reaped (6h idle TTL, cap 2000)`
: `${sx.active ?? 0} active session id${sx.active === 1 ? '' : 's'} — lazily reaped (LRU cap 1024, no background sweeper)`;
checks.push({ status: 'info', label: 'Sessions', detail });
}
}
}
catch { /* proxy not running — live session state unavailable, skip */ }
// ---- Identity drift (pool account snapshot vs live ~/.claude.json)

@@ -810,0 +830,0 @@ try {

@@ -25,2 +25,14 @@ /**

version?: string;
/**
* Live session-tracking counts, surfaced to internal callers only. Both
* structures reap lazily (no background sweeper — see session-rotation.ts),
* so this raw in-memory size also reveals whether lazy cleanup keeps up.
*/
sessions?: {
mode: 'pool';
stickyBindings: number;
} | {
mode: 'single';
active: number;
};
}

@@ -27,0 +39,0 @@ export interface HealthResponse {

+1
-0

@@ -84,2 +84,3 @@ /**

requests: requestCount,
...(s.sessions ? { sessions: s.sessions } : {}),
...(s.refreshFailures ? { refreshFailures: s.refreshFailures } : {}),

@@ -86,0 +87,0 @@ }

+1
-1

@@ -285,3 +285,3 @@ /**

readonly min: "1.0.0";
readonly maxTested: "2.1.212";
readonly maxTested: "2.1.214";
};

@@ -288,0 +288,0 @@ /**

@@ -809,3 +809,3 @@ /**

min: '1.0.0',
maxTested: '2.1.212',
maxTested: '2.1.214',
};

@@ -812,0 +812,0 @@ /**

@@ -195,3 +195,3 @@ /**

*/
selectSticky(stickyKey: string | null, family?: string | null): PoolAccount | null;
selectSticky(stickyKey: string | null, family?: string | null, now?: number): PoolAccount | null;
/**

@@ -198,0 +198,0 @@ * Rebind a sticky key to a different account — called by proxy after an

@@ -160,3 +160,3 @@ /**

}
const STICKY_TTL_MS = 6 * 60 * 60 * 1000; // 6h
const STICKY_IDLE_TTL_MS = 6 * 60 * 60 * 1000; // reap a binding 6h after its LAST use, not its creation
const STICKY_MAX_ENTRIES = 2_000; // lazy cleanup cap

@@ -338,10 +338,9 @@ const STICKY_CLEANUP_INTERVAL_MS = 30_000; // amortize the O(n) TTL/orphan sweep

*/
selectSticky(stickyKey, family) {
selectSticky(stickyKey, family, now = Date.now()) {
if (!stickyKey)
return this.select(family);
this.cleanupSticky();
this.cleanupSticky(now);
const binding = this.sticky.get(stickyKey);
if (binding) {
const bound = this.accounts.get(binding.alias);
const now = Date.now();
if (bound

@@ -352,2 +351,6 @@ && bound.rateLimit.status !== 'rejected'

&& computeHeadroom(bound.rateLimit, family) > POOL_HEADROOM_FLOOR) {
// Refresh the idle timer. A session that keeps taking turns must never
// be reaped or rebound while active — that would strand its warm prompt
// cache — so the TTL is re-based to now on every hit.
binding.lastUsedAt = now;
return bound;

@@ -358,3 +361,3 @@ }

if (picked) {
this.sticky.set(stickyKey, { alias: picked.alias, boundAt: Date.now() });
this.sticky.set(stickyKey, { alias: picked.alias, boundAt: now, lastUsedAt: now });
}

@@ -374,3 +377,4 @@ return picked;

return;
this.sticky.set(stickyKey, { alias, boundAt: Date.now() });
const now = Date.now();
this.sticky.set(stickyKey, { alias, boundAt: now, lastUsedAt: now });
}

@@ -383,4 +387,3 @@ /**

*/
cleanupSticky() {
const now = Date.now();
cleanupSticky(now = Date.now()) {
// TTL/orphan sweep is O(n); amortize it — run at most once per

@@ -393,3 +396,6 @@ // STICKY_CLEANUP_INTERVAL_MS instead of on every selectSticky (#642-audit).

for (const [key, b] of this.sticky) {
if (!this.accounts.has(b.alias) || now - b.boundAt > STICKY_TTL_MS) {
// Reap orphans (account gone) and bindings idle past the TTL. Idle is
// measured from lastUsedAt, which selectSticky refreshes every turn, so
// an actively-running conversation is never reaped here.
if (!this.accounts.has(b.alias) || now - b.lastUsedAt > STICKY_IDLE_TTL_MS) {
this.sticky.delete(key);

@@ -401,6 +407,8 @@ }

// the O(n log n) sort amortizes over many inserts rather than firing on every
// new conversation at the cap (#642-audit).
// new conversation at the cap (#642-audit). Evict least-recently-USED first
// (true LRU): a binding's only value is its warm prompt cache, and the ones
// untouched longest are the coldest — least worth keeping.
if (this.sticky.size > STICKY_MAX_ENTRIES) {
const target = Math.floor(STICKY_MAX_ENTRIES * 0.8);
const sorted = [...this.sticky.entries()].sort((a, b) => a[1].boundAt - b[1].boundAt);
const sorted = [...this.sticky.entries()].sort((a, b) => a[1].lastUsedAt - b[1].lastUsedAt);
const toDrop = sorted.slice(0, this.sticky.size - target);

@@ -407,0 +415,0 @@ for (const [key] of toDrop)

{
"name": "@askalf/dario",
"version": "5.2.7",
"version": "5.2.8",
"description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",

@@ -5,0 +5,0 @@ "type": "module",

Sorry, the diff of this file is too big to display