@hasna/testers 0.0.40 → 0.0.42

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/cli/index.js CHANGED
@@ -16940,17 +16940,32 @@ var init_secrets_resolver = () => {};
16940
16940
  function isSessionCookie(cookieName) {
16941
16941
  return !/(?:csrf|xsrf)/i.test(cookieName);
16942
16942
  }
16943
- function areCookiesFresh(persona) {
16943
+ function isPrimarySessionCookie(cookieName) {
16944
+ return isSessionCookie(cookieName) && !/refresh/i.test(cookieName);
16945
+ }
16946
+ function getCookieExpires(cookie) {
16947
+ if (typeof cookie.expires === "number" && Number.isFinite(cookie.expires)) {
16948
+ return cookie.expires;
16949
+ }
16950
+ if (typeof cookie.expires === "string") {
16951
+ const parsed = Number(cookie.expires);
16952
+ return Number.isFinite(parsed) ? parsed : null;
16953
+ }
16954
+ return null;
16955
+ }
16956
+ function hasFreshAuthCookies(persona) {
16944
16957
  if (!persona.auth?.cookies?.length)
16945
16958
  return false;
16946
16959
  const cookies = persona.auth.cookies;
16947
- if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
16960
+ const sessionCookies = cookies.filter((cookie) => ("name" in cookie) && isPrimarySessionCookie(String(cookie.name)));
16961
+ if (sessionCookies.length === 0) {
16948
16962
  return false;
16949
16963
  }
16950
16964
  const now2 = Date.now() / 1000;
16951
- const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
16952
- if (hasFutureExpiry)
16953
- return true;
16965
+ const expiringSessionCookies = sessionCookies.map(getCookieExpires).filter((expires) => expires !== null && expires > 0);
16966
+ if (expiringSessionCookies.length > 0) {
16967
+ return expiringSessionCookies.some((expires) => expires > now2 + 60);
16968
+ }
16954
16969
  const updatedAt = new Date(persona.updatedAt).getTime();
16955
16970
  return Date.now() - updatedAt < COOKIE_MAX_AGE_MS;
16956
16971
  }
@@ -17140,7 +17155,7 @@ async function ensurePersonaAuthenticated(page, persona, baseUrl) {
17140
17155
  if (!persona.auth) {
17141
17156
  return { success: true, method: "none" };
17142
17157
  }
17143
- if (areCookiesFresh(persona)) {
17158
+ if (hasFreshAuthCookies(persona)) {
17144
17159
  const restored = await restoreCookies(page, persona);
17145
17160
  if (restored) {
17146
17161
  return { success: true, method: "cookies" };
@@ -94025,7 +94040,7 @@ import chalk6 from "chalk";
94025
94040
  // package.json
94026
94041
  var package_default = {
94027
94042
  name: "@hasna/testers",
94028
- version: "0.0.40",
94043
+ version: "0.0.42",
94029
94044
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
94030
94045
  type: "module",
94031
94046
  main: "dist/index.js",
@@ -95752,6 +95767,40 @@ function formatProdDebugPlan(plan) {
95752
95767
  `);
95753
95768
  }
95754
95769
 
95770
+ // src/lib/persona-redaction.ts
95771
+ function stringKeys(value) {
95772
+ return value ? Object.keys(value).sort() : [];
95773
+ }
95774
+ function cookieNames(cookies) {
95775
+ if (!cookies)
95776
+ return [];
95777
+ return cookies.map((cookie) => cookie.name).filter((name) => typeof name === "string" && name.length > 0);
95778
+ }
95779
+ function redactPersona(persona) {
95780
+ if (!persona.auth)
95781
+ return { ...persona, auth: null };
95782
+ const names = cookieNames(persona.auth.cookies);
95783
+ const headerNames = stringKeys(persona.auth.headers);
95784
+ return {
95785
+ ...persona,
95786
+ auth: {
95787
+ emailConfigured: Boolean(persona.auth.email),
95788
+ passwordConfigured: Boolean(persona.auth.password),
95789
+ loginPath: persona.auth.loginPath,
95790
+ strategy: persona.auth.strategy,
95791
+ cookiesConfigured: names.length > 0,
95792
+ cookieCount: names.length,
95793
+ cookieNames: names,
95794
+ headersConfigured: headerNames.length > 0,
95795
+ headerNames,
95796
+ customScriptConfigured: Boolean(persona.auth.customScript)
95797
+ }
95798
+ };
95799
+ }
95800
+ function redactPersonas(personas) {
95801
+ return personas.map(redactPersona);
95802
+ }
95803
+
95755
95804
  // src/cli/index.tsx
95756
95805
  init_projects();
95757
95806
  init_personas();
@@ -100783,7 +100832,7 @@ personaCmd.command("list").description("List personas").option("--project <id>",
100783
100832
  globalOnly: opts.global ? true : undefined
100784
100833
  });
100785
100834
  if (opts.json) {
100786
- log(JSON.stringify(personas, null, 2));
100835
+ log(JSON.stringify(redactPersonas(personas), null, 2));
100787
100836
  return;
100788
100837
  }
100789
100838
  if (personas.length === 0) {
package/dist/index.js CHANGED
@@ -15499,17 +15499,32 @@ var COOKIE_MAX_AGE_MS = 60 * 60 * 1000;
15499
15499
  function isSessionCookie(cookieName) {
15500
15500
  return !/(?:csrf|xsrf)/i.test(cookieName);
15501
15501
  }
15502
- function areCookiesFresh(persona) {
15502
+ function isPrimarySessionCookie(cookieName) {
15503
+ return isSessionCookie(cookieName) && !/refresh/i.test(cookieName);
15504
+ }
15505
+ function getCookieExpires(cookie) {
15506
+ if (typeof cookie.expires === "number" && Number.isFinite(cookie.expires)) {
15507
+ return cookie.expires;
15508
+ }
15509
+ if (typeof cookie.expires === "string") {
15510
+ const parsed = Number(cookie.expires);
15511
+ return Number.isFinite(parsed) ? parsed : null;
15512
+ }
15513
+ return null;
15514
+ }
15515
+ function hasFreshAuthCookies(persona) {
15503
15516
  if (!persona.auth?.cookies?.length)
15504
15517
  return false;
15505
15518
  const cookies = persona.auth.cookies;
15506
- if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
15519
+ const sessionCookies = cookies.filter((cookie) => ("name" in cookie) && isPrimarySessionCookie(String(cookie.name)));
15520
+ if (sessionCookies.length === 0) {
15507
15521
  return false;
15508
15522
  }
15509
15523
  const now2 = Date.now() / 1000;
15510
- const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
15511
- if (hasFutureExpiry)
15512
- return true;
15524
+ const expiringSessionCookies = sessionCookies.map(getCookieExpires).filter((expires) => expires !== null && expires > 0);
15525
+ if (expiringSessionCookies.length > 0) {
15526
+ return expiringSessionCookies.some((expires) => expires > now2 + 60);
15527
+ }
15513
15528
  const updatedAt = new Date(persona.updatedAt).getTime();
15514
15529
  return Date.now() - updatedAt < COOKIE_MAX_AGE_MS;
15515
15530
  }
@@ -15699,7 +15714,7 @@ async function ensurePersonaAuthenticated(page, persona, baseUrl) {
15699
15714
  if (!persona.auth) {
15700
15715
  return { success: true, method: "none" };
15701
15716
  }
15702
- if (areCookiesFresh(persona)) {
15717
+ if (hasFreshAuthCookies(persona)) {
15703
15718
  const restored = await restoreCookies(page, persona);
15704
15719
  if (restored) {
15705
15720
  return { success: true, method: "cookies" };
@@ -1,11 +1,13 @@
1
1
  import type { Page } from "playwright";
2
2
  import type { Persona } from "../types/index.js";
3
3
  export declare function isSessionCookie(cookieName: string): boolean;
4
+ export declare function isPrimarySessionCookie(cookieName: string): boolean;
4
5
  export interface LoginResult {
5
6
  success: boolean;
6
7
  method: "cookies" | "login" | "none";
7
8
  error?: string;
8
9
  }
10
+ export declare function hasFreshAuthCookies(persona: Persona): boolean;
9
11
  /**
10
12
  * Perform login using a raw AuthConfig (e.g. from scenario.authConfig or an auth preset).
11
13
  * Resolves credentials via resolveCredential() supporting @secrets: and $ENV references.
@@ -1 +1 @@
1
- {"version":3,"file":"persona-auth.d.ts","sourceRoot":"","sources":["../../src/lib/persona-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+OD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACvG,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAgCtB;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAsBtB"}
1
+ {"version":3,"file":"persona-auth.d.ts","sourceRoot":"","sources":["../../src/lib/persona-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAkBD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAsB7D;AA2ND;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACvG,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAgCtB;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAsBtB"}
@@ -0,0 +1,19 @@
1
+ import type { Persona } from "../types/index.js";
2
+ export interface RedactedPersonaAuth {
3
+ emailConfigured: boolean;
4
+ passwordConfigured: boolean;
5
+ loginPath: string;
6
+ strategy: string;
7
+ cookiesConfigured: boolean;
8
+ cookieCount: number;
9
+ cookieNames: string[];
10
+ headersConfigured: boolean;
11
+ headerNames: string[];
12
+ customScriptConfigured: boolean;
13
+ }
14
+ export type RedactedPersona = Omit<Persona, "auth"> & {
15
+ auth: RedactedPersonaAuth | null;
16
+ };
17
+ export declare function redactPersona(persona: Persona): RedactedPersona;
18
+ export declare function redactPersonas(personas: Persona[]): RedactedPersona[];
19
+ //# sourceMappingURL=persona-redaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona-redaction.d.ts","sourceRoot":"","sources":["../../src/lib/persona-redaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,sBAAsB,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG;IACpD,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAClC,CAAC;AAaF,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,CAoB/D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,eAAe,EAAE,CAErE"}
package/dist/mcp/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "@hasna/testers",
55
- version: "0.0.40",
55
+ version: "0.0.42",
56
56
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
57
57
  type: "module",
58
58
  main: "dist/index.js",
@@ -20048,17 +20048,32 @@ var init_secrets_resolver = () => {};
20048
20048
  function isSessionCookie(cookieName) {
20049
20049
  return !/(?:csrf|xsrf)/i.test(cookieName);
20050
20050
  }
20051
- function areCookiesFresh(persona) {
20051
+ function isPrimarySessionCookie(cookieName) {
20052
+ return isSessionCookie(cookieName) && !/refresh/i.test(cookieName);
20053
+ }
20054
+ function getCookieExpires(cookie) {
20055
+ if (typeof cookie.expires === "number" && Number.isFinite(cookie.expires)) {
20056
+ return cookie.expires;
20057
+ }
20058
+ if (typeof cookie.expires === "string") {
20059
+ const parsed = Number(cookie.expires);
20060
+ return Number.isFinite(parsed) ? parsed : null;
20061
+ }
20062
+ return null;
20063
+ }
20064
+ function hasFreshAuthCookies(persona) {
20052
20065
  if (!persona.auth?.cookies?.length)
20053
20066
  return false;
20054
20067
  const cookies = persona.auth.cookies;
20055
- if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
20068
+ const sessionCookies = cookies.filter((cookie) => ("name" in cookie) && isPrimarySessionCookie(String(cookie.name)));
20069
+ if (sessionCookies.length === 0) {
20056
20070
  return false;
20057
20071
  }
20058
20072
  const now2 = Date.now() / 1000;
20059
- const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
20060
- if (hasFutureExpiry)
20061
- return true;
20073
+ const expiringSessionCookies = sessionCookies.map(getCookieExpires).filter((expires) => expires !== null && expires > 0);
20074
+ if (expiringSessionCookies.length > 0) {
20075
+ return expiringSessionCookies.some((expires) => expires > now2 + 60);
20076
+ }
20062
20077
  const updatedAt = new Date(persona.updatedAt).getTime();
20063
20078
  return Date.now() - updatedAt < COOKIE_MAX_AGE_MS;
20064
20079
  }
@@ -20248,7 +20263,7 @@ async function ensurePersonaAuthenticated(page, persona, baseUrl) {
20248
20263
  if (!persona.auth) {
20249
20264
  return { success: true, method: "none" };
20250
20265
  }
20251
- if (areCookiesFresh(persona)) {
20266
+ if (hasFreshAuthCookies(persona)) {
20252
20267
  const restored = await restoreCookies(page, persona);
20253
20268
  if (restored) {
20254
20269
  return { success: true, method: "cookies" };
@@ -53463,6 +53478,40 @@ var init_workflow_agent = __esm(() => {
53463
53478
  init_runner();
53464
53479
  });
53465
53480
 
53481
+ // src/lib/persona-redaction.ts
53482
+ function stringKeys(value) {
53483
+ return value ? Object.keys(value).sort() : [];
53484
+ }
53485
+ function cookieNames(cookies) {
53486
+ if (!cookies)
53487
+ return [];
53488
+ return cookies.map((cookie) => cookie.name).filter((name21) => typeof name21 === "string" && name21.length > 0);
53489
+ }
53490
+ function redactPersona(persona) {
53491
+ if (!persona.auth)
53492
+ return { ...persona, auth: null };
53493
+ const names = cookieNames(persona.auth.cookies);
53494
+ const headerNames = stringKeys(persona.auth.headers);
53495
+ return {
53496
+ ...persona,
53497
+ auth: {
53498
+ emailConfigured: Boolean(persona.auth.email),
53499
+ passwordConfigured: Boolean(persona.auth.password),
53500
+ loginPath: persona.auth.loginPath,
53501
+ strategy: persona.auth.strategy,
53502
+ cookiesConfigured: names.length > 0,
53503
+ cookieCount: names.length,
53504
+ cookieNames: names,
53505
+ headersConfigured: headerNames.length > 0,
53506
+ headerNames,
53507
+ customScriptConfigured: Boolean(persona.auth.customScript)
53508
+ }
53509
+ };
53510
+ }
53511
+ function redactPersonas(personas) {
53512
+ return personas.map(redactPersona);
53513
+ }
53514
+
53466
53515
  // src/db/environments.ts
53467
53516
  var exports_environments = {};
53468
53517
  __export(exports_environments, {
@@ -86939,7 +86988,7 @@ function buildServer() {
86939
86988
  authPassword,
86940
86989
  authLoginPath
86941
86990
  });
86942
- return json3(persona);
86991
+ return json3(redactPersona(persona));
86943
86992
  } catch (error40) {
86944
86993
  return errorResponse(error40);
86945
86994
  }
@@ -86951,7 +87000,7 @@ function buildServer() {
86951
87000
  }, async ({ projectId, enabled, globalOnly }) => {
86952
87001
  try {
86953
87002
  const personas = listPersonas({ projectId, enabled, globalOnly });
86954
- return json3({ items: personas, total: personas.length });
87003
+ return json3({ items: redactPersonas(personas), total: personas.length });
86955
87004
  } catch (error40) {
86956
87005
  return errorResponse(error40);
86957
87006
  }
@@ -86966,7 +87015,7 @@ function buildServer() {
86966
87015
  const db2 = getDatabase();
86967
87016
  const scenarioRows = db2.query("SELECT id, short_id, name FROM scenarios WHERE persona_id = ?").all(persona.id);
86968
87017
  return json3({
86969
- ...persona,
87018
+ ...redactPersona(persona),
86970
87019
  usedByScenarios: scenarioRows.map((r2) => ({ id: r2.id, shortId: r2.short_id, name: r2.name }))
86971
87020
  });
86972
87021
  } catch (error40) {
@@ -86989,7 +87038,7 @@ function buildServer() {
86989
87038
  }, async ({ id, version: version2, ...updates }) => {
86990
87039
  try {
86991
87040
  const persona = updatePersona(id, updates, version2);
86992
- return json3(persona);
87041
+ return json3(redactPersona(persona));
86993
87042
  } catch (error40) {
86994
87043
  return errorResponse(error40, {
86995
87044
  fetchCurrent: () => getPersona(id)
@@ -87020,7 +87069,7 @@ function buildServer() {
87020
87069
  if (!scenario)
87021
87070
  return errorResponse(notFoundErr(scenarioId, "Scenario"));
87022
87071
  const updated = updateScenario(scenario.id, { personaId: persona.id }, scenario.version);
87023
- return json3({ ...updated, attachedPersona: persona });
87072
+ return json3({ ...updated, attachedPersona: redactPersona(persona) });
87024
87073
  } catch (error40) {
87025
87074
  return errorResponse(error40);
87026
87075
  }
@@ -87418,7 +87467,7 @@ function buildServer() {
87418
87467
  projectId: scenario.projectId ?? undefined,
87419
87468
  personaId: persona.id
87420
87469
  });
87421
- return json3({ ...clone2, attachedPersona: persona, clonedFrom: scenario.id });
87470
+ return json3({ ...clone2, attachedPersona: redactPersona(persona), clonedFrom: scenario.id });
87422
87471
  } catch (e2) {
87423
87472
  return errorResponse(e2);
87424
87473
  }
@@ -87683,7 +87732,7 @@ Context: ${context2}` : ""}`,
87683
87732
  const updated = syncPersonaFromContact2(persona.id);
87684
87733
  if (!updated)
87685
87734
  return json3({ synced: false, message: "No linked contact found or no changes needed" });
87686
- return json3({ synced: true, persona: updated });
87735
+ return json3({ synced: true, persona: redactPersona(updated) });
87687
87736
  } catch (e2) {
87688
87737
  return errorResponse(e2);
87689
87738
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAgHpE,wBAAgB,WAAW,IAAI,SAAS,CA2+EvC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiHpE,wBAAgB,WAAW,IAAI,SAAS,CA2+EvC"}
@@ -46910,7 +46910,7 @@ import { join as join14 } from "path";
46910
46910
  // package.json
46911
46911
  var package_default = {
46912
46912
  name: "@hasna/testers",
46913
- version: "0.0.40",
46913
+ version: "0.0.42",
46914
46914
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
46915
46915
  type: "module",
46916
46916
  main: "dist/index.js",
@@ -48543,17 +48543,32 @@ var COOKIE_MAX_AGE_MS = 60 * 60 * 1000;
48543
48543
  function isSessionCookie(cookieName) {
48544
48544
  return !/(?:csrf|xsrf)/i.test(cookieName);
48545
48545
  }
48546
- function areCookiesFresh(persona) {
48546
+ function isPrimarySessionCookie(cookieName) {
48547
+ return isSessionCookie(cookieName) && !/refresh/i.test(cookieName);
48548
+ }
48549
+ function getCookieExpires(cookie) {
48550
+ if (typeof cookie.expires === "number" && Number.isFinite(cookie.expires)) {
48551
+ return cookie.expires;
48552
+ }
48553
+ if (typeof cookie.expires === "string") {
48554
+ const parsed = Number(cookie.expires);
48555
+ return Number.isFinite(parsed) ? parsed : null;
48556
+ }
48557
+ return null;
48558
+ }
48559
+ function hasFreshAuthCookies(persona) {
48547
48560
  if (!persona.auth?.cookies?.length)
48548
48561
  return false;
48549
48562
  const cookies = persona.auth.cookies;
48550
- if (!cookies.some((cookie) => ("name" in cookie) && isSessionCookie(String(cookie.name)))) {
48563
+ const sessionCookies = cookies.filter((cookie) => ("name" in cookie) && isPrimarySessionCookie(String(cookie.name)));
48564
+ if (sessionCookies.length === 0) {
48551
48565
  return false;
48552
48566
  }
48553
48567
  const now2 = Date.now() / 1000;
48554
- const hasFutureExpiry = cookies.some((c) => c.expires && c.expires > now2 + 60);
48555
- if (hasFutureExpiry)
48556
- return true;
48568
+ const expiringSessionCookies = sessionCookies.map(getCookieExpires).filter((expires) => expires !== null && expires > 0);
48569
+ if (expiringSessionCookies.length > 0) {
48570
+ return expiringSessionCookies.some((expires) => expires > now2 + 60);
48571
+ }
48557
48572
  const updatedAt = new Date(persona.updatedAt).getTime();
48558
48573
  return Date.now() - updatedAt < COOKIE_MAX_AGE_MS;
48559
48574
  }
@@ -48743,7 +48758,7 @@ async function ensurePersonaAuthenticated(page, persona, baseUrl) {
48743
48758
  if (!persona.auth) {
48744
48759
  return { success: true, method: "none" };
48745
48760
  }
48746
- if (areCookiesFresh(persona)) {
48761
+ if (hasFreshAuthCookies(persona)) {
48747
48762
  const restored = await restoreCookies(page, persona);
48748
48763
  if (restored) {
48749
48764
  return { success: true, method: "cookies" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.40",
3
+ "version": "0.0.42",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",