@askalf/dario 4.8.77 → 4.8.78

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/cch.d.ts CHANGED
@@ -16,3 +16,11 @@ export declare function cchWithSeed(bodyText: string, seed: bigint): string | nu
16
16
  * A null return means the caller should keep its random placeholder.
17
17
  */
18
18
  export declare function cchForBody(bodyText: string, version: string): string | null;
19
+ /**
20
+ * Return `bodyText` with the billing-tag cch replaced in place by the
21
+ * deterministic value for `version`, or unchanged when the version has no
22
+ * known seed or the body has no billing token. The replacement is anchored to
23
+ * the billing tag (CCH_RE), so conversation content that quotes a cch is never
24
+ * touched. Used by the proxy at outbound-serialize time.
25
+ */
26
+ export declare function stampCch(bodyText: string, version: string): string;
package/dist/cch.js CHANGED
@@ -102,10 +102,19 @@ export function xxh64(data, seed) {
102
102
  h = (h ^ (h >> 32n)) & U64;
103
103
  return h;
104
104
  }
105
- const CCH_RE = /cch=[0-9a-fA-F]{5}/;
105
+ // Match the cch token INSIDE the billing tag specifically — never a stray
106
+ // `cch=#####` quoted in conversation content (which sorts before `system` in
107
+ // the body, so a naive first-match would grab it, mis-hash, AND silently
108
+ // rewrite the user's text at stamp time — dario#528). Anchor on the
109
+ // `cc_entrypoint=<value>; cch=` that immediately precedes it in the billing
110
+ // header. The entrypoint value is BOUNDED ({1,32}) so the match stays linear
111
+ // on a 10 MB body — an unbounded `[^"]*?` span here is O(n^2) when the anchor
112
+ // repeats (CodeQL js/polynomial-redos). Anchoring is also what real CC must
113
+ // do, so this matches upstream behavior, not just our own correctness.
114
+ const CCH_RE = /(cc_entrypoint=[a-z0-9-]{1,32}; cch=)[0-9a-fA-F]{5}(?=;)/;
106
115
  /** Build the canonical cch pre-image bytes from a serialized request body. */
107
116
  function cchMaterial(bodyText) {
108
- const zeroed = bodyText.replace(CCH_RE, 'cch=00000'); // first occurrence only
117
+ const zeroed = bodyText.replace(CCH_RE, (_m, prefix) => `${prefix}00000`);
109
118
  const body = JSON.parse(zeroed);
110
119
  body.model = '';
111
120
  delete body.fallbacks;
@@ -137,3 +146,16 @@ export function cchForBody(bodyText, version) {
137
146
  return null;
138
147
  return cchWithSeed(bodyText, seed);
139
148
  }
149
+ /**
150
+ * Return `bodyText` with the billing-tag cch replaced in place by the
151
+ * deterministic value for `version`, or unchanged when the version has no
152
+ * known seed or the body has no billing token. The replacement is anchored to
153
+ * the billing tag (CCH_RE), so conversation content that quotes a cch is never
154
+ * touched. Used by the proxy at outbound-serialize time.
155
+ */
156
+ export function stampCch(bodyText, version) {
157
+ const cch = cchForBody(bodyText, version);
158
+ if (cch === null)
159
+ return bodyText;
160
+ return bodyText.replace(CCH_RE, (_m, prefix) => `${prefix}${cch}`);
161
+ }
package/dist/proxy.js CHANGED
@@ -9,7 +9,7 @@ import { arch, platform } from 'node:process';
9
9
  import { getAccessToken, getStatus } from './oauth.js';
10
10
  import { buildHealthResponse } from './health-response.js';
11
11
  import { buildCCRequest, applyCcPromptCaching, parseEffortSuffix, reverseMapResponse, createStreamingReverseMapper, orderHeadersForOutbound, CC_TEMPLATE } from './cc-template.js';
12
- import { cchForBody } from './cch.js';
12
+ import { stampCch } from './cch.js';
13
13
  import { describeTemplate, detectDrift, checkCCCompat } from './live-fingerprint.js';
14
14
  import { AccountPool, computeStickyKey, parseRateLimits, modelFamily, isInAuthCooldown, authCooldownMs } from './pool.js';
15
15
  import { Analytics, billingBucketFromClaim } from './analytics.js';
@@ -1768,17 +1768,16 @@ export async function startProxy(opts = {}) {
1768
1768
  }
1769
1769
  // dario#528: overwrite the random cch placeholder with the real
1770
1770
  // deterministic value for Claude Code versions whose seed we've
1771
- // reverse-engineered. cchForBody hashes a projection of THIS final
1772
- // body, so it must run after every mutation above. It returns null
1773
- // for unknown versions (keep the random placeholder) or bodies with
1774
- // no billing token. Only the template-replay path is stamped —
1775
- // passthrough / count_tokens forward the client's body (and its own
1776
- // cch) verbatim. Reversible kill-switch: DARIO_CCH=random.
1771
+ // reverse-engineered. stampCch hashes a projection of THIS final body
1772
+ // (so it must run after every mutation above) and replaces the cch
1773
+ // anchored to the billing tag never a cch quoted in conversation
1774
+ // content. No-op for unknown versions (keep the random placeholder).
1775
+ // Only the template-replay path is stamped passthrough /
1776
+ // count_tokens forward the client's body (and its own cch) verbatim.
1777
+ // Reversible kill-switch: DARIO_CCH=random.
1777
1778
  let outboundText = JSON.stringify(r);
1778
1779
  if (!passthrough && !isCountTokens && process.env.DARIO_CCH !== 'random') {
1779
- const realCch = cchForBody(outboundText, cliVersion);
1780
- if (realCch)
1781
- outboundText = outboundText.replace(/cch=[0-9a-fA-F]{5}/, `cch=${realCch}`);
1780
+ outboundText = stampCch(outboundText, cliVersion);
1782
1781
  }
1783
1782
  finalBody = Buffer.from(outboundText);
1784
1783
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "4.8.77",
3
+ "version": "4.8.78",
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": {