@inerrata-corporation/errata 2.0.6-dev.2082 → 2.0.6-dev.2138

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 (2) hide show
  1. package/errata.mjs +67 -9
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -77056,7 +77056,7 @@ function createWatchBreaker(deps) {
77056
77056
  }
77057
77057
 
77058
77058
  // src/engine.ts
77059
- var DAEMON_VERSION = true ? "2.0.6-dev.2082" : "2.0.0-alpha.0";
77059
+ var DAEMON_VERSION = true ? "2.0.6-dev.2138" : "2.0.0-alpha.0";
77060
77060
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
77061
77061
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
77062
77062
  var GIT_OP_MUTE_MS = 4e3;
@@ -82572,16 +82572,49 @@ import { execFileSync as execFileSync4 } from "node:child_process";
82572
82572
  import { existsSync as existsSync29, writeFileSync as writeFileSync22 } from "node:fs";
82573
82573
  import { join as join34 } from "node:path";
82574
82574
 
82575
+ // src/device-poll.ts
82576
+ var THROTTLED_POLL_MIN_WAIT_MS = 11e3;
82577
+ var THROTTLED_INTERVAL_CAP_MS = 6e4;
82578
+ function throttledPoll(res, intervalMs) {
82579
+ if (res.status !== 429 && res.status !== 503) return null;
82580
+ const next = Math.max(intervalMs, Math.min(intervalMs + 5e3, THROTTLED_INTERVAL_CAP_MS));
82581
+ return {
82582
+ intervalMs: next,
82583
+ waitMs: Math.max(next, retryAfterMs(res.headers) ?? 0, THROTTLED_POLL_MIN_WAIT_MS)
82584
+ };
82585
+ }
82586
+ function retryAfterMs(headers, now = Date.now()) {
82587
+ for (const name2 of ["retry-after", "x-retry-after"]) {
82588
+ const raw2 = headers.get(name2)?.trim();
82589
+ if (!raw2) continue;
82590
+ if (/^\d+$/.test(raw2)) return Number(raw2) * 1e3;
82591
+ const at = Date.parse(raw2);
82592
+ if (!Number.isNaN(at)) return Math.max(0, at - now);
82593
+ }
82594
+ return null;
82595
+ }
82596
+
82575
82597
  // src/oauth-login.ts
82576
82598
  init_src7();
82577
82599
  init_cloud_auth();
82578
82600
  import { createServer } from "node:http";
82579
82601
  var DEFAULT_OAUTH_SCOPE = "openid profile graph:read graph:write mcp:tools";
82580
82602
  var strip = (u) => u.replace(/\/+$/, "");
82581
- function deviceEndpoints(tokenEndpoint) {
82603
+ function deviceEndpoints(tokenEndpoint, deviceAuthorizationEndpoint) {
82582
82604
  const token = new URL(tokenEndpoint);
82583
82605
  const authRoot = token.pathname.replace(/\/(?:oauth2|mcp)\/token\/?$/, "");
82584
- if (authRoot === token.pathname) throw new Error("authorization server exposes no device endpoint");
82606
+ if (authRoot === token.pathname) {
82607
+ if (deviceAuthorizationEndpoint) {
82608
+ const device = new URL(deviceAuthorizationEndpoint);
82609
+ const code = `${device.origin}${device.pathname.replace(/\/+$/, "")}`;
82610
+ return {
82611
+ code,
82612
+ token: `${code}/token`,
82613
+ resource: new URL("/api/gate/session-token", device.origin).toString()
82614
+ };
82615
+ }
82616
+ throw new Error("authorization server exposes no device endpoint");
82617
+ }
82585
82618
  const prefix = authRoot.replace(/\/api\/auth\/?$/, "");
82586
82619
  const at = (path2) => new URL(`${path2}`, `${token.origin}${prefix || "/"}`).toString();
82587
82620
  return {
@@ -82593,7 +82626,7 @@ function deviceEndpoints(tokenEndpoint) {
82593
82626
  async function loginOAuthDevice(opts) {
82594
82627
  const fetchFn = opts.fetchFn ?? fetch;
82595
82628
  const endpoints = await discoverEndpoints(opts.cloudUrl, fetchFn);
82596
- const device = deviceEndpoints(endpoints.tokenEndpoint);
82629
+ const device = deviceEndpoints(endpoints.tokenEndpoint, endpoints.deviceAuthorizationEndpoint);
82597
82630
  const clientId = oauthClientId();
82598
82631
  const scope = opts.scope ?? DEFAULT_OAUTH_SCOPE;
82599
82632
  const codeResponse = await fetchFn(device.code, {
@@ -82615,10 +82648,12 @@ async function loginOAuthDevice(opts) {
82615
82648
  });
82616
82649
  const deadline = Date.now() + Math.min(code.expires_in * 1e3, opts.timeoutMs ?? Number.POSITIVE_INFINITY);
82617
82650
  let intervalMs = Math.max(2, code.interval ?? 5) * 1e3;
82651
+ let waitMs = intervalMs;
82618
82652
  let deviceSession = null;
82619
82653
  let approvedInstallation = null;
82620
82654
  while (Date.now() < deadline) {
82621
- await (opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms))))(intervalMs);
82655
+ await (opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms))))(waitMs);
82656
+ waitMs = intervalMs;
82622
82657
  const poll = await fetchFn(device.token, {
82623
82658
  method: "POST",
82624
82659
  headers: { "content-type": "application/json", accept: "application/json" },
@@ -82630,10 +82665,16 @@ async function loginOAuthDevice(opts) {
82630
82665
  });
82631
82666
  const payload = await poll.json().catch(() => null);
82632
82667
  if (!poll.ok) {
82668
+ const throttle = throttledPoll(poll, intervalMs);
82669
+ if (throttle) {
82670
+ ({ intervalMs, waitMs } = throttle);
82671
+ continue;
82672
+ }
82633
82673
  const error48 = typeof payload?.["error"] === "string" ? payload["error"] : "device_poll_failed";
82634
82674
  if (error48 === "authorization_pending") continue;
82635
82675
  if (error48 === "slow_down") {
82636
82676
  intervalMs += 5e3;
82677
+ waitMs = intervalMs;
82637
82678
  continue;
82638
82679
  }
82639
82680
  throw new Error(`device authorization failed: ${error48}`);
@@ -82692,7 +82733,8 @@ async function discoverEndpoints(cloudUrl, fetchFn = fetch) {
82692
82733
  return {
82693
82734
  authorizationEndpoint: j.authorization_endpoint,
82694
82735
  tokenEndpoint: j.token_endpoint,
82695
- ...j.registration_endpoint ? { registrationEndpoint: j.registration_endpoint } : {}
82736
+ ...j.registration_endpoint ? { registrationEndpoint: j.registration_endpoint } : {},
82737
+ ...j.device_authorization_endpoint ? { deviceAuthorizationEndpoint: j.device_authorization_endpoint } : {}
82696
82738
  };
82697
82739
  }
82698
82740
  }
@@ -82809,7 +82851,7 @@ function isDecision(value) {
82809
82851
  async function linkThroughConsole(opts) {
82810
82852
  const fetchFn = opts.fetchFn ?? fetch;
82811
82853
  const endpoints = await discoverEndpoints(opts.cloudUrl, fetchFn);
82812
- const device = deviceEndpoints(endpoints.tokenEndpoint);
82854
+ const device = deviceEndpoints(endpoints.tokenEndpoint, endpoints.deviceAuthorizationEndpoint);
82813
82855
  const clientId = oauthClientId();
82814
82856
  const link = {
82815
82857
  locator: opts.facts.locator,
@@ -82843,9 +82885,11 @@ async function linkThroughConsole(opts) {
82843
82885
  });
82844
82886
  const deadline = Date.now() + Math.min(code.expires_in * 1e3, opts.timeoutMs ?? Number.POSITIVE_INFINITY);
82845
82887
  let intervalMs = Math.max(2, code.interval ?? 5) * 1e3;
82888
+ let waitMs = intervalMs;
82846
82889
  const sleep = opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
82847
82890
  while (Date.now() < deadline) {
82848
- await sleep(intervalMs);
82891
+ await sleep(waitMs);
82892
+ waitMs = intervalMs;
82849
82893
  const poll = await fetchFn(device.token, {
82850
82894
  method: "POST",
82851
82895
  headers: { "content-type": "application/json", accept: "application/json" },
@@ -82857,10 +82901,16 @@ async function linkThroughConsole(opts) {
82857
82901
  });
82858
82902
  const payload = await poll.json().catch(() => null);
82859
82903
  if (!poll.ok) {
82904
+ const throttle = throttledPoll(poll, intervalMs);
82905
+ if (throttle) {
82906
+ ({ intervalMs, waitMs } = throttle);
82907
+ continue;
82908
+ }
82860
82909
  const error48 = typeof payload?.["error"] === "string" ? payload["error"] : "device_poll_failed";
82861
82910
  if (error48 === "authorization_pending") continue;
82862
82911
  if (error48 === "slow_down") {
82863
82912
  intervalMs += 5e3;
82913
+ waitMs = intervalMs;
82864
82914
  continue;
82865
82915
  }
82866
82916
  if (error48 === "access_denied") return { status: "denied" };
@@ -84886,9 +84936,11 @@ async function requestDeviceAuthorization(fetchFn, deviceEndpoint, p) {
84886
84936
  async function pollDeviceApproval(fetchFn, deviceEndpoint, p, sleep = (ms) => new Promise((r) => setTimeout(r, ms))) {
84887
84937
  const deadline = Date.now() + p.timeoutMs;
84888
84938
  let intervalMs = Math.max(1, p.intervalSeconds) * 1e3;
84939
+ let waitMs = intervalMs;
84889
84940
  for (; ; ) {
84890
84941
  if (Date.now() >= deadline) throw new Error("device approval timed out \u2014 run `errata login` again");
84891
- await sleep(intervalMs);
84942
+ await sleep(waitMs);
84943
+ waitMs = intervalMs;
84892
84944
  const res = await fetchFn(`${strip2(deviceEndpoint)}/token`, {
84893
84945
  method: "POST",
84894
84946
  headers: { "content-type": "application/json", accept: "application/json" },
@@ -84900,9 +84952,15 @@ async function pollDeviceApproval(fetchFn, deviceEndpoint, p, sleep = (ms) => ne
84900
84952
  });
84901
84953
  const j = await res.json().catch(() => ({}));
84902
84954
  if (res.ok && j.access_token) return j.access_token;
84955
+ const throttle = throttledPoll(res, intervalMs);
84956
+ if (throttle) {
84957
+ ({ intervalMs, waitMs } = throttle);
84958
+ continue;
84959
+ }
84903
84960
  if (j.error === "authorization_pending") continue;
84904
84961
  if (j.error === "slow_down") {
84905
84962
  intervalMs += 5e3;
84963
+ waitMs = intervalMs;
84906
84964
  continue;
84907
84965
  }
84908
84966
  throw new Error(`device approval failed: ${j.error ?? `HTTP ${res.status}`}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.6-dev.2082",
3
+ "version": "2.0.6-dev.2138",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {