caplyr 0.2.1 → 0.2.3

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 CHANGED
@@ -124,6 +124,9 @@ var Heartbeat = class {
124
124
  };
125
125
  /** Current protection status */
126
126
  this.status = "ACTIVE";
127
+ /** Local budget limits set via config (not from server) */
128
+ this.localDailyLimit = null;
129
+ this.localMonthlyLimit = null;
127
130
  this.endpoint = config.endpoint ?? "https://caplyr.com";
128
131
  this.apiKey = config.apiKey;
129
132
  this.interval = config.heartbeatInterval ?? 6e4;
@@ -137,9 +140,11 @@ var Heartbeat = class {
137
140
  */
138
141
  applyLocalLimits(budget) {
139
142
  if (budget.daily !== void 0) {
143
+ this.localDailyLimit = budget.daily;
140
144
  this.budgetStatus.daily_limit = budget.daily;
141
145
  }
142
146
  if (budget.monthly !== void 0) {
147
+ this.localMonthlyLimit = budget.monthly;
143
148
  this.budgetStatus.monthly_limit = budget.monthly;
144
149
  }
145
150
  }
@@ -169,7 +174,21 @@ var Heartbeat = class {
169
174
  throw new Error(`Heartbeat failed: ${res.status}`);
170
175
  }
171
176
  const data = await res.json();
172
- this.budgetStatus = data;
177
+ const localDailyUsed = this.budgetStatus.daily_used;
178
+ const localMonthlyUsed = this.budgetStatus.monthly_used;
179
+ const serverDailyUsed = Number(data.daily_used) || 0;
180
+ const serverMonthlyUsed = Number(data.monthly_used) || 0;
181
+ const serverDailyLimit = data.daily_limit != null ? Number(data.daily_limit) : null;
182
+ const serverMonthlyLimit = data.monthly_limit != null ? Number(data.monthly_limit) : null;
183
+ this.budgetStatus = {
184
+ ...data,
185
+ // Use whichever spend is higher — server or local tracking
186
+ daily_used: Math.max(serverDailyUsed, localDailyUsed),
187
+ monthly_used: Math.max(serverMonthlyUsed, localMonthlyUsed),
188
+ // Use the stricter (lower) limit — local config takes priority if lower
189
+ daily_limit: this.pickStricterLimit(serverDailyLimit, this.localDailyLimit),
190
+ monthly_limit: this.pickStricterLimit(serverMonthlyLimit, this.localMonthlyLimit)
191
+ };
173
192
  this.consecutiveFailures = 0;
174
193
  const newStatus = data.kill_switch_active ? "OFF" : data.status;
175
194
  if (newStatus !== this.status) {
@@ -185,6 +204,22 @@ var Heartbeat = class {
185
204
  }
186
205
  }
187
206
  }
207
+ /**
208
+ * Pick the stricter (lower) of two limits.
209
+ * If one is null, use the other.
210
+ */
211
+ pickStricterLimit(a, b) {
212
+ if (a === null) return b;
213
+ if (b === null) return a;
214
+ return Math.min(a, b);
215
+ }
216
+ /**
217
+ * Force an immediate heartbeat poll (useful for kill switch checks).
218
+ * Returns a promise that resolves when the poll completes.
219
+ */
220
+ async forcePoll() {
221
+ await this.beat();
222
+ }
188
223
  /**
189
224
  * Update local budget tracking (called after each request).
190
225
  * This provides real-time budget awareness between heartbeats.
package/dist/index.mjs CHANGED
@@ -91,6 +91,9 @@ var Heartbeat = class {
91
91
  };
92
92
  /** Current protection status */
93
93
  this.status = "ACTIVE";
94
+ /** Local budget limits set via config (not from server) */
95
+ this.localDailyLimit = null;
96
+ this.localMonthlyLimit = null;
94
97
  this.endpoint = config.endpoint ?? "https://caplyr.com";
95
98
  this.apiKey = config.apiKey;
96
99
  this.interval = config.heartbeatInterval ?? 6e4;
@@ -104,9 +107,11 @@ var Heartbeat = class {
104
107
  */
105
108
  applyLocalLimits(budget) {
106
109
  if (budget.daily !== void 0) {
110
+ this.localDailyLimit = budget.daily;
107
111
  this.budgetStatus.daily_limit = budget.daily;
108
112
  }
109
113
  if (budget.monthly !== void 0) {
114
+ this.localMonthlyLimit = budget.monthly;
110
115
  this.budgetStatus.monthly_limit = budget.monthly;
111
116
  }
112
117
  }
@@ -136,7 +141,21 @@ var Heartbeat = class {
136
141
  throw new Error(`Heartbeat failed: ${res.status}`);
137
142
  }
138
143
  const data = await res.json();
139
- this.budgetStatus = data;
144
+ const localDailyUsed = this.budgetStatus.daily_used;
145
+ const localMonthlyUsed = this.budgetStatus.monthly_used;
146
+ const serverDailyUsed = Number(data.daily_used) || 0;
147
+ const serverMonthlyUsed = Number(data.monthly_used) || 0;
148
+ const serverDailyLimit = data.daily_limit != null ? Number(data.daily_limit) : null;
149
+ const serverMonthlyLimit = data.monthly_limit != null ? Number(data.monthly_limit) : null;
150
+ this.budgetStatus = {
151
+ ...data,
152
+ // Use whichever spend is higher — server or local tracking
153
+ daily_used: Math.max(serverDailyUsed, localDailyUsed),
154
+ monthly_used: Math.max(serverMonthlyUsed, localMonthlyUsed),
155
+ // Use the stricter (lower) limit — local config takes priority if lower
156
+ daily_limit: this.pickStricterLimit(serverDailyLimit, this.localDailyLimit),
157
+ monthly_limit: this.pickStricterLimit(serverMonthlyLimit, this.localMonthlyLimit)
158
+ };
140
159
  this.consecutiveFailures = 0;
141
160
  const newStatus = data.kill_switch_active ? "OFF" : data.status;
142
161
  if (newStatus !== this.status) {
@@ -152,6 +171,22 @@ var Heartbeat = class {
152
171
  }
153
172
  }
154
173
  }
174
+ /**
175
+ * Pick the stricter (lower) of two limits.
176
+ * If one is null, use the other.
177
+ */
178
+ pickStricterLimit(a, b) {
179
+ if (a === null) return b;
180
+ if (b === null) return a;
181
+ return Math.min(a, b);
182
+ }
183
+ /**
184
+ * Force an immediate heartbeat poll (useful for kill switch checks).
185
+ * Returns a promise that resolves when the poll completes.
186
+ */
187
+ async forcePoll() {
188
+ await this.beat();
189
+ }
155
190
  /**
156
191
  * Update local budget tracking (called after each request).
157
192
  * This provides real-time budget awareness between heartbeats.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caplyr",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "AI Cost Control Plane — budget guardrails, auto-downgrade, and kill switch for AI API calls",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",