@cli4ai/lib
Advanced tools
+131
| /** | ||
| * Shared browser utilities for packages that depend on @cli4ai/chrome | ||
| * Auto-launches managed browser if not running | ||
| */ | ||
| import puppeteer, { Browser, Page } from 'puppeteer'; | ||
| import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from 'fs'; | ||
| import { join } from 'path'; | ||
| import { homedir } from 'os'; | ||
| // Persistent storage paths (same as chrome package) | ||
| const CLI4AI_DIR = join(homedir(), '.cli4ai'); | ||
| const CHROME_DIR = join(CLI4AI_DIR, 'chrome'); | ||
| const PROFILE_DIR = join(CHROME_DIR, 'profile'); | ||
| const WS_FILE = join(CHROME_DIR, 'ws-endpoint'); | ||
| const PID_FILE = join(CHROME_DIR, 'pid'); | ||
| function ensureDirs() { | ||
| if (!existsSync(CLI4AI_DIR)) mkdirSync(CLI4AI_DIR, { recursive: true }); | ||
| if (!existsSync(CHROME_DIR)) mkdirSync(CHROME_DIR, { recursive: true }); | ||
| } | ||
| function isBrowserRunning(): boolean { | ||
| if (!existsSync(PID_FILE)) return false; | ||
| const pid = parseInt(readFileSync(PID_FILE, 'utf-8').trim(), 10); | ||
| try { | ||
| process.kill(pid, 0); | ||
| return true; | ||
| } catch { | ||
| cleanup(); | ||
| return false; | ||
| } | ||
| } | ||
| function cleanup() { | ||
| if (existsSync(WS_FILE)) unlinkSync(WS_FILE); | ||
| if (existsSync(PID_FILE)) unlinkSync(PID_FILE); | ||
| } | ||
| async function launchBrowser(headless: boolean): Promise<Browser> { | ||
| ensureDirs(); | ||
| const browser = await puppeteer.launch({ | ||
| headless: headless ? 'shell' : false, | ||
| userDataDir: PROFILE_DIR, | ||
| args: [ | ||
| '--no-first-run', | ||
| '--no-default-browser-check', | ||
| '--disable-infobars', | ||
| ], | ||
| defaultViewport: null, | ||
| }); | ||
| const wsEndpoint = browser.wsEndpoint(); | ||
| writeFileSync(WS_FILE, wsEndpoint); | ||
| const browserProcess = browser.process(); | ||
| if (browserProcess?.pid) { | ||
| writeFileSync(PID_FILE, String(browserProcess.pid)); | ||
| } | ||
| return browser; | ||
| } | ||
| /** | ||
| * Get or create browser instance | ||
| * Auto-launches if not running | ||
| */ | ||
| export async function getBrowser(headless = false): Promise<Browser> { | ||
| if (isBrowserRunning() && existsSync(WS_FILE)) { | ||
| try { | ||
| const ws = readFileSync(WS_FILE, 'utf-8').trim(); | ||
| return await puppeteer.connect({ browserWSEndpoint: ws }); | ||
| } catch { | ||
| cleanup(); | ||
| } | ||
| } | ||
| return launchBrowser(headless); | ||
| } | ||
| /** | ||
| * Get a page from the browser | ||
| */ | ||
| export async function getPage(browser: Browser, newTab = false): Promise<Page> { | ||
| if (newTab) { | ||
| return browser.newPage(); | ||
| } | ||
| const pages = await browser.pages(); | ||
| return pages[pages.length - 1] || browser.newPage(); | ||
| } | ||
| /** | ||
| * Execute a function with a page, handling browser connection | ||
| */ | ||
| export async function withPage<T>( | ||
| fn: (page: Page) => Promise<T>, | ||
| options: { headless?: boolean; newTab?: boolean } = {} | ||
| ): Promise<T> { | ||
| const browser = await getBrowser(options.headless ?? false); | ||
| const page = await getPage(browser, options.newTab ?? false); | ||
| try { | ||
| return await fn(page); | ||
| } finally { | ||
| browser.disconnect(); | ||
| } | ||
| } | ||
| /** | ||
| * Execute a function with a new tab | ||
| */ | ||
| export async function withNewPage<T>( | ||
| fn: (page: Page) => Promise<T>, | ||
| headless = false | ||
| ): Promise<T> { | ||
| const browser = await getBrowser(headless); | ||
| const page = await browser.newPage(); | ||
| try { | ||
| return await fn(page); | ||
| } finally { | ||
| await page.close(); | ||
| browser.disconnect(); | ||
| } | ||
| } | ||
| /** | ||
| * Check if browser is currently running | ||
| */ | ||
| export function isRunning(): boolean { | ||
| return isBrowserRunning(); | ||
| } | ||
| export { Browser, Page }; |
| /** | ||
| * Shared browser utilities for packages that depend on @cli4ai/chrome | ||
| * Auto-launches managed browser if not running | ||
| */ | ||
| import { Browser, Page } from 'puppeteer'; | ||
| /** | ||
| * Get or create browser instance | ||
| * Auto-launches if not running | ||
| */ | ||
| export declare function getBrowser(headless?: boolean): Promise<Browser>; | ||
| /** | ||
| * Get a page from the browser | ||
| */ | ||
| export declare function getPage(browser: Browser, newTab?: boolean): Promise<Page>; | ||
| /** | ||
| * Execute a function with a page, handling browser connection | ||
| */ | ||
| export declare function withPage<T>(fn: (page: Page) => Promise<T>, options?: { | ||
| headless?: boolean; | ||
| newTab?: boolean; | ||
| }): Promise<T>; | ||
| /** | ||
| * Execute a function with a new tab | ||
| */ | ||
| export declare function withNewPage<T>(fn: (page: Page) => Promise<T>, headless?: boolean): Promise<T>; | ||
| /** | ||
| * Check if browser is currently running | ||
| */ | ||
| export declare function isRunning(): boolean; | ||
| export { Browser, Page }; | ||
| //# sourceMappingURL=browser.d.ts.map |
| {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA2DrD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAUnE;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7E;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAC9B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACrD,OAAO,CAAC,CAAC,CAAC,CAQZ;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAC9B,QAAQ,UAAQ,GACf,OAAO,CAAC,CAAC,CAAC,CASZ;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC"} |
+119
| /** | ||
| * Shared browser utilities for packages that depend on @cli4ai/chrome | ||
| * Auto-launches managed browser if not running | ||
| */ | ||
| import puppeteer, { Browser, Page } from 'puppeteer'; | ||
| import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from 'fs'; | ||
| import { join } from 'path'; | ||
| import { homedir } from 'os'; | ||
| // Persistent storage paths (same as chrome package) | ||
| const CLI4AI_DIR = join(homedir(), '.cli4ai'); | ||
| const CHROME_DIR = join(CLI4AI_DIR, 'chrome'); | ||
| const PROFILE_DIR = join(CHROME_DIR, 'profile'); | ||
| const WS_FILE = join(CHROME_DIR, 'ws-endpoint'); | ||
| const PID_FILE = join(CHROME_DIR, 'pid'); | ||
| function ensureDirs() { | ||
| if (!existsSync(CLI4AI_DIR)) | ||
| mkdirSync(CLI4AI_DIR, { recursive: true }); | ||
| if (!existsSync(CHROME_DIR)) | ||
| mkdirSync(CHROME_DIR, { recursive: true }); | ||
| } | ||
| function isBrowserRunning() { | ||
| if (!existsSync(PID_FILE)) | ||
| return false; | ||
| const pid = parseInt(readFileSync(PID_FILE, 'utf-8').trim(), 10); | ||
| try { | ||
| process.kill(pid, 0); | ||
| return true; | ||
| } | ||
| catch { | ||
| cleanup(); | ||
| return false; | ||
| } | ||
| } | ||
| function cleanup() { | ||
| if (existsSync(WS_FILE)) | ||
| unlinkSync(WS_FILE); | ||
| if (existsSync(PID_FILE)) | ||
| unlinkSync(PID_FILE); | ||
| } | ||
| async function launchBrowser(headless) { | ||
| ensureDirs(); | ||
| const browser = await puppeteer.launch({ | ||
| headless: headless ? 'shell' : false, | ||
| userDataDir: PROFILE_DIR, | ||
| args: [ | ||
| '--no-first-run', | ||
| '--no-default-browser-check', | ||
| '--disable-infobars', | ||
| ], | ||
| defaultViewport: null, | ||
| }); | ||
| const wsEndpoint = browser.wsEndpoint(); | ||
| writeFileSync(WS_FILE, wsEndpoint); | ||
| const browserProcess = browser.process(); | ||
| if (browserProcess?.pid) { | ||
| writeFileSync(PID_FILE, String(browserProcess.pid)); | ||
| } | ||
| return browser; | ||
| } | ||
| /** | ||
| * Get or create browser instance | ||
| * Auto-launches if not running | ||
| */ | ||
| export async function getBrowser(headless = false) { | ||
| if (isBrowserRunning() && existsSync(WS_FILE)) { | ||
| try { | ||
| const ws = readFileSync(WS_FILE, 'utf-8').trim(); | ||
| return await puppeteer.connect({ browserWSEndpoint: ws }); | ||
| } | ||
| catch { | ||
| cleanup(); | ||
| } | ||
| } | ||
| return launchBrowser(headless); | ||
| } | ||
| /** | ||
| * Get a page from the browser | ||
| */ | ||
| export async function getPage(browser, newTab = false) { | ||
| if (newTab) { | ||
| return browser.newPage(); | ||
| } | ||
| const pages = await browser.pages(); | ||
| return pages[pages.length - 1] || browser.newPage(); | ||
| } | ||
| /** | ||
| * Execute a function with a page, handling browser connection | ||
| */ | ||
| export async function withPage(fn, options = {}) { | ||
| const browser = await getBrowser(options.headless ?? false); | ||
| const page = await getPage(browser, options.newTab ?? false); | ||
| try { | ||
| return await fn(page); | ||
| } | ||
| finally { | ||
| browser.disconnect(); | ||
| } | ||
| } | ||
| /** | ||
| * Execute a function with a new tab | ||
| */ | ||
| export async function withNewPage(fn, headless = false) { | ||
| const browser = await getBrowser(headless); | ||
| const page = await browser.newPage(); | ||
| try { | ||
| return await fn(page); | ||
| } | ||
| finally { | ||
| await page.close(); | ||
| browser.disconnect(); | ||
| } | ||
| } | ||
| /** | ||
| * Check if browser is currently running | ||
| */ | ||
| export function isRunning() { | ||
| return isBrowserRunning(); | ||
| } | ||
| export { Browser, Page }; |
+10
-3
| { | ||
| "name": "@cli4ai/lib", | ||
| "version": "1.0.6", | ||
| "version": "1.0.7", | ||
| "description": "Shared CLI framework for cli4ai tools", | ||
| "author": "cliforai", | ||
| "license": "MIT", | ||
| "license": "BUSL-1.1", | ||
| "type": "module", | ||
@@ -20,2 +20,7 @@ "main": "dist/cli.js", | ||
| "default": "./dist/cli.js" | ||
| }, | ||
| "./browser.ts": { | ||
| "types": "./dist/browser.d.ts", | ||
| "import": "./dist/browser.js", | ||
| "default": "./dist/browser.js" | ||
| } | ||
@@ -43,3 +48,4 @@ }, | ||
| "dependencies": { | ||
| "commander": "^14.0.0" | ||
| "commander": "^14.0.0", | ||
| "puppeteer": "^24.0.0" | ||
| }, | ||
@@ -52,2 +58,3 @@ "devDependencies": { | ||
| "cli.ts", | ||
| "browser.ts", | ||
| "dist" | ||
@@ -54,0 +61,0 @@ ], |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Non-permissive License
LicenseA license not known to be considered permissive was found.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
35243
32.23%10
66.67%868
43.95%2
100%1
Infinity%90
-10%15
15.38%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added