@lightworkai.official/debug-capture 0.17.4 → 0.18.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.
package/dist/server.d.ts CHANGED
@@ -14,11 +14,15 @@ export interface SupportProfileFact {
14
14
  label: string;
15
15
  value: string;
16
16
  }
17
- /** Whoever the host has authenticated. Email is the account key. */
17
+ /**
18
+ * Whoever the host has authenticated. Email is the account key — or, for someone the host knows but holds no email
19
+ * for (a CIB officer with none in the ERP, 2026-10-27: every report they ever sent landed anonymous, nobody could call
20
+ * them back), the host's own id alone.
21
+ */
18
22
  export interface SupportUser {
19
- /** The host's own id for this person, recorded so a changed email still matches. */
23
+ /** The host's own id for this person, recorded so a changed email still matches — the key when there is no email. */
20
24
  id?: string | null;
21
- email: string;
25
+ email?: string | null;
22
26
  name?: string | null;
23
27
  /** What the host knows about their work — most important first. Optional; bounded on the way in. */
24
28
  profile?: SupportProfileFact[] | null;
package/dist/server.mjs CHANGED
@@ -23,17 +23,18 @@ function cleanProfile(profile) {
23
23
  var DEFAULT_TTL_SECONDS = 120;
24
24
  var b64 = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
25
25
  function createSupportToken(options) {
26
- const email = options.user.email?.trim().toLowerCase();
27
- if (!email || !email.includes("@"))
28
- throw new Error("createSupportToken: a real email is required");
26
+ const email = options.user.email?.trim().toLowerCase() || null;
27
+ const id = options.user.id?.trim() || null;
28
+ if (email ? !email.includes("@") : !id)
29
+ throw new Error("createSupportToken: a real email, or the host's own id for this person, is required");
29
30
  if (!options.secret || options.secret.length < 16) {
30
31
  throw new Error("createSupportToken: the realm's reporter secret is missing or too short");
31
32
  }
32
33
  const now = Math.floor(Date.now() / 1000);
33
34
  const expiresIn = Math.min(Math.max(Math.floor(options.ttlSeconds ?? DEFAULT_TTL_SECONDS), 30), 12 * 60 * 60);
34
35
  const body = `${b64({ alg: "HS256", typ: "JWT" })}.${b64({
35
- sub: options.user.id?.trim() || email,
36
- email,
36
+ sub: id ?? email,
37
+ ...email ? { email } : {},
37
38
  name: options.user.name?.trim() || null,
38
39
  ...cleanProfile(options.user.profile) ? { profile: cleanProfile(options.user.profile) } : {},
39
40
  jti: randomUUID(),
@@ -59,7 +60,7 @@ function supportTokenHandler(options) {
59
60
  return json({ error: "Support identity is not configured" }, 503);
60
61
  }
61
62
  const user = await options.user();
62
- if (!user?.email)
63
+ if (!user?.email?.trim() && !user?.id?.trim())
63
64
  return json({ error: "Unauthorized" }, 401);
64
65
  try {
65
66
  const { token, expiresIn } = createSupportToken({ secret, user, ttlSeconds: options.ttlSeconds });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightworkai.official/debug-capture",
3
- "version": "0.17.4",
3
+ "version": "0.18.0",
4
4
  "description": "Report-a-problem widget: rolling network/console/action capture, optional rrweb session replay, annotated screenshot, and submission to a Lightwork Support host",
5
5
  "type": "module",
6
6
  "module": "./dist/index.mjs",
package/src/server.ts CHANGED
@@ -34,11 +34,15 @@ export interface SupportProfileFact {
34
34
  value: string;
35
35
  }
36
36
 
37
- /** Whoever the host has authenticated. Email is the account key. */
37
+ /**
38
+ * Whoever the host has authenticated. Email is the account key — or, for someone the host knows but holds no email
39
+ * for (a CIB officer with none in the ERP, 2026-10-27: every report they ever sent landed anonymous, nobody could call
40
+ * them back), the host's own id alone.
41
+ */
38
42
  export interface SupportUser {
39
- /** The host's own id for this person, recorded so a changed email still matches. */
43
+ /** The host's own id for this person, recorded so a changed email still matches — the key when there is no email. */
40
44
  id?: string | null;
41
- email: string;
45
+ email?: string | null;
42
46
  name?: string | null;
43
47
  /** What the host knows about their work — most important first. Optional; bounded on the way in. */
44
48
  profile?: SupportProfileFact[] | null;
@@ -95,8 +99,9 @@ export function createSupportToken(options: {
95
99
  user: SupportUser;
96
100
  ttlSeconds?: number;
97
101
  }): { token: string; expiresIn: number } {
98
- const email = options.user.email?.trim().toLowerCase();
99
- if (!email || !email.includes("@")) throw new Error("createSupportToken: a real email is required");
102
+ const email = options.user.email?.trim().toLowerCase() || null;
103
+ const id = options.user.id?.trim() || null;
104
+ if (email ? !email.includes("@") : !id) throw new Error("createSupportToken: a real email, or the host's own id for this person, is required");
100
105
  if (!options.secret || options.secret.length < 16) {
101
106
  throw new Error("createSupportToken: the realm's reporter secret is missing or too short");
102
107
  }
@@ -105,9 +110,10 @@ export function createSupportToken(options: {
105
110
  const expiresIn = Math.min(Math.max(Math.floor(options.ttlSeconds ?? DEFAULT_TTL_SECONDS), 30), 12 * 60 * 60);
106
111
  const body = `${b64({ alg: "HS256", typ: "JWT" })}.${b64({
107
112
  // The host's own id when it has one, so the same person is recognised even
108
- // if their email later changes. The email stands in when it does not.
109
- sub: options.user.id?.trim() || email,
110
- email,
113
+ // if their email later changes. The email stands in when it does not; with
114
+ // no email the id is the whole of who they are, and the claim is left out.
115
+ sub: id ?? email,
116
+ ...(email ? { email } : {}),
111
117
  name: options.user.name?.trim() || null,
112
118
  // Only when there is one: an older support desk ignores the claim, a newer one records it.
113
119
  ...(cleanProfile(options.user.profile) ? { profile: cleanProfile(options.user.profile) } : {}),
@@ -184,7 +190,8 @@ export function supportTokenHandler(options: {
184
190
  }
185
191
 
186
192
  const user = await options.user();
187
- if (!user?.email) return json({ error: "Unauthorized" }, 401);
193
+ // Nobody — or somebody the host cannot name by an email or an id of its own.
194
+ if (!user?.email?.trim() && !user?.id?.trim()) return json({ error: "Unauthorized" }, 401);
188
195
 
189
196
  try {
190
197
  const { token, expiresIn } = createSupportToken({ secret, user, ttlSeconds: options.ttlSeconds });