@bitbaum/ai-kit 0.14.0 → 0.15.0

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.
@@ -149,8 +149,15 @@ export interface AiHealthHandlerOptions extends LivenessOptions {
149
149
  * When absent, the handler NEVER probes — it only reports passive health.
150
150
  * That default is deliberate: an app that forgets to configure a secret gets
151
151
  * a route that cannot spend money, rather than an open endpoint that can.
152
+ *
153
+ * Pass a FUNCTION to read it per request. A handler is normally built once
154
+ * and reused (its cache has to live somewhere), so a plain string is captured
155
+ * at that moment — which means the secret is whatever the environment held on
156
+ * the first request, and rotating it needs a process restart. A getter also
157
+ * makes the route testable: with a captured string, the first test that runs
158
+ * without a secret configured pins every later one to 501.
152
159
  */
153
- secret?: string;
160
+ secret?: string | (() => string | undefined);
154
161
  /** Passive health to report alongside. Optional. */
155
162
  health?: HealthTracker;
156
163
  }
package/dist/liveness.js CHANGED
@@ -189,17 +189,20 @@ export function createAiHealthHandler(options = {}) {
189
189
  if (!wantsProbe) {
190
190
  return json(200, { probed: false, ...passive });
191
191
  }
192
+ // Read per request when a getter was given, so rotating the secret does not
193
+ // need a restart and a route built before the env was set is not stuck.
194
+ const expected = typeof secret === "function" ? secret() : secret;
192
195
  // No secret configured means probing is switched off, which is a different
193
196
  // answer from "your secret is wrong" — say so, rather than implying the
194
197
  // caller could retry with a better credential.
195
- if (!secret) {
198
+ if (!expected) {
196
199
  return json(501, {
197
200
  probed: false,
198
201
  error: "Probing is not configured on this deployment (no secret set).",
199
202
  ...passive,
200
203
  });
201
204
  }
202
- if (!offered || !timingSafeEqual(offered, secret)) {
205
+ if (!offered || !timingSafeEqual(offered, expected)) {
203
206
  return json(401, { probed: false, error: "Bad or missing probe secret.", ...passive });
204
207
  }
205
208
  const result = await probe.run();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitbaum/ai-kit",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "One install for the AI layer of an app: which model to call, what to do when the vendor retires it, how to walk the fallback chain and know when none of it worked, how to read the three kinds of 429, a fair daily budget across users, headless AI form filling — and now the model registry (one SSOT for every callable id, with the paid/free boundary as a field) and the grounding harness (facts, contract, deterministic fabrication check).",
5
5
  "license": "MIT",
6
6
  "author": "Mao Nakamoto",
package/src/liveness.ts CHANGED
@@ -277,8 +277,15 @@ export interface AiHealthHandlerOptions extends LivenessOptions {
277
277
  * When absent, the handler NEVER probes — it only reports passive health.
278
278
  * That default is deliberate: an app that forgets to configure a secret gets
279
279
  * a route that cannot spend money, rather than an open endpoint that can.
280
+ *
281
+ * Pass a FUNCTION to read it per request. A handler is normally built once
282
+ * and reused (its cache has to live somewhere), so a plain string is captured
283
+ * at that moment — which means the secret is whatever the environment held on
284
+ * the first request, and rotating it needs a process restart. A getter also
285
+ * makes the route testable: with a captured string, the first test that runs
286
+ * without a secret configured pins every later one to 501.
280
287
  */
281
- secret?: string;
288
+ secret?: string | (() => string | undefined);
282
289
  /** Passive health to report alongside. Optional. */
283
290
  health?: HealthTracker;
284
291
  }
@@ -316,17 +323,21 @@ export function createAiHealthHandler(
316
323
  return json(200, { probed: false, ...passive });
317
324
  }
318
325
 
326
+ // Read per request when a getter was given, so rotating the secret does not
327
+ // need a restart and a route built before the env was set is not stuck.
328
+ const expected = typeof secret === "function" ? secret() : secret;
329
+
319
330
  // No secret configured means probing is switched off, which is a different
320
331
  // answer from "your secret is wrong" — say so, rather than implying the
321
332
  // caller could retry with a better credential.
322
- if (!secret) {
333
+ if (!expected) {
323
334
  return json(501, {
324
335
  probed: false,
325
336
  error: "Probing is not configured on this deployment (no secret set).",
326
337
  ...passive,
327
338
  });
328
339
  }
329
- if (!offered || !timingSafeEqual(offered, secret)) {
340
+ if (!offered || !timingSafeEqual(offered, expected)) {
330
341
  return json(401, { probed: false, error: "Bad or missing probe secret.", ...passive });
331
342
  }
332
343