@anchrd/intel-api 0.6.2 → 0.6.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.
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { reportUnexpectedError } from "../../shared/report-unexpected-error/report-unexpected-error.js";
2
3
  const StoredToken = z.strictObject({
3
4
  version: z.literal(1),
4
5
  accessToken: z.string().min(1),
@@ -41,9 +42,16 @@ export function createPortalTokenStore(deps) {
41
42
  const { version: _version, ...token } = parsed;
42
43
  return token;
43
44
  }
44
- catch {
45
+ catch (error) {
45
46
  // A row that no longer decrypts (rotated secret, tampering) is treated as absent so the
46
47
  // user is asked to reconnect instead of hitting an opaque failure on every call.
48
+ // ⚠️ Absent and unreadable look identical from the outside — both end in the sign-in screen
49
+ // — but only one of them is a defect. Without this line the difference is unobservable, and
50
+ // a connection that disappears has no explanation anywhere (#97). The row is named by its
51
+ // length alone; the sealed value never goes near a log.
52
+ reportUnexpectedError(new Error(`portal token for a user did not decrypt (sealed length ${row.sealed.length})`, {
53
+ cause: error,
54
+ }));
47
55
  return null;
48
56
  }
49
57
  },
@@ -1,4 +1,5 @@
1
1
  import { IntelError } from "../shared/intel-error/intel-error.js";
2
+ import { reportUnexpectedError } from "../shared/report-unexpected-error/report-unexpected-error.js";
2
3
  const CallTimeoutMs = 15_000;
3
4
  const MaxTools = 1_000;
4
5
  const MaxResultBytes = 1_000_000;
@@ -24,6 +25,11 @@ export function createTools(deps) {
24
25
  return stored.accessToken;
25
26
  const refreshed = stored.refreshToken ? await deps.refresh(stored) : null;
26
27
  if (!refreshed) {
28
+ // Losing a connection is a visible event for the person and an invisible one for everybody
29
+ // else: the screen simply asks them to sign in again, and no trace says why. The two reasons
30
+ // need telling apart — a portal that issues no refresh token at all is a configuration
31
+ // problem, a refresh that gets rejected is a different one (#97).
32
+ reportUnexpectedError(new Error(`portal token dropped: ${stored.refreshToken ? "refresh was rejected" : "no refresh token was ever issued"}`));
27
33
  // A token that cannot be renewed is dropped: leaving it would keep failing every call with a
28
34
  // stale credential. The browser answers this by signing in silently again (#60); an MCP
29
35
  // client sees the code and repeats its own authorization.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anchrd/intel-api",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {