@whittlelabs/sifter 0.4.1 → 0.4.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.
Files changed (3) hide show
  1. package/bin.js +29 -15
  2. package/bin.js.map +2 -2
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -15359,12 +15359,12 @@ var require_client = __commonJS({
15359
15359
  * (the manager behind it re-mints before expiry) instead of pinning the
15360
15360
  * token it happened to hold at construction.
15361
15361
  */
15362
- async buildHeaders() {
15362
+ async buildHeaders(forceRefresh = false) {
15363
15363
  const headers = { "Content-Type": "application/json" };
15364
15364
  if (this.apiKey) {
15365
15365
  headers["X-API-Key"] = this.apiKey;
15366
15366
  } else if (this.getAccessToken) {
15367
- const token = await this.getAccessToken();
15367
+ const token = await this.getAccessToken({ forceRefresh });
15368
15368
  if (token)
15369
15369
  headers["Authorization"] = `Bearer ${token}`;
15370
15370
  } else if (this.accessToken) {
@@ -15372,6 +15372,22 @@ var require_client = __commonJS({
15372
15372
  }
15373
15373
  return headers;
15374
15374
  }
15375
+ /**
15376
+ * Fetch with one auth retry. A 401 on a token-provider client usually means
15377
+ * the token was invalidated server-side (Keep restart, key rotation,
15378
+ * revoke-then-repair, clock skew) while the local expiry timer still thinks
15379
+ * it's valid. Force a re-mint and retry once before surfacing the failure.
15380
+ * `onResponse` (min-version contract) fires on the response we actually act
15381
+ * on. `body` is a string, so it is safe to resend.
15382
+ */
15383
+ async fetchWithAuthRetry(method, url, body) {
15384
+ let response = await fetch(url, { method, headers: await this.buildHeaders(), body });
15385
+ if (response.status === 401 && this.getAccessToken) {
15386
+ response = await fetch(url, { method, headers: await this.buildHeaders(true), body });
15387
+ }
15388
+ this.onResponse?.(response);
15389
+ return response;
15390
+ }
15375
15391
  // ── Attention Pools ──────────────────────────────────────────────
15376
15392
  async createAttentionPool(options) {
15377
15393
  return this.request("POST", "/api/attention-pools", options);
@@ -15563,12 +15579,7 @@ var require_client = __commonJS({
15563
15579
  // ── HTTP plumbing ────────────────────────────────────────────────
15564
15580
  async request(method, path, body) {
15565
15581
  const url = `${this.baseUrl}${path}`;
15566
- const response = await fetch(url, {
15567
- method,
15568
- headers: await this.buildHeaders(),
15569
- body: body ? JSON.stringify(body) : void 0
15570
- });
15571
- this.onResponse?.(response);
15582
+ const response = await this.fetchWithAuthRetry(method, url, body ? JSON.stringify(body) : void 0);
15572
15583
  const json = await response.json();
15573
15584
  if (!response.ok || !json.success) {
15574
15585
  const errorMessage = json.error?.message ?? `Request failed with status ${response.status}`;
@@ -15581,8 +15592,7 @@ var require_client = __commonJS({
15581
15592
  }
15582
15593
  async requestPaginated(method, path) {
15583
15594
  const url = `${this.baseUrl}${path}`;
15584
- const response = await fetch(url, { method, headers: await this.buildHeaders() });
15585
- this.onResponse?.(response);
15595
+ const response = await this.fetchWithAuthRetry(method, url);
15586
15596
  const json = await response.json();
15587
15597
  if (!response.ok || !json.success) {
15588
15598
  const errorMessage = json.error?.message ?? `Request failed with status ${response.status}`;
@@ -16470,10 +16480,10 @@ var require_agent_token = __commonJS({
16470
16480
  this.enabled = !!(config.clientId && config.clientSecret && config.keepApiUrl);
16471
16481
  this.onResponse = config.onResponse;
16472
16482
  }
16473
- async getToken() {
16483
+ async getToken(forceRefresh = false) {
16474
16484
  if (!this.enabled)
16475
16485
  return null;
16476
- if (this.token && Date.now() < this.expiresAt - 3e4) {
16486
+ if (!forceRefresh && this.token && Date.now() < this.expiresAt - 3e4) {
16477
16487
  return this.token;
16478
16488
  }
16479
16489
  if (this.pendingExchange) {
@@ -17954,8 +17964,12 @@ var require_shuttle = __commonJS({
17954
17964
  onResponse
17955
17965
  });
17956
17966
  const token = await tokens.getToken();
17957
- if (token)
17958
- return { kind: "tokenProvider", getToken: () => tokens.getToken() };
17967
+ if (token) {
17968
+ return {
17969
+ kind: "tokenProvider",
17970
+ getToken: (opts) => tokens.getToken(opts?.forceRefresh)
17971
+ };
17972
+ }
17959
17973
  }
17960
17974
  if (config.auth.apiKey) {
17961
17975
  return { kind: "apiKey", apiKey: config.auth.apiKey };
@@ -18494,7 +18508,7 @@ var import_shuttle = __toESM(require_dist4());
18494
18508
  // package.json
18495
18509
  var package_default = {
18496
18510
  name: "@whittlelabs/sifter",
18497
- version: "0.4.1",
18511
+ version: "0.4.2",
18498
18512
  description: "Whittle Sifter: paired AI reviewer for Whittle Sift attention pools.",
18499
18513
  bin: {
18500
18514
  "whittle-sifter": "./dist/bin.js"