website-api 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{createInterface as e}from"node:readline/promises";import{stdin as o,stdout as n}from"node:process";import i from"chalk";import t from"cli-table3";import{loadSites as s,getSite as r}from"../core/runtime.js";import{addRegistry as a,installEntry as l,listInstalled as c,loadIndex as d,removeInstalled as g,removeRegistry as m,resolveEntry as y,resolveRegistries as p,searchRegistries as h}from"../core/registry.js";async function f(t){if(!o.isTTY)return!1;const s=e({input:o,output:n});try{const e=(await s.question(`${t} ${i.gray("[y/N]")} `)).trim().toLowerCase();return"y"===e||"yes"===e}finally{s.close()}}function u(e){console.error(i.red(`Error: ${e}`)),process.exit(1)}export function registerExtCommands(e){const o=e.command("ext").description("Discover and install website extensions from a public registry");o.command("search [query]").description("Search configured registries for installable sites").option("--refresh","Bypass the cached index and re-fetch").action(async(e,o)=>{const n=await h(e??"",{refresh:o.refresh});if(0===n.length)return void console.log(i.yellow(`No sites${e?` matching "${e}"`:""} found.`));const s=new t({head:[i.bold.cyan("ID"),i.bold.cyan("Name"),i.bold.cyan("Domain"),i.bold.cyan("Registry"),i.bold.cyan("Description")],colWidths:[16,22,18,14,44],wordWrap:!0,style:{head:[],border:[]}});for(const e of n){const o=[];"browser"===e.transport&&o.push(i.magenta("[p]")),e.auth&&o.push(i.red("[l]"));const n=o.length?`${e.name} ${o.join(" ")}`:e.name;s.push([i.yellow(e.id),n,i.underline(e.domain),e.registry.name,e.description])}console.log(s.toString()),console.log(`\nInstall one with: ${i.bold.cyan("npx website-api ext install <id>")}\n`)}),o.command("info <id>").description("Show full catalog details for a single site").option("--registry <name>","Disambiguate when multiple registries offer the id").action(async(e,o)=>{const{source:n,entry:t}=await y(e,{registryName:o.registry});console.log(i.bold.green(`\n🌐 ${t.name} ${i.yellow(`(${t.id})`)}\n`)),console.log(` ${i.italic(t.description)}\n`);const s=[["Domain",t.domain],["Registry",`${n.name} (${n.repo})`],["Version",t.version??"—"],["Transport",t.transport??"http"],["Requires login",t.auth?"yes":"no"],["Tags",t.tags?.join(", ")||"—"],["Files",t.files.map(e=>e.name).join(", ")]];for(const[e,o]of s)console.log(` ${i.bold((e+":").padEnd(16))} ${o}`);console.log(`\nInstall with: ${i.bold.cyan(`npx website-api ext install ${t.id}`)}\n`)}),o.command("install <id>").alias("add-site").description("Download and install a site from a registry into your extensions folder").option("--registry <name>","Disambiguate when multiple registries offer the id").option("-y, --yes","Skip the confirmation prompt").option("--refresh","Bypass the cached index and re-fetch").action(async(e,o)=>{const{source:n,index:t,entry:a}=await y(e,{registryName:o.registry,refresh:o.refresh});console.log(i.bold(`\nAbout to install ${i.yellow(a.id)} — ${a.name}`)),console.log(` ${i.bold("Domain:")} ${a.domain}`),console.log(` ${i.bold("Registry:")} ${n.name} (${n.repo}@${t.commit.slice(0,10)})`),console.log(` ${i.bold("Files:")} ${a.files.map(e=>e.name).join(", ")}`),a.auth&&console.log(i.red(` ⚠ This site performs a login and will read saved credentials for ${a.domain}.`)),await s();const c=r(a.id);if("bundled"===c?.origin&&console.log(i.yellow(` ⚠ This will shadow the bundled "${a.id}" site.`)),console.log(i.gray(" ⚠ Installing runs third-party code with your browser session. Only install sites you trust.\n")),!o.yes&&!await f("Install this site?"))return void console.log(i.yellow("Aborted."));const{dir:d}=await l(n,t,a,{refresh:o.refresh});console.log(i.green(`\nāœ“ Installed ${a.id} → ${d}`)),console.log(`Run it with: ${i.bold.cyan(`npx website-api ${a.id}`)}\n`)}),o.command("list").description("List sites you have installed from registries").action(()=>{const e=c();if(0===e.length)return void console.log(i.yellow("No registry sites installed. Try: ")+i.cyan("npx website-api ext search"));const o=new t({head:[i.bold.cyan("ID"),i.bold.cyan("Version"),i.bold.cyan("Registry"),i.bold.cyan("Commit"),i.bold.cyan("Installed")],style:{head:[],border:[]}});for(const n of e)o.push([i.yellow(n.id),n.version??"—",n.repo,n.commit.slice(0,10),n.installedAt.slice(0,10)]);console.log(o.toString())}),o.command("remove <id>").alias("uninstall").description("Remove an installed registry site").action(e=>{g(e)?console.log(i.green(`āœ“ Removed ${e}`)):u(`"${e}" is not installed`)}),o.command("update [id]").description("Re-install installed sites whose registry commit has changed").option("-y, --yes","Skip the confirmation prompt").action(async(e,o)=>{const n=c().filter(o=>!e||o.id===e);0===n.length&&u(e?`"${e}" is not installed`:"no registry sites installed");let t=0;for(const e of n){let n;try{n=await y(e.id,{registryName:e.registry,refresh:!0})}catch{console.log(i.yellow(`• ${e.id}: no longer in registry, skipping`));continue}n.index.commit!==e.commit?(console.log(`• ${e.id}: ${e.commit.slice(0,10)} → ${n.index.commit.slice(0,10)}`),(o.yes||await f(` Update ${e.id}?`))&&(await l(n.source,n.index,n.entry),console.log(i.green(` āœ“ updated ${e.id}`)),t++)):console.log(i.gray(`• ${e.id}: up to date`))}console.log(t?i.green(`\nUpdated ${t} site(s).`):i.gray("\nNothing to update."))});const n=o.command("registry").description("Manage the registries searched for sites");n.command("list").description("List configured registries (in search priority order)").action(()=>{const e=new t({head:[i.bold.cyan("Name"),i.bold.cyan("Repo"),i.bold.cyan("Branch")],style:{head:[],border:[]}});for(const o of p())e.push([o.name,o.repo,o.branch]);console.log(e.toString())}),n.command("add <spec>").description("Add a registry (owner/repo, owner/repo#branch, or a github.com URL)").action(async e=>{const o=a(e);try{const e=await d(o,{refresh:!0});console.log(i.green(`āœ“ Added registry ${o.name} (${o.repo}) — ${e.sites.length} site(s) available`))}catch(e){console.log(i.yellow(`Added ${o.repo}, but its index.json could not be fetched: ${e instanceof Error?e.message:String(e)}`))}}),n.command("remove <repoOrName>").description("Remove a configured registry").action(e=>{m(e)?console.log(i.green(`āœ“ Removed registry ${e}`)):u(`registry "${e}" not found in config`)})}
1
+ import{createInterface as o}from"node:readline/promises";import{stdin as e,stdout as n}from"node:process";import{existsSync as t,readdirSync as i,statSync as s,writeFileSync as r}from"node:fs";import{join as a,resolve as l}from"node:path";import{pathToFileURL as c}from"node:url";import d from"chalk";import m from"cli-table3";import{loadSites as g,getSite as p}from"../core/runtime.js";import{defineSite as y}from"../core/define-site.js";import{createContext as f}from"../core/context.js";import{parseArgsForWebsite as h}from"../util/args-parser.js";import{addRegistry as u,installEntry as $,listInstalled as b,loadIndex as w,removeInstalled as x,removeRegistry as j,resolveEntry as v,resolveRegistries as R,searchRegistries as N}from"../core/registry.js";async function D(t){if(!e.isTTY)return!1;const i=o({input:e,output:n});try{const o=(await i.question(`${t} ${d.gray("[y/N]")} `)).trim().toLowerCase();return"y"===o||"yes"===o}finally{i.close()}}function S(o){console.error(d.red(`Error: ${o}`)),process.exit(1)}const I=o=>o instanceof Error?o.message:String(o);export function registerExtCommands(o){o.enablePositionalOptions();const e=o.command("ext").description("Discover and install website extensions from a public registry").enablePositionalOptions();e.command("test <path> [args...]").description("Load and run a site .js file (or its folder) directly from disk — no install").allowUnknownOption().passThroughOptions().action(async(o,e)=>{const n=function(o){const e=l(process.cwd(),o);if(t(e)||S(`path not found: ${o}`),s(e).isFile())return e;const n=i(e);for(const o of["index.mjs","index.js"])if(n.includes(o))return a(e,o);const r=n.find(o=>/\.(m?js)$/.test(o)&&!o.includes(".test."));if(r)return a(e,r);S(`no .js/.mjs entry found in ${o}`)}(o);let m;try{m=await import(c(n).href)}catch(o){S(`failed to import ${n}: ${I(o)}`)}const g=m.default??m.site;let p,u;g&&"object"==typeof g||S(`${n} does not default-export a site object`);try{p=y(g)}catch(o){S(I(o))}p.origin="extension";try{const o=(e??[]).filter(o=>"--"!==o);u=h(p.positionals,p.parameters,o)}catch(o){S(I(o))}if(u.helpRequested)return void function(o,e){if(console.log(d.bold.green(`\n${o.name} (${o.id})`)+d.gray(` — ${e}\n`)),console.log(` ${d.italic(o.description)}\n`),o.positionals.length){console.log(d.bold("Positionals:"));for(const e of o.positionals)console.log(` ${d.cyan(e.name.padEnd(16))} ${e.description}`);console.log()}console.log(d.bold("Options:"));for(const e of o.parameters){const o=(e.short?`-${e.short}, `:" ")+`--${e.name}`+("boolean"===e.type?"":" <value>");console.log(` ${d.yellow(o.padEnd(26))} ${e.description}`)}console.log()}(p,n);console.error(d.gray(`ā–¶ running "${p.id}" from ${n} (not installed)`));const{ctx:$,dispose:b}=f(p,u.options);try{const o=await p.run($),e="string"==typeof o?o:JSON.stringify(o,null,2);u.options.out?(r(u.options.out,e+"\n","utf8"),console.error(d.green(`āœ“ wrote ${u.options.out}`))):console.log(e)}catch(o){S(I(o))}finally{await b()}}),e.command("search [query]").description("Search configured registries for installable sites").option("--refresh","Bypass the cached index and re-fetch").action(async(o,e)=>{const n=await N(o??"",{refresh:e.refresh});if(0===n.length)return void console.log(d.yellow(`No sites${o?` matching "${o}"`:""} found.`));const t=new m({head:[d.bold.cyan("ID"),d.bold.cyan("Name"),d.bold.cyan("Domain"),d.bold.cyan("Registry"),d.bold.cyan("Description")],colWidths:[16,22,18,14,44],wordWrap:!0,style:{head:[],border:[]}});for(const o of n){const e=[];"browser"===o.transport&&e.push(d.magenta("[p]")),o.auth&&e.push(d.red("[l]"));const n=e.length?`${o.name} ${e.join(" ")}`:o.name;t.push([d.yellow(o.id),n,d.underline(o.domain),o.registry.name,o.description])}console.log(t.toString()),console.log(`\nInstall one with: ${d.bold.cyan("npx website-api ext install <id>")}\n`)}),e.command("info <id>").description("Show full catalog details for a single site").option("--registry <name>","Disambiguate when multiple registries offer the id").action(async(o,e)=>{const{source:n,entry:t}=await v(o,{registryName:e.registry});console.log(d.bold.green(`\n🌐 ${t.name} ${d.yellow(`(${t.id})`)}\n`)),console.log(` ${d.italic(t.description)}\n`);const i=[["Domain",t.domain],["Registry",`${n.name} (${n.repo})`],["Version",t.version??"—"],["Transport",t.transport??"http"],["Requires login",t.auth?"yes":"no"],["Tags",t.tags?.join(", ")||"—"],["Files",t.files.map(o=>o.name).join(", ")]];for(const[o,e]of i)console.log(` ${d.bold((o+":").padEnd(16))} ${e}`);console.log(`\nInstall with: ${d.bold.cyan(`npx website-api ext install ${t.id}`)}\n`)}),e.command("install <id>").alias("add-site").description("Download and install a site from a registry into your extensions folder").option("--registry <name>","Disambiguate when multiple registries offer the id").option("-y, --yes","Skip the confirmation prompt").option("--refresh","Bypass the cached index and re-fetch").action(async(o,e)=>{const{source:n,index:t,entry:i}=await v(o,{registryName:e.registry,refresh:e.refresh});console.log(d.bold(`\nAbout to install ${d.yellow(i.id)} — ${i.name}`)),console.log(` ${d.bold("Domain:")} ${i.domain}`),console.log(` ${d.bold("Registry:")} ${n.name} (${n.repo}@${t.commit.slice(0,10)})`),console.log(` ${d.bold("Files:")} ${i.files.map(o=>o.name).join(", ")}`),i.auth&&console.log(d.red(` ⚠ This site performs a login and will read saved credentials for ${i.domain}.`)),await g();const s=p(i.id);if("bundled"===s?.origin&&console.log(d.yellow(` ⚠ This will shadow the bundled "${i.id}" site.`)),console.log(d.gray(" ⚠ Installing runs third-party code with your browser session. Only install sites you trust.\n")),!e.yes&&!await D("Install this site?"))return void console.log(d.yellow("Aborted."));const{dir:r}=await $(n,t,i,{refresh:e.refresh});console.log(d.green(`\nāœ“ Installed ${i.id} → ${r}`)),console.log(`Run it with: ${d.bold.cyan(`npx website-api ${i.id}`)}\n`)}),e.command("list").description("List sites you have installed from registries").action(()=>{const o=b();if(0===o.length)return void console.log(d.yellow("No registry sites installed. Try: ")+d.cyan("npx website-api ext search"));const e=new m({head:[d.bold.cyan("ID"),d.bold.cyan("Version"),d.bold.cyan("Registry"),d.bold.cyan("Commit"),d.bold.cyan("Installed")],style:{head:[],border:[]}});for(const n of o)e.push([d.yellow(n.id),n.version??"—",n.repo,n.commit.slice(0,10),n.installedAt.slice(0,10)]);console.log(e.toString())}),e.command("remove <id>").alias("uninstall").description("Remove an installed registry site").action(o=>{x(o)?console.log(d.green(`āœ“ Removed ${o}`)):S(`"${o}" is not installed`)}),e.command("update [id]").description("Re-install installed sites whose registry commit has changed").option("-y, --yes","Skip the confirmation prompt").action(async(o,e)=>{const n=b().filter(e=>!o||e.id===o);0===n.length&&S(o?`"${o}" is not installed`:"no registry sites installed");let t=0;for(const o of n){let n;try{n=await v(o.id,{registryName:o.registry,refresh:!0})}catch{console.log(d.yellow(`• ${o.id}: no longer in registry, skipping`));continue}n.index.commit!==o.commit?(console.log(`• ${o.id}: ${o.commit.slice(0,10)} → ${n.index.commit.slice(0,10)}`),(e.yes||await D(` Update ${o.id}?`))&&(await $(n.source,n.index,n.entry),console.log(d.green(` āœ“ updated ${o.id}`)),t++)):console.log(d.gray(`• ${o.id}: up to date`))}console.log(t?d.green(`\nUpdated ${t} site(s).`):d.gray("\nNothing to update."))});const n=e.command("registry").description("Manage the registries searched for sites");n.command("list").description("List configured registries (in search priority order)").action(()=>{const o=new m({head:[d.bold.cyan("Name"),d.bold.cyan("Repo"),d.bold.cyan("Branch")],style:{head:[],border:[]}});for(const e of R())o.push([e.name,e.repo,e.branch]);console.log(o.toString())}),n.command("add <spec>").description("Add a registry (owner/repo, owner/repo#branch, or a github.com URL)").action(async o=>{const e=u(o);try{const o=await w(e,{refresh:!0});console.log(d.green(`āœ“ Added registry ${e.name} (${e.repo}) — ${o.sites.length} site(s) available`))}catch(o){console.log(d.yellow(`Added ${e.repo}, but its index.json could not be fetched: ${o instanceof Error?o.message:String(o)}`))}}),n.command("remove <repoOrName>").description("Remove a configured registry").action(o=>{j(o)?console.log(d.green(`āœ“ Removed registry ${o}`)):S(`registry "${o}" not found in config`)})}
@@ -1 +1 @@
1
- import{resolve as e}from"node:path";import{buildCookieString as i,resolveCookies as o,resolveCredentials as t,resolveUserAgent as r}from"../capabilities/cookies.js";import{createHttp as a}from"../capabilities/http.js";import{applyFingerprint as s}from"../capabilities/fingerprint.js";import{connectChrome as n}from"../capabilities/browser.js";import{createSaver as c}from"../capabilities/download.js";export function createContext(p,d={},l={}){const u=l.env??process.env,g=!!d.debug,m="required"===p.cookies;let w;const b=()=>(void 0===w&&(w=o(p.domain,d,m,l)),w),f=()=>i(b()),h=()=>r(d,u),k=()=>t(p.domain,d,l),v=a({fetchImpl:l.fetchImpl,cookieString:f,userAgent:h,debug:g}),D=l.connectBrowser??n;let j,y;const I=void 0!==d.close?!!d.close:!(d.keepOpen||p.keepBrowserOpen),x=()=>(y||(y=(async()=>(j=await D(p.landingUrl,{cdpEndpoint:u.CDP_ENDPOINT,close:I,debug:g}),await s(j.page,p.fingerprint,h()),p.auth&&await p.auth.ensureLoggedIn({page:j.page,debug:g,getCredentials:k}),j.page))()),y),C=d.outDir?e(l.cwd??process.cwd(),d.outDir):null,O=c(d.outDir??null,l.cwd);return{ctx:{site:p,domain:p.domain,options:d,debug:g,outDir:C,cookies:b,cookieString:f,credentials:k,userAgent:h,http:v,browser:x,eval:async e=>(await x()).evaluate(e),save:O},async dispose(){if(j)try{await j.dispose()}catch{}}}}
1
+ import{resolve as e}from"node:path";import{buildCookieString as i,resolveCookies as o,resolveCredentials as t,resolveUserAgent as r}from"../capabilities/cookies.js";import{createHttp as a}from"../capabilities/http.js";import{applyFingerprint as s}from"../capabilities/fingerprint.js";import{connectChrome as n}from"../capabilities/browser.js";import{createSaver as c}from"../capabilities/download.js";export function createContext(p,d={},l={}){const u=l.env??process.env,g=!!d.debug,m="required"===p.cookies;let w;const b=()=>(void 0===w&&(w=o(p.cookieDomain,d,m,l)),w),f=()=>i(b()),h=()=>r(d,u),k=()=>t(p.domain,d,l),v=a({fetchImpl:l.fetchImpl,cookieString:f,userAgent:h,debug:g}),D=l.connectBrowser??n;let j,y;const I=void 0!==d.close?!!d.close:!(d.keepOpen||p.keepBrowserOpen),x=()=>(y||(y=(async()=>(j=await D(p.landingUrl,{cdpEndpoint:u.CDP_ENDPOINT,close:I,debug:g}),await s(j.page,p.fingerprint,h()),p.auth&&await p.auth.ensureLoggedIn({page:j.page,debug:g,getCredentials:k}),j.page))()),y),C=d.outDir?e(l.cwd??process.cwd(),d.outDir):null,O=c(d.outDir??null,l.cwd);return{ctx:{site:p,domain:p.domain,options:d,debug:g,outDir:C,cookies:b,cookieString:f,credentials:k,userAgent:h,http:v,browser:x,eval:async e=>(await x()).evaluate(e),save:O},async dispose(){if(j)try{await j.dispose()}catch{}}}}
@@ -1 +1 @@
1
- import{toLoginStrategy as t}from"../capabilities/login/login-strategy.js";export function isSite(t){return"object"==typeof t&&null!==t&&!0===t.__site}export function defineSite(e){if(isSite(e))return e;for(const t of["id","name","domain","description"])if(!e[t]||"string"!=typeof e[t])throw new Error(`Site is missing required string field "${t}"`);const n=Array.isArray(e.endpoints)&&e.endpoints.length>0;if(!e.run&&!n)throw new Error(`Site "${e.id}" must define either "endpoints" or "run"`);const i=e.run?async t=>e.run(t):(r=e.endpoints,async t=>{const e=r[0],n="html"===e.responseType?"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8":"text"===e.responseType?"text/plain,*/*;q=0.8":"application/json, text/plain, */*",i={method:e.method||"GET",headers:{Accept:n,...e.headers}};let o;return o="html"===e.responseType||"text"===e.responseType?await t.http.text(e.url,i):await t.http.json(e.url,i),e.transform?e.transform(o,t):o});var r;return{id:e.id,name:e.name,domain:e.domain,description:e.description,transport:e.transport??"http",cookies:e.cookies??"required",fingerprint:e.fingerprint??"stealth",keepBrowserOpen:e.keepBrowserOpen??!1,auth:e.auth?t(e.auth):void 0,parameters:e.parameters??[],positionals:e.positionals??[],landingUrl:e.endpoints?.[0]?.url??`https://${e.domain}`,run:i,__site:!0}}
1
+ import{toLoginStrategy as t}from"../capabilities/login/login-strategy.js";export function isSite(t){return"object"==typeof t&&null!==t&&!0===t.__site}export function defineSite(e){if(isSite(e))return e;for(const t of["id","name","domain","description"])if(!e[t]||"string"!=typeof e[t])throw new Error(`Site is missing required string field "${t}"`);const i=Array.isArray(e.endpoints)&&e.endpoints.length>0;if(!e.run&&!i)throw new Error(`Site "${e.id}" must define either "endpoints" or "run"`);const n=e.run?async t=>e.run(t):(r=e.endpoints,async t=>{const e=r[0],i="html"===e.responseType?"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8":"text"===e.responseType?"text/plain,*/*;q=0.8":"application/json, text/plain, */*",n={method:e.method||"GET",headers:{Accept:i,...e.headers}};let o;return o="html"===e.responseType||"text"===e.responseType?await t.http.text(e.url,n):await t.http.json(e.url,n),e.transform?e.transform(o,t):o});var r;return{id:e.id,name:e.name,domain:e.domain,cookieDomain:e.cookieDomain??e.domain,description:e.description,transport:e.transport??"http",cookies:e.cookies??"required",fingerprint:e.fingerprint??"stealth",keepBrowserOpen:e.keepBrowserOpen??!1,auth:e.auth?t(e.auth):void 0,parameters:e.parameters??[],positionals:e.positionals??[],landingUrl:e.endpoints?.[0]?.url??`https://${e.domain}`,run:n,__site:!0}}
@@ -0,0 +1,20 @@
1
+ import type { SiteContext } from "../../types.js";
2
+ /** Subset of the `/api/organizations` entry fields this site relies on. */
3
+ export interface ClaudeOrg {
4
+ uuid: string;
5
+ name: string;
6
+ /** Set (e.g. "stripe_subscription") on paid orgs, null/absent on free ones. */
7
+ billing_type?: string | null;
8
+ rate_limit_tier?: string;
9
+ capabilities?: string[];
10
+ [key: string]: unknown;
11
+ }
12
+ /** Fetches the organizations visible to the logged-in browser session. */
13
+ export declare function fetchOrganizations(ctx: SiteContext): Promise<ClaudeOrg[]>;
14
+ /** Heuristic: a paid org carries a `billing_type` (e.g. "stripe_subscription"). */
15
+ export declare function isPaidOrg(org: ClaudeOrg): boolean;
16
+ /**
17
+ * Picks the org to query. Honors an explicit selector (matched by uuid or
18
+ * name), otherwise prefers the paid account, falling back to the first org.
19
+ */
20
+ export declare function selectOrg(orgs: ClaudeOrg[], selector?: string): ClaudeOrg;
@@ -0,0 +1 @@
1
+ export async function fetchOrganizations(r){const n=await r.http.json("https://claude.ai/api/organizations");if(!Array.isArray(n)||0===n.length)throw new Error("No Claude organizations found in browser session");return n}export function isPaidOrg(r){return"string"==typeof r.billing_type&&r.billing_type.length>0}export function selectOrg(r,n){if(n){const t=n.toLowerCase().trim(),i=r.find(r=>r.uuid.toLowerCase()===t||r.name?.toLowerCase()===t);if(!i)throw new Error(`No Claude organization matching "${n}"`);return i}return r.find(isPaidOrg)??r[0]}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../../types.js").Site[];
2
+ export default _default;
@@ -0,0 +1 @@
1
+ import{defineSite as a}from"../../core/define-site.js";import{fetchOrganizations as e,selectOrg as i}from"./claude-helpers.js";const t=a({id:"claude-orgs",name:"Claude Organizations",domain:"claude.ai",description:"Lists the Claude organizations available to the logged-in browser session.",endpoints:[{url:"https://claude.ai/api/organizations"}]}),n=a({id:"claude-usage",name:"Claude Usage",domain:"claude.ai",description:"Fetches Claude usage percentages and reset times for an organization (the paid account by default).",parameters:[{name:"org",type:"string",description:"Organization UUID or name to query (defaults to the paid account, else the first org)."}],run:async a=>{const t=await e(a),n=i(t,a.options.org),o=await a.http.json(`https://claude.ai/api/organizations/${n.uuid}/usage`);return{organization:{uuid:n.uuid,name:n.name,billing_type:n.billing_type??null},usage:o}}});export default[t,n];
@@ -1,5 +1,16 @@
1
1
  import { type UniversalGoogleSchema } from "../../util/google-json.js";
2
2
  /** Schema mapping Gemini's batchexecute response indices to a usage summary. */
3
3
  export declare const GEMINI_USAGE_SCHEMA: UniversalGoogleSchema;
4
+ /**
5
+ * Pulls the per-session params Gemini embeds in its app HTML (`WIZ_global_data`):
6
+ * SNlM0e → `at` anti-forgery token (required for the POST)
7
+ * cfb2h → `bl` backend release id
8
+ * FdrFJe → `f.sid` session id
9
+ */
10
+ export declare function extractWizParams(html: string): {
11
+ at: string | null;
12
+ bl: string | null;
13
+ fsid: string | null;
14
+ };
4
15
  declare const _default: import("../../types.js").Site;
5
16
  export default _default;
@@ -1 +1 @@
1
- import{defineSite as e}from"../../core/define-site.js";import{decodeGoogleJsonWithSchema as t}from"../../util/google-json.js";export const GEMINI_USAGE_SCHEMA={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 e({id:"gemini-usage",name:"Gemini Usage",domain:"gemini.google.com",description:"Fetches Gemini account usage/quota details via browser-attached Playwright.",transport:"browser",cookies:"optional",endpoints:[{url:"https://gemini.google.com/usage"}],run:async e=>{const o=await e.browser(),i=new Promise((e,t)=>{const i=setTimeout(()=>t(new Error("Timeout waiting for Gemini usage RPC payload. Make sure you are logged into gemini.google.com in Chrome.")),15e3);o.on("response",async t=>{if(t.url().includes("jSf9Qc"))try{const o=await t.text();clearTimeout(i),e(o)}catch{}})});e.debug&&console.log("Reloading to capture Gemini usage network request..."),await o.reload({waitUntil:"domcontentloaded"});const a=await i;return t(a,"jSf9Qc",GEMINI_USAGE_SCHEMA)}});
1
+ import{defineSite as e}from"../../core/define-site.js";import{decodeGoogleJsonWithSchema as t}from"../../util/google-json.js";export const GEMINI_USAGE_SCHEMA={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}}}};const o="jSf9Qc";export function extractWizParams(e){const t=t=>{const o=e.match(new RegExp(`"${t}":"(.*?)"`));return o?o[1]:null};return{at:t("SNlM0e"),bl:t("cfb2h"),fsid:t("FdrFJe")}}export default e({id:"gemini-usage",name:"Gemini Usage",domain:"gemini.google.com",cookieDomain:"google.com",description:"Fetches Gemini account usage/quota details directly over HTTP (no browser).",cookies:"required",endpoints:[{url:"https://gemini.google.com/usage"}],run:async e=>{const i=await e.http.text("https://gemini.google.com/app"),{at:n,bl:r,fsid:a}=extractWizParams(i);if(!n)throw new Error("Could not find Gemini session token (SNlM0e). Make sure you are logged into gemini.google.com in Chrome.");const s=new URLSearchParams({rpcids:o,"source-path":"/usage",hl:"en",rt:"c"});r&&s.set("bl",r),a&&s.set("f.sid",a);const m=new URLSearchParams({"f.req":JSON.stringify([[[o,"[]",null,"generic"]]]),at:n});e.debug&&console.log("Posting Gemini usage batchexecute request...");const c=await e.http.text(`https://gemini.google.com/_/BardChatUi/data/batchexecute?${s.toString()}`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded;charset=UTF-8","x-same-domain":"1",referer:"https://gemini.google.com/"},body:m.toString()});return t(c,o,GEMINI_USAGE_SCHEMA)}});
@@ -185,6 +185,13 @@ export interface SiteDef {
185
185
  domain: string;
186
186
  /** Short description of what data this site fetches. */
187
187
  description: string;
188
+ /**
189
+ * Domain to resolve Chrome cookies for, when it differs from `domain`. Use
190
+ * for sites whose auth cookies live on a parent domain — e.g. Gemini runs on
191
+ * `gemini.google.com` but its session cookies are scoped to `google.com`.
192
+ * Defaults to `domain`.
193
+ */
194
+ cookieDomain?: string;
188
195
  /** Transport used to reach the site. Defaults to "http". */
189
196
  transport?: "http" | "browser";
190
197
  /** Whether valid cookies are required. Defaults to "required". */
@@ -216,6 +223,8 @@ export interface Site {
216
223
  id: string;
217
224
  name: string;
218
225
  domain: string;
226
+ /** Domain used for cookie resolution (defaults to `domain`). */
227
+ cookieDomain: string;
219
228
  description: string;
220
229
  transport: "http" | "browser";
221
230
  cookies: "required" | "optional";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "website-api",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "CLI and library to fetch website API data",
5
5
  "main": "./dist/src/website-api.js",
6
6
  "types": "./dist/src/website-api.d.ts",