caplyr 0.2.0 → 0.2.2
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/index.js +36 -3
- package/dist/index.mjs +36 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -124,12 +124,30 @@ var Heartbeat = class {
|
|
|
124
124
|
};
|
|
125
125
|
/** Current protection status */
|
|
126
126
|
this.status = "ACTIVE";
|
|
127
|
-
|
|
127
|
+
/** Local budget limits set via config (not from server) */
|
|
128
|
+
this.localDailyLimit = null;
|
|
129
|
+
this.localMonthlyLimit = null;
|
|
130
|
+
this.endpoint = config.endpoint ?? "https://caplyr.com";
|
|
128
131
|
this.apiKey = config.apiKey;
|
|
129
132
|
this.interval = config.heartbeatInterval ?? 6e4;
|
|
130
133
|
this.onStatusChange = config.onStatusChange;
|
|
131
134
|
this.onError = config.onError;
|
|
132
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Apply local budget limits from config.
|
|
138
|
+
* These act as client-side enforcement even if the server
|
|
139
|
+
* doesn't return limits (e.g. during heartbeat failures).
|
|
140
|
+
*/
|
|
141
|
+
applyLocalLimits(budget) {
|
|
142
|
+
if (budget.daily !== void 0) {
|
|
143
|
+
this.localDailyLimit = budget.daily;
|
|
144
|
+
this.budgetStatus.daily_limit = budget.daily;
|
|
145
|
+
}
|
|
146
|
+
if (budget.monthly !== void 0) {
|
|
147
|
+
this.localMonthlyLimit = budget.monthly;
|
|
148
|
+
this.budgetStatus.monthly_limit = budget.monthly;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
133
151
|
/**
|
|
134
152
|
* Start the heartbeat loop.
|
|
135
153
|
* Immediately sends first heartbeat, then repeats on interval.
|
|
@@ -156,7 +174,17 @@ var Heartbeat = class {
|
|
|
156
174
|
throw new Error(`Heartbeat failed: ${res.status}`);
|
|
157
175
|
}
|
|
158
176
|
const data = await res.json();
|
|
159
|
-
this.budgetStatus
|
|
177
|
+
const localDailyUsed = this.budgetStatus.daily_used;
|
|
178
|
+
const localMonthlyUsed = this.budgetStatus.monthly_used;
|
|
179
|
+
this.budgetStatus = {
|
|
180
|
+
...data,
|
|
181
|
+
// Use whichever spend is higher — server or local tracking
|
|
182
|
+
daily_used: Math.max(data.daily_used ?? 0, localDailyUsed),
|
|
183
|
+
monthly_used: Math.max(data.monthly_used ?? 0, localMonthlyUsed),
|
|
184
|
+
// Preserve local limits if server doesn't provide them
|
|
185
|
+
daily_limit: data.daily_limit ?? this.localDailyLimit,
|
|
186
|
+
monthly_limit: data.monthly_limit ?? this.localMonthlyLimit
|
|
187
|
+
};
|
|
160
188
|
this.consecutiveFailures = 0;
|
|
161
189
|
const newStatus = data.kill_switch_active ? "OFF" : data.status;
|
|
162
190
|
if (newStatus !== this.status) {
|
|
@@ -665,8 +693,9 @@ function protect(client, config) {
|
|
|
665
693
|
"Caplyr: apiKey is required. Get yours at https://app.caplyr.com"
|
|
666
694
|
);
|
|
667
695
|
}
|
|
696
|
+
const userWantsProtect = config.budget && !config.mode;
|
|
668
697
|
const resolvedConfig = {
|
|
669
|
-
mode:
|
|
698
|
+
mode: userWantsProtect ? "cost_protect" : "alert_only",
|
|
670
699
|
downgradeThreshold: 0.8,
|
|
671
700
|
batchSize: 10,
|
|
672
701
|
flushInterval: 3e4,
|
|
@@ -682,6 +711,10 @@ function protect(client, config) {
|
|
|
682
711
|
instances.set(resolvedConfig.apiKey, shared);
|
|
683
712
|
}
|
|
684
713
|
const { shipper, heartbeat } = shared;
|
|
714
|
+
if (resolvedConfig.budget) {
|
|
715
|
+
const budgetConfig = typeof resolvedConfig.budget === "number" ? { monthly: resolvedConfig.budget } : resolvedConfig.budget;
|
|
716
|
+
heartbeat.applyLocalLimits(budgetConfig);
|
|
717
|
+
}
|
|
685
718
|
const provider = detectProvider(client);
|
|
686
719
|
switch (provider) {
|
|
687
720
|
case "anthropic":
|
package/dist/index.mjs
CHANGED
|
@@ -91,12 +91,30 @@ var Heartbeat = class {
|
|
|
91
91
|
};
|
|
92
92
|
/** Current protection status */
|
|
93
93
|
this.status = "ACTIVE";
|
|
94
|
-
|
|
94
|
+
/** Local budget limits set via config (not from server) */
|
|
95
|
+
this.localDailyLimit = null;
|
|
96
|
+
this.localMonthlyLimit = null;
|
|
97
|
+
this.endpoint = config.endpoint ?? "https://caplyr.com";
|
|
95
98
|
this.apiKey = config.apiKey;
|
|
96
99
|
this.interval = config.heartbeatInterval ?? 6e4;
|
|
97
100
|
this.onStatusChange = config.onStatusChange;
|
|
98
101
|
this.onError = config.onError;
|
|
99
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Apply local budget limits from config.
|
|
105
|
+
* These act as client-side enforcement even if the server
|
|
106
|
+
* doesn't return limits (e.g. during heartbeat failures).
|
|
107
|
+
*/
|
|
108
|
+
applyLocalLimits(budget) {
|
|
109
|
+
if (budget.daily !== void 0) {
|
|
110
|
+
this.localDailyLimit = budget.daily;
|
|
111
|
+
this.budgetStatus.daily_limit = budget.daily;
|
|
112
|
+
}
|
|
113
|
+
if (budget.monthly !== void 0) {
|
|
114
|
+
this.localMonthlyLimit = budget.monthly;
|
|
115
|
+
this.budgetStatus.monthly_limit = budget.monthly;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
100
118
|
/**
|
|
101
119
|
* Start the heartbeat loop.
|
|
102
120
|
* Immediately sends first heartbeat, then repeats on interval.
|
|
@@ -123,7 +141,17 @@ var Heartbeat = class {
|
|
|
123
141
|
throw new Error(`Heartbeat failed: ${res.status}`);
|
|
124
142
|
}
|
|
125
143
|
const data = await res.json();
|
|
126
|
-
this.budgetStatus
|
|
144
|
+
const localDailyUsed = this.budgetStatus.daily_used;
|
|
145
|
+
const localMonthlyUsed = this.budgetStatus.monthly_used;
|
|
146
|
+
this.budgetStatus = {
|
|
147
|
+
...data,
|
|
148
|
+
// Use whichever spend is higher — server or local tracking
|
|
149
|
+
daily_used: Math.max(data.daily_used ?? 0, localDailyUsed),
|
|
150
|
+
monthly_used: Math.max(data.monthly_used ?? 0, localMonthlyUsed),
|
|
151
|
+
// Preserve local limits if server doesn't provide them
|
|
152
|
+
daily_limit: data.daily_limit ?? this.localDailyLimit,
|
|
153
|
+
monthly_limit: data.monthly_limit ?? this.localMonthlyLimit
|
|
154
|
+
};
|
|
127
155
|
this.consecutiveFailures = 0;
|
|
128
156
|
const newStatus = data.kill_switch_active ? "OFF" : data.status;
|
|
129
157
|
if (newStatus !== this.status) {
|
|
@@ -632,8 +660,9 @@ function protect(client, config) {
|
|
|
632
660
|
"Caplyr: apiKey is required. Get yours at https://app.caplyr.com"
|
|
633
661
|
);
|
|
634
662
|
}
|
|
663
|
+
const userWantsProtect = config.budget && !config.mode;
|
|
635
664
|
const resolvedConfig = {
|
|
636
|
-
mode:
|
|
665
|
+
mode: userWantsProtect ? "cost_protect" : "alert_only",
|
|
637
666
|
downgradeThreshold: 0.8,
|
|
638
667
|
batchSize: 10,
|
|
639
668
|
flushInterval: 3e4,
|
|
@@ -649,6 +678,10 @@ function protect(client, config) {
|
|
|
649
678
|
instances.set(resolvedConfig.apiKey, shared);
|
|
650
679
|
}
|
|
651
680
|
const { shipper, heartbeat } = shared;
|
|
681
|
+
if (resolvedConfig.budget) {
|
|
682
|
+
const budgetConfig = typeof resolvedConfig.budget === "number" ? { monthly: resolvedConfig.budget } : resolvedConfig.budget;
|
|
683
|
+
heartbeat.applyLocalLimits(budgetConfig);
|
|
684
|
+
}
|
|
652
685
|
const provider = detectProvider(client);
|
|
653
686
|
switch (provider) {
|
|
654
687
|
case "anthropic":
|