cbrowser 19.1.9 → 19.2.1
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/README.md +2 -2
- package/dist/analysis/accessibility-empathy.d.ts +5 -0
- package/dist/analysis/accessibility-empathy.d.ts.map +1 -1
- package/dist/analysis/accessibility-empathy.js +62 -76
- package/dist/analysis/accessibility-empathy.js.map +1 -1
- package/dist/analysis/agent-ready-audit.d.ts.map +1 -1
- package/dist/analysis/agent-ready-audit.js +7 -5
- package/dist/analysis/agent-ready-audit.js.map +1 -1
- package/dist/analysis/competitive-benchmark.js +2 -2
- package/dist/analysis/competitive-benchmark.js.map +1 -1
- package/dist/analysis/natural-language.d.ts +12 -4
- package/dist/analysis/natural-language.d.ts.map +1 -1
- package/dist/analysis/natural-language.js +2395 -504
- package/dist/analysis/natural-language.js.map +1 -1
- package/dist/analysis/persona-comparison.d.ts.map +1 -1
- package/dist/analysis/persona-comparison.js +5 -1
- package/dist/analysis/persona-comparison.js.map +1 -1
- package/dist/browser.d.ts +14 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +20 -2
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +44 -7
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +8 -1
- package/dist/daemon.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +4 -4
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools/account-personas.d.ts +3 -2
- package/dist/mcp-tools/account-personas.d.ts.map +1 -1
- package/dist/mcp-tools/account-personas.js +13 -4
- package/dist/mcp-tools/account-personas.js.map +1 -1
- package/dist/mcp-tools/base/advanced-interaction-tools.js +1 -1
- package/dist/mcp-tools/base/advanced-interaction-tools.js.map +1 -1
- package/dist/mcp-tools/base/analysis-tools.js +4 -4
- package/dist/mcp-tools/base/analysis-tools.js.map +1 -1
- package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/audit-tools.js +42 -15
- package/dist/mcp-tools/base/audit-tools.js.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/browser-state-tools.js +25 -4
- package/dist/mcp-tools/base/browser-state-tools.js.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/cognitive-tools.js +14 -2
- package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
- package/dist/mcp-tools/base/gif-tools.d.ts +19 -0
- package/dist/mcp-tools/base/gif-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/gif-tools.js +53 -24
- package/dist/mcp-tools/base/gif-tools.js.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/persona-comparison-tools.js +38 -28
- package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
- package/dist/mcp-tools/base/values-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/values-tools.js +12 -0
- package/dist/mcp-tools/base/values-tools.js.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.d.ts +17 -0
- package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/visual-testing-tools.js +184 -148
- package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
- package/dist/mcp-tools/inline-image.d.ts +82 -0
- package/dist/mcp-tools/inline-image.d.ts.map +1 -0
- package/dist/mcp-tools/inline-image.js +163 -0
- package/dist/mcp-tools/inline-image.js.map +1 -0
- package/dist/personas.d.ts +32 -0
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +90 -3
- package/dist/personas.js.map +1 -1
- package/dist/security/audit-wrapper.d.ts.map +1 -1
- package/dist/security/audit-wrapper.js +1 -0
- package/dist/security/audit-wrapper.js.map +1 -1
- package/dist/security/script-gate.d.ts +33 -0
- package/dist/security/script-gate.d.ts.map +1 -0
- package/dist/security/script-gate.js +37 -0
- package/dist/security/script-gate.js.map +1 -0
- package/dist/security/tool-permissions.d.ts.map +1 -1
- package/dist/security/tool-permissions.js +4 -0
- package/dist/security/tool-permissions.js.map +1 -1
- package/dist/types.d.ts +39 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,9 +231,9 @@ AI agents need boundaries. CBrowser classifies every action by risk level:
|
|
|
231
231
|
| 🔴 **Red** | Submit, delete, purchase | Requires verification |
|
|
232
232
|
| ⬛ **Black** | Bypass auth, inject scripts | Never executes |
|
|
233
233
|
|
|
234
|
-
**How red is decided:** by the element an action would activate as well as by the selector text. The selector string is classified first, as before, so naming a dangerous action in a selector is refused even when the element reads harmless. The element is then classified however it is addressed: the same "Delete account" button is refused by its label, its id, its test id or a structural path, and an element with only a delegated click listener is judged by its own text. A click on a container is judged at the point it lands - beneath fixed bars and overlays, into open shadow roots, slots and same-origin frames, up to the control, pointer-cursor item or text-bearing item there - and the click is sent to that judged point. A refused click is never retried with a "healed" selector. Gated: `click`, `smart_click`, hover-click and the daemon's click; `press_key`, `type_text` and `keyboard` (Enter, including a typed or pressed newline, or Space on the focused control - followed into shadow roots and same-origin frames - and Enter in a field whose form submits one, judged together with a combobox's `aria-activedescendant` item); a `drag` whose press and release share a control; and the NL test runner's `click at X, Y`. Names below a control (icon labels, image alts, slotted and open-shadow text, `<label for>`) and the path a form submits to (a submitter's `formaction`, otherwise the form's `action`) feed the same keyword classification. `force: true` on the MCP `click`, `press_key`, `type_text` and `
|
|
234
|
+
**How red is decided:** by the element an action would activate as well as by the selector text. The selector string is classified first, as before, so naming a dangerous action in a selector is refused even when the element reads harmless. The element is then classified however it is addressed: the same "Delete account" button is refused by its label, its id, its test id or a structural path, and an element with only a delegated click listener is judged by its own text. A click on a container is judged at the point it lands - beneath fixed bars and overlays, into open shadow roots, slots and same-origin frames, up to the control, pointer-cursor item or text-bearing item there - and the click is sent to that judged point. A refused click is never retried with a "healed" selector. Gated: `click`, `smart_click`, hover-click and the daemon's click; `press_key`, `type_text` and `keyboard` (Enter, including a typed or pressed newline, or Space on the focused control - followed into shadow roots and same-origin frames - and Enter in a field whose form submits one, judged together with a combobox's `aria-activedescendant` item); a `drag` whose press and release share a control; and the NL test runner's `click at X, Y`. Caller-supplied page script is red by itself, whatever it does: `evaluate_script` over MCP, `evaluate`/`eval` on the CLI and the daemon's evaluate are refused without force and run nothing, because a script can activate any control without passing the element gate (`document.querySelector('#delete-account').click()` is the click `click` refuses, in one line). Names below a control (icon labels, image alts, slotted and open-shadow text, `<label for>`) and the path a form submits to (a submitter's `formaction`, otherwise the form's `action`) feed the same keyword classification. `force: true` on the MCP `click`, `press_key`, `type_text`, `drag` and `evaluate_script` tools (`smart_click` has none: use `click`), or `--force` on the CLI, proceeds and reports the zone.
|
|
235
235
|
|
|
236
|
-
Not judged, by design: clicking or typing into a text field or a contenteditable editor, and choosing a value with a native checkbox, radio or select or a `role=checkbox`, `radio` or `switch` - the submit that commits them is judged, but a switch or checkbox that saves on its own as it changes commits immediately and is not refused; and a link's address (a link to `/buy` is navigation). Not gated: cognitive journeys and other engines that choose their own clicks
|
|
236
|
+
Not judged, by design: clicking or typing into a text field or a contenteditable editor, and choosing a value with a native checkbox, radio or select or a `role=checkbox`, `radio` or `switch` - the submit that commits them is judged, but a switch or checkbox that saves on its own as it changes commits immediately and is not refused; and a link's address (a link to `/buy` is navigation). Not gated: cognitive journeys and other engines that choose their own clicks. Known limits: labels inside closed shadow roots or drawn by CSS, controls inside cross-origin frames, icon-only controls with no accessible name, a confirmation button with neutral text ("Confirm", "OK") inside a dialog, a form with no `action` or an empty `formaction` (it submits to the page's own address, which is not judged), an `aria-activedescendant` id that only resolves outside the field's own document or shadow root (not followed; ARIA references do not cross roots), a Tab inside typed text that moves focus before a later Enter, one round trip between the check and the click in which a page could swap the element, and keyword false positives: a "Purchase history" tab, a "Buyer protection" link, a `role=option` value picker such as "Pay with PayPal", a harmless "Apply" in a form posting to `/checkout/discount`, and an inert heading such as "Checkout" clicked by id (refusing it costs one forced retry; missing a listener-only "Delete account" cannot be undone). Classification uses English keyword patterns; when the element cannot be read, the selector text alone decides.
|
|
237
237
|
|
|
238
238
|
---
|
|
239
239
|
|
|
@@ -49,6 +49,11 @@ export declare function filterBarriersToViewport<T extends {
|
|
|
49
49
|
};
|
|
50
50
|
export declare function formatEmpathyAuditReport(result: EmpathyAuditResult): string;
|
|
51
51
|
export declare function generateEmpathyAuditHtmlReport(result: EmpathyAuditResult): string;
|
|
52
|
+
/**
|
|
53
|
+
* Map disability names to personas.
|
|
54
|
+
* v14.2.5: Added elderly-user mapping (issue #190 - persona dropout)
|
|
55
|
+
*/
|
|
56
|
+
export declare const EMPATHY_PERSONA_ALIASES: Record<string, string>;
|
|
52
57
|
/**
|
|
53
58
|
* The published WCAG list, DERIVED from the barriers rather than accumulated
|
|
54
59
|
* alongside them.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessibility-empathy.d.ts","sourceRoot":"","sources":["../../src/analysis/accessibility-empathy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EASpB,MAAM,aAAa,CAAC;AAuKrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACnH,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAWhC;AAwjED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAmF3E;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAkTjF;AAqJD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,KAAK,CAAC;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAC7E,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,EAChC,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,EACnC,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC5B,MAAM,EAAE,CAkBV;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"accessibility-empathy.d.ts","sourceRoot":"","sources":["../../src/analysis/accessibility-empathy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EASpB,MAAM,aAAa,CAAC;AAuKrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACnH,QAAQ,EAAE,CAAC,EAAE,EACb,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAWhC;AAwjED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAmF3E;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAkTjF;AAqJD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoC1D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,KAAK,CAAC;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAC7E,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,EAChC,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,EACnC,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,KAAK,GAC5B,MAAM,EAAE,CAkBV;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAsW7B"}
|
|
@@ -2521,6 +2521,47 @@ function inferGoalType(goal) {
|
|
|
2521
2521
|
return "navigate_to";
|
|
2522
2522
|
return "complete_action";
|
|
2523
2523
|
}
|
|
2524
|
+
/**
|
|
2525
|
+
* Map disability names to personas.
|
|
2526
|
+
* v14.2.5: Added elderly-user mapping (issue #190 - persona dropout)
|
|
2527
|
+
*/
|
|
2528
|
+
export const EMPATHY_PERSONA_ALIASES = {
|
|
2529
|
+
"motor-tremor": "motor-impairment-tremor",
|
|
2530
|
+
"motor": "motor-impairment-tremor",
|
|
2531
|
+
"tremor": "motor-impairment-tremor",
|
|
2532
|
+
"low-vision": "low-vision-magnified",
|
|
2533
|
+
"vision": "low-vision-magnified",
|
|
2534
|
+
"magnified": "low-vision-magnified",
|
|
2535
|
+
"adhd": "cognitive-adhd",
|
|
2536
|
+
"cognitive": "cognitive-adhd",
|
|
2537
|
+
"attention": "cognitive-adhd",
|
|
2538
|
+
"dyslexia": "dyslexic-user",
|
|
2539
|
+
"dyslexic": "dyslexic-user",
|
|
2540
|
+
"reading": "dyslexic-user",
|
|
2541
|
+
"deaf": "deaf-user",
|
|
2542
|
+
"hearing": "deaf-user",
|
|
2543
|
+
"elderly": "elderly-low-vision",
|
|
2544
|
+
"elderly-user": "elderly-low-vision", // v14.2.5: Added missing mapping
|
|
2545
|
+
"elderly-low-vision": "elderly-low-vision",
|
|
2546
|
+
"senior": "elderly-low-vision",
|
|
2547
|
+
"old": "elderly-low-vision", // v14.2.5: Additional synonym
|
|
2548
|
+
"color-blind": "color-blind-deuteranopia",
|
|
2549
|
+
"colorblind": "color-blind-deuteranopia",
|
|
2550
|
+
"deuteranopia": "color-blind-deuteranopia",
|
|
2551
|
+
// v18.35.0: New research-backed cognitive disability personas
|
|
2552
|
+
"autism": "autism-spectrum",
|
|
2553
|
+
"autistic": "autism-spectrum",
|
|
2554
|
+
"asd": "autism-spectrum",
|
|
2555
|
+
"autism-spectrum": "autism-spectrum",
|
|
2556
|
+
"intellectual-disability": "intellectual-disability",
|
|
2557
|
+
"intellectual": "intellectual-disability",
|
|
2558
|
+
"learning-disability": "intellectual-disability",
|
|
2559
|
+
"aphasia": "aphasia-receptive",
|
|
2560
|
+
"aphasia-receptive": "aphasia-receptive",
|
|
2561
|
+
"wernicke": "aphasia-receptive",
|
|
2562
|
+
"dyscalculia": "dyscalculia",
|
|
2563
|
+
"numeracy": "dyscalculia",
|
|
2564
|
+
};
|
|
2524
2565
|
/**
|
|
2525
2566
|
* The published WCAG list, DERIVED from the barriers rather than accumulated
|
|
2526
2567
|
* alongside them.
|
|
@@ -2569,90 +2610,35 @@ export async function runEmpathyAudit(url, options) {
|
|
|
2569
2610
|
const results = [];
|
|
2570
2611
|
const allWcagViolations = new Set();
|
|
2571
2612
|
const allBarriers = [];
|
|
2572
|
-
//
|
|
2573
|
-
//
|
|
2574
|
-
const personaMap =
|
|
2575
|
-
"motor-tremor": "motor-impairment-tremor",
|
|
2576
|
-
"motor": "motor-impairment-tremor",
|
|
2577
|
-
"tremor": "motor-impairment-tremor",
|
|
2578
|
-
"low-vision": "low-vision-magnified",
|
|
2579
|
-
"vision": "low-vision-magnified",
|
|
2580
|
-
"magnified": "low-vision-magnified",
|
|
2581
|
-
"adhd": "cognitive-adhd",
|
|
2582
|
-
"cognitive": "cognitive-adhd",
|
|
2583
|
-
"attention": "cognitive-adhd",
|
|
2584
|
-
"dyslexia": "dyslexic-user",
|
|
2585
|
-
"dyslexic": "dyslexic-user",
|
|
2586
|
-
"reading": "dyslexic-user",
|
|
2587
|
-
"deaf": "deaf-user",
|
|
2588
|
-
"hearing": "deaf-user",
|
|
2589
|
-
"elderly": "elderly-low-vision",
|
|
2590
|
-
"elderly-user": "elderly-low-vision", // v14.2.5: Added missing mapping
|
|
2591
|
-
"elderly-low-vision": "elderly-low-vision",
|
|
2592
|
-
"senior": "elderly-low-vision",
|
|
2593
|
-
"old": "elderly-low-vision", // v14.2.5: Additional synonym
|
|
2594
|
-
"color-blind": "color-blind-deuteranopia",
|
|
2595
|
-
"colorblind": "color-blind-deuteranopia",
|
|
2596
|
-
"deuteranopia": "color-blind-deuteranopia",
|
|
2597
|
-
// v18.35.0: New research-backed cognitive disability personas
|
|
2598
|
-
"autism": "autism-spectrum",
|
|
2599
|
-
"autistic": "autism-spectrum",
|
|
2600
|
-
"asd": "autism-spectrum",
|
|
2601
|
-
"autism-spectrum": "autism-spectrum",
|
|
2602
|
-
"intellectual-disability": "intellectual-disability",
|
|
2603
|
-
"intellectual": "intellectual-disability",
|
|
2604
|
-
"learning-disability": "intellectual-disability",
|
|
2605
|
-
"aphasia": "aphasia-receptive",
|
|
2606
|
-
"aphasia-receptive": "aphasia-receptive",
|
|
2607
|
-
"wernicke": "aphasia-receptive",
|
|
2608
|
-
"dyscalculia": "dyscalculia",
|
|
2609
|
-
"numeracy": "dyscalculia",
|
|
2610
|
-
};
|
|
2613
|
+
// Disability shorthand -> roster name (module-level so the MCP handler
|
|
2614
|
+
// resolves with the same table).
|
|
2615
|
+
const personaMap = EMPATHY_PERSONA_ALIASES;
|
|
2611
2616
|
// Run audit for each disability type
|
|
2612
2617
|
// v18.35.0: Accept any persona — wrap non-disability personas with default accessibility traits
|
|
2613
|
-
|
|
2614
|
-
|
|
2618
|
+
// Every requested persona resolves through the one resolver BEFORE anything
|
|
2619
|
+
// launches. An unknown name used to fall through to a "persona" with no
|
|
2620
|
+
// traits that scored 0 -- the worst possible score -- with errors: [] and
|
|
2621
|
+
// isDisabilityPersona: true, which a dashboard reads as "the site failed
|
|
2622
|
+
// completely". It now throws UnknownPersonaError instead. (2026-10-09)
|
|
2623
|
+
try {
|
|
2624
|
+
const { loadAccountPersonas } = await import("../mcp-tools/account-personas.js");
|
|
2625
|
+
const { getSessionApiKey } = await import("../mcp-tools/base/cognitive-tools.js");
|
|
2626
|
+
await loadAccountPersonas(getSessionApiKey());
|
|
2627
|
+
}
|
|
2628
|
+
catch { /* falls back to disk and built-ins */ }
|
|
2629
|
+
const { resolvePersonaForTool } = await import("../personas.js");
|
|
2630
|
+
const resolvedNames = disabilities.map((d) => resolvePersonaForTool(d, personaMap).name);
|
|
2631
|
+
for (const [index, disability] of disabilities.entries()) {
|
|
2632
|
+
const personaName = resolvedNames[index];
|
|
2615
2633
|
let persona = getAccessibilityPersona(personaName);
|
|
2616
2634
|
let isDisabilityPersona = !!persona;
|
|
2617
2635
|
if (!persona) {
|
|
2618
2636
|
// Try to find as a regular cognitive persona and wrap it
|
|
2619
2637
|
const { getAnyPersona, createCognitivePersona } = await import("../personas.js");
|
|
2620
2638
|
let cognitivePersona = getAnyPersona(personaName);
|
|
2621
|
-
// CMS
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
const { getSessionApiKey } = await import("../mcp-tools/base/cognitive-tools.js");
|
|
2625
|
-
const apiKey = getSessionApiKey();
|
|
2626
|
-
if (apiKey) {
|
|
2627
|
-
const cmsUrl = process.env.CMS_URL || "http://localhost:3200";
|
|
2628
|
-
const res = await fetch(`${cmsUrl}/api/personas`, {
|
|
2629
|
-
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
2630
|
-
});
|
|
2631
|
-
if (res.ok) {
|
|
2632
|
-
const data = await res.json();
|
|
2633
|
-
const cmsMatch = data.personas.find((p) => p.name.toLowerCase() === personaName.toLowerCase() ||
|
|
2634
|
-
p.name.toLowerCase().replace(/\s+/g, '-') === personaName.toLowerCase() ||
|
|
2635
|
-
p.name.toLowerCase().replace(/\s+/g, '_') === personaName.toLowerCase() ||
|
|
2636
|
-
(p.slug && p.slug.toLowerCase() === personaName.toLowerCase()));
|
|
2637
|
-
if (cmsMatch) {
|
|
2638
|
-
const parsedTraits = typeof cmsMatch.traits === "string" ? JSON.parse(cmsMatch.traits) : cmsMatch.traits;
|
|
2639
|
-
cognitivePersona = createCognitivePersona(cmsMatch.name, cmsMatch.name, parsedTraits, {
|
|
2640
|
-
ageRange: cmsMatch.age_range || undefined,
|
|
2641
|
-
});
|
|
2642
|
-
// Attach CMS accessibility traits if stored
|
|
2643
|
-
if (cmsMatch.accessibility_traits) {
|
|
2644
|
-
const at = typeof cmsMatch.accessibility_traits === "string" ? JSON.parse(cmsMatch.accessibility_traits) : cmsMatch.accessibility_traits;
|
|
2645
|
-
cognitivePersona.accessibilityTraits = at;
|
|
2646
|
-
}
|
|
2647
|
-
console.log(`[empathy_audit] Found custom CMS persona: "${cmsMatch.name}" with ${Object.keys(parsedTraits).length} traits${cmsMatch.accessibility_traits ? ' + disability modeling' : ''}`);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
}
|
|
2652
|
-
catch (e) {
|
|
2653
|
-
console.debug(`[empathy_audit] CMS persona lookup failed: ${e.message}`);
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2639
|
+
// No CMS fallback here any more: the account's CMS personas were loaded
|
|
2640
|
+
// into the scoped registry above, so getAnyPersona already sees them, and
|
|
2641
|
+
// a name nothing resolves was refused before the loop. (2026-10-09)
|
|
2656
2642
|
if (cognitivePersona) {
|
|
2657
2643
|
// Wrap cognitive persona as accessibility persona, inferring traits from name + cognitive traits
|
|
2658
2644
|
const ct = cognitivePersona.cognitiveTraits || {};
|