@askalf/dario 4.8.73 → 4.8.74

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.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * /health response builder — extracted so the public-vs-internal disclosure rule
3
+ * is unit-testable without spinning a proxy.
4
+ *
5
+ * dario's /health is auth-free (docker healthchecks + `depends_on: service_healthy`
6
+ * need it before any secret is configured). When dario sits behind a Cloudflare
7
+ * tunnel with a public /health bypass (uptime monitoring), that endpoint is
8
+ * world-readable — so it must not leak OAuth internals (token countdown, request
9
+ * volume, refresh errors). The Cloudflare edge stamps `cf-ray` on every request it
10
+ * proxies, so its presence marks a request as having come from the public internet.
11
+ * Internal callers (the docker healthcheck, `dario doctor`, the self-probe) hit
12
+ * dario directly on loopback with no CF headers and still get the full detail.
13
+ *
14
+ * The HTTP status (200 healthy / 503 degraded) is identical either way, so external
15
+ * uptime monitoring that keys on the status code is unaffected.
16
+ */
17
+ export interface HealthStatusLike {
18
+ status: string;
19
+ canRefresh?: boolean;
20
+ expiresIn?: string;
21
+ refreshFailures?: number;
22
+ lastRefreshError?: string;
23
+ }
24
+ export interface HealthResponse {
25
+ httpStatus: number;
26
+ body: Record<string, unknown>;
27
+ }
28
+ export declare function buildHealthResponse(s: HealthStatusLike, requestCount: number, viaPublicTunnel: boolean): HealthResponse;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * /health response builder — extracted so the public-vs-internal disclosure rule
3
+ * is unit-testable without spinning a proxy.
4
+ *
5
+ * dario's /health is auth-free (docker healthchecks + `depends_on: service_healthy`
6
+ * need it before any secret is configured). When dario sits behind a Cloudflare
7
+ * tunnel with a public /health bypass (uptime monitoring), that endpoint is
8
+ * world-readable — so it must not leak OAuth internals (token countdown, request
9
+ * volume, refresh errors). The Cloudflare edge stamps `cf-ray` on every request it
10
+ * proxies, so its presence marks a request as having come from the public internet.
11
+ * Internal callers (the docker healthcheck, `dario doctor`, the self-probe) hit
12
+ * dario directly on loopback with no CF headers and still get the full detail.
13
+ *
14
+ * The HTTP status (200 healthy / 503 degraded) is identical either way, so external
15
+ * uptime monitoring that keys on the status code is unaffected.
16
+ */
17
+ export function buildHealthResponse(s, requestCount, viaPublicTunnel) {
18
+ const dead = s.status === 'broken' ||
19
+ s.status === 'none' ||
20
+ (s.status === 'expired' && s.canRefresh === false);
21
+ const httpStatus = dead ? 503 : 200;
22
+ const liveness = { status: dead ? 'degraded' : 'ok' };
23
+ const body = viaPublicTunnel
24
+ ? liveness
25
+ : {
26
+ ...liveness,
27
+ oauth: s.status,
28
+ expiresIn: s.expiresIn,
29
+ requests: requestCount,
30
+ ...(s.refreshFailures ? { refreshFailures: s.refreshFailures } : {}),
31
+ ...(s.lastRefreshError ? { lastRefreshError: s.lastRefreshError } : {}),
32
+ };
33
+ return { httpStatus, body };
34
+ }
package/dist/proxy.js CHANGED
@@ -7,6 +7,7 @@ import { homedir } from 'node:os';
7
7
  import { setDefaultResultOrder } from 'node:dns';
8
8
  import { arch, platform } from 'node:process';
9
9
  import { getAccessToken, getStatus } from './oauth.js';
10
+ import { buildHealthResponse } from './health-response.js';
10
11
  import { buildCCRequest, applyCcPromptCaching, parseEffortSuffix, reverseMapResponse, createStreamingReverseMapper, orderHeadersForOutbound, CC_TEMPLATE } from './cc-template.js';
11
12
  import { describeTemplate, detectDrift, checkCCCompat } from './live-fingerprint.js';
12
13
  import { AccountPool, computeStickyKey, parseRateLimits, modelFamily, isInAuthCooldown, authCooldownMs } from './pool.js';
@@ -1110,18 +1111,12 @@ export async function startProxy(opts = {}) {
1110
1111
  // react instead of cheerfully passing while every /v1/messages 401s.
1111
1112
  if (urlPath === '/health' || urlPath === '/') {
1112
1113
  const s = await getStatus();
1113
- const dead = s.status === 'broken' || s.status === 'none' ||
1114
- (s.status === 'expired' && s.canRefresh === false);
1115
- const httpStatus = dead ? 503 : 200;
1114
+ // Public requests arrive through the Cloudflare tunnel (the edge stamps
1115
+ // `cf-ray`); they get only the liveness verdict, never the OAuth internals.
1116
+ // See buildHealthResponse for the full rationale.
1117
+ const { httpStatus, body } = buildHealthResponse(s, requestCount, req.headers['cf-ray'] !== undefined);
1116
1118
  res.writeHead(httpStatus, JSON_HEADERS);
1117
- res.end(JSON.stringify({
1118
- status: dead ? 'degraded' : 'ok',
1119
- oauth: s.status,
1120
- expiresIn: s.expiresIn,
1121
- requests: requestCount,
1122
- ...(s.refreshFailures ? { refreshFailures: s.refreshFailures } : {}),
1123
- ...(s.lastRefreshError ? { lastRefreshError: s.lastRefreshError } : {}),
1124
- }));
1119
+ res.end(JSON.stringify(body));
1125
1120
  return;
1126
1121
  }
1127
1122
  if (!checkAuth(req)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "4.8.73",
3
+ "version": "4.8.74",
4
4
  "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
5
  "type": "module",
6
6
  "bin": {