website-api 1.0.6 → 1.1.2

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 (84) hide show
  1. package/dist/bin/cli.js +1 -1
  2. package/dist/src/capabilities/browser.d.ts +23 -0
  3. package/dist/src/capabilities/browser.js +1 -0
  4. package/dist/src/capabilities/cookies.d.ts +24 -0
  5. package/dist/src/capabilities/cookies.js +1 -0
  6. package/dist/src/capabilities/download.d.ts +11 -0
  7. package/dist/src/capabilities/download.js +1 -0
  8. package/dist/src/capabilities/fingerprint.d.ts +14 -0
  9. package/dist/src/capabilities/fingerprint.js +1 -0
  10. package/dist/src/capabilities/http.d.ts +18 -0
  11. package/dist/src/capabilities/http.js +1 -0
  12. package/dist/src/{util → capabilities/login}/login-helper.d.ts +12 -5
  13. package/dist/src/capabilities/login/login-helper.js +1 -0
  14. package/dist/src/capabilities/login/login-strategy.d.ts +15 -0
  15. package/dist/src/capabilities/login/login-strategy.js +1 -0
  16. package/dist/src/cli/ext.d.ts +3 -0
  17. package/dist/src/cli/ext.js +1 -0
  18. package/dist/src/core/context.d.ts +24 -0
  19. package/dist/src/core/context.js +1 -0
  20. package/dist/src/core/define-site.d.ts +9 -0
  21. package/dist/src/core/define-site.js +1 -0
  22. package/dist/src/core/loader.d.ts +21 -0
  23. package/dist/src/core/loader.js +1 -0
  24. package/dist/src/core/registry.d.ts +116 -0
  25. package/dist/src/core/registry.js +1 -0
  26. package/dist/src/core/runtime.d.ts +20 -0
  27. package/dist/src/core/runtime.js +1 -0
  28. package/dist/src/{website → sites}/chase.com/download-helper.d.ts +5 -4
  29. package/dist/src/sites/chase.com/download-helper.js +1 -0
  30. package/dist/src/sites/chase.com/index.d.ts +2 -0
  31. package/dist/src/sites/chase.com/index.js +1 -0
  32. package/dist/src/sites/chatgpt.com/index.d.ts +10 -0
  33. package/dist/src/sites/chatgpt.com/index.js +1 -0
  34. package/dist/src/sites/cursor.com/index.d.ts +6 -0
  35. package/dist/src/sites/cursor.com/index.js +1 -0
  36. package/dist/src/sites/gemini.google.com/index.d.ts +5 -0
  37. package/dist/src/sites/gemini.google.com/index.js +1 -0
  38. package/dist/src/sites/google.com/google-helpers.d.ts +24 -0
  39. package/dist/src/sites/google.com/google-helpers.js +1 -0
  40. package/dist/src/sites/google.com/index.d.ts +2 -0
  41. package/dist/src/sites/google.com/index.js +1 -0
  42. package/dist/src/sites/ollama.com/index.d.ts +9 -0
  43. package/dist/src/sites/ollama.com/index.js +1 -0
  44. package/dist/src/sites/perplexity.ai/index.d.ts +50 -0
  45. package/dist/src/sites/perplexity.ai/index.js +1 -0
  46. package/dist/src/sites/pseg.com/index.d.ts +2 -0
  47. package/dist/src/sites/pseg.com/index.js +1 -0
  48. package/dist/src/sites/pseg.com/pseg-helpers.d.ts +13 -0
  49. package/dist/src/sites/pseg.com/pseg-helpers.js +1 -0
  50. package/dist/src/types.d.ts +194 -46
  51. package/dist/src/util/args-parser.js +1 -1
  52. package/dist/src/website-api.d.ts +9 -34
  53. package/dist/src/website-api.js +1 -1
  54. package/package.json +10 -1
  55. package/dist/src/adapter/base-adapter.d.ts +0 -41
  56. package/dist/src/adapter/base-adapter.js +0 -1
  57. package/dist/src/adapter/playwright-attatch-chrome-adapter.d.ts +0 -16
  58. package/dist/src/adapter/playwright-attatch-chrome-adapter.js +0 -1
  59. package/dist/src/adapter/playwright-core.d.ts +0 -35
  60. package/dist/src/adapter/playwright-core.js +0 -1
  61. package/dist/src/adapter/universal-adapter.d.ts +0 -10
  62. package/dist/src/adapter/universal-adapter.js +0 -1
  63. package/dist/src/util/login-helper.js +0 -1
  64. package/dist/src/website/chase.com/account-helper.d.ts +0 -20
  65. package/dist/src/website/chase.com/account-helper.js +0 -1
  66. package/dist/src/website/chase.com/chase-adapter.d.ts +0 -43
  67. package/dist/src/website/chase.com/chase-adapter.js +0 -1
  68. package/dist/src/website/chase.com/download-helper.js +0 -1
  69. package/dist/src/website/chatgpt.com/chatgpt-adapter.d.ts +0 -11
  70. package/dist/src/website/chatgpt.com/chatgpt-adapter.js +0 -1
  71. package/dist/src/website/cursor.com/cursor-adapter.d.ts +0 -6
  72. package/dist/src/website/cursor.com/cursor-adapter.js +0 -1
  73. package/dist/src/website/example.com/example-adapter.d.ts +0 -12
  74. package/dist/src/website/example.com/example-adapter.js +0 -1
  75. package/dist/src/website/gemini.google.com/gemini-adapter.d.ts +0 -12
  76. package/dist/src/website/gemini.google.com/gemini-adapter.js +0 -1
  77. package/dist/src/website/google.com/google-adapter.d.ts +0 -62
  78. package/dist/src/website/google.com/google-adapter.js +0 -1
  79. package/dist/src/website/ollama.com/ollama-adapter.d.ts +0 -2
  80. package/dist/src/website/ollama.com/ollama-adapter.js +0 -1
  81. package/dist/src/website/perplexity.ai/perplexity-adapter.d.ts +0 -2
  82. package/dist/src/website/perplexity.ai/perplexity-adapter.js +0 -1
  83. package/dist/src/website/pseg.com/pseg-adapter.d.ts +0 -45
  84. package/dist/src/website/pseg.com/pseg-adapter.js +0 -1
@@ -1,35 +0,0 @@
1
- import { type Browser, type Page } from "playwright-core";
2
- export interface PlaywrightOptions {
3
- cdpEndpoint?: string;
4
- debug?: boolean;
5
- close?: boolean;
6
- }
7
- export interface PlaywrightTabResult {
8
- page: Page;
9
- browser: Browser;
10
- [Symbol.asyncDispose](): Promise<void>;
11
- }
12
- /**
13
- * Connects to an existing Chrome instance via CDP and retrieves or creates a tab for the target URL.
14
- *
15
- * @param targetUrl The website URL to open/connect to.
16
- * @param options Connection options.
17
- */
18
- export declare function connectChrome(targetUrl: string, options?: PlaywrightOptions): Promise<PlaywrightTabResult>;
19
- /**
20
- * Runs or prepares a Playwright task on a target URL inside an existing Chrome tab.
21
- *
22
- * Supports two modes:
23
- * 1. Callback Mode (3 arguments): Connects, runs action, extracts HTML, and returns { html }.
24
- * 2. Disposable Page Mode (2 arguments): Connects and returns a Page proxy implementing [Symbol.asyncDispose]
25
- * which automatically extracts the HTML into options.htmlResult on disposal.
26
- *
27
- * @param endpoints The endpoints configuration of the adapter.
28
- * @param options Query and debug options.
29
- * @param action Optional action callback to interact with the page.
30
- */
31
- export declare function runWithPlaywright(endpoints: {
32
- url: string;
33
- }[] | undefined, options: {
34
- debug?: boolean;
35
- }, action?: (page: Page) => Promise<void>): Promise<any>;
@@ -1 +0,0 @@
1
- var e=this&&this.__addDisposableResource||function(e,r,o){if(null!=r){if("object"!=typeof r&&"function"!=typeof r)throw new TypeError("Object expected.");var t,n;if(o){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");t=r[Symbol.asyncDispose]}if(void 0===t){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");t=r[Symbol.dispose],o&&(n=t)}if("function"!=typeof t)throw new TypeError("Object not disposable.");n&&(t=function(){try{n.call(this)}catch(e){return Promise.reject(e)}}),e.stack.push({value:r,dispose:t,async:o})}else o&&e.stack.push({async:!0});return r},r=this&&this.__disposeResources||function(e){return function(r){function o(o){r.error=r.hasError?new e(o,r.error,"An error was suppressed during disposal."):o,r.hasError=!0}var t,n=0;return function e(){for(;t=r.stack.pop();)try{if(!t.async&&1===n)return n=0,r.stack.push(t),Promise.resolve().then(e);if(t.dispose){var s=t.dispose.call(t.value);if(t.async)return n|=2,Promise.resolve(s).then(e,function(r){return o(r),e()})}else n|=1}catch(e){o(e)}if(1===n)return r.hasError?Promise.reject(r.error):Promise.resolve();if(r.hasError)throw r.error}()}}("function"==typeof SuppressedError?SuppressedError:function(e,r,o){var t=new Error(o);return t.name="SuppressedError",t.error=e,t.suppressed=r,t});import{chromium as o}from"playwright-core";export async function connectChrome(e,r={}){const t=r.cdpEndpoint||process.env.CDP_ENDPOINT||"http://localhost:9222",n=!!r.debug,s=await o.connectOverCDP(t),i=s.contexts()[0];if(!i)throw new Error("No active browser context found. Is Chrome running with remote debugging enabled?");let c=i.pages().find(r=>{try{const o=new URL(e).hostname.replace("www.","");return new URL(r.url()).hostname.endsWith(o)||r.url().startsWith(e)}catch{return r.url().startsWith(e)}}),a=!1;return c?n&&console.log(`Found existing tab with target URL: ${e}. Reusing it...`):(n&&console.log(`Target URL not found. Opening a new tab for: ${e}...`),c=await i.newPage(),await c.goto(e,{waitUntil:"domcontentloaded"}),a=!0),{page:c,browser:s,async[Symbol.asyncDispose](){if(a&&!1!==r.close){n&&console.log("Closing newly opened tab...");try{await c.close()}catch{}}n&&console.log("Disconnecting from Chrome CDP..."),await s.close()}}}export async function runWithPlaywright(o,t,n){if(!o||0===o.length)throw new Error("No endpoints defined in adapter.");const s=o[0].url;if(!n){const e=await connectChrome(s,{cdpEndpoint:process.env.CDP_ENDPOINT,debug:t.debug,close:t.close});return new Proxy(e.page,{get(r,o,n){if(o===Symbol.asyncDispose)return async()=>{try{const e=await r.content();t.htmlResult=e}catch(e){t.debug&&console.error("Failed to extract page content in disposable:",e)}await e[Symbol.asyncDispose]()};const s=Reflect.get(r,o,n);return"function"==typeof s?s.bind(r):s}})}{const o={stack:[],error:void 0,hasError:!1};try{const r=e(o,await connectChrome(s,{cdpEndpoint:process.env.CDP_ENDPOINT,debug:t.debug,close:t.close}),!0);await n(r.page);return{html:await r.page.content()}}catch(e){o.error=e,o.hasError=!0}finally{const e=r(o);e&&await e}}}
@@ -1,10 +0,0 @@
1
- import type { WebsiteAdapter } from "../types.js";
2
- /**
3
- * Creates a universal fallback adapter for any domain that doesn't have
4
- * a dedicated adapter. Parses the websiteId as a URL and uses the default
5
- * single-endpoint fetch flow.
6
- *
7
- * @param websiteId - A URL or domain string (e.g. "example.com/api/data")
8
- * @returns A WebsiteAdapter if the URL is valid, or null otherwise.
9
- */
10
- export declare function createUniversalAdapter(websiteId: string): WebsiteAdapter | null;
@@ -1 +0,0 @@
1
- import{defineAdapter as t}from"./base-adapter.js";export function createUniversalAdapter(r){let e=r;e.startsWith("http://")||e.startsWith("https://")||(e="https://"+e);try{const a=new URL(e);return t({id:r,name:r,domain:a.hostname,description:`Universal adapter for ${a.hostname}`,endpoints:[{url:a.href}]})}catch{return null}}
@@ -1 +0,0 @@
1
- export async function performFormLogin(t){const{page:i,emailSelector:e,emailValue:o,passwordSelector:a,passwordValue:n,submitButtonSelector:r,delayMs:s=1e3,intendedUrl:l,loginUrl:c,expectedRedirectUrlPattern:u=l,debug:p=!1,pwdSelector:d='input[type="password"]',dashboardSelectors:f=[]}=t;if(l){if(!i.url().includes(l)){p&&console.log(`[LoginHelper] Navigating to intended URL: ${l}`);try{await i.goto(l,{waitUntil:"domcontentloaded"})}catch(t){p&&console.warn(`[LoginHelper] Warning navigating to ${l}:`,t)}}}try{await i.waitForLoadState("domcontentloaded")}catch(t){}let g=!1,w=i;p&&console.log("[LoginHelper] Waiting for session state to settle (detecting logon form or dashboard)...");const b=Date.now();for(;Date.now()-b<15e3;){try{if(await i.locator(d).first().isVisible()){g=!0,w=i;break}}catch{}let t=!1;for(const e of i.frames())try{if(await e.locator(d).first().isVisible()){g=!0,w=e,t=!0;break}}catch{}if(t)break;let e=!1;for(const t of f)try{if(await i.locator(t).first().isVisible()){g=!1,e=!0;break}}catch{}if(e)break;await i.waitForTimeout(500)}if(!g&&Date.now()-b>=15e3)try{await i.waitForSelector(e,{state:"visible",timeout:1e3}),g=!0,w=i}catch{try{await i.waitForSelector(a,{state:"visible",timeout:1e3}),g=!0,w=i}catch{g=!1}}if(!g)return p&&console.log("[LoginHelper] Already logged in (Dashboard active). Skipping login flow."),!1;let m=e,y=a,h=r;const L=[e,"input#userId-input","input#userId",'input[name="usr_name"]','input[type="text"][id*="user"]','input[type="text"][id*="Id"]','input[type="text"]','input[type="email"]'];for(const t of L)try{const i=w.locator(t).first();if(await i.isVisible()){m=t;break}}catch{}const k=[a,"input#password-input","input#password",'input[type="password"]','input[name*="password"]'];for(const t of k)try{const i=w.locator(t).first();if(await i.isVisible()){y=t;break}}catch{}const S=[r,'button[type="submit"]',"#signin-button","button#signin-button",'input[type="submit"]'];for(const t of S)try{const i=w.locator(t).first();if(await i.isVisible()){h=t;break}}catch{}if(p&&console.log(`[LoginHelper] Login form detected inside frame: ${w===i?"main":w.url()}. Proceeding using email='${m}', password='${y}', submit='${h}'...`),await w.fill(m,o),await w.fill(y,n),s>0&&(p&&console.log(`[LoginHelper] Waiting ${s}ms before submission...`),await w.waitForTimeout(s)),p&&console.log(`[LoginHelper] Clicking submit button '${h}'...`),await w.click(h),u){p&&console.log(`[LoginHelper] Waiting for redirection matching '${u}'...`);try{await i.waitForURL(u,{timeout:15e3})}catch{try{await i.waitForNavigation({waitUntil:"networkidle",timeout:8e3})}catch{}}}return!0}
@@ -1,20 +0,0 @@
1
- export interface ChaseAccount {
2
- name: string;
3
- last4: string;
4
- balance: number | string;
5
- type: string;
6
- category: string;
7
- raw?: any;
8
- }
9
- export interface ChaseAccountGroup {
10
- groupName: string;
11
- accounts: ChaseAccount[];
12
- }
13
- /**
14
- * Recursively parses and extracts account-like elements from the Chase REST API JSON payload.
15
- */
16
- export declare function decodeChaseAccounts(jsonResult: any): ChaseAccountGroup[];
17
- /**
18
- * Returns a beautiful, premium explained summary of the decoded Chase bank accounts.
19
- */
20
- export declare function explainAccounts(groups: ChaseAccountGroup[], rawJson?: any): string;
@@ -1 +0,0 @@
1
- export function decodeChaseAccounts(t){const e=[];if(!t||"object"!=typeof t)return e;const a=t=>{if(!t||"object"!=typeof t)return[];let e=[];Array.isArray(t.productInfos)&&(e=e.concat(t.productInfos));for(const n of Object.keys(t))"object"==typeof t[n]&&(e=e.concat(a(t[n])));return e},n=a(t),c=new Map;for(const t of n)t.accountId&&c.set(String(t.accountId),t),t.mask&&c.set(String(t.mask),t);const o=t=>{if(!t||"object"!=typeof t)return[];let e=[];const a=c.get(String(t.accountId||t.id||""))||(t.mask?c.get(String(t.mask)):void 0),n="string"==typeof(t.nickname||t.accountName||t.name||t.displayName||a?.nickName||a?.cardDefaultNickName),i=function(t){if(!t||"object"!=typeof t)return;const e=t.ledgerBalance??t.availableBalance??t.currentBalance??t.balance??t.amount??t.accountValue;if("number"==typeof e)return e;const a=t.bankingAccountDetail?.availableBalance??t.bankingAccountDetail?.currentBalance??t.cardAccountDetail?.currentBalance??t.detail?.accountValue??t.detail?.currentBalance;return"number"==typeof a?a:void 0}(t);if(n&&void 0!==i)return[{...t,balance:i}];for(const a of Object.keys(t)){const n=t[a];if(Array.isArray(n))for(const t of n)e=e.concat(o(t));else"object"==typeof n&&(e=e.concat(o(n)))}return e},i=new Set,r=o(t),s=[],u=[],l=[],p=[];for(const t of r){const e=t.mask||t.last4||t.accountNumberLast4Digits||t.suffix||"N/A",a=c.get(String(t.accountId||t.id||""))||c.get(e),n=t.nickname||t.accountName||t.name||t.displayName||a?.nickName||a?.cardDefaultNickName||"Account",o=t.mask||t.last4||t.accountNumberLast4Digits||t.suffix||a?.mask||"N/A",r=`${n.toLowerCase()}_${o}`;if(i.has(r))continue;i.add(r);let m=t.accountType||t.accountTileDetailType||t.detailType||"Unknown",f=t.accountCategoryType||t.categoryType||t.accountTileType||t.groupType||"Unknown";if(a&&a.productId){const t=a.productId.split("-");t.length>=2&&(f=t[0],m=t[1])}const g={name:n,last4:o,balance:t.balance,type:m,category:f,raw:t};"DDA"===f||"CHK"===m?s.push(g):"CARD"===f||"BAC"===m?u.push(g):"INVESTMENT"===f||"BR2"===m?l.push(g):p.push(g)}return s.length>0&&e.push({groupName:"Bank accounts",accounts:s}),l.length>0&&e.push({groupName:"Investment accounts by J.P. Morgan",accounts:l}),u.length>0&&e.push({groupName:"Credit cards",accounts:u}),p.length>0&&e.push({groupName:"Other accounts",accounts:p}),e}export function explainAccounts(t,e){const a=["💼 Chase Account Summary (REST API Decoded):"];if(0===t.length)a.push("\n⚠️ No accounts could be successfully decoded from the JSON response."),e&&a.push("Please check the raw response below to verify the payload structure.");else{let e=0,n=0;for(const c of t){a.push(`\n📁 ${c.groupName}:`);for(const t of c.accounts){const c="number"==typeof t.balance?`$${t.balance.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:2})}`:String(t.balance);if(a.push(` 🔹 ${t.name} (...${t.last4}) - Balance: ${c} [Type: ${t.type}]`),"number"==typeof t.balance){("CARD"===t.category||"BAC"===t.type||"CARD"===t.raw?.categoryType||"CARD"===t.raw?.accountCategoryType||"CARD"===t.raw?.accountTileType)&&"CHK"!==t.type||t.category.toLowerCase().includes("debt")||t.balance<0?n+=Math.abs(t.balance):e+=t.balance}}}if(e>0||n>0){a.push("\n📈 Portfolio Overview:"),a.push(` 💸 Total Liquid Assets: $${e.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:2})}`),a.push(` 💳 Total Credit Debts: $${n.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:2})}`);const t=e-n;a.push(` 💎 Net Cash/Portfolio Value: $${t.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:2})}`)}}return e&&(a.push("\n📋 Raw REST API JSON Response:"),a.push(JSON.stringify(e,null,2))),a.join("\n")}
@@ -1,43 +0,0 @@
1
- import { PlaywrightAdapter } from "../../adapter/playwright-attatch-chrome-adapter.js";
2
- export default class extends PlaywrightAdapter {
3
- id: string;
4
- name: string;
5
- domain: string;
6
- description: string;
7
- optionalCookies: boolean;
8
- endpoints: {
9
- url: string;
10
- }[];
11
- positionals: {
12
- name: string;
13
- description: string;
14
- required: boolean;
15
- variadic: boolean;
16
- }[];
17
- parameters: ({
18
- name: string;
19
- type: "boolean";
20
- description: string;
21
- short: string;
22
- default: boolean;
23
- } | {
24
- name: string;
25
- type: "number";
26
- description: string;
27
- short?: undefined;
28
- default?: undefined;
29
- } | {
30
- name: string;
31
- type: "boolean";
32
- description: string;
33
- default: boolean;
34
- short?: undefined;
35
- } | {
36
- name: string;
37
- type: "string";
38
- description: string;
39
- short?: undefined;
40
- default?: undefined;
41
- })[];
42
- fetchData(cookies: any, options: any): Promise<string>;
43
- }
@@ -1 +0,0 @@
1
- var e=this&&this.__addDisposableResource||function(e,o,r){if(null!=o){if("object"!=typeof o&&"function"!=typeof o)throw new TypeError("Object expected.");var t,s;if(r){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");t=o[Symbol.asyncDispose]}if(void 0===t){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");t=o[Symbol.dispose],r&&(s=t)}if("function"!=typeof t)throw new TypeError("Object not disposable.");s&&(t=function(){try{s.call(this)}catch(e){return Promise.reject(e)}}),e.stack.push({value:o,dispose:t,async:r})}else r&&e.stack.push({async:!0});return o},o=this&&this.__disposeResources||function(e){return function(o){function r(r){o.error=o.hasError?new e(r,o.error,"An error was suppressed during disposal."):r,o.hasError=!0}var t,s=0;return function e(){for(;t=o.stack.pop();)try{if(!t.async&&1===s)return s=0,o.stack.push(t),Promise.resolve().then(e);if(t.dispose){var n=t.dispose.call(t.value);if(t.async)return s|=2,Promise.resolve(n).then(e,function(o){return r(o),e()})}else s|=1}catch(e){r(e)}if(1===s)return o.hasError?Promise.reject(o.error):Promise.resolve();if(o.hasError)throw o.error}()}}("function"==typeof SuppressedError?SuppressedError:function(e,o,r){var t=new Error(r);return t.name="SuppressedError",t.error=e,t.suppressed=o,t});import{PlaywrightAdapter as r}from"../../adapter/playwright-attatch-chrome-adapter.js";import{performFormLogin as t}from"../../util/login-helper.js";import{downloadAccounts as s}from"./download-helper.js";export default class extends r{id="chase";name="Chase Bank";domain="chase.com";description="Logs into Chase, lists downloadable accounts, and downloads statement/transaction CSV files.";optionalCookies=!0;endpoints=[{url:"https://www.chase.com/"}];positionals=[{name:"accounts",description:"Account indexes to select (e.g. 1 3). Leave empty for all.",required:!1,variadic:!0}];parameters=[{name:"list",type:"boolean",description:"List downloadable accounts only",short:"l",default:!1},{name:"download",type:"boolean",description:"Save selected account CSV file(s) in the current directory or --out-dir",short:"d",default:!1},{name:"limit",type:"number",description:"Only process the first n accounts"},{name:"first",type:"boolean",description:"Shortcut for --limit 1",default:!1},{name:"filename",type:"string",description:"Save one selected account to this filename (requires --download and exactly one account)"},{name:"activity",type:"string",description:"Activity option: current-display, all-transactions, date-range (default: all)"},{name:"range",type:"string",description:"Alias for --activity"},{name:"from",type:"string",description:"Start date for date-range (mm/dd/yyyy)"},{name:"to",type:"string",description:"End date for date-range (mm/dd/yyyy)"},{name:"out-dir",type:"string",description:"Write each CSV to <dir> instead of returning it"}];async fetchData(r,n){const a={stack:[],error:void 0,hasError:!1};try{const o=!!n.debug,{username:r,password:i}=this.resolveCredentials(n);o&&console.log(`[ChaseAdapter] Resolved credentials for ${r}`);const c=e(a,await this.connect({...n,close:!1}),!0);return o&&console.log("[ChaseAdapter] Performing form login using login helper..."),await t({page:c,intendedUrl:"https://secure.chase.com/web/auth/dashboard#/dashboard/overview",emailSelector:".public-logon.input.logon-xs-toggle",emailValue:r,passwordSelector:".public-logon.input-password.logon-xs-toggle",passwordValue:i,submitButtonSelector:"#signin-button",delayMs:1e3,pwdSelector:'input[type="password"], input[name*="password"], input[id*="password"], .public-logon.input-password',dashboardSelectors:[".accounts-group-container-bc","#account-groups-component-bc",'[data-testid="accounts-group-container"]',".innerTile","#DDA_ACCOUNTS"],debug:o}),await c.waitForTimeout(3e3),o&&console.log("[ChaseAdapter] Running statement downloader flow..."),await s(c,n)}catch(e){a.error=e,a.hasError=!0}finally{const e=o(a);e&&await e}}}
@@ -1 +0,0 @@
1
- import t from"node:fs/promises";import e from"node:path";import n from"node:process";const a={CARD:{mode:"cardGet",count:"/svc/rr/accounts/secure/gateway/credit-card/transactions/inquiry-maintenance/digital-transaction-activity/v1/transaction-counts",csv:"/svc/rr/accounts/secure/gateway/credit-card/transactions/inquiry-maintenance/digital-transaction-activity/v1/transaction-activities"},DDA:{mode:"formPost",count:"/svc/rr/accounts/secure/v1/account/activity/download/count/dda/list",csv:"/svc/rr/accounts/secure/v1/account/activity/download/dda/list"}},r=new Map([["current",{key:"current",label:"Current display, including filters"}],["current-display",{key:"current",label:"Current display, including filters"}],["current display",{key:"current",label:"Current display, including filters"}],["current display, including filters",{key:"current",label:"Current display, including filters"}],["all",{key:"all",label:"All transactions"}],["all-transactions",{key:"all",label:"All transactions"}],["all transactions",{key:"all",label:"All transactions"}],["date-range",{key:"date-range",label:"Choose a date range"}],["date range",{key:"date-range",label:"Choose a date range"}],["choose-date-range",{key:"date-range",label:"Choose a date range"}],["choose a date range",{key:"date-range",label:"Choose a date range"}]]);export function normalizeActivity(t){const e=String(t||"").trim().toLowerCase(),n=r.get(e||"all");if(!n)throw new Error(`Unknown activity option: ${t}`);return n}export async function fetchDownloadOptions(t){t.url().includes("secure.chase.com")||await t.goto("https://secure.chase.com/web/auth/dashboard#/dashboard/overview",{waitUntil:"domcontentloaded"});const e=await t.evaluate(async t=>{const e=await fetch(t.url,{method:"POST",credentials:"include",headers:t.headers,body:""});return{status:e.status,text:await e.text()}},{url:"/svc/rr/accounts/secure/v1/account/activity/download/options/list",headers:{"content-type":"application/x-www-form-urlencoded; charset=UTF-8","x-jpmc-channel":"id=C30","x-jpmc-csrf-token":"NONE"}});if(e.status<200||e.status>=300)throw new Error("Download options request failed with status "+e.status);return JSON.parse(e.text)}export function collectAccounts(t){return(t.downloadAccountActivityOptions||[]).map(t=>({id:String(t.accountId||"").trim(),summaryType:String(t.summaryType||"").trim(),detailType:String(t.detailType||"").trim(),nickname:String(t.nickName||"").trim(),mask:String(t.mask||"").trim()})).filter(t=>t.id&&("CARD"===t.summaryType||"DDA"===t.summaryType)&&t.detailType)}export function formatAccountLabel(t){const e=[];return t.nickname&&e.push(t.nickname),t.mask&&e.push(`****${t.mask}`),e.length||e.push(`${t.summaryType}/${t.detailType}/${t.id}`),e.join(" ")}export function accountFileName(t){return`${[t.nickname||"account",t.summaryType,t.detailType,t.id].map(t=>String(t).trim().replace(/[^A-Za-z0-9._-]+/g,"-")).filter(Boolean).join("-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"account"}.csv`}export function summarizeAccounts(t){const e=[];e.push(`Found ${t.length} downloadable account${1===t.length?"":"s"}:`);for(const[n,a]of t.entries())e.push(`${n+1}. ${formatAccountLabel(a)} | ${a.summaryType},${a.detailType},${a.id}`);return e.join("\n")}export function selectAccounts(t,e){let n=[];if(e.accounts&&(n=e.accounts.split(/[\s,]+/).map(Number).filter(t=>!isNaN(t))),!n.length){const n=void 0!==e.limit?e.limit:e.first?1:null;return t.slice(0,n||void 0)}const a=[],r=new Set;for(const e of n){if(e<1||e>t.length)throw new Error(`Account number ${e} is out of range. Run list to see 1-${t.length}.`);r.has(e)||(r.add(e),a.push(t[e-1]))}return a}function o(t){return`${t.getFullYear()}${String(t.getMonth()+1).padStart(2,"0")}${String(t.getDate()).padStart(2,"0")}`}function c(t){const e=String(t||"").match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);if(!e)throw new Error(`Invalid date "${t}". Use mm/dd/yyyy format.`);const[,n,a,r]=e;return`${r}${n.padStart(2,"0")}${a.padStart(2,"0")}`}function i(t,e){const n=String(t??""),a=n.trimStart();if(/^<!doctype html/i.test(a)||/^<html/i.test(a))throw new Error(`Download for ${e} returned HTML instead of CSV. The session may have expired.`);return n}export async function fetchAccountCsv(t,e,n,r){const i=function(t){const e=a[t.summaryType];if(!e)throw new Error(`Unsupported account type for ${formatAccountLabel(t)}: ${t.summaryType}`);return e}(e),s=function(t,e,n){if("CARD"===t.summaryType){const a=new Date,r=new Date(a);r.setFullYear(a.getFullYear()-2);const i={"account-activity-download-type-code":"CSV","digital-account-identifier":t.id};if("date-range"===n){if(!e.from||!e.to)throw new Error("date-range requires from and to in mm/dd/yyyy format");i["start-date"]=c(e.from),i["end-date"]=c(e.to)}else"all"===n&&(i["start-date"]=o(r),i["end-date"]=o(a),i["eligibility-indicator"]="true");return i}const a={transactionType:"ALL",filterTranType:"ALL",statementPeriodId:"ALL",downloadType:"CSV",accountId:t.id};if("all"===n&&"DDA"===t.summaryType&&(a.dateOption="LAST_24_MONTHS"),"date-range"===n){if(!e.from||!e.to)throw new Error("date-range requires from and to in mm/dd/yyyy format");a.dateOption="DATE_RANGE",a.dateLo=e.from,a.dateHi=e.to}return a}(e,n,r),d={body:s,countUrl:i.count,csvUrl:i.csv,csrfUrl:"/svc/rl/accounts/secure/v1/csrf/token/list",mode:i.mode,headers:{"content-type":"application/x-www-form-urlencoded; charset=UTF-8","x-jpmc-channel":"id=C30","x-jpmc-csrf-token":"NONE"}},l=await t.evaluate(async t=>{const e=t=>new URLSearchParams(t).toString(),n=(n,a)=>fetch(n+"?"+e(a),{method:"GET",credentials:"include",headers:t.headers}),a=(n,a,r=t.headers)=>fetch(n,{method:"POST",credentials:"include",headers:r,body:e(a)}),r="cardGet"===t.mode?await n(t.countUrl,t.body):await a(t.countUrl,t.body);if(!r.ok)throw new Error("Download count request failed with status "+r.status);const o=await fetch(t.csrfUrl,{method:"POST",credentials:"include",headers:t.headers,body:""});if(!o.ok)throw new Error("CSRF token request failed with status "+o.status);const c=await o.json(),i=c.csrfToken||c.response?.csrfToken;if(!i)throw new Error("CSRF token was not present in Chase token response");const s={...t.body,csrftoken:i,submit:"Submit"},d="cardGet"===t.mode?await n(t.csvUrl,s):await a(t.csvUrl,s,{"content-type":"application/x-www-form-urlencoded"});return{status:d.status,contentType:d.headers.get("content-type")||"",text:await d.text()}},d);if(l.status<200||l.status>=300)throw new Error(`Download request failed with status ${l.status} for ${formatAccountLabel(e)}`);if(l.contentType&&!/csv|text|octet-stream/i.test(l.contentType))throw new Error(`Download for ${formatAccountLabel(e)} returned ${l.contentType||"unknown content type"}`);return l.text}export async function downloadAccounts(a,r){const o=normalizeActivity(r.activity||r.range);if(!("date-range"!==o.key||r.from&&r.to))throw new Error("activity date-range requires from and to in mm/dd/yyyy format");const c=collectAccounts(await fetchDownloadOptions(a));if(!c.length)throw new Error("No downloadable accounts were found in the Chase download options response.");if(r.list)return summarizeAccounts(c);const s=selectAccounts(c,r),d=r.download?e.resolve(n.cwd(),r.outDir||"."):null;d&&await t.mkdir(d,{recursive:!0});const l=[];for(const[n,c]of s.entries()){const u=i(await fetchAccountCsv(a,c,r,o.key),formatAccountLabel(c));if(d){const n=r.filename&&1===s.length?r.filename:accountFileName(c),a=e.join(d,n);await t.writeFile(a,u,"utf8"),l.push(`Saved: ${a}`)}else l.push(`\n===== ${n+1}/${s.length}: ${formatAccountLabel(c)} =====\n`+(u.endsWith("\n")?u:`${u}\n`))}return l.join("\n")}
@@ -1,11 +0,0 @@
1
- /**
2
- * ChatGPT adapter — fetches rate limit usage from the wham/usage API.
3
- *
4
- * This is a multi-step flow:
5
- * 1. Exchange Chrome cookies for a Bearer JWT via the Next-Auth session endpoint
6
- * 2. Use the JWT to query the private wham/usage endpoint
7
- *
8
- * See request.md in this directory for full endpoint documentation.
9
- */
10
- declare const _default: import("../../types.js").WebsiteAdapter;
11
- export default _default;
@@ -1 +0,0 @@
1
- import{defineAdapter as e}from"../../adapter/base-adapter.js";export default e({id:"codex-usage",name:"ChatGPT / Codex Usage",domain:"chatgpt.com",description:"Fetches ChatGPT rate limit usage and quota details from the private wham/usage API.",async fetchData(e,t){const a=this.buildCookieString(e),s=this.resolveUserAgent(t),o=await this.fetchJson("https://chatgpt.com/api/auth/session",{headers:{Cookie:a,"User-Agent":s,Accept:"application/json"}});if(!o?.accessToken)throw new Error("No ChatGPT login found in browser");return this.fetchJson("https://chatgpt.com/backend-api/wham/usage",{headers:{authorization:`Bearer ${o.accessToken}`}})}});
@@ -1,6 +0,0 @@
1
- /**
2
- * Cursor.com adapter — fetches the active usage summary.
3
- * Simple single-endpoint adapter: just declare the endpoint, BaseAdapter handles the rest.
4
- */
5
- declare const _default: import("../../types.js").WebsiteAdapter;
6
- export default _default;
@@ -1 +0,0 @@
1
- import{defineAdapter as r}from"../../adapter/base-adapter.js";export default r({id:"cursor-usage",name:"Cursor Usage",domain:"cursor.com",description:"Fetches the active Cursor usage summary from the private usage-summary API.",endpoints:[{url:"https://cursor.com/api/usage-summary"}]});
@@ -1,12 +0,0 @@
1
- import { PlaywrightAdapter } from "../../adapter/playwright-attatch-chrome-adapter.js";
2
- export default class extends PlaywrightAdapter {
3
- id: string;
4
- name: string;
5
- domain: string;
6
- description: string;
7
- optionalCookies: boolean;
8
- endpoints: {
9
- url: string;
10
- }[];
11
- fetchData(cookies: any, options: any): Promise<void>;
12
- }
@@ -1 +0,0 @@
1
- var r=this&&this.__addDisposableResource||function(r,e,o){if(null!=e){if("object"!=typeof e&&"function"!=typeof e)throw new TypeError("Object expected.");var s,t;if(o){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");s=e[Symbol.asyncDispose]}if(void 0===s){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");s=e[Symbol.dispose],o&&(t=s)}if("function"!=typeof s)throw new TypeError("Object not disposable.");t&&(s=function(){try{t.call(this)}catch(r){return Promise.reject(r)}}),r.stack.push({value:e,dispose:s,async:o})}else o&&r.stack.push({async:!0});return e},e=this&&this.__disposeResources||function(r){return function(e){function o(o){e.error=e.hasError?new r(o,e.error,"An error was suppressed during disposal."):o,e.hasError=!0}var s,t=0;return function r(){for(;s=e.stack.pop();)try{if(!s.async&&1===t)return t=0,e.stack.push(s),Promise.resolve().then(r);if(s.dispose){var n=s.dispose.call(s.value);if(s.async)return t|=2,Promise.resolve(n).then(r,function(e){return o(e),r()})}else t|=1}catch(r){o(r)}if(1===t)return e.hasError?Promise.reject(e.error):Promise.resolve();if(e.hasError)throw e.error}()}}("function"==typeof SuppressedError?SuppressedError:function(r,e,o){var s=new Error(o);return s.name="SuppressedError",s.error=r,s.suppressed=e,s});import{PlaywrightAdapter as o}from"../../adapter/playwright-attatch-chrome-adapter.js";export default class extends o{id="ExampleAdapter";name="Example Playwright Service";domain="example.com";description="Fetches and prints the HTML content of example.com using Playwright connecting to existing Chrome.";optionalCookies=!0;endpoints=[{url:"https://example.com"}];async fetchData(o,s){const t={stack:[],error:void 0,hasError:!1};try{r(t,await this.connect(s),!0)}catch(r){t.error=r,t.hasError=!0}finally{const r=e(t);r&&await r}}}
@@ -1,12 +0,0 @@
1
- import { PlaywrightAdapter } from "../../adapter/playwright-attatch-chrome-adapter.js";
2
- export default class extends PlaywrightAdapter {
3
- id: string;
4
- name: string;
5
- domain: string;
6
- description: string;
7
- optionalCookies: boolean;
8
- endpoints: {
9
- url: string;
10
- }[];
11
- fetchData(cookies: any, options: any): Promise<any>;
12
- }
@@ -1 +0,0 @@
1
- var e=this&&this.__addDisposableResource||function(e,r,o){if(null!=r){if("object"!=typeof r&&"function"!=typeof r)throw new TypeError("Object expected.");var t,s;if(o){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");t=r[Symbol.asyncDispose]}if(void 0===t){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");t=r[Symbol.dispose],o&&(s=t)}if("function"!=typeof t)throw new TypeError("Object not disposable.");s&&(t=function(){try{s.call(this)}catch(e){return Promise.reject(e)}}),e.stack.push({value:r,dispose:t,async:o})}else o&&e.stack.push({async:!0});return r},r=this&&this.__disposeResources||function(e){return function(r){function o(o){r.error=r.hasError?new e(o,r.error,"An error was suppressed during disposal."):o,r.hasError=!0}var t,s=0;return function e(){for(;t=r.stack.pop();)try{if(!t.async&&1===s)return s=0,r.stack.push(t),Promise.resolve().then(e);if(t.dispose){var i=t.dispose.call(t.value);if(t.async)return s|=2,Promise.resolve(i).then(e,function(r){return o(r),e()})}else s|=1}catch(e){o(e)}if(1===s)return r.hasError?Promise.reject(r.error):Promise.resolve();if(r.hasError)throw r.error}()}}("function"==typeof SuppressedError?SuppressedError:function(e,r,o){var t=new Error(o);return t.name="SuppressedError",t.error=e,t.suppressed=r,t});import{PlaywrightAdapter as o}from"../../adapter/playwright-attatch-chrome-adapter.js";import{decodeGoogleJsonWithSchema as t}from"../../util/google-json.js";const s={planCode:{path:[0]},planName:{path:[0],transform:e=>2===e?"Gemini Pro":1===e?"Gemini Free":`Unknown (${e})`},limits:{path:[1],items:{rawLimit:{path:[0]},percentageUsed:{path:[1],transform:e=>parseFloat((100*e).toFixed(2))},tierName:{path:[2],transform:e=>1===e?"Hourly Usage Limit":2===e?"Weekly Usage Limit":`Unknown Tier (${e})`},resetTime:{path:[3,0,0],transform:e=>e?new Date(1e3*e).toISOString():null}}}};export default class extends o{id="gemini-usage";name="Gemini Usage";domain="gemini.google.com";description="Fetches Gemini account usage/quota details via browser-attached Playwright.";optionalCookies=!0;endpoints=[{url:"https://gemini.google.com/usage"}];async fetchData(o,i){const n={stack:[],error:void 0,hasError:!1};try{const r=e(n,await this.connect(i),!0),o=new Promise((e,o)=>{const t=setTimeout(()=>{o(new Error("Timeout waiting for Gemini usage RPC payload. Make sure you are logged into gemini.google.com in Chrome."))},15e3);r.on("response",async r=>{if(r.url().includes("jSf9Qc"))try{const o=await r.text();clearTimeout(t),e(o)}catch(e){}})});i.debug&&console.log("Triggering page reload/navigation to capture Gemini usage network request..."),await r.reload({waitUntil:"domcontentloaded"});const a=await o;return t(a,"jSf9Qc",s)}catch(e){n.error=e,n.hasError=!0}finally{const e=r(n);e&&await e}}}
@@ -1,62 +0,0 @@
1
- import { PlaywrightAdapter } from "../../adapter/playwright-attatch-chrome-adapter.js";
2
- export default class extends PlaywrightAdapter {
3
- id: string;
4
- name: string;
5
- domain: string;
6
- description: string;
7
- optionalCookies: boolean;
8
- endpoints: {
9
- url: string;
10
- }[];
11
- positionals: {
12
- name: string;
13
- description: string;
14
- required: boolean;
15
- variadic: boolean;
16
- }[];
17
- parameters: ({
18
- name: string;
19
- type: string;
20
- description: string;
21
- default: number;
22
- short?: undefined;
23
- } | {
24
- name: string;
25
- type: string;
26
- description: string;
27
- short: string;
28
- default?: undefined;
29
- })[];
30
- fetchData(cookies: any, options: any): Promise<{
31
- question: any;
32
- answer: string | null;
33
- finalUrl: string;
34
- endpoint: any;
35
- searchPage: {
36
- title: any;
37
- url: any;
38
- bodyText: string;
39
- };
40
- endpointResult: {
41
- title: any;
42
- url: any;
43
- bodyText: string;
44
- htmlPrefix: any;
45
- decoded: any;
46
- } | null;
47
- requests: {
48
- method: any;
49
- type: any;
50
- url: any;
51
- status: any;
52
- mimeType: any;
53
- decodedFormat: string | null;
54
- recordCount: number;
55
- bodyPrefix: any;
56
- }[];
57
- }>;
58
- private waitForAiAnswer;
59
- private collectPageState;
60
- private buildFolwrEndpoint;
61
- private summarizeNetwork;
62
- }
@@ -1 +0,0 @@
1
- var e=this&&this.__addDisposableResource||function(e,t,r){if(null!=t){if("object"!=typeof t&&"function"!=typeof t)throw new TypeError("Object expected.");var o,n;if(r){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");o=t[Symbol.asyncDispose]}if(void 0===o){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");o=t[Symbol.dispose],r&&(n=o)}if("function"!=typeof o)throw new TypeError("Object not disposable.");n&&(o=function(){try{n.call(this)}catch(e){return Promise.reject(e)}}),e.stack.push({value:t,dispose:o,async:r})}else r&&e.stack.push({async:!0});return t},t=this&&this.__disposeResources||function(e){return function(t){function r(r){t.error=t.hasError?new e(r,t.error,"An error was suppressed during disposal."):r,t.hasError=!0}var o,n=0;return function e(){for(;o=t.stack.pop();)try{if(!o.async&&1===n)return n=0,t.stack.push(o),Promise.resolve().then(e);if(o.dispose){var s=o.dispose.call(o.value);if(o.async)return n|=2,Promise.resolve(s).then(e,function(t){return r(t),e()})}else n|=1}catch(e){r(e)}if(1===n)return t.hasError?Promise.reject(t.error):Promise.resolve();if(t.hasError)throw t.error}()}}("function"==typeof SuppressedError?SuppressedError:function(e,t,r){var o=new Error(r);return o.name="SuppressedError",o.error=e,o.suppressed=t,o});import{PlaywrightAdapter as r}from"../../adapter/playwright-attatch-chrome-adapter.js";export default class extends r{id="google-ai";name="Google AI Overview";domain="google.com";description="Fetches Google's AI Overview and AI Mode answers using browser-attached Playwright.";optionalCookies=!0;endpoints=[{url:"https://www.google.com"}];positionals=[{name:"question",description:"The search query or question to ask Google",required:!0,variadic:!0}];parameters=[{name:"raw-limit",type:"number",description:"Max raw search/endpoint response chars to include",default:12e3},{name:"timeout",type:"number",description:"Playwright timeout in milliseconds",default:9e4},{name:"text",type:"boolean",description:"Print only the extracted AI Overview answer text",short:"t"}];async fetchData(r,o){const n={stack:[],error:void 0,hasError:!1};try{const t=o.question,r=void 0!==o.rawLimit?Number(o.rawLimit):12e3,s=void 0!==o.timeout?Number(o.timeout):9e4,i=e(n,await this.connect(o),!0),c=await i.context().newCDPSession(i);await c.send("Network.enable",{maxTotalBufferSize:1e8,maxResourceBufferSize:1e8});const g=[],y=new Map,w=e=>{const t=function(e){const t=/^https?:\/\/([^/]+)(\/[^?#]*)?/i.exec(String(e??""));return t&&/(^|\.)google\.[^/]+$/i.test(t[1])?t[2]||"/":null}(e);return"/search"===t||t?.startsWith("/async/")||t?.includes("batchexecute")};c.on("Network.requestWillBeSent",e=>{const t=e.request||{};w(t.url)&&(y.set(e.requestId,g.length),g.push({id:e.requestId,type:e.type,method:t.method,url:t.url,postData:t.postData||null,status:null,mimeType:null,body:null}))}),c.on("Network.responseReceived",e=>{const t=y.get(e.requestId);null!=t&&(g[t].status=e.response.status,g[t].mimeType=e.response.mimeType)}),c.on("Network.loadingFinished",async e=>{const t=y.get(e.requestId);if(null!=t)try{const o=g[t].mimeType||"";if(!/text|json|html|javascript|x-protobuf/.test(o))return;const n=await c.send("Network.getResponseBody",{requestId:e.requestId});g[t].body=n.base64Encoded?null:(n.body||"").slice(0,r)}catch(e){}});const b=`https://www.google.com/search?${a({q:t,udm:"50"})}`;o.debug&&console.log(`Navigating to Google Search: ${b}`),await i.goto(b,{waitUntil:"domcontentloaded"});const k=await this.waitForAiAnswer(i,s),x=await this.collectPageState(i,r),S=await this.buildFolwrEndpoint(i);let v=null;if(S?.url){o.debug&&(console.log(`Discovered folwr endpoint: ${S.url}`),console.log("Querying folwr endpoint in-page via browser fetch..."));try{const e=await i.evaluate(async e=>{const t=await fetch(e);return await t.text()},S.url),t=function(e){try{const t=l(e).trimStart();if(t.startsWith("<")||t.includes("class=")||t.includes("id="))return m(t);const r=u(e);for(const e of r){const t=h(e.value);if(t)return m(t)}}catch(e){}return null}(e),o=p(e),n=!o||o.includes("<")||o.includes("class=")?null:o;v={title:x.title,url:S.url,bodyText:e,htmlPrefix:e.slice(0,r),answer:t||n,decoded:d({body:e,contentType:"text/plain"})}}catch(e){o.debug&&console.warn("Failed to query folwr endpoint in-page:",e)}}await i.waitForTimeout(500);return{question:t,answer:f(v?.answer||k||p(x.bodyText),r)||null,finalUrl:i.url(),endpoint:S||null,searchPage:{title:x.title,url:x.url,bodyText:f(x.bodyText,r)},endpointResult:v?{title:v.title,url:v.url,bodyText:f(v.bodyText,r),htmlPrefix:v.htmlPrefix,decoded:v.decoded}:null,requests:this.summarizeNetwork(g,r)}}catch(e){n.error=e,n.hasError=!0}finally{const e=t(n);e&&await e}}async waitForAiAnswer(e,t){const r=Date.now();for(;Date.now()-r<Math.min(t,45e3);){await e.waitForTimeout(750);const t=await e.evaluate(()=>{const e=Array.from(document.querySelectorAll('[jsname="KFl8ub"], [data-attrid], .kp-wholepage')).map(e=>e.innerText?.trim()).filter(Boolean).find(e=>!/^(Sources|Related|AI Mode response is ready)$/i.test(e));if(e)return e;const t=(document.body?.innerText||"").split("\n").map(e=>e.trim()).filter(Boolean),r=t.findIndex(e=>/AI Mode response is ready/i.test(e));return r>0?t[r-1]:null});if(t)return t}return null}async collectPageState(e,t){return await e.evaluate(e=>{const t=document.body?.innerText||"",r=document.documentElement?.outerHTML||"";return{title:document.title,url:document.location.href,bodyText:t.slice(0,e),htmlPrefix:r.slice(0,e)}},t)}async buildFolwrEndpoint(e){const t=await e.evaluate(()=>{const e=document.querySelector("[data-garc][data-lro-token][data-lro-signature][data-ei]");if(!e)return{url:null,error:"Missing AI Mode token container"};const t=document.getElementById("rKxeg")?.getAttribute("data-stkp")||null;return{origin:document.location.origin,search:document.location.search,stkp:t,fmt:document.querySelector("[data-madl]")?"madl":"adl",tokens:{ei:e.dataset.ei,garc:e.dataset.garc,lroToken:e.dataset.lroToken,lroSignature:e.dataset.lroSignature,xsrfFolwrToken:e.dataset.xsrfFolwrToken||null,srtst:e.dataset.srtst||null,hasGarc:!!e.dataset.garc,hasLroToken:!!e.dataset.lroToken,hasLroSignature:!!e.dataset.lroSignature,hasXsrf:!!e.dataset.xsrfFolwrToken,hasStkp:!!t}}});if(!t?.origin)return t;const r=function(e){const t={};for(const r of String(e??"").replace(/^\?/,"").split("&")){if(!r)continue;const e=r.indexOf("="),o=-1===e?r:r.slice(0,e),n=-1===e?"":r.slice(e+1);t[i(o)]=i(n)}return t}(t.search),n={},s=["q","udm","mstk","csuir","mtid","ved","vet","sei","dpr","hl","gl","source","vsrid","lns_img","cinpts"];for(const e of s)r[e]&&(n[e]=r[e]);t.tokens.srtst&&(n.srtst=t.tokens.srtst),n.garc=t.tokens.garc,n.mlro=t.tokens.lroToken,n.mlros=t.tokens.lroSignature,n.ei=t.tokens.ei,t.stkp&&(n.stkp=t.stkp);const l={_fmt:t.fmt};t.tokens.xsrfFolwrToken&&(l._xsrf=t.tokens.xsrfFolwrToken);const c=a(n),u=Object.entries(l).map(([e,t])=>`${o(e)}:${o(t)}`).join(",");return{url:`${t.origin}/async/folwr?${c}&async=${u}`,tokens:{ei:t.tokens.ei,hasGarc:t.tokens.hasGarc,hasLroToken:t.tokens.hasLroToken,hasLroSignature:t.tokens.hasLroSignature,hasXsrf:t.tokens.hasXsrf,hasStkp:t.tokens.hasStkp}}}summarizeNetwork(e,t){return e.map(e=>{const r=null==e.body?null:d({body:e.body,contentType:e.mimeType});return{method:e.method,type:e.type,url:e.url,status:e.status,mimeType:e.mimeType,decodedFormat:r?.format??null,recordCount:r?.records?.length??0,bodyPrefix:null==e.body?null:e.body.slice(0,t)}})}}function o(e){let t="";const r=String(e??"");for(let e=0;e<r.length;e++){const o=r.codePointAt(e);if(void 0===o)continue;const s=String.fromCodePoint(o);if(o>65535&&e++," "===s)t+="+";else if("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~".includes(s))t+=s;else for(const e of n(o))t+=`%${e.toString(16).toUpperCase().padStart(2,"0")}`}return t}function n(e){return e<=127?[e]:e<=2047?[192|e>>6,128|63&e]:e<=65535?[224|e>>12,128|e>>6&63,128|63&e]:[240|e>>18,128|e>>12&63,128|e>>6&63,128|63&e]}function s(e){let t="";for(let r=0;r<e.length;r++){const o=e[r];let n=o;192==(224&o)?n=(31&o)<<6|63&e[++r]:224==(240&o)?n=(15&o)<<12|(63&e[++r])<<6|63&e[++r]:240==(248&o)&&(n=(7&o)<<18|(63&e[++r])<<12|(63&e[++r])<<6|63&e[++r]),t+=String.fromCodePoint(n)}return t}function i(e){const t=String(e??"").replace(/\+/g," ");let r="";for(let e=0;e<t.length;e++){if("%"!==t[e]||!/[0-9a-fA-F]{2}/.test(t.slice(e+1,e+3))){r+=t[e];continue}const o=[];for(;"%"===t[e]&&/[0-9a-fA-F]{2}/.test(t.slice(e+1,e+3));)o.push(Number.parseInt(t.slice(e+1,e+3),16)),e+=3;e--;try{r+=s(o)}catch{r+=o.map(e=>`%${e.toString(16).toUpperCase().padStart(2,"0")}`).join("")}}return r}function a(e){return Object.entries(e).filter(([,e])=>null!=e&&""!==e).map(([e,t])=>`${o(e)}=${o(t)}`).join("&")}function l(e){return e.replace(/^\s*\)\]\}'\s*\n?/,"")}function c(e){try{return JSON.parse(e)}catch{return}}function u(e){const t=[];for(const r of l(e).split(/\r?\n/)){const e=r.trim();if(!e)continue;const o=/^([a-zA-Z0-9_-]+);(.*)$/.exec(e);if(!o)continue;const n=o[2].trim();t.push({id:o[1],value:c(n)??n})}return t}function d({body:e,contentType:t=""}){const r=String(e??""),o=l(r).trimStart(),n=/^\s*\)\]\}'/.test(r),s=/^[\[{]/.test(o)?c(o):void 0,i=void 0===s?u(r):[];let a="text";return(t.includes("html")||/^\s*</.test(o))&&(a="html"),n&&void 0!==s?a="google-xssi-json":n&&i.length?a="google-xssi-record-stream":void 0!==s?a="json":i.length&&(a="google-record-stream"),{format:a,xssiPrefixed:n,parsed:s,records:i}}function f(e,t=4e3){return String(e??"").replace(/\u0000/g,"").replace(/[ \t]+\n/g,"\n").trim().slice(0,t)}function p(e){const t=String(e??"").split("\n").map(e=>e.trim()).filter(Boolean),r=t.findIndex(e=>/AI Mode response is ready/i.test(e));if(r>0)return t[r-1];const o=/^(Skip to main content|Accessibility help|Accessibility feedback|AI Mode|All|Images|Videos|News|More|Search Results|Sources|Related)$/i;return t.find(e=>!o.test(e))??null}function m(e){let t=e.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&nbsp;/g," ");return t=function(e){let t="",r=0;for(;r<e.length;){const o=e.toLowerCase().indexOf("<style",r);if(-1===o){t+=e.slice(r);break}t+=e.slice(r,o);const n=e.toLowerCase().indexOf("</style>",o);if(-1===n)break;r=n+8}let o="";r=0;for(;r<t.length;){const e=t.toLowerCase().indexOf("<script",r);if(-1===e){o+=t.slice(r);break}o+=t.slice(r,e);const n=t.toLowerCase().indexOf("<\/script>",e);if(-1===n)break;r=n+9}return o}(t),t=t.replace(/<[^>]+>/g," "),t.replace(/\s+/g," ").trim()}function h(e){if(!e)return null;if("string"==typeof e){const t=e.trim();return t.startsWith("<")||t.includes("class=")||t.includes("id=")?e:null}if(Array.isArray(e))for(const t of e){const e=h(t);if(e)return e}else if("object"==typeof e){if("string"==typeof e.html)return e.html;if("string"==typeof e.aimc_block?.html)return e.aimc_block.html;if("string"==typeof e.value&&(e.value.startsWith("<")||e.value.includes("class=")))return e.value;for(const t of Object.values(e)){const e=h(t);if(e)return e}}return null}
@@ -1,2 +0,0 @@
1
- declare const _default: import("../../types.js").WebsiteAdapter;
2
- export default _default;
@@ -1 +0,0 @@
1
- import{defineAdapter as e}from"../../adapter/base-adapter.js";export default e({id:"ollama-usage",name:"Ollama Usage",domain:"ollama.com",description:"Fetches Ollama plan and usage details from the authenticated settings page.",endpoints:[{url:"https://ollama.com/settings",responseType:"html",headers:{Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},transform(e){const t="string"==typeof e?e:String(e),n=a(t,"Session usage"),i=a(t,"Weekly usage");return{time:(new Date).toISOString(),Plan:s(t),"Session Usage":n.usage,"Session Reset":n.reset,"Weekly Usage":i.usage,"Weekly Reset":i.reset}}}]});function s(e){const s=e.match(/Cloud Usage[\s\S]*?<\/span>[\s\S]*?<span[^>]*>([\s\S]*?)<\/span/i);return s?.[1]?.trim()??"unknown"}function a(e,s){const a=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");const t=new RegExp(`<div[\\s\\S]*?<span[^>]*>\\s*${a}\\s*<\\/span>[\\s\\S]*?aria-label="${a}\\s+([^"]+)"[\\s\\S]*?data-time="([^"]+)"`,"i"),n=e.match(t);return{usage:n?.[1]?.replace(/\s+used$/i,"").trim()??"unknown",reset:n?.[2]?.trim()??"unknown"}}
@@ -1,2 +0,0 @@
1
- declare const _default: import("../../types.js").WebsiteAdapter;
2
- export default _default;
@@ -1 +0,0 @@
1
- import{randomUUID as e}from"node:crypto";import{defineAdapter as t}from"../../adapter/base-adapter.js";const n="https://www.perplexity.ai/rest/sse/perplexity_ask";export default t({id:"perplexity",name:"Perplexity AI Ask",domain:"perplexity.ai",description:"Fetches live streaming answers from Perplexity AI using its private REST/SSE API.",positionals:[{name:"question",description:"The query or question to ask Perplexity AI",required:!0,variadic:!0}],parameters:[{name:"model",type:"string",description:"Model preference (e.g. 'claude46sonnet', 'sonar-reasoning')",default:"claude46sonnet",short:"m"},{name:"out",type:"string",description:"Write decoded response JSON to file instead of stdout"},{name:"timeout",type:"number",description:"Request timeout in milliseconds",default:75e3},{name:"text",type:"boolean",description:"Print only the extracted text answer",short:"t"}],async fetchData(t,r){const i=r.question,a=r.model||"claude46sonnet",c=void 0!==r.timeout?Number(r.timeout):75e3;if(!t||0===t.length)throw new Error("No login found in browser. Please log in to perplexity.ai in Google Chrome.");const d=this.buildCookieString(t),l={params:{attachments:[],language:"en-US",timezone:Intl.DateTimeFormat().resolvedOptions().timeZone||"America/New_York",search_focus:"internet",sources:["web"],frontend_uuid:e(),mode:"copilot",model_preference:"claude46sonnet",is_related_query:!1,is_sponsored:!1,frontend_context_uuid:e(),prompt_source:"user",query_source:"home",is_incognito:!1,time_from_first_type:1,local_search_enabled:!1,use_schematized_api:!0,send_back_text_in_streaming_api:!1,supported_block_use_cases:["answer_modes","media_items","knowledge_cards","inline_entity_cards","place_widgets","finance_widgets","prediction_market_widgets","sports_widgets","flight_status_widgets","news_widgets","shopping_widgets","jobs_widgets","search_result_widgets","inline_images","inline_assets","placeholder_cards","diff_blocks","inline_knowledge_cards","entity_group_v2","refinement_filters","canvas_mode","maps_preview","answer_tabs","price_comparison_widgets","preserve_latex","generic_onboarding_widgets","in_context_suggestions","pending_followups","inline_claims","unified_assets","workflow_steps","background_agents"],client_coordinates:null,mentions:[],dsl_query:"",skip_search_enabled:!0,is_nav_suggestions_disabled:!1,source:"default",always_search_override:!1,override_no_search:!1,should_ask_for_mcp_tool_confirmation:!0,browser_agent_allow_once_from_toggle:!1,force_enable_browser_agent:!1,supported_features:["browser_agent_permission_banner_v1.1"],extended_context:!1,version:"2.18"},query_str:""};l.params.frontend_uuid=e(),l.params.frontend_context_uuid=e(),l.params.model_preference=a,l.params.dsl_query=i,l.params.time_from_first_type=1,l.query_str=i;const u=new AbortController,_=setTimeout(()=>u.abort(),c),p=this.resolveUserAgent(r);let f;try{f=await fetch(n,{method:"POST",headers:{accept:"text/event-stream","accept-language":"en-US,en;q=0.9","content-type":"application/json",origin:"https://www.perplexity.ai",referer:"https://www.perplexity.ai/","user-agent":p,"x-perplexity-request-endpoint":n,"x-perplexity-request-reason":"ask-query-state-provider","x-perplexity-request-try-number":"1","x-request-id":l.params.frontend_uuid,cookie:d},body:JSON.stringify(l),signal:u.signal})}catch(e){if(e instanceof Error&&"AbortError"===e.name)throw new Error(`Perplexity request timed out after ${c}ms.`);throw e}finally{clearTimeout(_)}const m=f.headers.get("content-type")||"";if(!f.ok){const e=await f.text();throw new Error(`HTTP ${f.status} (${f.statusText}): ${e}`)}if(m.includes("text/event-stream")){const e=await async function(e){const t=new TextDecoder,n=[],r={};let i=null,a="";function c(e){const t=[];for(const n of e.split("\n"))n.startsWith("data:")&&t.push(n.slice(5).trimStart());if(t.length)try{const e=JSON.parse(t.join("\n"));if(!e||"object"==typeof e&&0===Object.keys(e).length)return;n.push(e),function(e,t){for(const n of t?.blocks??[]){const t=n.intended_usage;for(const[r,s]of Object.entries(n))"intended_usage"!==r&&"diff_block"!==r&&t&&(e[t]=s);const r=n.diff_block;if(r?.field){e[r.field]??=null;for(const t of r.patches??[])e[r.field]=s(e[r.field],t)}}}(r,e),(e.final||"COMPLETED"===e.status||e.text_completed)&&(i=e)}catch{}}const d=e.body?.getReader();if(d)for(;;){const{value:e,done:n}=await d.read();if(n)break;for(a+=t.decode(e,{stream:!0}).replace(/\r\n/g,"\n");;){const e=a.indexOf("\n\n");if(-1===e)break;c(a.slice(0,e)),a=a.slice(e+2)}}else if(e.body&&Symbol.asyncIterator in e.body)for await(const n of e.body)for(a+=t.decode(n,{stream:!0}).replace(/\r\n/g,"\n");;){const e=a.indexOf("\n\n");if(-1===e)break;c(a.slice(0,e)),a=a.slice(e+2)}a+=t.decode(),a.trim()&&c(a);!i&&n.length&&(i=n[n.length-1]);return{chunks:n,state:r,final:i,answer:o(i,r)}}(f);return{endpoint:n,http_code:f.status,content_type:m,request:{query:i,model_preference:a,frontend_uuid:l.params.frontend_uuid,frontend_context_uuid:l.params.frontend_context_uuid},answer:e.answer,state:e.state,final:e.final,chunks:e.chunks}}{const e=await f.text();let t;try{t=JSON.parse(e)}catch{t=e}return{endpoint:n,http_code:f.status,content_type:m,request:{query:i,model_preference:a,frontend_uuid:l.params.frontend_uuid,frontend_context_uuid:l.params.frontend_context_uuid},body:t}}}});function r(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function s(e,t){const n=t.op,s=t.path??"";if(""===s){if("replace"===n||"add"===n)return t.value;if("remove"===n)return null}const o=s.split("/").slice(1).map(r);null==e&&(e=/^\d+$/.test(o[0]??"")?[]:{});let i=e;for(let e=0;e<o.length-1;e++){const t=o[e],n=/^\d+$/.test(o[e+1]??"");if(Array.isArray(i)){const e=Number(t);for(;i.length<=e;)i.push(n?[]:{});i=i[e]}else i[t]??=n?[]:{},i=i[t]}const a=o.at(-1);if(null==a)return e;if(Array.isArray(i)){const e="-"===a?i.length:Number(a);"add"===n?i.splice(e,0,t.value):"replace"===n?i[e]=t.value:"remove"===n&&i.splice(e,1)}else"remove"===n?delete i[a]:"add"!==n&&"replace"!==n||(i[a]=t.value);return e}function o(e,t){for(const t of e?.blocks??[]){const e=t.markdown_block;if(null!=e?.answer)return e.answer;if(Array.isArray(e?.chunks))return e.chunks.join("")}for(const e of["ask_text","ask_text_0_markdown","markdown_block"]){const n=t[e];if(null!=n?.answer)return n.answer;if(Array.isArray(n?.chunks))return n.chunks.join("")}return null}
@@ -1,45 +0,0 @@
1
- import { PlaywrightAdapter } from "../../adapter/playwright-attatch-chrome-adapter.js";
2
- export default class extends PlaywrightAdapter {
3
- id: string;
4
- name: string;
5
- domain: string;
6
- description: string;
7
- optionalCookies: boolean;
8
- endpoints: {
9
- url: string;
10
- }[];
11
- positionals: {
12
- name: string;
13
- description: string;
14
- required: boolean;
15
- }[];
16
- parameters: ({
17
- name: string;
18
- type: "string";
19
- description: string;
20
- default: string;
21
- short: string;
22
- } | {
23
- name: string;
24
- type: "boolean";
25
- description: string;
26
- short: string;
27
- default?: undefined;
28
- })[];
29
- fetchData(cookies: any, options: any): Promise<string>;
30
- private gotoDashboardIfNeeded;
31
- private ensurePropertyOverlayOpen;
32
- private getProperties;
33
- private selectPropertyByIndex;
34
- private selectPropertyByName;
35
- private openDownloadPanelIfNeeded;
36
- private setNativeSelectValue;
37
- private getSelectOptions;
38
- private setDownloadOptions;
39
- private fetchDownloadCsvText;
40
- private normalizeInterval;
41
- private intervalToValue;
42
- private inferServiceTypeFromPropertyTitle;
43
- private extractAddressNumber;
44
- private formatPropertyLabel;
45
- }
@@ -1 +0,0 @@
1
- var e=this&&this.__addDisposableResource||function(e,t,r){if(null!=t){if("object"!=typeof t&&"function"!=typeof t)throw new TypeError("Object expected.");var o,a;if(r){if(!Symbol.asyncDispose)throw new TypeError("Symbol.asyncDispose is not defined.");o=t[Symbol.asyncDispose]}if(void 0===o){if(!Symbol.dispose)throw new TypeError("Symbol.dispose is not defined.");o=t[Symbol.dispose],r&&(a=o)}if("function"!=typeof o)throw new TypeError("Object not disposable.");a&&(o=function(){try{a.call(this)}catch(e){return Promise.reject(e)}}),e.stack.push({value:t,dispose:o,async:r})}else r&&e.stack.push({async:!0});return t},t=this&&this.__disposeResources||function(e){return function(t){function r(r){t.error=t.hasError?new e(r,t.error,"An error was suppressed during disposal."):r,t.hasError=!0}var o,a=0;return function e(){for(;o=t.stack.pop();)try{if(!o.async&&1===a)return a=0,t.stack.push(o),Promise.resolve().then(e);if(o.dispose){var i=o.dispose.call(o.value);if(o.async)return a|=2,Promise.resolve(i).then(e,function(t){return r(t),e()})}else a|=1}catch(e){r(e)}if(1===a)return t.hasError?Promise.reject(t.error):Promise.resolve();if(t.hasError)throw t.error}()}}("function"==typeof SuppressedError?SuppressedError:function(e,t,r){var o=new Error(r);return o.name="SuppressedError",o.error=e,o.suppressed=t,o});import{PlaywrightAdapter as r}from"../../adapter/playwright-attatch-chrome-adapter.js";import{performFormLogin as o}from"../../util/login-helper.js";export default class extends r{id="pseg-usage";name="PSEG Usage";domain="mysmartenergy.nj.pseg.com";description="Downloads PSEG Smart Energy usage data (CSV) or lists available properties.";optionalCookies=!0;endpoints=[{url:"https://mysmartenergy.nj.pseg.com"}];positionals=[{name:"property",description:"Property name (e.g. '100 Electric') or 1-based index to download usage for.",required:!1}];parameters=[{name:"interval",type:"string",description:"Select interval: 15, 30, hourly, daily, weekly, monthly, billing",default:"billing",short:"i"},{name:"list",type:"boolean",description:"List all downloadable properties instead of downloading",short:"l"}];async fetchData(r,a){const i={stack:[],error:void 0,hasError:!1};try{const t=!!a.debug,{username:r,password:n}=this.resolveCredentials(a),s=e(i,await this.connect(a),!0);if(await o({page:s,intendedUrl:"https://mysmartenergy.nj.pseg.com/Dashboard",emailSelector:"#LoginEmail",emailValue:r,passwordSelector:"#LoginPassword",passwordValue:n,submitButtonSelector:"button.loginBtn",delayMs:1e3,debug:t}),a.list){t&&console.log("Listing downloadable properties...");const e=await this.getProperties(s,a);let r=["Downloadable properties:"];for(const t of e){const e=t.isCurrent?" [current]":"";r.push(`${this.formatPropertyLabel(t)}${e} | ${t.address} | ${t.owner}`)}return r.join("\n")}const l=a.property;if(!l)throw new Error("Missing required argument: <property> or --list");let c="Electric",d=this.normalizeInterval(a.interval),p=l;const u=String(l).trim();if(/^\d+$/.test(u)){const e=await this.selectPropertyByIndex(s,Number(u),a);c=e.serviceType,p=e.label}else{const e=await this.selectPropertyByName(s,u,a);c=e.serviceType,p=e.label}t&&console.log(`Opening dashboard and downloading ${p} usage CSV for ${d}...`),await this.gotoDashboardIfNeeded(s,a),await this.openDownloadPanelIfNeeded(s,t),d=await this.setDownloadOptions(s,c,d);return await this.fetchDownloadCsvText(s)}catch(e){i.error=e,i.hasError=!0}finally{const e=t(i);e&&await e}}async gotoDashboardIfNeeded(e,t){if(!e.url().toLowerCase().startsWith("https://mysmartenergy.nj.pseg.com/dashboard")){t.debug&&console.log(`Current URL '${e.url()}' is not in dashboard. Navigating to Dashboard...`),await e.goto("https://mysmartenergy.nj.pseg.com/Dashboard",{waitUntil:"domcontentloaded"});if(await e.locator("#LoginEmail").isVisible().catch(()=>!1)){t.debug&&console.log("Session expired or redirected to login. Re-authenticating...");const{username:r,password:a}=this.resolveCredentials(t);await o({page:e,intendedUrl:"https://mysmartenergy.nj.pseg.com/Dashboard",emailSelector:"#LoginEmail",emailValue:r,passwordSelector:"#LoginPassword",passwordValue:a,submitButtonSelector:"button.loginBtn",delayMs:1e3,debug:!!t.debug})}}}async ensurePropertyOverlayOpen(e,t){await this.gotoDashboardIfNeeded(e,t);if(!await e.locator('.selectPropertyContainer input[placeholder="Search"]').isVisible().catch(()=>!1)){const r=e.getByRole("link",{name:"Select Property"});await r.waitFor({state:"visible",timeout:5e3}).catch(()=>{});try{await r.click()}catch(r){t.debug&&console.warn("Failed to click 'Select Property' by role, trying text locator:",r),await e.locator('a:has-text("Select Property")').first().click()}await e.waitForSelector(".selectPropertyContainer",{state:"visible",timeout:5e3})}}async getProperties(e,t){return await this.ensurePropertyOverlayOpen(e,t),await e.evaluate(()=>Array.from(document.querySelectorAll(".selectPropertyContainer li")).map(e=>{const t=Array.from(e.querySelectorAll("h4"));return{propertyId:e.getAttribute("data-property-id")||"",propertyType:e.getAttribute("data-property-type")||"",title:e.querySelector("h2")?.textContent?.trim()||"",owner:t[0]?.textContent?.trim()||"",address:t[1]?.textContent?.trim()||"",isCurrent:e.classList.contains("current")}}))}async selectPropertyByIndex(e,t,r){const o=await this.getProperties(e,r);if(!Number.isInteger(t)||t<1||t>o.length)throw new Error(`Property index ${t} is out of range. Use 1-${o.length}.`);const a=o[t-1];return await e.goto(`https://mysmartenergy.nj.pseg.com/Dashboard/SetMeterGroup?meterGroupId=${a.propertyId}`,{waitUntil:"domcontentloaded"}),await e.waitForTimeout(1e3),{label:this.formatPropertyLabel(a),title:a.title,serviceType:this.inferServiceTypeFromPropertyTitle(a.title)}}async selectPropertyByName(e,t,r){const o=String(t).trim().toLowerCase();if(!o)throw new Error("Property name is required.");const a=await this.getProperties(e,r);let i=a.find(e=>String(e.title).trim().toLowerCase()===o);if(i||(i=a.find(e=>this.formatPropertyLabel(e).toLowerCase()===o)),!i){const e=a.map(e=>this.formatPropertyLabel(e)).filter(Boolean).join(", ");throw new Error(`Property not found: ${t}. Available properties: ${e}`)}return await e.goto(`https://mysmartenergy.nj.pseg.com/Dashboard/SetMeterGroup?meterGroupId=${i.propertyId}`,{waitUntil:"domcontentloaded"}),await e.waitForTimeout(1e3),{label:this.formatPropertyLabel(i),title:i.title,serviceType:this.inferServiceTypeFromPropertyTitle(i.title)}}async openDownloadPanelIfNeeded(e,t){const r="#downloadOptions";if(!await e.locator(r).isVisible().catch(()=>!1)){t&&console.log("Clicking 'Data' link...");try{await e.getByRole("link",{name:"Data"}).click()}catch(r){t&&console.warn("Failed to click 'Data' by role, trying text locator:",r),await e.locator('a:has-text("Data")').first().click()}try{return void await e.waitForSelector(r,{state:"visible",timeout:5e3})}catch{}t&&console.log("Clicking 'download' link...");try{await e.getByRole("link",{name:"download"}).click()}catch(r){t&&console.warn("Failed to click 'download' by role, trying text locator:",r),await e.locator('a:has-text("download")').first().click()}await e.waitForSelector(r,{state:"visible",timeout:1e4})}}async setNativeSelectValue(e,t,r){const o=`select[name="${t}"]`;return await e.evaluate(({selector:e,value:t})=>{const r=document.querySelector(e);if(!r)throw new Error(`${e} select not found`);return r.value=t,r.dispatchEvent(new Event("change",{bubbles:!0})),{value:r.value}},{selector:o,value:r})}async getSelectOptions(e,t){const r=`select[name="${t}"]`;return await e.evaluate(e=>{const t=document.querySelector(e);if(!t)throw new Error(`${e} select not found`);return{value:t.value,options:Array.from(t.options).map(e=>({value:e.value,text:e.textContent?.trim()||""}))}},r)}async setDownloadOptions(e,t,r){const o="Gas"===t?"4":"1";if("Gas"===t&&"Billing"!==r)throw new Error("Gas usage only supports the Billing interval.");const a=this.intervalToValue(r);if(!a)throw new Error(`Unsupported interval mapping: ${r}`);await e.waitForSelector('select[name="SelectedServiceType"]',{state:"attached",timeout:5e3});const i=await this.setNativeSelectValue(e,"SelectedServiceType",o);if(i.value!==o)throw new Error(`Failed to set service type: ${JSON.stringify(i)}`);await e.waitForTimeout(500),await e.waitForSelector('select[name="SelectedInterval"]',{state:"attached",timeout:5e3});const n=await this.getSelectOptions(e,"SelectedInterval");if(!new Set(n.options.map(e=>e.value)).has(a)){const e=n.options.map(e=>e.text).join(", ");throw new Error(`Interval ${r} is not available for ${t}. Available intervals: ${e}`)}const s=n.value===a?n:await this.setNativeSelectValue(e,"SelectedInterval",a);if(s.value!==a)throw new Error(`Failed to set interval: ${JSON.stringify(s)}`);return r}async fetchDownloadCsvText(e){return await e.evaluate(async()=>{const e=document.querySelector("#downloadOptions");if(!e)throw new Error("Download form not found");const t=await fetch(e.action,{method:"POST",body:new FormData(e),credentials:"same-origin"});if(!t.ok)throw new Error("Download request failed with status "+t.status+" "+t.statusText);return await t.text()})}normalizeInterval(e){const t={15:"15-Minute","15-minute":"15-Minute",30:"30-Minute","30-minute":"30-Minute",hourly:"Hourly",daily:"Daily",weekly:"Weekly",monthly:"Monthly",billing:"Billing"}[String(e||"").trim().toLowerCase()];if(!t)throw new Error(`Invalid interval: ${e}. Use 15, 30, hourly, daily, weekly, monthly, or billing.`);return t}intervalToValue(e){return{"15-Minute":"3","30-Minute":"4",Hourly:"5",Daily:"6",Weekly:"8",Monthly:"9",Billing:"7"}[e]}inferServiceTypeFromPropertyTitle(e){if(/\bgas\b/i.test(e))return"Gas";if(/\belectric\b/i.test(e))return"Electric";throw new Error(`Could not infer service type from property title: ${e}`)}extractAddressNumber(e){const t=String(e||"").match(/^(\d+)/);return t?t[1]:""}formatPropertyLabel(e){const t=String(e.title||"").trim();if(t)return t;const r=e.propertyType||this.inferServiceTypeFromPropertyTitle(e.title);return`${this.extractAddressNumber(e.address)||e.propertyId||"Unknown"} ${r}`}}