@bivelio/savings-layer 0.4.20 → 0.4.22
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/README.md +20 -8
- package/dist/gateway/bin.js +350 -337
- package/dist/index.d.ts +113 -4
- package/dist/index.js +665 -656
- package/package.json +1 -1
package/dist/gateway/bin.js
CHANGED
|
@@ -1,36 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {spawn}from'child_process';import {existsSync,readFileSync,mkdirSync,writeFileSync}from'fs';import {homedir}from'os';import {join,dirname,delimiter}from'path';import {fileURLToPath}from'url';import {randomUUID,createHash}from'crypto';import {createServer}from'http';import*as B from'zlib';import {connect}from'net';var
|
|
3
|
-
n.messages))return null;let o
|
|
4
|
-
return null}}function
|
|
5
|
-
n)!==
|
|
6
|
-
|
|
7
|
-
s.value>0?s.value:1e5;
|
|
8
|
-
clear_tool_uses_20250919",
|
|
9
|
-
null&&typeof
|
|
10
|
-
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
toLowerCase();return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
let
|
|
23
|
-
n
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
2
|
+
import {spawn}from'child_process';import {existsSync,readFileSync,mkdirSync,writeFileSync}from'fs';import {homedir}from'os';import {join,dirname,delimiter}from'path';import {fileURLToPath}from'url';import {randomUUID,createHash}from'crypto';import {createServer}from'http';import*as B from'zlib';import {connect}from'net';var ve="context-management-2025-06-27";function ze(){return {edits:[{type:"clear_tool_uses_20250919",trigger:{type:"input_tokens",value:5e4},keep:{type:"tool_uses",value:8},clear_at_least:{type:"input_tokens",value:5e3}}]}}function St(t,e){if(e!==void 0&&e!==""&&e!=="identity")return null;try{let n=JSON.parse(t.toString("utf8"));if(n===null||typeof n!="object"||Array.isArray(n)||typeof n.model!="string"||!Array.isArray(
|
|
3
|
+
n.messages))return null;let o=$n(n.context_management),r=Nn(n.context_management);return n.context_management=jn(n.context_management),{cuerpo:Buffer.from(JSON.stringify(n),"utf8"),nuestros:o,triggerDelCliente:r}}catch{
|
|
4
|
+
return null}}function $n(t){let e=t!==null&&typeof t=="object"&&!Array.isArray(t)?t.edits:void 0,n=(Array.isArray(e)?e:[]).filter(o=>o!==null&&typeof o=="object"&&!Array.isArray(o)&&o.type===Ue);return xt(
|
|
5
|
+
n)!==xt([ze().edits[0]])}function Nn(t){let e=t!==null&&typeof t=="object"&&!Array.isArray(t)?t.edits:void 0,n=(Array.isArray(e)?e:[]).filter(r=>r!==null&&typeof r=="object"&&!Array.isArray(r)&&r.type===
|
|
6
|
+
Ue);if(n.length===0)return null;let o=Number.POSITIVE_INFINITY;for(let r of n){let s=r.trigger,i=s!==null&&typeof s=="object"&&s.type==="input_tokens"&&typeof s.value=="number"&&Number.isFinite(s.value)&&
|
|
7
|
+
s.value>0?s.value:1e5;i<o&&(o=i);}return o}function xt(t){return JSON.stringify(t,(e,n)=>n!==null&&typeof n=="object"&&!Array.isArray(n)?Object.fromEntries(Object.keys(n).sort().map(o=>[o,n[o]])):n)}var Ue="\
|
|
8
|
+
clear_tool_uses_20250919",wt="clear_thinking_20251015";function jn(t){let e=ze().edits[0],n=t!==null&&typeof t=="object"&&!Array.isArray(t)?{...t}:{},o=(Array.isArray(n.edits)?n.edits:[]).filter(i=>i!==
|
|
9
|
+
null&&typeof i=="object"&&!Array.isArray(i)&&i.type!==Ue),r=o.filter(i=>i.type===wt),s=o.filter(i=>i.type!==wt);return {...n,edits:[...r,...s,e]}}function Et(t,e,n=false){let o=" (TTL tuning ON if your li\
|
|
10
|
+
cence includes cache_ttl_tuning; fail-closed)",r=n?" \xB7 context elision dials FORCED ON BY HAND (BVSALA_GATEWAY_DIALES_ELISION=1): they measure as a net loss, R=4.05 vs R*=0.0127":" \xB7 context elisio\
|
|
11
|
+
n dials OFF";return t==="0"?{linea:"licence channel: CLOSED (BVSALA_GATEWAY_ELISION=0) \u2014 no TTL tuning and no context elision dials",aviso:false}:e?{linea:(t==="1"?"licence channel: OPEN (BVSALA_GAT\
|
|
12
|
+
EWAY_ELISION=1)":"licence channel: OPEN by default (=0 closes it)")+o+r,aviso:n}:{linea:"licence channel: NOT OPENED \u2014 BIVELIO_SERVICE_KEY is missing (without it there is no org whose licence to check\
|
|
13
|
+
); no TTL tuning and no context elision dials"+(t==="1"?". Open a new terminal or export the key and relaunch":""),aviso:t==="1"}}function Tt(t){return t===void 0||t.trim()===""?ve:t.split(",").map(n=>n.
|
|
14
|
+
trim()).filter(n=>n.length>0).includes(ve)?t:`${t},${ve}`}function j(t){let e=t.toLowerCase();return e.startsWith("gpt-")||e.startsWith("o1")||e.startsWith("o3")||e.startsWith("o4")?/^gpt-5\.6/.test(e)?1.25:1:e.startsWith("gemini")?1:1.25}function Ve(t){let e=t.
|
|
15
|
+
toLowerCase();return e.startsWith("claude")||e.startsWith("anthropic/")?2:j(t)}var zn=.3,Un=4096,Vn=(t,e)=>t==="cache_control"?void 0:e;function Fe(t){return t===void 0?"":JSON.stringify(t,Vn)??""}function Fn(t){return Array.isArray(t)?t.filter(e=>!(e!==null&&typeof e=="object"&&
|
|
16
|
+
e.defer_loading===true)):t}function He(t){return createHash("sha1").update(t).digest("base64url").slice(0,16)}function W(t,e){if(e!==void 0&&e!==""&&e!=="identity")return null;try{let n=JSON.parse(t.toString("u\
|
|
17
|
+
tf8"));if(n===null||typeof n!="object"||Array.isArray(n))return null;let o=n;return typeof o.model!="string"||!Array.isArray(o.messages)?null:o}catch{return null}}function At(t){let e=Fe(t.system),n=Fe(
|
|
18
|
+
Fn(t.tools)),o=Array.isArray(t.messages)?t.messages.slice(0,Un):[],r=[],s=[];for(let i of o){let a=Fe(i);r.push(He(a)),s.push(Buffer.byteLength(a,"utf8"));}return {system:He(e),tools:He(n),mensajes:r,bytes:s,
|
|
19
|
+
bytesFijos:Buffer.byteLength(e,"utf8")+Buffer.byteLength(n,"utf8")}}function _t(t,e){if(t===void 0||t.system!==e.system||t.tools!==e.tools)return 1;let n=e.bytesFijos;for(let s of e.bytes)n+=s;if(n<=0)
|
|
20
|
+
return 1;let o=0;for(;o<t.mensajes.length&&o<e.mensajes.length&&t.mensajes[o]===e.mensajes[o];)o++;let r=0;for(let s=o;s<e.bytes.length;s++)r+=e.bytes[s]??0;return r/n}function Ct(t){let{fraccionNueva:e,
|
|
21
|
+
umbral:n=zn}=t;return e>=n}function qe(t,e){if(t===null||typeof t!="object")return;if(Array.isArray(t)){for(let r of t)qe(r,e);return}let n=t,o=n.cache_control;if(o!==null&&typeof o=="object"&&!Array.
|
|
22
|
+
isArray(o)){let r=o;r.ttl==="1h"?(r.ttl="5m",e.rebajadas++):(r.ttl==="5m"||r.ttl===void 0)&&e.yaEran5m++;}for(let[r,s]of Object.entries(n))r!=="cache_control"&&s!==null&&typeof s=="object"&&qe(s,e);}function Rt(t,e){
|
|
23
|
+
let n=Buffer.isBuffer(t)?W(t,e):t;if(n===null)return null;try{let o={rebajadas:0,yaEran5m:0};for(let r of ["system","messages","tools"])qe(n[r],o);return o.rebajadas===0?null:{cuerpo:Buffer.from(JSON.stringify(
|
|
24
|
+
n),"utf8"),rebajadas:o.rebajadas,yaEran5m:o.yaEran5m}}catch{return null}}var ye=class{constructor(e=512,n=3600*1e3){this.maxEntradas=e;this.maxEdadMs=n;}maxEntradas;maxEdadMs;visto=new Map;ultima(e,n){let o=this.visto.get(e);if(o!==void 0){if(n-o.vistoMs>this.maxEdadMs){this.
|
|
25
|
+
visto.delete(e);return}return o.huella}}anotar(e,n,o){for(this.visto.delete(e),this.visto.set(e,{huella:n,vistoMs:o});this.visto.size>this.maxEntradas;){let r=this.visto.keys().next();if(r.done===true)break;
|
|
26
|
+
this.visto.delete(r.value);}}get tamano(){return this.visto.size}};var It="mid-conversation-output-config-2026-07-01",We=["low","medium","high","xhigh","max"],Hn=new Set(["claude-fable-5-1","claude-mythos-5-1","claude-opus-5"]);function qn(t){return Hn.has(t)}function Je(t){
|
|
27
|
+
return t!==void 0&&We.includes(t)}function Kn(t){return {role:"system",content:[],output_config:{effort:t}}}function Wn(t){if(t.output_config!==void 0&&t.output_config!==null)return true;let e=t.messages;
|
|
28
|
+
return Array.isArray(e)?e.some(n=>typeof n=="object"&&n!==null&&n.output_config!==void 0):false}function Ot(t,e,n){let o=W(t,n);if(o===null||typeof o.model!="string"||!qn(o.model)||Wn(o))return null;let r=o.
|
|
29
|
+
messages;return Array.isArray(r)?(o.messages=[...r,Kn(e)],{cuerpo:Buffer.from(JSON.stringify(o),"utf8"),nivel:e}):null}function Pt(t){let e=(t??"").split(",").map(n=>n.trim()).filter(Boolean);return e.
|
|
30
|
+
includes(It)||e.push(It),e.join(",")}function Dt(t,e,n){return t===void 0||t===""?{linea:`per-turn effort: OFF (BVSALA_GATEWAY_ESFUERZO=${We.join("|")} turns it on)`,aviso:false}:Je(t)?e?n?{linea:`per-tu\
|
|
31
|
+
rn effort: ARMED at "${t}" (ON if your licence includes per_turn_effort; fail-closed). Only fable-5-1, mythos-5-1 and opus-5; every other model passes through unchanged. Does NOT reset the prefix cach\
|
|
32
|
+
e: the dial is appended at the end and nothing earlier is rewritten`,aviso:false}:{linea:"per-turn effort: NOT ARMED \u2014 the licence channel is CLOSED (BVSALA_GATEWAY_ELISION=0 turns off the levers)",
|
|
33
|
+
aviso:true}:{linea:"per-turn effort: NOT ARMED \u2014 BIVELIO_SERVICE_KEY is missing (without it there is no org whose licence to check). Open a new terminal or export the key and relaunch",aviso:true}:{linea:`\
|
|
34
|
+
per-turn effort: NOT ARMED \u2014 "${t}" is not a level. Use one of: ${We.join(", ")}`,aviso:true}}var xe={"gpt-4.1":{inputPer1k:.002,outputPer1k:.008,cachedInputPer1k:5e-4},"gpt-4.1-mini":{inputPer1k:4e-4,outputPer1k:.0016,cachedInputPer1k:1e-4},"gpt-4.1-nano":{inputPer1k:1e-4,outputPer1k:4e-4,cachedInputPer1k:25e-6},
|
|
34
35
|
"gpt-4o":{inputPer1k:.0025,outputPer1k:.01,cachedInputPer1k:.00125},"gpt-4o-mini":{inputPer1k:15e-5,outputPer1k:6e-4,cachedInputPer1k:75e-6},"gpt-4-turbo":{inputPer1k:.01,outputPer1k:.03},"gpt-4":{inputPer1k:.03,
|
|
35
36
|
outputPer1k:.06},"gpt-3.5-turbo":{inputPer1k:5e-4,outputPer1k:.0015},"gpt-5":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},"gpt-5-codex":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},
|
|
36
37
|
"gpt-5-mini":{inputPer1k:25e-5,outputPer1k:.002,cachedInputPer1k:25e-6},"gpt-5-nano":{inputPer1k:5e-5,outputPer1k:4e-4,cachedInputPer1k:5e-6},"gpt-5.1-codex":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},
|
|
@@ -47,333 +48,345 @@ able-5":{inputPer1k:.01,outputPer1k:.05,cachedInputPer1k:.001},"claude-fable-5-1
|
|
|
47
48
|
cachedInputPer1k:1e-4},"claude-3-5-sonnet":{inputPer1k:.003,outputPer1k:.015,cachedInputPer1k:3e-4},"claude-3-5-haiku":{inputPer1k:8e-4,outputPer1k:.004,cachedInputPer1k:8e-5},"claude-3-opus":{inputPer1k:.015,
|
|
48
49
|
outputPer1k:.075,cachedInputPer1k:.0015},"claude-3-haiku":{inputPer1k:25e-5,outputPer1k:.00125,cachedInputPer1k:3e-5},"gemini-3.7-flash":{inputPer1k:75e-5,outputPer1k:.00375,cachedInputPer1k:75e-6},"g\
|
|
49
50
|
emini-3.5-flash":{inputPer1k:.0015,outputPer1k:.009,cachedInputPer1k:15e-5},"gemini-3.5-flash-lite":{inputPer1k:3e-4,outputPer1k:.0025,cachedInputPer1k:3e-5},"gemini-3.1-pro-preview":{inputPer1k:.002,
|
|
50
|
-
outputPer1k:.012,cachedInputPer1k:2e-4},"gemini-2.5-pro":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},"gemini-2.5-flash":{inputPer1k:3e-4,outputPer1k:.0025,cachedInputPer1k:3e-5}};function
|
|
51
|
-
if(!n)return;let o=
|
|
52
|
-
"."&&c!=="_")continue;let m=s.slice(
|
|
53
|
-
|
|
54
|
-
n.inputPer1k;return (o.cachedInputPer1k??o.inputPer1k)<r}function
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
s])=>`${r}\u2192${s}`).join(", ");return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
aviso:true}}var
|
|
62
|
-
|
|
51
|
+
outputPer1k:.012,cachedInputPer1k:2e-4},"gemini-2.5-pro":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},"gemini-2.5-flash":{inputPer1k:3e-4,outputPer1k:.0025,cachedInputPer1k:3e-5}};function Ge(t,e,n){
|
|
52
|
+
if(!n)return;let o=t[n];if(o)return o;let r=n.lastIndexOf("/"),s=r>=0?n.slice(r+1):n,i=t[s];if(i)return i;for(let a of e){if(!s.startsWith(a))continue;let c=s.charAt(a.length);if(c!==""&&c!=="-"&&c!==
|
|
53
|
+
"."&&c!=="_")continue;let m=s.slice(a.length+1);if(!(/^\d/.test(m)&&!/^\d{4}/.test(m)))return t[a]}}function Ye(t){return Object.keys(t).sort((e,n)=>n.length-e.length)}var Lt=Ye(xe);function M(t){return Ge(
|
|
54
|
+
xe,Lt,t)}var Jn=Object.fromEntries(Object.keys(xe).map(t=>[t,t]));function Mt(t){return Ge(Jn,Lt,t)}var $t={"claude-fable-5":"claude-fable-5-1"};function Gn(t,e){let n=M(t),o=M(e);if(n===void 0||o===void 0||n.inputPer1k!==o.inputPer1k||n.outputPer1k!==o.outputPer1k||j(t)!==j(e))return false;let r=n.cachedInputPer1k??
|
|
55
|
+
n.inputPer1k;return (o.cachedInputPer1k??o.inputPer1k)<r}function Yn(t){let e=Mt(t);if(e===void 0||e!==t)return null;let n=$t[e];return n===void 0||n===e||!Gn(e,n)?null:{solicitado:t,servido:n}}var Qn=/(^|,)\s*context-1m/i;
|
|
56
|
+
function Xn(t){let e=t.tool_choice;if(e===null||typeof e!="object")return false;let n=e.type;return n==="any"||n==="tool"}function Nt(t,e,n={}){if(n.betas!==void 0&&Qn.test(n.betas))return null;let o=W(t,
|
|
57
|
+
e);if(o===null||Xn(o))return null;let r=Yn(o.model);return r===null?null:(o.model=r.servido,{cuerpo:Buffer.from(JSON.stringify(o),"utf8"),cambio:r})}function jt(t,e,n){let o=Object.entries($t).map(([r,
|
|
58
|
+
s])=>`${r}\u2192${s}`).join(", ");return t!=="1"?{linea:"sibling model routing: OFF (BVSALA_GATEWAY_MODELO_HERMANO=1 turns it on)",aviso:false}:e?n?{linea:`sibling model routing: ARMED (${o}; ON if your \
|
|
59
|
+
licence includes sibling_model_routing; fail-closed). Changes the model you see in the response; cache reads cost 0.025\xD7 instead of 0.1\xD7 (list price). Turning it on mid-conversation pays one cold pref\
|
|
60
|
+
ix write, once. If the provider rejects the rewritten request (400), it is re-sent unchanged once`,aviso:false}:{linea:"sibling model routing: NOT ARMED \u2014 the licence channel is CLOSED (BVSALA_GATEWAY_EL\
|
|
61
|
+
ISION=0 turns off all three levers)",aviso:true}:{linea:"sibling model routing: NOT ARMED \u2014 BIVELIO_SERVICE_KEY is missing (without it there is no org whose licence to check). Open a new terminal or exp\
|
|
62
|
+
ort the key and relaunch",aviso:true}}var Bt=new Set;function P(t,e){Bt.has(t)||(Bt.add(t),console.warn(e));}var J="0.4.22",zt="x-bvsala-version";var ee=class{options;now;flushIntervalMs;maxBatch;maxBuffer;buffer=[];seatId;ci;timer;inflight=null;fallosSeguidos=0;constructor(e){this.options=e,this.now=e.now??(()=>Date.now()),this.flushIntervalMs=
|
|
63
|
+
e.flushIntervalMs??15e3,this.maxBatch=e.maxBatch??50,this.maxBuffer=e.maxBuffer??1e3,this.ci=e.ci??false;}setSeat(e,n){e&&(this.seatId=e),this.ci=n;}record(e){this.buffer.push({report:e,at:this.now()}),this.
|
|
63
64
|
buffer.length>this.maxBuffer&&this.buffer.splice(0,this.buffer.length-this.maxBuffer),this.buffer.length>=this.maxBatch&&this.flush();}start(){this.timer||(this.timer=setInterval(()=>{this.flush();},this.
|
|
64
|
-
flushIntervalMs),this.timer.unref?.());}stop(){this.timer&&(clearInterval(this.timer),this.timer=void 0),this.flush();}async stopAndDrain(
|
|
65
|
+
flushIntervalMs),this.timer.unref?.());}stop(){this.timer&&(clearInterval(this.timer),this.timer=void 0),this.flush();}async stopAndDrain(e){this.timer&&(clearInterval(this.timer),this.timer=void 0);let n=e??
|
|
65
66
|
Math.ceil(this.maxBuffer/this.maxBatch)+4,o=0;for(let r=0;r<n&&this.buffer.length>0;r++){let s=this.buffer.length;if(await this.flush(),o=this.buffer.length>=s?o+1:0,o>=2)break}}async flush(){if(this.
|
|
66
|
-
inflight)return this.inflight;if(this.buffer.length!==0){this.inflight=this.hacerFlush();try{await this.inflight;}finally{this.inflight=null;}}}async hacerFlush(){let
|
|
67
|
-
fetch;if(!
|
|
68
|
-
er ${this.options.serviceAccountKey}`,[
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
]";return new RegExp(`-${
|
|
82
|
-
|
|
83
|
-
if(
|
|
84
|
-
|
|
85
|
-
ONS","GITLAB_CI","CIRCLECI","TRAVIS","JENKINS_URL","BUILDKITE","TF_BUILD","TEAMCITY_VERSION","BITBUCKET_BUILD_NUMBER","CODEBUILD_BUILD_ID","HEROKU_TEST_RUN_ID","DRONE"];function
|
|
86
|
-
return false;for(let
|
|
87
|
-
o.encode(
|
|
88
|
-
|${
|
|
89
|
-
if(!o)return;let r=await
|
|
67
|
+
inflight)return this.inflight;if(this.buffer.length!==0){this.inflight=this.hacerFlush();try{await this.inflight;}finally{this.inflight=null;}}}async hacerFlush(){let e=this.options.fetchImpl??globalThis.
|
|
68
|
+
fetch;if(!e)return;let n=this.buffer.splice(0,this.maxBatch);try{let o=n.map(s=>this.toWire(s)),r=await e(this.options.url,{method:"POST",headers:{"content-type":"application/json",authorization:`Bear\
|
|
69
|
+
er ${this.options.serviceAccountKey}`,[zt]:`${this.options.surface??"sdk"}/${J}`},body:JSON.stringify(o)});r.ok?this.fallosSeguidos=0:r.status>=500?(this.fallosSeguidos++,this.requeue(n),this.avisarDeFallo(
|
|
70
|
+
`the server responded ${r.status}`)):r.status===401||r.status===403?(P(`metering-auth-${r.status}`,`[bv-sala] usage reporting is being rejected with HTTP ${r.status} at ${this.options.url}. Check 'ser\
|
|
71
|
+
viceAccountKey' (rotated? revoked? from another organization?). Until it works, no usage is reported: the dashboard will say it sees no traffic and billable seats will show zero.`),this.options.onAuthError?.(
|
|
72
|
+
r.status)):(this.fallosSeguidos++,this.avisarDeFallo(r.status===404||r.status===405?`HTTP ${r.status}: the reporting URL looks wrong`:`the server rejected the batch with HTTP ${r.status}`));}catch(o){this.
|
|
73
|
+
fallosSeguidos++,this.requeue(n),this.avisarDeFallo(o instanceof Error?o.message:"network error");}}avisarDeFallo(e){if(this.options.silenceWarnings)return;let n=this.fallosSeguidos;n<3||(n-3)%12!==0||
|
|
74
|
+
console.warn(`[bv-sala] usage reporting has failed ${n} times in a row against ${this.options.url} (${e}). Requests keep working, but usage is not arriving: the dashboard will say it sees no traffic a\
|
|
75
|
+
nd billable seats will show zero. Check 'meteringUrl' \u2014 by default it is \`\${licenseServerUrl}/api/metering/usage\`.`);}requeue(e){this.buffer=[...e,...this.buffer],this.buffer.length>this.maxBuffer&&
|
|
76
|
+
(this.buffer.length=this.maxBuffer);}toWire(e){let n=e.report;return {orgId:this.options.orgId??"self",...this.seatId?{seatId:this.seatId}:{},...this.ci?{ci:true}:{},requestId:n.requestId,model:n.model,at:e.
|
|
77
|
+
at,avoidedLlm:n.calls.avoidedLlm,actualLlm:n.calls.actualLlm,baselineInputTokens:n.tokens.baselineInput,actualInputTokens:n.tokens.actualInput,eliminatedInputTokens:n.tokens.eliminatedInput,compressedInputTokens:n.
|
|
78
|
+
tokens.compressedInput,baselineOutputTokens:n.tokens.baselineOutput,actualOutputTokens:n.tokens.actualOutput,providerCachedInputTokens:n.tokens.providerCachedInput??0,cacheResult:n.cache.result,...n.cost.
|
|
79
|
+
eliminatedInputFloorCost!==void 0&&Number.isFinite(n.cost.eliminatedInputFloorCost)&&n.cost.eliminatedInputFloorCost>0&&n.tokens.eliminatedInput>0?{eliminatedInputFloorCost:n.cost.eliminatedInputFloorCost}:
|
|
80
|
+
{},baselineCost:n.cost.baselineEstimate,actualCost:n.cost.actual,netSavings:n.cost.netSavings,...n.cost.basis?{basis:n.cost.basis}:{},...n.attribution?.sessionId?{sessionId:n.attribution.sessionId}:{},
|
|
81
|
+
...n.attribution?.agentId?{agentId:n.attribution.agentId}:{}}}};function we(){return globalThis.process}function Zn(t){if(/^[0-9a-f]{12}$/.test(t)||/^\d{1,3}(\.\d{1,3}){3}$/.test(t)||/^ip-\d{1,3}(-\d{1,3}){3}$/i.test(t))return true;let e="[bcdfghjklmnpqrstvwxz2456789\
|
|
82
|
+
]";return new RegExp(`-${e}{8,10}-${e}{5}$`).test(t)}function eo(t){return !!(t.AWS_LAMBDA_FUNCTION_NAME??t.LAMBDA_TASK_ROOT??t.VERCEL??t.VERCEL_ENV??t.K_SERVICE??t.FUNCTION_TARGET??t.FUNCTIONS_WORKER_RUNTIME??
|
|
83
|
+
t.NETLIFY)}async function to(){if(we()?.execPath)try{let e=await import(["node","os"].join(":")),n=typeof e.hostname=="function"?e.hostname():"";return n.length>0?n:void 0}catch{return}}async function no(t,e=to){
|
|
84
|
+
if(t.KUBERNETES_SERVICE_HOST||eo(t))return;let n=t.HOSTNAME??t.COMPUTERNAME??await e();if(!(!n||Zn(n)))return n}async function oo(){let t=we();if(!t||!t.env)return;let e=t.env,n=await no(e),o=[t.platform,
|
|
85
|
+
t.arch,t.execPath,n,e.USER??e.USERNAME??e.LOGNAME,e.HOME??e.USERPROFILE].filter(r=>typeof r=="string"&&r.length>0);return o.length?o.join("|"):void 0}var ro=["CI","CONTINUOUS_INTEGRATION","GITHUB_ACTI\
|
|
86
|
+
ONS","GITLAB_CI","CIRCLECI","TRAVIS","JENKINS_URL","BUILDKITE","TF_BUILD","TEAMCITY_VERSION","BITBUCKET_BUILD_NUMBER","CODEBUILD_BUILD_ID","HEROKU_TEST_RUN_ID","DRONE"];function Se(){let t=we()?.env;if(!t)
|
|
87
|
+
return false;for(let e of ro){let n=t[e];if(n&&n!=="false"&&n!=="0")return true}return false}async function so(t,e){let n=globalThis.crypto,o=new TextEncoder;if(n?.subtle)try{let i=await n.subtle.importKey("raw",
|
|
88
|
+
o.encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await n.subtle.sign("HMAC",i,o.encode(e));return [...new Uint8Array(a)].map(c=>c.toString(16).padStart(2,"0")).join("")}catch{}let r=2166136261,s=`${t}\
|
|
89
|
+
|${e}`;for(let i=0;i<s.length;i++)r^=s.charCodeAt(i),r=Math.imul(r,16777619);return (r>>>0).toString(16).padStart(8,"0")}async function Ee(t){let n=we()?.env?.BVSALA_SEAT_ID;if(n&&io.test(n))return n;let o=await oo();
|
|
90
|
+
if(!o)return;let r=await so(t,o);if(r)return `m-${r.slice(0,32)}`}var io=/^[A-Za-z0-9._:/-]{1,128}$/;var G=class{resto="";uso=null;elisionObservada=null;eventos={};observar(e){this.resto+=typeof e=="string"?e:e.toString("utf8");let n;for(;(n=this.resto.indexOf(`
|
|
90
91
|
|
|
91
92
|
`))>=0;){let o=this.resto.slice(0,n);this.resto=this.resto.slice(n+2);let r=/^event: (.+)$/m.exec(o)?.[1];if(r&&(this.eventos[r]=(this.eventos[r]??0)+1),r!=="message_start"&&r!=="message_delta")continue;
|
|
92
|
-
let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let
|
|
93
|
+
let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let i=JSON.parse(s),a=Ut(i.context_management);a&&(this.elisionObservada=a);let c=i.usage??i.message?.usage;if(c){let m=c.cache_creation||this.uso?.cache_creation?
|
|
93
94
|
{...this.uso?.cache_creation,...c.cache_creation}:void 0;this.uso={...this.uso,...c,...m?{cache_creation:m}:{}};}}catch{}}}usage(){return this.uso}usageFinal(){return (this.eventos.message_delta??0)>0?this.
|
|
94
|
-
uso:null}elision(){return this.elisionObservada}};function
|
|
95
|
-
n)||n<=0?0:
|
|
96
|
-
cleared_input_tokens;typeof s=="number"&&Number.isFinite(s)&&s>0&&(n+=s,r.type==="clear_tool_uses_20250919"&&(o+=s));}return {borrados:
|
|
97
|
-
try{let
|
|
98
|
-
|
|
95
|
+
uso:null}elision(){return this.elisionObservada}};function ao(t,e){if(!e||e.atribuible!==true||!(e.tokensLimpiadosToolUses>0))return 0;let n=t.cachedInputPer1k;return typeof n!="number"||!Number.isFinite(
|
|
96
|
+
n)||n<=0?0:e.tokensLimpiadosToolUses/1e3*n}function Ut(t){if(t===null||typeof t!="object")return null;let e=t.applied_edits;if(!Array.isArray(e)||e.length===0)return null;let n=0,o=0;for(let r of e){let s=r.
|
|
97
|
+
cleared_input_tokens;typeof s=="number"&&Number.isFinite(s)&&s>0&&(n+=s,r.type==="clear_tool_uses_20250919"&&(o+=s));}return {borrados:e.length,tokensLimpiados:n,tokensLimpiadosToolUses:o}}function Vt(t){
|
|
98
|
+
try{let e=JSON.parse(typeof t=="string"?t:t.toString("utf8"));return Ut(e.context_management)}catch{return null}}var Te=class{resto="";uso=null;eventos={};observar(e){this.resto+=typeof e=="string"?e:
|
|
99
|
+
e.toString("utf8");let n;for(;(n=this.resto.indexOf(`
|
|
99
100
|
|
|
100
|
-
`))>=0;){let o=this.resto.slice(0,n);this.resto=this.resto.slice(n+2);let r=/^event: (.+)$/m.exec(o)?.[1];r&&(this.eventos[r]=(this.eventos[r]??0)+1);let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let
|
|
101
|
-
parse(s);if((r??
|
|
102
|
-
0),n=Math.min(Math.max(0,
|
|
103
|
-
0),cache_read_input_tokens:n,cache_creation_input_tokens:o}}function
|
|
104
|
-
1e3*
|
|
105
|
-
0,
|
|
106
|
-
cacheEscrita:
|
|
107
|
-
"").trim().toLowerCase()){case "gzip":case "x-gzip":n=B.gunzipSync(
|
|
108
|
-
if(typeof r!="function")return null;n=r(
|
|
109
|
-
function
|
|
110
|
-
true,o)}function
|
|
111
|
-
baselineOutput:0,actualOutput:0,providerCachedInput:0},cache:{result:"miss"},cost:{baselineEstimate:n,actual:o,netSavings:n>0?Math.max(-100,Math.min(1,(n-o)/n)):0,basis:"shadow"}}}function
|
|
112
|
-
|
|
113
|
-
n.tokens.cacheEscrita1h,
|
|
101
|
+
`))>=0;){let o=this.resto.slice(0,n);this.resto=this.resto.slice(n+2);let r=/^event: (.+)$/m.exec(o)?.[1];r&&(this.eventos[r]=(this.eventos[r]??0)+1);let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let i=JSON.
|
|
102
|
+
parse(s);if((r??i.type)!=="response.completed")continue;let a=i.response?.usage;a&&(this.uso=co(a));}catch{}}}usage(){return this.uso}usageFinal(){return this.uso}};function co(t){let e=Math.max(0,t.input_tokens??
|
|
103
|
+
0),n=Math.min(Math.max(0,t.input_tokens_details?.cached_tokens??0),e),o=Math.min(Math.max(0,t.input_tokens_details?.cache_write_tokens??0),e-n);return {input_tokens:e-n-o,output_tokens:Math.max(0,t.output_tokens??
|
|
104
|
+
0),cache_read_input_tokens:n,cache_creation_input_tokens:o}}function Ft(t,e,n){let o=t.cachedInputPer1k??t.inputPer1k;return e.entrada/1e3*t.inputPer1k+e.cacheLeida/1e3*o+(e.cacheEscrita-e.cacheEscrita1h)/
|
|
105
|
+
1e3*t.inputPer1k*j(n)+e.cacheEscrita1h/1e3*t.inputPer1k*2+e.salida/1e3*t.outputPer1k}function Ht(t,e){let n=M(t);if(!n)return null;let o=e.input_tokens??0,r=e.output_tokens??0,s=e.cache_read_input_tokens??
|
|
106
|
+
0,i=e.cache_creation,a=e.cache_creation_input_tokens??(i?.ephemeral_5m_input_tokens??0)+(i?.ephemeral_1h_input_tokens??0),c=Math.min(i?.ephemeral_1h_input_tokens??0,a),m={entrada:o,salida:r,cacheLeida:s,
|
|
107
|
+
cacheEscrita:a,cacheEscrita1h:c};return {costeUsd:Ft(n,m,t),precio:n,tokens:m}}function qt(t,e){let o=Qe(t,e)?.model;return typeof o=="string"?o:""}function Qe(t,e){try{let n=t,o={maxOutputLength:lo};switch((e??
|
|
108
|
+
"").trim().toLowerCase()){case "gzip":case "x-gzip":n=B.gunzipSync(t,o);break;case "deflate":n=B.inflateSync(t,o);break;case "br":n=B.brotliDecompressSync(t,o);break;case "zstd":{let r=B.zstdDecompressSync;
|
|
109
|
+
if(typeof r!="function")return null;n=r(t,o);break}default:break}return JSON.parse(n.toString("utf8"))}catch{return null}}var lo=5*1024*1024;function Kt(){return typeof B.zstdDecompressSync=="function"}
|
|
110
|
+
function Xe(t){try{return JSON.parse(typeof t=="string"?t:t.toString("utf8")).usage??null}catch{return null}}function Wt(t,e,n,o,r,s,i){return Yt(t,e,n,false,o,r,s,i)}function Jt(t,e,n,o){return Yt(t,e,n,
|
|
111
|
+
true,o)}function Gt(t,e,n,o,r){return {requestId:t,model:e,...r&&(r.sessionId||r.agentId)?{attribution:r}:{},calls:{avoidedLlm:0,actualLlm:0},tokens:{baselineInput:0,actualInput:0,eliminatedInput:0,compressedInput:0,
|
|
112
|
+
baselineOutput:0,actualOutput:0,providerCachedInput:0},cache:{result:"miss"},cost:{baselineEstimate:n,actual:o,netSavings:n>0?Math.max(-100,Math.min(1,(n-o)/n)):0,basis:"shadow"}}}function uo(t,e){let n=M(
|
|
113
|
+
t.solicitado);return n===void 0?0:Math.max(0,Ft(n,e.tokens,t.solicitado)-e.costeUsd)}function Yt(t,e,n,o,r,s,i,a){let c=n.tokens.entrada+n.tokens.cacheLeida+n.tokens.cacheEscrita,m=n.tokens.cacheEscrita-
|
|
114
|
+
n.tokens.cacheEscrita1h,g=!o&&i&&i.rebajadas>0&&i.yaEran5m===0&&m>0?m/1e3*n.precio.inputPer1k*(2-1.25):0,u=!o&&a?uo(a,n):0,h=n.costeUsd+g+u,k=g+u,l=o?0:ao(n.precio,s);return {requestId:t,model:e,...r&&
|
|
114
115
|
(r.sessionId||r.agentId)?{attribution:r}:{},calls:o?{avoidedLlm:1,actualLlm:0}:{avoidedLlm:0,actualLlm:1},tokens:{baselineInput:c,actualInput:o?0:c,eliminatedInput:o?0:s?.tokensLimpiados??0,compressedInput:0,
|
|
115
116
|
baselineOutput:n.tokens.salida,actualOutput:o?0:n.tokens.salida,providerCachedInput:o?0:n.tokens.cacheLeida},cache:{result:o?"singleflight":"miss"},cost:o?{baselineEstimate:n.costeUsd,actual:0,netSavings:1,
|
|
116
|
-
basis:"estimated"}:{baselineEstimate:h,actual:n.costeUsd,netSavings:h>0?Math.max(-100,Math.min(1,
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
`):""}var
|
|
120
|
-
`),n=Math.max(1,
|
|
121
|
-
test(
|
|
117
|
+
basis:"estimated"}:{baselineEstimate:h,actual:n.costeUsd,netSavings:h>0?Math.max(-100,Math.min(1,k/h)):0,basis:"measured",...l>0?{eliminatedInputFloorCost:l}:{}}}}var po=1024,de=120,mo=8,fo=256*1024,ho=/error|exception|traceback|warn|fatal|fail|panic|assert|denied|refused|timeout|\bat\s+\S+:\d+/i,Zt=/^\s*(export\s|import\s|function\s|class\s|interface\s|type\s|enum\s|def\s|fn\s|func\s|impl\s|struct\s|trait\s|package\s|module\s|@\w)/;
|
|
118
|
+
function go(t){return Buffer.byteLength(typeof t=="string"?t:JSON.stringify(t??""))}function bo(t){return typeof t=="string"?t:Array.isArray(t)?t.map(e=>e&&typeof e=="object"&&typeof e.text=="string"?
|
|
119
|
+
e.text:"").join(`
|
|
120
|
+
`):""}var vo=/^\s*\d+[\t→]/;function en(t){return t.replace(vo,"")}function yo(t){if(!t)return "otro";let e=t.split(`
|
|
121
|
+
`),n=Math.max(1,e.length),o=0,r=0,s=0;for(let i of e){let a=en(i);(/^[+-](?![+-])/.test(a)||a.startsWith("@@")||a.startsWith("diff --git"))&&(o+=1),Zt.test(a)&&(r+=1),(/^\s*(\d{4}-\d{2}-\d{2}|\[\d|\d{2}:\d{2}:\d{2})/.
|
|
122
|
+
test(a)||/\b(INFO|DEBUG|TRACE|WARN|ERROR)\b/.test(a))&&(s+=1);}return o/n>.25?"diff":s/n>.3?"log":r/n>.08?"codigo":"otro"}function ko(t,e){if(e==="diff"||e==="otro")return Buffer.byteLength(t);let n=t.
|
|
122
123
|
split(`
|
|
123
|
-
`),o=new Set;for(let s=0;s<n.length;s++){let
|
|
124
|
-
n[s])+1;return Math.min(r,Buffer.byteLength(
|
|
125
|
-
n:${JSON.stringify(
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
let
|
|
129
|
-
n("tools",o);if(Array.isArray(
|
|
130
|
-
"object"){n("?:invalido",o);continue}let r=o,s=typeof r.role=="string"?r.role:"?";if(typeof r.content=="string")n(`${s}:text`,r.content);else if(Array.isArray(r.content))for(let
|
|
131
|
-
:${
|
|
132
|
-
return n}function
|
|
133
|
-
h))return {clave:
|
|
134
|
-
|
|
135
|
-
var
|
|
136
|
-
o:null}escribir(
|
|
137
|
-
|
|
138
|
-
value;if(n===void 0)break;this.entradas.delete(n);}}};function
|
|
139
|
-
"|").update(
|
|
140
|
-
t");
|
|
141
|
-
5m";hayPrevio=false;atPrevio=0;acumulado=
|
|
142
|
-
s,this.previoUltimoBp=-1,this.previoTtl="5m",this.hayPrevio=true,this.atPrevio=
|
|
143
|
-
|
|
144
|
-
n.tokensEntrada+=
|
|
145
|
-
n.tokensEntrada,this.acumulado.entradaUsd+=n.entradaUsd,this.acumulado.totalUsd+=n.totalUsd,n}let
|
|
146
|
-
|
|
147
|
-
[]:this.previo.slice(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
atPrevio=
|
|
124
|
+
`),o=new Set;for(let s=0;s<n.length;s++){let i=en(n[s]);(e==="log"?ho.test(i):Zt.test(i))&&(o.add(s),e==="log"&&(s>0&&o.add(s-1),s<n.length-1&&o.add(s+1)));}let r=de;for(let s of o)r+=Buffer.byteLength(
|
|
125
|
+
n[s])+1;return Math.min(r,Buffer.byteLength(t))}var xo=(t,e)=>t==="cache_control"?void 0:e;function wo(t){if(typeof t=="string")return `text:${t}`;if(!t||typeof t!="object"||Array.isArray(t))return `jso\
|
|
126
|
+
n:${JSON.stringify(t??"")}`;let e=t;return e.type==="text"&&typeof e.text=="string"&&Object.keys(e).every(n=>n==="type"||n==="text"||n==="cache_control")?`text:${e.text}`:`json:${JSON.stringify(e,xo)}`}
|
|
127
|
+
function So(t){return createHash("sha256").update(wo(t)).digest("hex").slice(0,16)}function Eo(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t.cache_control;return !e||typeof e!="object"?null:
|
|
128
|
+
e.ttl==="1h"?"1h":"5m"}function tn(t){let e=[],n=(o,r)=>{let s=go(r),i={clave:So(r),tipo:o,marca:Eo(r),bytes:s,bytesV1:s,clase:"otro",esError:false};if(o.endsWith(":tool_result")&&r&&typeof r=="object"){
|
|
129
|
+
let a=r;i.esError=a.is_error===true;let c=bo(a.content);i.clase=yo(c);let m=s-Buffer.byteLength(c);i.bytesV1=Math.min(s,Math.max(0,m)+ko(c,i.clase));}e.push(i);};if(Array.isArray(t.tools))for(let o of t.tools)
|
|
130
|
+
n("tools",o);if(Array.isArray(t.system))for(let o of t.system)n("system",o);else (t.system??void 0)!==void 0&&n("system",t.system);for(let o of Array.isArray(t.messages)?t.messages:[]){if(!o||typeof o!=
|
|
131
|
+
"object"){n("?:invalido",o);continue}let r=o,s=typeof r.role=="string"?r.role:"?";if(typeof r.content=="string")n(`${s}:text`,r.content);else if(Array.isArray(r.content))for(let i of r.content)n(`${s}\
|
|
132
|
+
:${i?.type??"?"}`,i);}return e}function To(){return {nacimiento:new Map,turno:0,elididosV2m:new Set}}function Ao(t,e){let n=t.turno++;for(let o of e)t.nacimiento.has(o.clave)||t.nacimiento.set(o.clave,n);
|
|
133
|
+
return n}function nn(t,e,n,o,r=fo){let s=e.reduce((u,h,k)=>h.tipo.endsWith(":tool_result")?k:u,-1),i=(u,h)=>u.tipo.endsWith(":tool_result")&&u.bytes>=po&&h!==s;return e.map((u,h)=>{if(!i(u,
|
|
134
|
+
h))return {clave:u.clave,bytes:u.bytes};let k=o-(n.nacimiento.get(u.clave)??o);return k>=2&&k*u.bytes/1024>=mo?{clave:`${u.clave}:e`,bytes:de}:{clave:u.clave,bytes:u.bytes}});}function _o(t){let e=M(t);return e?{inputPer1k:e.inputPer1k,lecturaPer1k:e.cachedInputPer1k??e.inputPer1k,escrituraPer1k:e.inputPer1k*
|
|
135
|
+
j(t),escritura1hPer1k:e.inputPer1k*Ve(t)}:null}function Qt(){return {lecturaUsd:0,escrituraUsd:0,entradaUsd:0,totalUsd:0,tokensLeidos:0,tokensEscritos:0,tokensEntrada:0,tokensInvalidados:0,invalidacionUsd:0}}
|
|
136
|
+
var et={"1h":36e5,"5m":3e5},Co=20,Ro=6e3,Io=2e4,te=class{constructor(e=Io){this.tope=e;}tope;entradas=new Map;get size(){return this.entradas.size}viva(e,n){let o=this.entradas.get(e);return o&&n<=o.expira?
|
|
137
|
+
o:null}escribir(e,n,o){this.entradas.delete(e),this.entradas.set(e,{expira:n+et[o],ttl:o}),this.entradas.size>this.tope&&this.podar(n);}refrescar(e,n){let o=this.entradas.get(e);o&&(this.entradas.delete(
|
|
138
|
+
e),this.entradas.set(e,{expira:n+et[o.ttl],ttl:o.ttl}));}podar(e){for(let[n,o]of this.entradas)e>o.expira&&this.entradas.delete(n);for(;this.entradas.size>this.tope;){let n=this.entradas.keys().next().
|
|
139
|
+
value;if(n===void 0)break;this.entradas.delete(n);}}};function Oo(t,e){let n=new Array(e.length),o=createHash("sha256").update(`model:${t}`).digest("hex");for(let r=0;r<e.length;r++)o=createHash("sha256").update(o).update(
|
|
140
|
+
"|").update(e[r]).digest("hex").slice(0,32),n[r]=o;return n}function Po(t){let e=new Array(t.length),n=-1,o="";for(let r=0;r<t.length;r++){let s=t[r],i=s.endsWith(":tool_use")||s.endsWith(":tool_resul\
|
|
141
|
+
t");i&&s===o||(n+=1),o=i?s:"",e[r]=n;}return e}function Xt(t,e){return t.endsWith(":image")?Math.min(e,Ro):e}var pe=class{constructor(e,n){this.transformar=e;this.cache=n??new te;}transformar;estado=To();cache;previo=[];previoUltimoBp=-1;previoTtl="\
|
|
142
|
+
5m";hayPrevio=false;atPrevio=0;acumulado=Qt();paso(e){let n=Qt(),o=Ao(this.estado,e.bloques),r=_o(e.model),s=this.transformar(e.bloques,this.estado,o);if(!r||e.entradaReal<=0||s.length===0)return this.previo=
|
|
143
|
+
s,this.previoUltimoBp=-1,this.previoTtl="5m",this.hayPrevio=true,this.atPrevio=e.at,n;let i=s.map((d,b)=>e.bloques[b]?.tipo??""),a=s.map((d,b)=>e.bloques[b]?.marca??null),c=e.bloques.reduce((d,b)=>d+Xt(
|
|
144
|
+
b.tipo,b.bytes),0),m=c>0?e.entradaReal/c:0,g=s.map((d,b)=>Xt(i[b],d.bytes)*m),u=Oo(e.model,s.map(d=>d.clave)),h=Po(i),k=a.map((d,b)=>d?b:-1).filter(d=>d>=0);if(k.length===0){for(let d=0;d<s.length;d++)
|
|
145
|
+
n.tokensEntrada+=g[d],n.entradaUsd+=g[d]/1e3*r.inputPer1k;return n.totalUsd=n.entradaUsd,this.previo=s,this.previoUltimoBp=-1,this.previoTtl="5m",this.hayPrevio=true,this.atPrevio=e.at,this.acumulado.tokensEntrada+=
|
|
146
|
+
n.tokensEntrada,this.acumulado.entradaUsd+=n.entradaUsd,this.acumulado.totalUsd+=n.totalUsd,n}let l=-1;for(let d of k){let b=h[d]-(Co-1);for(let x=d;x>=0&&h[x]>=b;x--)if(this.cache.viva(u[x],e.at)){x>
|
|
147
|
+
l&&(l=x);break}}for(let d=0;d<=l;d++)this.cache.refrescar(u[d],e.at);let p=k[k.length-1];for(let d of k)d>l&&this.cache.escribir(u[d],e.at,a[d]);let w=0,E=this.hayPrevio&&e.at-this.atPrevio>et[this.previoTtl]?
|
|
148
|
+
[]:this.previo.slice(l+1,this.previoUltimoBp+1),C=0;for(let d=0;d<s.length;d++){let b=s[d],x=g[d];if(d<=l){n.tokensLeidos+=x,n.lecturaUsd+=x/1e3*r.lecturaPer1k;continue}if(d>p){n.tokensEntrada+=x,n.entradaUsd+=
|
|
149
|
+
x/1e3*r.inputPer1k;continue}for(;k[w]<d;)w+=1;let H=a[k[w]]==="1h"?r.escritura1hPer1k:r.escrituraPer1k;n.tokensEscritos+=x,n.escrituraUsd+=x/1e3*H;let T=C;for(;T<E.length&&E[T].clave!==b.clave;)T+=1;T<
|
|
150
|
+
E.length&&(E[T].bytes===b.bytes&&(n.tokensInvalidados+=x,n.invalidacionUsd+=x/1e3*(H-r.lecturaPer1k)),C=T+1);}return this.previo=s,this.previoUltimoBp=p,this.previoTtl=a[p]??"5m",this.hayPrevio=true,this.
|
|
151
|
+
atPrevio=e.at,n.totalUsd=n.lecturaUsd+n.escrituraUsd+n.entradaUsd,this.acumulado.lecturaUsd+=n.lecturaUsd,this.acumulado.escrituraUsd+=n.escrituraUsd,this.acumulado.entradaUsd+=n.entradaUsd,this.acumulado.
|
|
151
152
|
totalUsd+=n.totalUsd,this.acumulado.tokensLeidos+=n.tokensLeidos,this.acumulado.tokensEscritos+=n.tokensEscritos,this.acumulado.tokensEntrada+=n.tokensEntrada,this.acumulado.tokensInvalidados+=n.tokensInvalidados,
|
|
152
|
-
this.acumulado.invalidacionUsd+=n.invalidacionUsd,n}};function
|
|
153
|
-
0),o=(
|
|
154
|
-
0)>0};if(r.bloques.length===0)return null;let s=this.cadenaPara(this.clave(
|
|
155
|
-
if(
|
|
156
|
-
"hex").slice(0,16)}\xB7${
|
|
157
|
-
cachesDe(n);if(o={base:new
|
|
158
|
-
|
|
159
|
-
fetchImpl??fetch,this.log=
|
|
160
|
-
enVuelo:(this.enVuelo=this.pedir().finally(()=>{this.enVuelo=null;}),this.enVuelo)}async pedir(){try{let
|
|
161
|
-
timeout(1e4)});if(!
|
|
162
|
-
|
|
163
|
-
Date.now()+o,this.avisado=!1;}catch(
|
|
164
|
-
|
|
165
|
-
referencias:o}}var
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
`).update(
|
|
153
|
+
this.acumulado.invalidacionUsd+=n.invalidacionUsd,n}};function on(t){return t.map(e=>({clave:e.clave,bytes:e.bytes}))}var Lo=64,Ae=class{cadenas=new Map;cachesPorModelo=new Map;observar(e){let n=e.uso.cache_creation_input_tokens??(e.uso.cache_creation?.ephemeral_5m_input_tokens??0)+(e.uso.cache_creation?.ephemeral_1h_input_tokens??
|
|
154
|
+
0),o=(e.uso.input_tokens??0)+(e.uso.cache_read_input_tokens??0)+n;if(o<=0)return null;let r={bloques:tn(e.cuerpo),at:e.at,model:e.model,entradaReal:o,escritura1h:(e.uso.cache_creation?.ephemeral_1h_input_tokens??
|
|
155
|
+
0)>0};if(r.bloques.length===0)return null;let s=this.cadenaPara(this.clave(e),e.model),i=s.base.paso(r),a=s.v0.paso(r);return i.totalUsd<=0&&a.totalUsd<=0?null:{baselineUsd:i.totalUsd,politicaUsd:a.totalUsd}}clave(e){
|
|
156
|
+
if(e.sessionId)return `s:${e.sessionId}\xB7a:${e.agentId??""}\xB7${e.model}`;let n=Array.isArray(e.cuerpo.messages)?e.cuerpo.messages[0]:void 0;return `m:${createHash("sha256").update(JSON.stringify(n??null)).digest(
|
|
157
|
+
"hex").slice(0,16)}\xB7${e.model}`}cachesDe(e){let n=this.cachesPorModelo.get(e);return n||(n={base:new te,v0:new te},this.cachesPorModelo.set(e,n)),n}cadenaPara(e,n){let o=this.cadenas.get(e);if(!o){
|
|
158
|
+
let r=this.cachesDe(n);if(o={base:new pe(s=>on(s),r.base),v0:new pe((s,i,a)=>nn("v0",s,i,a),r.v0),ultimoUso:0},this.cadenas.set(e,o),this.cadenas.size>Lo){let s=null,i=1/0;for(let[a,c]of this.cadenas)
|
|
159
|
+
c.ultimoUso<i&&(i=c.ultimoUso,s=a);s!==null&&this.cadenas.delete(s);}}return o.ultimoUso=Date.now(),o}};var _e=class{url;clave;ttlMs;fetchImpl;log;features=new Set;validoHasta=0;enVuelo=null;avisado=false;constructor(e){this.url=e.url,this.clave=e.serviceAccountKey,this.ttlMs=e.ttlMs??3e5,this.fetchImpl=e.
|
|
160
|
+
fetchImpl??fetch,this.log=e.log??(()=>{}),this.refrescar();}tiene(e){return Date.now()>this.validoHasta&&this.enVuelo===null&&this.refrescar(),this.features.has(e)}refrescar(){return this.enVuelo?this.
|
|
161
|
+
enVuelo:(this.enVuelo=this.pedir().finally(()=>{this.enVuelo=null;}),this.enVuelo)}async pedir(){try{let e=await this.fetchImpl(this.url,{method:"GET",headers:{authorization:`Bearer ${this.clave}`},signal:AbortSignal.
|
|
162
|
+
timeout(1e4)});if(!e.ok){this.cerrar(`entitlements HTTP ${e.status}`);return}let n=await e.json();if(!Array.isArray(n.features)||!n.features.every(r=>typeof r=="string")){this.cerrar("entitlements res\
|
|
163
|
+
ponse has an unexpected shape");return}this.features=new Set(n.features);let o=typeof n.refreshAfterSeconds=="number"&&n.refreshAfterSeconds>0?Math.min(n.refreshAfterSeconds*1e3,this.ttlMs):this.ttlMs;
|
|
164
|
+
this.validoHasta=Date.now()+o,this.avisado=!1;}catch(e){this.cerrar(String(e));}}cerrar(e){this.features=new Set,this.validoHasta=Date.now()+Math.min(6e4,this.ttlMs),this.avisado||(this.avisado=true,this.
|
|
165
|
+
log(`[gateway] entitlements unavailable (${e}) \u2014 savings features disabled (fail-closed); traffic keeps flowing`));}};function rn(t){let e=0,n=0;for(;;){let r=t.indexOf('"input_schema"',n);if(r<0)break;e+=1,n=r+14;}let o=0;for(n=0;;){let r=t.indexOf('"tool_reference"',n);if(r<0)break;o+=1,n=r+16;}return {definiciones:e,
|
|
166
|
+
referencias:o}}var Mo=40;function sn(t){return t.referencias>0||t.definiciones<Mo?null:`[bvsala gateway] your client sends ${t.definiciones} FULL tool definitions in every request and does not use too\
|
|
167
|
+
l search. With a proxy in front, Claude Code turns tool search off by default: \`bvsala claude\` turns it back on, but only in a NEW WINDOW. If you launched the terminal by hand, export ENABLE_TOOL_SEAR\
|
|
168
|
+
CH=true. In our own corpus that block was 24.5% of input spend.`}var tt=5*1024*1024,No=["authorization","x-api-key","anthropic-version","anthropic-beta"];function an(t,e,n,o=""){return createHash("sha256").update(t).update(`
|
|
169
|
+
`).update(e).update(`
|
|
169
170
|
`).update(o).update(`
|
|
170
|
-
`).update(n).digest("hex")}function
|
|
171
|
-
`)}var
|
|
172
|
-
s=0,
|
|
173
|
-
this.enVuelo.delete(
|
|
174
|
-
1),o}tieneEnVuelo(
|
|
175
|
-
|
|
176
|
-
{},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
(d.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
"
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
171
|
+
`).update(n).digest("hex")}function cn(t){return No.map(e=>`${e}=${t(e)??""}`).join(`
|
|
172
|
+
`)}var Ce=class{enVuelo=new Map;primarias=0;gemelas=0;registrar(e){if(this.enVuelo.has(e))return null;this.primarias+=1;let n,o=new Promise(a=>n=a);this.enVuelo.set(e,{promesa:o,resolver:n});let r=[],
|
|
173
|
+
s=0,i=false;return {capturar:a=>{if(!i){if(s+=a.length,s>tt){i=true,r.length=0;return}r.push(a);}},terminar:(a,c)=>{let m=!i&&a>=200&&a<300;this.enVuelo.get(e)?.resolver(m?{status:a,headers:c,chunks:r}:null),
|
|
174
|
+
this.enVuelo.delete(e);},abortar:()=>{this.enVuelo.get(e)?.resolver(null),this.enVuelo.delete(e);}}}async esperar(e){let n=this.enVuelo.get(e);if(!n)return null;let o=await n.promesa;return o&&(this.gemelas+=
|
|
175
|
+
1),o}tieneEnVuelo(e){return this.enVuelo.has(e)}};var zo=["content-encoding","content-length","transfer-encoding"],Uo=new Set(["connection","keep-alive","proxy-connection","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding",
|
|
176
|
+
"upgrade","expect"]);function ne(t){let e=t?.cause;if(e==null)return String(t);let n=e instanceof AggregateError&&e.errors.length>0?e.errors.map(o=>String(o)).join("; "):String(e);return `${String(t)} \
|
|
177
|
+
\xB7 cause: ${n}`}var nt=class extends Error{code;puerto;constructor(e,n,o){super(e==="EADDRINUSE"?`port ${n} is already in use: the gateway could not start`:e==="EACCES"?`port ${n} cannot be opened: \
|
|
178
|
+
permission denied`:`could not listen on 127.0.0.1:${n} (${e})`,{cause:o}),this.name="ErrorDeEscucha",this.code=e,this.puerto=n;}};async function ln(t={}){let e=(t.upstream??"https://api.anthropic.com").
|
|
179
|
+
replace(/\/+$/,""),n=t.fetchImpl??fetch,o=t.log??(l=>console.log(l)),r;if(t.serviceAccountKey){r=new ee({url:t.meteringUrl??"https://savings.bivelio.com/api/metering/usage",serviceAccountKey:t.serviceAccountKey,
|
|
180
|
+
surface:"gateway",...t.fetchImpl?{fetchImpl:t.fetchImpl}:{},onAuthError:p=>P("gateway-ingest-auth",`[bvsala gateway] the usage ingest is rejecting the credential (${p}). Traffic keeps flowing (warn, n\
|
|
181
|
+
ever break), but this usage is not reaching your dashboard. Check BIVELIO_SERVICE_KEY and your subscription status at savings.bivelio.com.`)});let l=Se();t.seatId?r.setSeat(t.seatId,l):Ee(t.serviceAccountKey).
|
|
182
|
+
then(p=>r?.setSeat(p,l)).catch(()=>{}),r.start();}else P("gateway-sin-clave","[bvsala gateway] no BIVELIO_SERVICE_KEY: the gateway forwards traffic but does not meter it. Get your key at savings.biveli\
|
|
183
|
+
o.com/dashboard/install to see your spend in the dashboard.");let s=t.coalescer?new Ce:null,i=t.sombra&&r?new Ae:null,a=new ye,c=t.elision!==false&&t.serviceAccountKey?new _e({url:new URL("/api/org/entit\
|
|
184
|
+
lements",t.meteringUrl??"https://savings.bivelio.com/api/metering/usage").toString(),serviceAccountKey:t.serviceAccountKey,...t.fetchImpl?{fetchImpl:t.fetchImpl}:{},log:o}):null,m=l=>l==="/v1/messages"?
|
|
185
|
+
new G:l.endsWith("/responses")?new Te:null,g=(l,p,w,D,E,C,d,b,x,H,T)=>{if(!r||!w)return;let $=r,se=w,A=qt(p,C);if(!A){P(`gateway-sin-modelo:${E}`,`[bvsala gateway] could not read the model from the ${E}\
|
|
186
|
+
request body (content-encoding: ${C??"none"}); its usage is forwarded but not metered.`);return}let I=!D&&T?T.servido:A,O=Ht(I,w);if(O){let X=D?Jt(l,A,O,d):Wt(l,I,O,d,b,x,T);r.record(X);let Z=X.cost.
|
|
187
|
+
eliminatedInputFloorCost;o(`[gateway] POST ${E} ${D?"\xB7 COALESCED (call avoided)":""} \xB7 ${I!==A?`${A}\u2192${I} (sibling model)`:A} \xB7 $${O.costeUsd.toFixed(6)} (in ${O.tokens.entrada}, cache\u2191${O.
|
|
188
|
+
tokens.cacheEscrita}${O.tokens.cacheEscrita1h?` (1h ${O.tokens.cacheEscrita1h})`:""} cache\u2193${O.tokens.cacheLeida}, out ${O.tokens.salida})`+(b?` \xB7 elision: ${b.borrados} clear(s), ${b.tokensLimpiados}\
|
|
189
|
+
tok cleared by the provider`+(Z!==void 0?` \xB7 floor $${Z.toFixed(6)} (cache read price)`:b.tokensLimpiadosToolUses>0?b.atribuible?" \xB7 no cache read price in the catalog: no floor":" \xB7 your own c\
|
|
190
|
+
ontext_management settings: counted, not credited":b.tokensLimpiados>0?" \xB7 thinking only (your config): counted, not credited":""):""));let q=b?.atribuible===true&&b.tokensLimpiadosToolUses>0;i&&!D&&
|
|
191
|
+
E==="/v1/messages"&&setImmediate(()=>{try{let S=Qe(p,C);if(!S||typeof S!="object")return;let y=i.observar({cuerpo:S,uso:se,model:A,at:H??Date.now(),...d?.sessionId!==void 0?{sessionId:d.sessionId}:{},
|
|
192
|
+
...d?.agentId!==void 0?{agentId:d.agentId}:{}});if(!y)return;if(q){o(`[gateway] shadow estimate \xB7 ${A} \xB7 the provider already cleared context via our settings: chain advances, nothing emitted`);
|
|
193
|
+
return}$.record(Gt(`${l}:shadow`,A,y.baselineUsd,y.politicaUsd,d)),o(`[gateway] shadow estimate \xB7 ${A} \xB7 baseline $${y.baselineUsd.toFixed(6)} \u2192 with elision $${y.politicaUsd.toFixed(6)}`);}catch{}});}else
|
|
194
|
+
P(`gateway-sin-precio:${A}`,`[bvsala gateway] model "${A}" is not in the price catalog; its usage is forwarded but not metered.`);},u=createServer(async(l,p)=>{let w=randomUUID(),D=Date.now(),E=new AbortController;p.on(
|
|
195
|
+
"close",()=>{p.writableEnded||E.abort();});let C=null,d=new URL(l.url??"/",e),b=null,x=Buffer.alloc(0),H=false,T=null,$=null,se=(S,y)=>{if(S===null||T===null||!T.nuestros)return S;let K=y?.cache_creation,
|
|
196
|
+
Ne=y?.cache_creation_input_tokens??(K?.ephemeral_5m_input_tokens??0)+(K?.ephemeral_1h_input_tokens??0),ie=(y?.input_tokens??0)+(y?.cache_read_input_tokens??0)+Ne+S.tokensLimpiados,ae=T.triggerDelCliente!==
|
|
197
|
+
null&&ie>=T.triggerDelCliente;return {...S,atribuible:!ae}},A=l.headers["content-encoding"],I=Array.isArray(A)?A[0]:A,O=S=>{let y=Array.isArray(S)?S[0]:S;return y!==void 0&&/^[A-Za-z0-9._:/-]{1,128}$/.
|
|
198
|
+
test(y)?y:void 0},X=O(l.headers["x-claude-code-session-id"]),Z=O(l.headers["x-claude-code-agent-id"]),q=X!==void 0||Z!==void 0?{...X!==void 0?{sessionId:X}:{},...Z!==void 0?{agentId:Z}:{}}:void 0;try{
|
|
199
|
+
let S=[];for await(let f of l)S.push(f);let y=Buffer.concat(S);x=y,d=new URL(l.url??"/",e);let K=s!==null&&l.method==="POST"&&y.length>0,Ne=cn(f=>{let v=l.headers[f];return Array.isArray(v)?v.join(", "):
|
|
200
|
+
v}),ie=K?an("POST",d.pathname+d.search,y,Ne):"";if(K)if(s.tieneEnVuelo(ie)){let f=await s.esperar(ie);if(f){p.writeHead(f.status,f.headers);let v=m(d.pathname)??new G;for(let ue of f.chunks)p.write(ue),
|
|
201
|
+
v.observar(ue);p.end();let le=v.usage()??(d.pathname==="/v1/messages"?Xe(Buffer.concat(f.chunks)):null);g(w,y,le,!0,d.pathname,I,q);return}}else C=s.registrar(ie);let ae=l.headers.connection,Pn=new Set(
|
|
202
|
+
(Array.isArray(ae)?ae.join(","):ae??"").split(",").map(f=>f.trim().toLowerCase()).filter(f=>f!=="")),V={};for(let[f,v]of Object.entries(l.headers))f==="host"||f==="content-length"||v===void 0||Uo.has(
|
|
203
|
+
f)||Pn.has(f)||(V[f]=Array.isArray(v)?v.join(", "):v);let Dn={...V},L=y;if(c!==null&&t.dialesElision===!0&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("context_elision"))try{let f=St(y,I);
|
|
204
|
+
f!==null&&(L=f.cuerpo,T={nuestros:f.nuestros,triggerDelCliente:f.triggerDelCliente},V["anthropic-beta"]=Tt(V["anthropic-beta"]));}catch{}if(l.method==="POST"&&d.pathname==="/v1/messages"&&(I??"")==="")
|
|
205
|
+
try{let f=sn(rn(y.toString("utf8")));f&&P("gateway-busqueda-herramientas",f);}catch{}let bt=c!==null&&t.modeloHermano===!0&&l.method==="POST"&&(d.pathname==="/v1/messages"||d.pathname==="/v1/messages/c\
|
|
206
|
+
ount_tokens");if(bt&&!c.tiene("sibling_model_routing")&&P("gateway-hermano-sin-feature","[bvsala gateway] sibling model routing: the flag is set but your org's license does not include sibling_model_r\
|
|
207
|
+
outing; the request is forwarded unchanged (fail-closed)."),bt&&c.tiene("sibling_model_routing"))try{let v=Nt(L,L===y?I:"identity",{betas:V["anthropic-beta"]});v!==null&&(L=v.cuerpo,$=v.cambio);}catch{}
|
|
208
|
+
let he=!1;if(c!==null&&Je(t.esfuerzo)&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("per_turn_effort"))try{let f=L===y?I:"identity",v=Ot(L,t.esfuerzo,f);v!==null&&(L=v.cuerpo,V["anthropic-b\
|
|
209
|
+
eta"]=Pt(V["anthropic-beta"]),he=!0);}catch{}let ge=null;if(c!==null&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("cache_ttl_tuning"))try{let v=W(L,L===y?I:"identity");if(v!==null){let le=At(
|
|
210
|
+
v),ue=`${q?.sessionId??"sin-sesion"}:${le.system}`,kt=Date.now(),Ln=a.ultima(ue,kt);if(a.anotar(ue,le,kt),Ct({fraccionNueva:_t(Ln,le)})){let be=Rt(v);be!==null&&(L=be.cuerpo,ge={rebajadas:be.rebajadas,
|
|
211
|
+
yaEran5m:be.yaEran5m});}}}catch{}let _;try{_=await n(d,{method:l.method,headers:V,body:["GET","HEAD"].includes(l.method??"")?void 0:L,redirect:"manual",signal:E.signal});}catch(f){C?.abortar(),p.headersSent||
|
|
212
|
+
(p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: upstream unreachable",detail:ne(f)}))),o(`[gateway] ${l.method} ${d.pathname} \u2192 upstream unreachable:\
|
|
213
|
+
${ne(f)}`);return}if(($!==null||he)&&_.status===400&&!E.signal.aborted){let f="";try{f=(await _.text()).slice(0,300).replace(/\s+/g," ");}catch{}o(`[gateway] the upstream rejected (400) the rewritten \
|
|
214
|
+
request${$?` to ${$.servido}`:""}${he?" (per-turn effort)":""}; forwarding it unchanged \xB7 ${f}`),$=null,he=!1,ge=null,T=null;try{_=await n(d,{method:l.method,headers:Dn,body:y,redirect:"manual",signal:E.
|
|
215
|
+
signal});}catch(v){C?.abortar(),p.headersSent||(p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: upstream unreachable",detail:ne(v)}))),o(`[gateway] ${l.
|
|
216
|
+
method} ${d.pathname} \u2192 upstream unreachable on retry: ${ne(v)}`);return}}let je=Object.fromEntries(_.headers.entries());for(let f of zo)delete je[f];p.writeHead(_.status,je);let vt=_.headers.get(
|
|
217
|
+
"content-type")??"",Be=vt.includes("text/event-stream")||vt===""&&_.ok&&m(d.pathname)!==null;b=Be?m(d.pathname):null;let ce=!Be&&_.ok&&r!==void 0&&l.method==="POST"&&d.pathname==="/v1/messages"&&(_.headers.
|
|
218
|
+
get("content-type")??"").includes("application/json")?[]:null,yt=0;if(_.body)for await(let f of _.body){let v=Buffer.from(f);p.write(v),b?.observar(v),ce&&(yt+=v.length,yt>tt?ce=null:ce.push(v)),C?.capturar(
|
|
219
|
+
v);}if(p.end(),C?.terminar(_.status,je),b&&_.ok){H=!0;let f=b.usage(),v=se(b instanceof G?b.elision():null,f);g(w,y,f,!1,d.pathname,I,q,v,ge,D,$),r||o(`[gateway] ${l.method} ${d.pathname} \u2192 ${_.status}\
|
|
220
|
+
(stream) \xB7 not metered: no BIVELIO_SERVICE_KEY`);}else if(ce){let f=Buffer.concat(ce),v=Xe(f);g(w,y,v,!1,d.pathname,I,q,se(Vt(f),v),ge,D,$);}else o(`[gateway] ${l.method} ${d.pathname} \u2192 ${_.status}${Be?
|
|
221
|
+
" (stream)":""}`);}catch(S){if(C?.abortar(),E.signal.aborted){let y=!H&&b?b.usageFinal():null;if(y){o(`[gateway] ${l.method} ${d.pathname} \u2192 client closed after the final usage; metered anyway`);let K=se(
|
|
222
|
+
b instanceof G?b.elision():null,y);g(w,x,y,false,d.pathname,I,q,K,void 0,D,$);}else o(`[gateway] ${l.method} ${d.pathname} \u2192 client aborted; upstream cancelled`);}else if(o(`[gateway] ${l.method} ${d.
|
|
223
|
+
pathname} \u2192 error while serving: ${ne(S)}`),!p.headersSent){p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: stream interrupted",detail:ne(S)}));return}
|
|
224
|
+
p.writableEnded||p.end();}});u.on("upgrade",(l,p)=>{o(`[gateway] ${l.method} ${new URL(l.url??"/","http://x").pathname} \u2192 426 (WebSocket not supported; the client falls back to HTTP)`),p.once("err\
|
|
225
|
+
or",()=>{}),p.once("finish",()=>p.destroy()),p.end(`HTTP/1.1 426 Upgrade Required\r
|
|
220
226
|
connection: close\r
|
|
221
227
|
content-length: 0\r
|
|
222
228
|
\r
|
|
223
|
-
`);})
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
/
|
|
229
|
+
`);});let h=t.puerto??8402;try{await new Promise((l,p)=>{u.once("error",p),u.listen(h,"127.0.0.1",()=>l());});}catch(l){r?.stop();let p=l?.code;throw new nt(typeof p=="string"?p:"EUNKNOWN",h,l)}return {puerto:u.
|
|
230
|
+
address().port,cerrar:async()=>{await r?.stopAndDrain(),await new Promise(l=>u.close(()=>l()));}}}var ot=["claude","codex","goose","qwen"];function Re(t){return ot.includes(t??"")}function dn(t,e={}){let n=e.path??process.env.PATH??"",o=e.
|
|
231
|
+
existe??existsSync,r=null;for(let s of n.split(";").filter(i=>i.length>0)){for(let i of [".exe",".com"]){let a=join(s,t+i);if(o(a))return {tipo:"exe",ruta:a}}if(r===null)for(let i of [".cmd",".bat"]){let a=join(s,t+
|
|
232
|
+
i);if(o(a)){r=a;break}}}return r!==null?{tipo:"solo-shim",ruta:r}:{tipo:"no-encontrado"}}function pn(t={}){let n=(t.env??process.env).BVSALA_CODEX_AUTH?.toLowerCase();if(n==="api-key"||n==="apikey")return "\
|
|
233
|
+
api-key";if(n==="chatgpt")return "chatgpt";let o=null;try{o=t.leerAuth?t.leerAuth():null;}catch{o=null;}if(o===null)return "api-key";try{let r=JSON.parse(o);return typeof r.auth_mode=="string"?r.auth_mode.
|
|
234
|
+
toLowerCase().replace(/[^a-z]/g,"")==="apikey"?"api-key":"chatgpt":r.tokens!==void 0&&r.tokens!==null?"chatgpt":"api-key"}catch{return "api-key"}}var Fo={"api-key":{upstream:"https://api.openai.com",prefijo:"\
|
|
235
|
+
/v1"},chatgpt:{upstream:"https://chatgpt.com",prefijo:"/backend-api/codex"}};function rt(t,e,n,o={},r=process.env){let s=`http://127.0.0.1:${e}`;switch(t){case "claude":return {bin:"claude",args:n,env:{
|
|
229
236
|
ANTHROPIC_BASE_URL:s,ENABLE_TOOL_SEARCH:r.ENABLE_TOOL_SEARCH??"true"}};case "goose":return {bin:"goose",args:n,env:{ANTHROPIC_HOST:s}};case "qwen":return {bin:"qwen",args:n,env:{ANTHROPIC_BASE_URL:s},nota:'\
|
|
230
|
-
Qwen Code
|
|
231
|
-
"-c",`openai_base_url="${s}${
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
receta:({
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
`
|
|
264
|
-
|
|
265
|
-
|
|
237
|
+
Qwen Code only routes through here with `security.auth.selectedType: "anthropic"` in ~/.qwen/settings.json (see `bvsala doctor`).'};case "codex":{let i=o.modoCodex??"api-key",a=Fo[i];return {bin:"codex",
|
|
238
|
+
args:["-c",`openai_base_url="${s}${a.prefijo}"`,...n],env:{},upstream:a.upstream,nota:i==="chatgpt"?"ChatGPT auth detected: the gateway routes to chatgpt.com/backend-api/codex. Subscription traffic is\
|
|
239
|
+
metered (list-price equivalent) but not billed: there is no per-token invoice.":"API key auth: the gateway routes to api.openai.com/v1."}}}}var Ho=["gateway","doctor","skill"],mn=new Set(["help","--help","-h"]),fn=new Set(["version","--version","-v"]);function hn(t){let[e,...n]=t;return e===void 0||mn.has(e)?{tipo:"ayuda",salida:0}:fn.has(
|
|
240
|
+
e)?{tipo:"version"}:Re(e)?{tipo:"arrancar",comando:e,resto:n}:Ho.includes(e)?n.some(o=>mn.has(o))?{tipo:"ayuda",salida:0}:n.some(o=>fn.has(o))?{tipo:"version"}:{tipo:"arrancar",comando:e,resto:n}:{tipo:"\
|
|
241
|
+
ayuda",salida:1}}function gn(t,e,n){let o=t??{},r=typeof o.puerto=="number"?o.puerto:n;if(o.code==="EADDRINUSE")return `port ${r} is already in use (another gateway, or a terminal launched earlier?): u\
|
|
242
|
+
se --puerto ${r+1} or stop the other process`;if(o.code==="EACCES")return `port ${r} cannot be opened: permission denied (EACCES; ports below 1024 need privileges): use --puerto ${Math.max(1024,r)} or \
|
|
243
|
+
higher`;let s=t instanceof Error?t.message:String(t);return `could not start the gateway for \`bvsala ${e}\`: ${s}`}function qo(t){let e=Number.parseInt(t.slice(8,10),10);return Number.isFinite(e)&&e%2===0?"palanca":"control"}function bn(t){let e;try{e=JSON.parse(t());}catch{return null}if(typeof e!="object"||e===null)return null;let n=e,o=n.espia;return typeof o!="number"||!Number.isInteger(o)||o<1||o>65535?null:{activo:n.
|
|
244
|
+
activo!==false,espia:o}}function vn(t){let{enrolamiento:e,espiaVivo:n,upstreamEnv:o,elisionEnv:r,hoyUtcIso:s}=t;if(!e)return {linea:"",aviso:false};if(!e.activo)return {linea:"A/B plan: enrolment paused (acti\
|
|
245
|
+
vo:false)",aviso:false};if(!n)return {linea:`A/B plan: enrolled but the capture proxy is NOT listening on :${e.espia} \u2014 NOT chaining (today's traffic will not enter the corpus). Start it with \`pnpm anillo\
|
|
246
|
+
2:espia\`.`,aviso:true};let i=qo(s),a=`http://127.0.0.1:${e.espia}`,c=o!==void 0&&o!=="",m=r!==void 0&&r!=="",g=[];return g.push(`A/B plan: ${s} UTC \u2192 arm ${i.toUpperCase()}`),g.push(c?`upstream ke\
|
|
247
|
+
pt from the environment (${o})`:`chained to :${e.espia}`),m&&g.push(`elision flag kept from the environment (=${r})`),g.push("context elision dials OFF (2026-09-09): the arm no longer turns them on"),
|
|
248
|
+
g.push("assignment PER SESSION since 2026-09-10 (brazoDeSesion), not by day parity"),{...c?{}:{upstream:a},brazo:i,linea:g.join(", "),aviso:false}}var Go="check: the gateway log must show `[gateway] POST /v1/messages \u2026`; if you see `/v1/v1/\
|
|
249
|
+
messages`, there is one /v1 too many; if you see a bare `POST /messages`, one is missing.",Yo=[{id:"claude",nombre:"Claude Code",binario:"claude",configs:[],detectable:true,receta:({base:t})=>["use `bvs\
|
|
250
|
+
ala claude` \u2014 starts the gateway on an ephemeral port, launches `claude` pointed at it and shuts it down on exit.",`by hand: ANTHROPIC_BASE_URL=${t} claude (no /v1: the client appends /v1/messa\
|
|
251
|
+
ges)`]},{id:"codex",nombre:"Codex CLI",binario:"codex",configs:[join(".codex","config.toml"),join(".codex","auth.json")],detectable:true,receta:({base:t,rutas:e})=>["use `bvsala codex` \u2014 the base URL prefix d\
|
|
252
|
+
epends on the auth mode and the wrapper resolves it:",` API key \u2192 openai_base_url=${t}/v1`,` ChatGPT subscription \u2192 openai_base_url=${t}/backend-api/codex (must end exactly t\
|
|
253
|
+
here)`,`${e[1]}: only checked for existence; never read.`]},{id:"opencode",nombre:"opencode",binario:"opencode",configs:[join(".config","opencode","opencode.json")],detectable:true,receta:({baseV1:t,rutas:e})=>[
|
|
254
|
+
`mechanism: ${e[0]} \u2192 provider.anthropic.options.baseURL (the project's opencode.json overrides the global one). WITH /v1.`,"{",' "provider": {',' "anthropic": {',` "options": { "baseURL\
|
|
255
|
+
": "${t}" }`," }"," }","}",'alternative: "baseURL": "{env:BVSALA_BASE_URL}" and export the variable; pitfall: without the variable, baseURL is empty.']},{id:"goose",nombre:"Goose",binario:"goose",
|
|
256
|
+
configs:[join(".config","goose","config.yaml")],detectable:true,receta:({base:t,rutas:e})=>[`mechanism: ANTHROPIC_HOST environment variable (overrides ${e[0]}). NO /v1.`,`ANTHROPIC_HOST=${t} ANTHROPIC_API_\
|
|
257
|
+
KEY=\u2026 goose`]},{id:"crush",nombre:"Crush",binario:"crush",configs:[join(".config","crush","crushrc")],detectable:true,receta:({baseV1:t,rutas:e})=>[`mechanism: ${e[0]} (Bash); crush.json is deprecated\
|
|
258
|
+
. WITH /v1 and NO trailing slash (the Go SDK resolves relative paths: /v1/ \u2192 /v1/v1/messages).`,`provider add bvsala --type anthropic --base-url ${t} --api-key "$ANTHROPIC_API_KEY"`]},{id:"qwen",
|
|
259
|
+
nombre:"Qwen Code",binario:"qwen",configs:[join(".qwen","settings.json")],detectable:true,receta:({base:t,rutas:e})=>[`mechanism: ANTHROPIC_BASE_URL environment variable + ${e[0]} with selectedType = anthr\
|
|
260
|
+
opic. NO /v1 (the official TS SDK appends /v1/messages).`,`ANTHROPIC_BASE_URL=${t} ANTHROPIC_API_KEY=\u2026 qwen`,`check by hand that ${e[0]} has selectedType: anthropic \u2014 the doctor does not read the\
|
|
261
|
+
file.`]},{id:"droid",nombre:"Droid (Factory)",binario:"droid",configs:[join(".factory","settings.json")],detectable:true,receta:({base:t,rutas:e})=>[`mechanism: ${e[0]} \u2192 customModels[] with provider "ant\
|
|
262
|
+
hropic". NO /v1 [inferred: validate by hand against the gateway log].`,"{",' "customModels": ['," {",' "model": "<Anthropic model id>",',' "displayName": "<display name>",',' "provi\
|
|
263
|
+
der": "anthropic",',` "baseUrl": "${t}",`,' "apiKey": "<your Anthropic API key>"'," }"," ]","}"]},{id:"cline",nombre:"Cline",configs:[],detectable:false,receta:({baseV1:t})=>['mechanism: UI\
|
|
264
|
+
\u2192 Anthropic provider \u2192 tick "Use custom base URL". WITH /v1 (Cline defaults to https://api.anthropic.com/v1 and only normalizes the exact official URL).',`Base URL: ${t}`]},{id:"kilo",nombre:"\
|
|
265
|
+
Kilo Code",configs:[],detectable:false,receta:({baseV1:t})=>['mechanism: UI \u2192 custom provider, Provider API "Anthropic Messages". Try WITH /v1 first (Cline lineage) [inferred: validate by hand].',`B\
|
|
266
|
+
ase URL: ${t}`]}];function Qo(t,e){let n=t.split(delimiter).filter(r=>r.length>0),o=process.platform==="win32"?["",".exe",".cmd",".bat"]:[""];return r=>n.some(s=>o.some(i=>e(join(s,r+i))))}async function yn(t,e={}){
|
|
267
|
+
if(!Number.isInteger(t)||t<1||t>65535)throw new RangeError(`[bvsala doctor] invalid port: ${String(t)}`);let n=e.home??homedir(),o=e.existe??existsSync,r=e.enPath??Qo(e.path??process.env.PATH??"",o),s=`http://127.\
|
|
268
|
+
0.0.1:${t}`,i=`${s}/v1`,a=[],c=[`bvsala doctor \u2014 local gateway at ${s}`,"Print only: it writes no configuration. Detection by existence (binaries on the PATH and","user configuration files); the \
|
|
269
|
+
contents of a file are never read.",""];for(let m of Yo){let g=m.configs.map(p=>join(n,p)),u=m.binario!==void 0&&r(m.binario),h=g.filter(p=>o(p)),k=u||h.length>0;k&&a.push(m.id);let l;m.detectable?k?l=`d\
|
|
270
|
+
etected \u2014 ${[...u?[`binary \`${m.binario}\` on PATH`]:[],...h.map(w=>`config present: ${w}`)].join("; ")}`:l="not detected":l="no automatic detection (editor extension)",c.push(`\xBB ${m.id}: ${l}`),
|
|
271
|
+
c.push(` ${m.nombre}`);for(let p of m.receta({base:s,baseV1:i,rutas:g}))c.push(` ${p}`);c.push(` ${Go}`),c.push("");}return c.push("BYOK rule: the recipes for the Anthropic-compatible terminals (Goo\
|
|
272
|
+
se, Qwen Code, opencode, Crush, Droid, Cline,","Kilo) are for API keys. Never route the OAuth of a Claude Pro/Max subscription through the gateway from a","third-party harness \u2014 Anthropic forbids it. \
|
|
273
|
+
Claude Code and Codex on a subscription are routed and metered,","but not billed: there is no per-token invoice.","Per-terminal detail: docs/terminales/<terminal>.md"),{texto:c.join(`
|
|
274
|
+
`),detectados:a}}var kn="savings-mode";function xn(t,e){for(let n of t){let o=join(n,"skills",kn,"SKILL.md");if(e.existe(o))return o}return null}var wn="<!-- bvsala:savings-mode:inicio -->",Ie="\
|
|
275
|
+
<!-- bvsala:savings-mode:fin -->";function Xo(t){let e=t.replace(/^---\n[\s\S]*?\n---\n*/,"").trim();return `${wn}
|
|
276
|
+
${e}
|
|
266
277
|
|
|
267
278
|
_Always on (AGENTS.md has no activation step). Delete this block to turn it off; \`bvsala skill --codex --forzar\` restores it._
|
|
268
|
-
${
|
|
269
|
-
|
|
270
|
-
`),{ok:true,destino:r,mensaje:
|
|
279
|
+
${Ie}`}function Sn(t){let e=xn(t.origenes,t.fs);if(!e)return {ok:false,destino:"",mensaje:`[bvsala skill] cannot find the bundled SKILL.md (roots tried: ${t.origenes.join(", ")})`};let n=Xo(t.fs.leer(e)),
|
|
280
|
+
o=t.proyecto?t.cwd:join(t.home,".codex"),r=join(o,"AGENTS.md");if(!t.fs.existe(r))return t.fs.crearDir(o),t.fs.escribir(r,n+`
|
|
281
|
+
`),{ok:true,destino:r,mensaje:st(r,"installed")};let s=t.fs.leer(r),i=s.indexOf(wn),a=s.indexOf(Ie);return i===-1||a===-1||a<i?(t.fs.escribir(r,s.trimEnd()+`
|
|
271
282
|
|
|
272
283
|
`+n+`
|
|
273
|
-
`),{ok:true,destino:r,mensaje:
|
|
274
|
-
slice(0,
|
|
275
|
-
|
|
276
|
-
[bvsala skill] AGENTS.md
|
|
277
|
-
[bvsala skill]
|
|
278
|
-
mensaje:`[bvsala skill]
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
[bvsala skill]
|
|
282
|
-
[bvsala skill]
|
|
283
|
-
match(/\n/g)??[]).length;o+=
|
|
284
|
-
Math.ceil(
|
|
285
|
-
return this.modelToTokenizer.set(
|
|
286
|
-
get(o);if(r)return r}}return this.estimators.get(this.fallbackId)}count(
|
|
287
|
-
|
|
288
|
-
argv;function
|
|
284
|
+
`),{ok:true,destino:r,mensaje:st(r,"appended to the existing AGENTS.md")}):s.slice(i,a+Ie.length)===n?{ok:true,destino:r,mensaje:`[bvsala skill] already installed and up to date: ${r}`}:t.forzar?(t.fs.escribir(
|
|
285
|
+
r,s.slice(0,i)+n+s.slice(a+Ie.length)),{ok:true,destino:r,mensaje:st(r,"block updated")}):{ok:false,destino:r,mensaje:`[bvsala skill] the savings-mode block in ${r} has DIFFERENT content \u2014 not overwriting \
|
|
286
|
+
it; use --forzar to update it`}}function st(t,e){return `[bvsala skill] ${e}: ${t}
|
|
287
|
+
[bvsala skill] AGENTS.md is ALWAYS active in Codex (there is no activation step); delete the block to turn it off.
|
|
288
|
+
[bvsala skill] to measure its effect: work a few days with it and a few without, under \`bvsala codex\`, and compare in the dashboard.`}function En(t){let e=xn(t.origenes,t.fs);if(!e)return {ok:false,destino:"",
|
|
289
|
+
mensaje:`[bvsala skill] cannot find the bundled SKILL.md (roots tried: ${t.origenes.join(", ")})`};let n=t.proyecto?join(t.cwd,".claude","skills"):join(t.home,".claude","skills"),o=join(n,kn),r=join(o,"SKILL.md"),
|
|
290
|
+
s=t.fs.leer(e);if(t.fs.existe(r)&&!t.forzar){let i=t.fs.leer(r)===s;return {ok:i,destino:r,mensaje:i?`[bvsala skill] already installed and up to date: ${r}`:`[bvsala skill] ${r} already exists with DIF\
|
|
291
|
+
FERENT content \u2014 not overwriting it; use --forzar to overwrite`}}return t.fs.crearDir(o),t.fs.escribir(r,s),{ok:true,destino:r,mensaje:`[bvsala skill] installed: ${r}
|
|
292
|
+
[bvsala skill] start (or restart) Claude Code and ask for "savings mode" \u2014 or let it activate on its own when you mention limits.
|
|
293
|
+
[bvsala skill] to measure its real effect: work a few days with it and a few without, under \`bvsala claude\`, and compare in the dashboard.`}}var it=class{id;exact=false;constructor(e="heuristic"){this.id=e;}count(e){if(e.length===0)return 0;let n=e.match(/[A-Za-z]+|\d+|\s+|[^\sA-Za-z\d]/g)??[],o=0;for(let r of n){let s=r[0];if(/\s/.test(s)){let i=(r.
|
|
294
|
+
match(/\n/g)??[]).length;o+=i+Math.floor(Math.max(0,r.length-1)/2);}else /[A-Za-z]/.test(s)?o+=Math.max(1,Math.ceil(r.length/4)):/\d/.test(s)?o+=Math.max(1,Math.ceil(r.length/2)):o+=1;}return Math.max(o,
|
|
295
|
+
Math.ceil(e.length/4))}},at=new it;var ct=class{estimators=new Map;modelToTokenizer=new Map;fallbackId;constructor(e=at){this.fallbackId=e.id,this.estimators.set(e.id,e);}register(e){return this.estimators.set(e.id,e),this}mapModel(e,n){
|
|
296
|
+
return this.modelToTokenizer.set(e,n),this}resolve(e){let n=e.tokenizer?this.estimators.get(e.tokenizer):void 0;if(n)return n;if(e.model){let o=this.modelToTokenizer.get(e.model);if(o){let r=this.estimators.
|
|
297
|
+
get(o);if(r)return r}}return this.estimators.get(this.fallbackId)}count(e,n){return this.resolve(n).count(e)}};new ct;new TextEncoder;new TextDecoder;new TextDecoder;var hr={"gpt-4o-mini":{deltaTokens:-16,salidaDelBrazoA:72,contrato:"triage (6 fields, long enums)",pares:8},"gpt-4o":{deltaTokens:3,salidaDelBrazoA:75,contrato:"triage (6 fields, long enums)",pares:8}};
|
|
298
|
+
Ye(hr);function Sr(t,e=300){return new Promise(n=>{let o=connect({port:t,host:"127.0.0.1"}),r=s=>{o.destroy(),n(s);};o.setTimeout(e,()=>r(false)),o.once("connect",()=>r(true)),o.once("error",()=>r(false));})}var Me=hn(process.
|
|
299
|
+
argv.slice(2));function Er(){console.log(`bvsala ${J} \u2014 BiVelio Savings Layer
|
|
289
300
|
|
|
290
|
-
|
|
291
|
-
gateway [--puerto N]
|
|
292
|
-
|
|
293
|
-
claude [args\u2026]
|
|
294
|
-
codex [args\u2026]
|
|
295
|
-
auth
|
|
296
|
-
goose [args\u2026]
|
|
297
|
-
qwen [args\u2026]
|
|
298
|
-
doctor [--puerto N]
|
|
301
|
+
Commands:
|
|
302
|
+
gateway [--puerto N] Local metering proxy (default port 8402) for terminals that
|
|
303
|
+
honour a base URL: Claude Code, opencode, Goose, Crush\u2026
|
|
304
|
+
claude [args\u2026] Run Claude Code through the gateway. No configuration needed.
|
|
305
|
+
codex [args\u2026] Run Codex CLI through the gateway (API key: api.openai.com;
|
|
306
|
+
ChatGPT auth: chatgpt.com/backend-api/codex \u2014 metered, not billed).
|
|
307
|
+
goose [args\u2026] Run Goose (ANTHROPIC_HOST) through the gateway.
|
|
308
|
+
qwen [args\u2026] Run Qwen Code (ANTHROPIC_BASE_URL) through the gateway.
|
|
309
|
+
doctor [--puerto N] Detect installed terminals and print the exact configuration for each.
|
|
299
310
|
skill [--codex] [--proyecto] [--forzar]
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
311
|
+
Install the "savings-mode" skill for Claude Code (~/.claude/skills;
|
|
312
|
+
with --proyecto, .claude/skills in the current directory): terse
|
|
313
|
+
output plus surgical tool use to stretch subscription limits.
|
|
314
|
+
With --codex, the same discipline as a marked block in AGENTS.md
|
|
315
|
+
(~/.codex; with --proyecto, ./AGENTS.md) \u2014 Codex has no skills and
|
|
316
|
+
AGENTS.md is always active. Its effect is measured by the gateway,
|
|
317
|
+
not promised.
|
|
318
|
+
--version | -v Print the version only.
|
|
319
|
+
--help | -h This help; also accepted after gateway, doctor or skill
|
|
320
|
+
(\`bvsala gateway --help\` starts nothing).
|
|
307
321
|
|
|
308
|
-
|
|
309
|
-
BIVELIO_SERVICE_KEY
|
|
310
|
-
BVSALA_SEAT_ID
|
|
311
|
-
BVSALA_GATEWAY_UPSTREAM
|
|
312
|
-
BVSALA_GATEWAY_COALESCE =1
|
|
313
|
-
BVSALA_GATEWAY_SHADOW =1
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
BVSALA_GATEWAY_ELISION =0
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
\xB7 context_elision \u2014
|
|
320
|
-
|
|
321
|
-
\xB7 cache_ttl_tuning \u2014
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
expl\xEDcita, =1 o =0, y no depender\xE1s de la versi\xF3n instalada.)
|
|
322
|
+
Environment:
|
|
323
|
+
BIVELIO_SERVICE_KEY Service key (bvk_\u2026): enables metering and seat identity.
|
|
324
|
+
BVSALA_SEAT_ID Stable seat identity per replica (optional).
|
|
325
|
+
BVSALA_GATEWAY_UPSTREAM Alternative upstream (default: the terminal's own).
|
|
326
|
+
BVSALA_GATEWAY_COALESCE =1 enables duplicate-request coalescing (experimental).
|
|
327
|
+
BVSALA_GATEWAY_SHADOW =1 enables the shadow lane for context elision (experimental):
|
|
328
|
+
computes what the elision policy would have saved WITHOUT touching
|
|
329
|
+
traffic and reports it separately (never counts toward the fee).
|
|
330
|
+
BVSALA_GATEWAY_ELISION =0 turns OFF the provider-side levers, which are armed by default.
|
|
331
|
+
Each one only takes effect if your organization's license carries
|
|
332
|
+
its feature (checked fail-closed):
|
|
333
|
+
\xB7 context_elision \u2014 adjusts ONLY the context_management block
|
|
334
|
+
of each /v1/messages; the provider does the trimming.
|
|
335
|
+
\xB7 cache_ttl_tuning \u2014 lowers the cache-write TTL from 1h (2x the
|
|
336
|
+
input price) to 5m (1.25x) while the session stays warm. It does
|
|
337
|
+
NOT touch a single token the model reads: it changes the rate,
|
|
338
|
+
not the content. In measured traffic, 99.1% of turns chain within
|
|
339
|
+
5 minutes, and that surcharge was 8.0% of the bill.
|
|
340
|
+
When in doubt, the request travels untouched. (Up to 0.4.13 the flag
|
|
341
|
+
was inverted: "=1 arms". In an A/B always pass it explicitly, =1 or
|
|
342
|
+
=0, so you do not depend on the installed version.)
|
|
330
343
|
BVSALA_GATEWAY_MODELO_HERMANO
|
|
331
|
-
=1
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
344
|
+
=1 serves claude-fable-5 with claude-fable-5-1 (sibling model routing):
|
|
345
|
+
same input, output and cache-write rates; cache READS drop from
|
|
346
|
+
0.001 to 0.00025 per 1K tokens (catalog). The saving is the difference
|
|
347
|
+
of two prices over the same usage: measured without an A/B, and it
|
|
348
|
+
counts toward the measured headline in the dashboard.
|
|
349
|
+
Only if the license carries sibling_model_routing (fail-closed).
|
|
350
|
+
It CHANGES the model you see in the response (/cost, /status), and
|
|
351
|
+
turning it on mid-thread pays ONE cold write of the prefix (each
|
|
352
|
+
model has its own cache): start it together with the terminal.
|
|
353
|
+
Off = unset the variable or =0.
|
|
341
354
|
BVSALA_GATEWAY_ESFUERZO
|
|
342
|
-
=low|medium|high|xhigh|max
|
|
343
|
-
a
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
BVSALA_CODEX_AUTH api-key | chatgpt \u2014
|
|
351
|
-
BVSALA_GATEWAY_LOG =1
|
|
352
|
-
|
|
353
|
-
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 (Claude Code,
|
|
355
|
+
=low|medium|high|xhigh|max sets per-turn effort (experimental):
|
|
356
|
+
appends a message with output_config.effort to each /v1/messages
|
|
357
|
+
without touching the root (does not reset the prefix cache). Only if
|
|
358
|
+
the license carries per_turn_effort (fail-closed) and only with
|
|
359
|
+
claude-fable-5-1, claude-mythos-5-1 and claude-opus-5; with any other
|
|
360
|
+
model the request travels untouched. It is the only lever that can
|
|
361
|
+
affect response quality: OFF by default and not yet measured against
|
|
362
|
+
the quality gate.
|
|
363
|
+
BVSALA_CODEX_AUTH api-key | chatgpt \u2014 forces the Codex auth mode.
|
|
364
|
+
BVSALA_GATEWAY_LOG =1 shows the per-request log inside the wrappers too (silenced by
|
|
365
|
+
default so it does not overwrite the TUI; use the dashboard instead).
|
|
366
|
+
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 (Claude Code, API key only) your models in /model.
|
|
354
367
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
puerto"),
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
elisionEnv:process.env.BVSALA_GATEWAY_ELISION,hoyUtcIso:new Date().toISOString().slice(0,10)}),
|
|
364
|
-
{puerto:
|
|
365
|
-
env.BVSALA_GATEWAY_COALESCE==="1"?{coalescer
|
|
366
|
-
|
|
367
|
-
env.BVSALA_METERING_URL}:{}})
|
|
368
|
-
]
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
N?.upstream&&process.env.BVSALA_GATEWAY_UPSTREAM!==N.upstream&&console.error(`[bvsala ${
|
|
372
|
-
upstream}).
|
|
373
|
-
|
|
374
|
-
[bvsala ${
|
|
375
|
-
npm (${o.ruta})
|
|
376
|
-
|
|
377
|
-
o)}.
|
|
378
|
-
|
|
379
|
-
SIGINT",()=>{
|
|
368
|
+
The gateway never blocks traffic: without a key, or with ingestion down, it forwards and warns.
|
|
369
|
+
Your prompts never leave your machine \u2014 only counts and cost. More: savings.bivelio.com`);}Me.tipo==="ayuda"&&(Er(),process.exit(Me.salida));Me.tipo==="version"&&(console.log(J),process.exit(0));var{
|
|
370
|
+
comando:R,resto:U}=Me,$e=Re(R);if(R==="skill"){let t=dirname(fileURLToPath(import.meta.url)),e={origenes:[join(t,"..",".."),join(t,"..")],home:homedir(),cwd:process.cwd(),proyecto:U.includes("--proyecto"),forzar:U.includes("-\
|
|
371
|
+
-forzar"),fs:{existe:existsSync,leer:o=>readFileSync(o,"utf8"),escribir:(o,r)=>writeFileSync(o,r),crearDir:o=>mkdirSync(o,{recursive:true})}},n=U.includes("--codex")?Sn(e):En(e);console.log(n.mensaje),process.exit(n.ok?0:1);}var mt=U.indexOf(
|
|
372
|
+
"--puerto"),Q=$e?0:mt>=0?Number(U[mt+1]):8402;(!Number.isInteger(Q)||(R==="doctor"?Q<1:Q<0)||Q>65535)&&(console.error(`[bvsala ${R}] invalid port: ${U[mt+1]}`),process.exit(1));if(R==="doctor"){U.includes(
|
|
373
|
+
"--write")&&(console.error("[bvsala doctor] --write does not exist yet: the doctor only prints; copy the block by hand."),process.exit(1));let t=await yn(Q);console.log(t.texto),process.exit(0);}var N=$e?
|
|
374
|
+
rt(R,0,U,{modoCodex:R==="codex"?pn({leerAuth:()=>readFileSync(join(homedir(),".codex","auth.json"),"utf8")}):void 0}):null,re=process.env.BIVELIO_SERVICE_KEY,Tr=$e&&process.env.BVSALA_GATEWAY_LOG!=="1",pt=N?.upstream?
|
|
375
|
+
null:bn(()=>readFileSync(process.env.BVSALA_ANILLO2_FICHERO??join(homedir(),".bvsala","anillo2.json"),"utf8")),fe=vn({enrolamiento:pt,espiaVivo:pt!==null&&await Sr(pt.espia),upstreamEnv:process.env.BVSALA_GATEWAY_UPSTREAM,
|
|
376
|
+
elisionEnv:process.env.BVSALA_GATEWAY_ELISION,hoyUtcIso:new Date().toISOString().slice(0,10)}),ft=process.env.BVSALA_GATEWAY_UPSTREAM??fe.upstream,De=process.env.BVSALA_GATEWAY_ELISION??fe.elision,F;try{
|
|
377
|
+
F=await ln({puerto:Q,...Tr?{log:()=>{}}:{},...re?{serviceAccountKey:re}:{},...process.env.BVSALA_SEAT_ID?{seatId:process.env.BVSALA_SEAT_ID}:{},...ft?{upstream:ft}:N?.upstream?{upstream:N.upstream}:{},
|
|
378
|
+
...process.env.BVSALA_GATEWAY_COALESCE==="1"?{coalescer:!0}:{},...process.env.BVSALA_GATEWAY_SHADOW==="1"?{sombra:!0}:{},elision:De!=="0",...process.env.BVSALA_GATEWAY_DIALES_ELISION==="1"?{dialesElision:!0}:
|
|
379
|
+
{},...process.env.BVSALA_GATEWAY_MODELO_HERMANO==="1"?{modeloHermano:!0}:{},...process.env.BVSALA_GATEWAY_ESFUERZO?{esfuerzo:process.env.BVSALA_GATEWAY_ESFUERZO}:{},...process.env.BVSALA_METERING_URL?
|
|
380
|
+
{meteringUrl:process.env.BVSALA_METERING_URL}:{}});}catch(t){console.error(`[bvsala ${R}] ${gn(t,R,Q)}`),process.exit(1);}var Ar=ft??N?.upstream??"https://api.anthropic.com";console.log(`[bvsala gateway\
|
|
381
|
+
] v${J} listening on http://127.0.0.1:${F.puerto} \u2192 ${Ar}`);console.log(`[bvsala gateway] metering: ${re?"ON (service key present)":"OFF (BIVELIO_SERVICE_KEY not set)"}`);{let t=Et(De,re!==void 0,
|
|
382
|
+
process.env.BVSALA_GATEWAY_DIALES_ELISION==="1");(t.aviso?console.error:console.log)(`[bvsala gateway] ${t.linea}`);let e=jt(process.env.BVSALA_GATEWAY_MODELO_HERMANO,re!==void 0,De!=="0");(e.aviso?console.
|
|
383
|
+
error:console.log)(`[bvsala gateway] ${e.linea}`);let n=Dt(process.env.BVSALA_GATEWAY_ESFUERZO,re!==void 0,De!=="0");(n.aviso?console.error:console.log)(`[bvsala gateway] ${n.linea}`),fe.linea&&(fe.aviso?
|
|
384
|
+
console.error:console.log)(`[bvsala gateway] ${fe.linea}`);}process.env.BVSALA_GATEWAY_UPSTREAM&&N?.upstream&&process.env.BVSALA_GATEWAY_UPSTREAM!==N.upstream&&console.error(`[bvsala ${R}] WARNING: BVS\
|
|
385
|
+
ALA_GATEWAY_UPSTREAM=${process.env.BVSALA_GATEWAY_UPSTREAM} overrides this terminal's upstream (${N.upstream}). If that is not what you want, unset the variable.`);R==="codex"&&!Kt()&&console.error(`[\
|
|
386
|
+
bvsala codex] WARNING: this Node (${process.version}) cannot decompress zstd and Codex sends its body in zstd: traffic passes through but is NOT metered. Use Node >= 22.15.`);if(N&&$e){let t=rt(R,F.puerto,
|
|
387
|
+
U,{...N.upstream?{modoCodex:N.upstream.includes("chatgpt.com")?"chatgpt":"api-key"}:{}});t.nota&&console.log(`[bvsala ${R}] ${t.nota}`);let e=t.bin;if(process.platform==="win32"){let o=dn(t.bin);o.tipo===
|
|
388
|
+
"exe"?e=o.ruta:o.tipo==="solo-shim"&&(console.error(`[bvsala ${R}] \`${t.bin}\` is installed as an npm shim (${o.ruta}) and Windows cannot launch it safely with arguments (CVE-2024-27980). Install it \
|
|
389
|
+
with its native installer (which leaves a ${t.bin}.exe on the PATH) or use the manual recipe in docs/terminales/.`),await F.cerrar(),process.exit(127));}let n=spawn(e,t.args,{stdio:"inherit",env:{...process.
|
|
390
|
+
env,...t.env}});n.on("error",o=>{console.error(`[bvsala ${R}] could not launch \`${t.bin}\`: ${String(o)}. Is it installed and on the PATH?`),F.cerrar().then(()=>process.exit(127));}),n.on("exit",(o,r)=>{
|
|
391
|
+
F.cerrar().then(()=>process.exit(o??(r?1:0)));});}else console.log(`[bvsala gateway] point your terminal at it (or use \`bvsala ${ot.join("|")}\`): ANTHROPIC_BASE_URL=http://127.0.0.1:${F.puerto} claud\
|
|
392
|
+
e \xB7 bvsala doctor for the others`);var On=async()=>{await F.cerrar(),process.exit(0);};process.on("SIGINT",()=>{On();});process.on("SIGTERM",()=>{On();});
|