@askalf/dario
Advanced tools
+28
-0
@@ -58,2 +58,30 @@ /** | ||
| export declare function billingBucketFromClaim(claim: string | null | undefined): BillingBucket; | ||
| /** | ||
| * The `representative-claim` values that mean "billed against the subscription | ||
| * pool" — the place dario exists to keep traffic. `five_hour`/`seven_day` and | ||
| * their server-side `_fallback` variants are all subscription billing (see | ||
| * `billingBucketFromClaim` + discussion #1). Anything else is either a | ||
| * non-subscription billing classification or the `unknown` sentinel below. | ||
| */ | ||
| export declare const SUBSCRIPTION_CLAIMS: ReadonlySet<string>; | ||
| /** | ||
| * The sentinel `claim` dario assigns when a response carried no rate-limit | ||
| * header at all (non-200s, stream aborts, early rejects — see `pool.ts` | ||
| * `parseRateLimits` / `EMPTY_SNAPSHOT`). It is NOT a billing classification, | ||
| * so the overage-guard must never halt on it. | ||
| */ | ||
| export declare const NO_BILLING_CLAIM = "unknown"; | ||
| /** | ||
| * True when a claim represents real *non-subscription* billing — the | ||
| * condition the overage-guard halts on (see `overage-guard.ts`, #288). | ||
| * | ||
| * Deliberately an allow-list, not `claim === 'overage'`: it halts on anything | ||
| * that is NOT a known subscription claim AND NOT the `unknown` sentinel. That | ||
| * catches `overage` and `api` as before, but ALSO any new credit/SDK bucket | ||
| * string Anthropic introduces — e.g. the 2026-06-15 Agent-SDK/headless split, | ||
| * whose credit-bucket claim dario has never observed (it keeps traffic in the | ||
| * pool) and so cannot hardcode. `unknown` is exempt: halting on it would halt | ||
| * the proxy on every transient non-200/stream-abort. | ||
| */ | ||
| export declare function isNonSubscriptionBilling(claim: string | null | undefined): boolean; | ||
| export declare class Analytics extends EventEmitter { | ||
@@ -60,0 +88,0 @@ private records; |
+37
-0
@@ -40,2 +40,39 @@ /** | ||
| } | ||
| /** | ||
| * The `representative-claim` values that mean "billed against the subscription | ||
| * pool" — the place dario exists to keep traffic. `five_hour`/`seven_day` and | ||
| * their server-side `_fallback` variants are all subscription billing (see | ||
| * `billingBucketFromClaim` + discussion #1). Anything else is either a | ||
| * non-subscription billing classification or the `unknown` sentinel below. | ||
| */ | ||
| export const SUBSCRIPTION_CLAIMS = new Set([ | ||
| 'five_hour', | ||
| 'seven_day', | ||
| 'five_hour_fallback', | ||
| 'seven_day_fallback', | ||
| ]); | ||
| /** | ||
| * The sentinel `claim` dario assigns when a response carried no rate-limit | ||
| * header at all (non-200s, stream aborts, early rejects — see `pool.ts` | ||
| * `parseRateLimits` / `EMPTY_SNAPSHOT`). It is NOT a billing classification, | ||
| * so the overage-guard must never halt on it. | ||
| */ | ||
| export const NO_BILLING_CLAIM = 'unknown'; | ||
| /** | ||
| * True when a claim represents real *non-subscription* billing — the | ||
| * condition the overage-guard halts on (see `overage-guard.ts`, #288). | ||
| * | ||
| * Deliberately an allow-list, not `claim === 'overage'`: it halts on anything | ||
| * that is NOT a known subscription claim AND NOT the `unknown` sentinel. That | ||
| * catches `overage` and `api` as before, but ALSO any new credit/SDK bucket | ||
| * string Anthropic introduces — e.g. the 2026-06-15 Agent-SDK/headless split, | ||
| * whose credit-bucket claim dario has never observed (it keeps traffic in the | ||
| * pool) and so cannot hardcode. `unknown` is exempt: halting on it would halt | ||
| * the proxy on every transient non-200/stream-abort. | ||
| */ | ||
| export function isNonSubscriptionBilling(claim) { | ||
| if (!claim || claim === NO_BILLING_CLAIM) | ||
| return false; | ||
| return !SUBSCRIPTION_CLAIMS.has(claim); | ||
| } | ||
| // Anthropic pricing (per 1M tokens, USD). Not authoritative — used for | ||
@@ -42,0 +79,0 @@ // rough burn-rate display in the /analytics summary. |
+20
-11
| /** | ||
| * Overage-guard — halt the proxy on the first `representative-claim: overage` | ||
| * response to prevent silent API-rate bleed. | ||
| * Overage-guard — halt the proxy the first time a response bills to anything | ||
| * other than the subscription pool, to prevent silent API-rate bleed. | ||
| * | ||
| * Subscribers should never see a single overage hit during normal | ||
| * Subscribers should never see a single non-subscription hit during normal | ||
| * operation. One means something is wrong (wire-shape drift, classifier | ||
@@ -12,7 +12,14 @@ * change, account misconfig, billing-flip after a CC release) and | ||
| * request emits a record carrying its `claim` (raw representative-claim | ||
| * value). When `claim === 'overage'` lands, the guard transitions to a | ||
| * halted state and emits a `'halt'` event. The HTTP request path checks | ||
| * `isHalted()` on every incoming request and returns 503 with an | ||
| * Anthropic-shaped error body when halted. | ||
| * value). When a non-subscription claim lands — `overage`, `api`, or any | ||
| * new credit/SDK-bucket string (the 2026-06-15 Agent-SDK split) — the guard | ||
| * transitions to a halted state and emits a `'halt'` event. The detection is | ||
| * an allow-list (`isNonSubscriptionBilling`): it fires on anything that is | ||
| * not a known subscription claim and not the `unknown` sentinel (which means | ||
| * "no rate-limit header" — a transient non-200/abort, not a billing flip). | ||
| * The HTTP request path checks `isHalted()` on every incoming request and | ||
| * returns 503 with an Anthropic-shaped error body when halted. | ||
| * | ||
| * The name predates the broadening (it caught only `overage` through v4.8.x, | ||
| * #288); the issue lineage + `dario_overage_guard` error type are kept stable. | ||
| * | ||
| * Resume paths: | ||
@@ -30,3 +37,3 @@ * - explicit: `dario resume` CLI → POST /admin/resume → `clear('manual')` | ||
| import { EventEmitter } from 'node:events'; | ||
| import type { Analytics, RequestRecord } from './analytics.js'; | ||
| import { type Analytics, type RequestRecord } from './analytics.js'; | ||
| export interface HaltState { | ||
@@ -63,5 +70,7 @@ since: number; | ||
| /** | ||
| * Subscribe to an Analytics instance. Every record emitted with | ||
| * `claim === 'overage'` triggers halt (when behavior === 'halt') or a | ||
| * warn-only event (when behavior === 'warn'). | ||
| * Subscribe to an Analytics instance. Every record whose claim is a | ||
| * non-subscription billing classification (`isNonSubscriptionBilling` — | ||
| * `overage`, `api`, or a new credit/SDK bucket) triggers halt (when | ||
| * behavior === 'halt') or a warn-only event (when behavior === 'warn'). | ||
| * Subscription claims and the `unknown` sentinel are ignored. | ||
| * | ||
@@ -68,0 +77,0 @@ * Idempotent — calling attach() a second time replaces the listener |
+26
-16
| /** | ||
| * Overage-guard — halt the proxy on the first `representative-claim: overage` | ||
| * response to prevent silent API-rate bleed. | ||
| * Overage-guard — halt the proxy the first time a response bills to anything | ||
| * other than the subscription pool, to prevent silent API-rate bleed. | ||
| * | ||
| * Subscribers should never see a single overage hit during normal | ||
| * Subscribers should never see a single non-subscription hit during normal | ||
| * operation. One means something is wrong (wire-shape drift, classifier | ||
@@ -12,7 +12,14 @@ * change, account misconfig, billing-flip after a CC release) and | ||
| * request emits a record carrying its `claim` (raw representative-claim | ||
| * value). When `claim === 'overage'` lands, the guard transitions to a | ||
| * halted state and emits a `'halt'` event. The HTTP request path checks | ||
| * `isHalted()` on every incoming request and returns 503 with an | ||
| * Anthropic-shaped error body when halted. | ||
| * value). When a non-subscription claim lands — `overage`, `api`, or any | ||
| * new credit/SDK-bucket string (the 2026-06-15 Agent-SDK split) — the guard | ||
| * transitions to a halted state and emits a `'halt'` event. The detection is | ||
| * an allow-list (`isNonSubscriptionBilling`): it fires on anything that is | ||
| * not a known subscription claim and not the `unknown` sentinel (which means | ||
| * "no rate-limit header" — a transient non-200/abort, not a billing flip). | ||
| * The HTTP request path checks `isHalted()` on every incoming request and | ||
| * returns 503 with an Anthropic-shaped error body when halted. | ||
| * | ||
| * The name predates the broadening (it caught only `overage` through v4.8.x, | ||
| * #288); the issue lineage + `dario_overage_guard` error type are kept stable. | ||
| * | ||
| * Resume paths: | ||
@@ -30,2 +37,3 @@ * - explicit: `dario resume` CLI → POST /admin/resume → `clear('manual')` | ||
| import { EventEmitter } from 'node:events'; | ||
| import { isNonSubscriptionBilling } from './analytics.js'; | ||
| export class OverageGuard extends EventEmitter { | ||
@@ -44,5 +52,7 @@ opts; | ||
| /** | ||
| * Subscribe to an Analytics instance. Every record emitted with | ||
| * `claim === 'overage'` triggers halt (when behavior === 'halt') or a | ||
| * warn-only event (when behavior === 'warn'). | ||
| * Subscribe to an Analytics instance. Every record whose claim is a | ||
| * non-subscription billing classification (`isNonSubscriptionBilling` — | ||
| * `overage`, `api`, or a new credit/SDK bucket) triggers halt (when | ||
| * behavior === 'halt') or a warn-only event (when behavior === 'warn'). | ||
| * Subscription claims and the `unknown` sentinel are ignored. | ||
| * | ||
@@ -63,3 +73,3 @@ * Idempotent — calling attach() a second time replaces the listener | ||
| this.analyticsListener = (r) => { | ||
| if (r.claim === 'overage') { | ||
| if (isNonSubscriptionBilling(r.claim)) { | ||
| this.onOverageDetected(r); | ||
@@ -120,3 +130,3 @@ } | ||
| const title = this.opts.behavior === 'halt' ? 'dario halted' : 'dario warning'; | ||
| const msg = `Request classified as 'overage' (per-token billing)${this.opts.behavior === 'halt' ? '. Proxy halted. Run `dario resume` to continue.' : ''}`; | ||
| const msg = `Request classified as '${r.claim}' (non-subscription billing)${this.opts.behavior === 'halt' ? '. Proxy halted. Run `dario resume` to continue.' : ''}`; | ||
| this.opts.notifier(title, msg); | ||
@@ -188,8 +198,8 @@ } | ||
| message: `dario halted to prevent API-rate bleed. A request was classified ` + | ||
| `as 'overage' (per-token billing) instead of your subscription pool. ` + | ||
| `To resume: run \`dario resume\` in another terminal, or wait until ` + | ||
| `${isoCooldown} for the cooldown to auto-clear. ` + | ||
| `Details: github.com/askalf/dario/issues/288`, | ||
| `as '${state.request.claim}' (non-subscription billing) instead of ` + | ||
| `your subscription pool. To resume: run \`dario resume\` in another ` + | ||
| `terminal, or wait until ${isoCooldown} for the cooldown to ` + | ||
| `auto-clear. Details: github.com/askalf/dario/issues/288`, | ||
| }, | ||
| }; | ||
| } |
+7
-5
@@ -324,7 +324,9 @@ import { type IncomingMessage } from 'node:http'; | ||
| /** | ||
| * Overage-guard — halt the proxy on the first response carrying | ||
| * `representative-claim: overage`. Subscribers should never see a | ||
| * single overage hit during normal operation; one means something | ||
| * is wrong (wire-shape drift, classifier change, account misconfig) | ||
| * and continuing to forward bleeds against per-token billing. | ||
| * Overage-guard — halt the proxy on the first response that bills to | ||
| * anything other than the subscription pool (`overage`, `api`, or a new | ||
| * credit/SDK bucket — the 2026-06-15 split). Subscribers should never see | ||
| * a single non-subscription hit during normal operation; one means | ||
| * something is wrong (wire-shape drift, classifier change, account | ||
| * misconfig) and continuing to forward bleeds against per-token billing. | ||
| * Auto-disabled in upstream-API-key passthrough mode (see proxy setup). | ||
| * | ||
@@ -331,0 +333,0 @@ * Default: enabled, halt behavior, 30-min cooldown, OS-notify on. |
@@ -28,3 +28,3 @@ /** | ||
| import { renderKvRow } from '../layout.js'; | ||
| import { billingBucketFromClaim } from '../../analytics.js'; | ||
| import { billingBucketFromClaim, isNonSubscriptionBilling } from '../../analytics.js'; | ||
| const MAX_BUFFER = 5000; | ||
@@ -147,3 +147,3 @@ export const HitsTab = { | ||
| const cooldown = formatRemaining(state.halt.cooldownUntil - Date.now()); | ||
| const line1 = ` ${fg('red', '⚠ HALTED')} overage detected at ${since} on ${state.halt.request.model} (account=${state.halt.request.account})`; | ||
| const line1 = ` ${fg('red', '⚠ HALTED')} ${state.halt.request.claim} detected at ${since} on ${state.halt.request.model} (account=${state.halt.request.account})`; | ||
| const line2 = ` ${dim('→ New /v1/messages requests return 503 until')} ${fg('cyan', 'R')} ${dim('here, or')} ${fg('cyan', 'dario resume')}${dim(' from any shell. Auto-resume in')} ${cooldown}${dim('.')}`; | ||
@@ -163,5 +163,8 @@ lines.push(line1); | ||
| const r = newestFirst[i]; | ||
| const isOverage = r.claim === 'overage'; | ||
| // Flag any non-subscription billing red — the same condition the | ||
| // overage-guard halts on (overage, api, or a credit/SDK bucket), not | ||
| // just literal `overage`. See isNonSubscriptionBilling (#288). | ||
| const isNonSub = isNonSubscriptionBilling(r.claim); | ||
| const marker = i === state.selectedIdx ? fg('cyan', '▎') | ||
| : isOverage ? fg('red', '!') | ||
| : isNonSub ? fg('red', '!') | ||
| : ' '; | ||
@@ -175,8 +178,8 @@ const row = marker + ' ' + | ||
| pad(formatStatus(r.status), colStatus); | ||
| // Overage rows render in red even when unselected; selection still | ||
| // wins via the inverse() wrapper so the user can drill into one. | ||
| // Non-subscription rows render in red even when unselected; selection | ||
| // still wins via the inverse() wrapper so the user can drill into one. | ||
| let styled; | ||
| if (i === state.selectedIdx) | ||
| styled = inverse(truncate(row, w - 2)); | ||
| else if (isOverage) | ||
| else if (isNonSub) | ||
| styled = fg('red', truncate(row, w - 2)); | ||
@@ -183,0 +186,0 @@ else |
@@ -118,3 +118,3 @@ /** | ||
| // Red banner header — this is the loud surface when halted | ||
| lines.push(' ' + fg('red', '⚠ HALTED') + ' ' + dim(`overage detected ${formatAgo(s.since)} ago`)); | ||
| lines.push(' ' + fg('red', '⚠ HALTED') + ' ' + dim(`${s.request.claim} detected ${formatAgo(s.since)} ago`)); | ||
| lines.push(' ' + renderKvRow('Request', `${s.request.model} ${dim('account=' + s.request.account)}`, w - 4)); | ||
@@ -121,0 +121,0 @@ lines.push(' ' + renderKvRow('Cause', `representative-claim = ${fg('red', s.request.claim)}`, w - 4)); |
+1
-1
| { | ||
| "name": "@askalf/dario", | ||
| "version": "4.8.75", | ||
| "version": "4.8.76", | ||
| "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", |
+18
-17
@@ -203,5 +203,5 @@ <p align="center"> | ||
| A subscriber should never see a single `representative-claim: overage` response during normal operation. One means something is wrong — wire-shape drift, a classifier change, an account misconfig — and continuing to forward requests in the same shape bleeds real money (accounts with extra-usage enabled) or returns a wall of rejections (accounts without it). The first hit is the signal; the second through hundredth are damage. | ||
| A subscriber should never see a single response billed outside their subscription pool during normal operation. One means something is wrong — wire-shape drift, a classifier change, an account misconfig — and continuing to forward requests in the same shape bleeds real money (accounts with extra-usage enabled) or returns a wall of rejections (accounts without it). The first hit is the signal; the second through hundredth are damage. | ||
| So the moment any upstream response carries `representative-claim: overage`, dario **halts the proxy**. Every subsequent request returns `503` with an Anthropic-shaped error body the client surfaces verbatim, until you run `dario resume`, press `R` on the TUI, or the cooldown clears (default 30 min). The halt is visible across the TUI's Status, Hits, and Analytics tabs, fires a best-effort native OS notification, and emits named SSE events. | ||
| So the moment any upstream response bills to something other than your subscription pool — `representative-claim: overage`, `api`, or a new credit/SDK bucket like the one the 2026-06-15 Agent-SDK split introduces — dario **halts the proxy**. The check is an allow-list, not a match on `overage`: anything that isn't a known subscription claim (`five_hour`/`seven_day` and their fallbacks) and isn't the `unknown` no-header sentinel trips it, so a credit-bucket claim dario has never seen still halts. Every subsequent request returns `503` with an Anthropic-shaped error body the client surfaces verbatim, until you run `dario resume`, press `R` on the TUI, or the cooldown clears (default 30 min). The halt is visible across the TUI's Status, Hits, and Analytics tabs, fires a best-effort native OS notification, and emits named SSE events. (In `--upstream-api-key` passthrough mode the guard is off — `api` billing is the point there, not a failure.) | ||
@@ -372,24 +372,25 @@ ``` | ||
| ## Also by askalf | ||
| ## Own Your Stack | ||
| | Project | What it does | | ||
| dario is the routing layer of **[Own Your Stack](https://github.com/askalf)** — open tools for owning your AI infrastructure instead of renting it by the token. One subscription. Your box. Your terms. | ||
| | Tool | | | ||
| |---|---| | ||
| | [askalf platform](https://askalf.org) | Self-hosted AI workforce — agents that run real business + life work. Uses dario as its LLM substrate. *Early access at [askalf.org](https://askalf.org).* | | ||
| | [hands](https://github.com/askalf/hands) | Cross-platform computer-use agent — your LLM on your mouse, keyboard, and screen. Routes through dario or any Anthropic-compat. | | ||
| | [deepdive](https://github.com/askalf/deepdive) | Local research agent. One command, cited answer. Plan → search → headless fetch → extract → synthesize. | | ||
| | [agent](https://github.com/askalf/agent) | Connect any device to an askalf fleet — runs the shell or Claude Code tasks the fleet dispatches. | | ||
| | [browser-bridge](https://github.com/askalf/browser-bridge) | Stealth headless Chromium in a container, CDP on 9222. Playwright / Puppeteer / MCP-compatible. | | ||
| | [claude-sync](https://github.com/askalf/claude-sync) | Sync Claude Code sessions across machines via a portable `.ccsync` file. | | ||
| | [pgflex](https://github.com/askalf/pgflex) | One Postgres API, two modes — real PostgreSQL for production, PGlite (WASM) for dev. | | ||
| | [redisflex](https://github.com/askalf/redisflex) | One Redis API, two modes — ioredis for production, in-process for dev. Includes a BullMQ-shaped in-memory queue. | | ||
| | **[dario](https://github.com/askalf/dario)** | own your routing — your subscription, in any tool *(you are here)* | | ||
| | **[deepdive](https://github.com/askalf/deepdive)** | own your research — local agent, cited answers | | ||
| | **[hands](https://github.com/askalf/hands)** | own your computer-use — your LLM on your own mouse & keyboard | | ||
| | **[agent](https://github.com/askalf/agent)** | own your fleet — connect any device, dispatch real work | | ||
| | **[browser-bridge](https://github.com/askalf/browser-bridge)** | own your browser — stealth headless Chromium, your CDP endpoint | | ||
| | **[claude-sync](https://github.com/askalf/claude-sync)** | own your sessions — sync Claude Code across machines | | ||
| | **[askalf platform](https://askalf.org)** | own your operation — the self-hosted AI workforce, running on this stack | | ||
| --- | ||
| ## Built by Sprayberry Labs | ||
| ## Built by Thomas Sprayberry | ||
| This is one of the open-source building blocks from **[Sprayberry Labs](https://sprayberrylabs.com)** — an independent studio (Atlanta, GA) that ships bespoke software and **fixed-price code & security audits**, delivered with the AI workforce these tools are part of. | ||
| dario is part of **Own Your Stack** — the open toolkit behind **[Sprayberry Labs](https://sprayberrylabs.com)**, an independent studio (Atlanta, GA) that ships bespoke software with the autonomous AI workforce these tools are part of. | ||
| Part of the [askalf](https://askalf.org) ecosystem — a self-hosted AI workforce platform, now in early access. | ||
| Built in the open, scars included. Follow the build → **[@ask_alf](https://x.com/ask_alf)** · **[sprayberrylabs.com/own-your-stack](https://sprayberrylabs.com/own-your-stack)** | ||
| **Got a codebase that needs an expert read?** → **[Scan a repo — free mini-audit](https://sprayberrylabs.com)**, or see the **$1,500 fixed-price Audit** and build Sprints. · [sprayberrylabs.com](https://sprayberrylabs.com) · hello@sprayberrylabs.com | ||
| --- | ||
| Part of **[Own Your Stack](https://github.com/askalf)** — own your AI infrastructure instead of renting it. Built by Thomas Sprayberry. |
Sorry, the diff of this file is too big to display
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1234594
0.55%24228
0.45%395
0.25%