@whittlelabs/sifter 0.4.0 → 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 +55 -31
  2. package/bin.js.map +2 -2
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -15342,18 +15342,52 @@ var require_client = __commonJS({
15342
15342
  var prompt_execution_1 = require_prompt_execution();
15343
15343
  var JobsClient = class {
15344
15344
  baseUrl;
15345
- headers;
15345
+ apiKey;
15346
+ accessToken;
15347
+ getAccessToken;
15346
15348
  onResponse;
15347
15349
  constructor(config) {
15348
15350
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
15349
- this.headers = { "Content-Type": "application/json" };
15350
- if (config.apiKey) {
15351
- this.headers["X-API-Key"] = config.apiKey;
15352
- } else if (config.accessToken) {
15353
- this.headers["Authorization"] = `Bearer ${config.accessToken}`;
15354
- }
15351
+ this.apiKey = config.apiKey;
15352
+ this.accessToken = config.accessToken;
15353
+ this.getAccessToken = config.getAccessToken;
15355
15354
  this.onResponse = config.onResponse;
15356
15355
  }
15356
+ /**
15357
+ * Headers for one request. The auth header is resolved per call so a
15358
+ * long-lived client picks up a refreshed token from `getAccessToken`
15359
+ * (the manager behind it re-mints before expiry) instead of pinning the
15360
+ * token it happened to hold at construction.
15361
+ */
15362
+ async buildHeaders(forceRefresh = false) {
15363
+ const headers = { "Content-Type": "application/json" };
15364
+ if (this.apiKey) {
15365
+ headers["X-API-Key"] = this.apiKey;
15366
+ } else if (this.getAccessToken) {
15367
+ const token = await this.getAccessToken({ forceRefresh });
15368
+ if (token)
15369
+ headers["Authorization"] = `Bearer ${token}`;
15370
+ } else if (this.accessToken) {
15371
+ headers["Authorization"] = `Bearer ${this.accessToken}`;
15372
+ }
15373
+ return headers;
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
+ }
15357
15391
  // ── Attention Pools ──────────────────────────────────────────────
15358
15392
  async createAttentionPool(options) {
15359
15393
  return this.request("POST", "/api/attention-pools", options);
@@ -15543,22 +15577,9 @@ var require_client = __commonJS({
15543
15577
  }
15544
15578
  }
15545
15579
  // ── HTTP plumbing ────────────────────────────────────────────────
15546
- /** @internal exposed for the subscribe loop */
15547
- buildUrl(path) {
15548
- return `${this.baseUrl}${path}`;
15549
- }
15550
- /** @internal exposed for the subscribe loop */
15551
- authHeaders() {
15552
- return { ...this.headers };
15553
- }
15554
15580
  async request(method, path, body) {
15555
- const url = this.buildUrl(path);
15556
- const response = await fetch(url, {
15557
- method,
15558
- headers: this.headers,
15559
- body: body ? JSON.stringify(body) : void 0
15560
- });
15561
- this.onResponse?.(response);
15581
+ const url = `${this.baseUrl}${path}`;
15582
+ const response = await this.fetchWithAuthRetry(method, url, body ? JSON.stringify(body) : void 0);
15562
15583
  const json = await response.json();
15563
15584
  if (!response.ok || !json.success) {
15564
15585
  const errorMessage = json.error?.message ?? `Request failed with status ${response.status}`;
@@ -15570,9 +15591,8 @@ var require_client = __commonJS({
15570
15591
  return json.data;
15571
15592
  }
15572
15593
  async requestPaginated(method, path) {
15573
- const url = this.buildUrl(path);
15574
- const response = await fetch(url, { method, headers: this.headers });
15575
- this.onResponse?.(response);
15594
+ const url = `${this.baseUrl}${path}`;
15595
+ const response = await this.fetchWithAuthRetry(method, url);
15576
15596
  const json = await response.json();
15577
15597
  if (!response.ok || !json.success) {
15578
15598
  const errorMessage = json.error?.message ?? `Request failed with status ${response.status}`;
@@ -16460,10 +16480,10 @@ var require_agent_token = __commonJS({
16460
16480
  this.enabled = !!(config.clientId && config.clientSecret && config.keepApiUrl);
16461
16481
  this.onResponse = config.onResponse;
16462
16482
  }
16463
- async getToken() {
16483
+ async getToken(forceRefresh = false) {
16464
16484
  if (!this.enabled)
16465
16485
  return null;
16466
- if (this.token && Date.now() < this.expiresAt - 3e4) {
16486
+ if (!forceRefresh && this.token && Date.now() < this.expiresAt - 3e4) {
16467
16487
  return this.token;
16468
16488
  }
16469
16489
  if (this.pendingExchange) {
@@ -17862,7 +17882,7 @@ var require_shuttle = __commonJS({
17862
17882
  const jobsClient = new jobs_1.JobsClient({
17863
17883
  baseUrl: this.config.auth.jobsApiUrl,
17864
17884
  apiKey: auth.kind === "apiKey" ? auth.apiKey : void 0,
17865
- accessToken: auth.kind === "serviceToken" ? auth.accessToken : void 0,
17885
+ getAccessToken: auth.kind === "tokenProvider" ? auth.getToken : void 0,
17866
17886
  onResponse: enforceMinVersion
17867
17887
  });
17868
17888
  const registry = new registry_1.ExecutorRegistry(this.brand.executorAllowlist);
@@ -17944,8 +17964,12 @@ var require_shuttle = __commonJS({
17944
17964
  onResponse
17945
17965
  });
17946
17966
  const token = await tokens.getToken();
17947
- if (token)
17948
- return { kind: "serviceToken", accessToken: token };
17967
+ if (token) {
17968
+ return {
17969
+ kind: "tokenProvider",
17970
+ getToken: (opts) => tokens.getToken(opts?.forceRefresh)
17971
+ };
17972
+ }
17949
17973
  }
17950
17974
  if (config.auth.apiKey) {
17951
17975
  return { kind: "apiKey", apiKey: config.auth.apiKey };
@@ -18484,7 +18508,7 @@ var import_shuttle = __toESM(require_dist4());
18484
18508
  // package.json
18485
18509
  var package_default = {
18486
18510
  name: "@whittlelabs/sifter",
18487
- version: "0.4.0",
18511
+ version: "0.4.2",
18488
18512
  description: "Whittle Sifter: paired AI reviewer for Whittle Sift attention pools.",
18489
18513
  bin: {
18490
18514
  "whittle-sifter": "./dist/bin.js"