@push.rocks/smartpuppeteer 2.2.0 → 2.4.0

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.
@@ -0,0 +1,14 @@
1
+ export interface IOwnedProcessIdentity {
2
+ pid: number;
3
+ parentPid: number;
4
+ processGroupId: number;
5
+ sessionId: number;
6
+ startTime: string;
7
+ state: string;
8
+ }
9
+ export declare const readOwnedProcessIdentity: (pid: number) => Promise<IOwnedProcessIdentity | undefined>;
10
+ export declare const listOwnedProcessGroupMembers: (rootIdentity: IOwnedProcessIdentity) => Promise<IOwnedProcessIdentity[]>;
11
+ export declare const killFrozenOwnedProcessGroup: (rootIdentity: IOwnedProcessIdentity) => boolean;
12
+ export declare const signalOwnedProcessGroup: (rootIdentity: IOwnedProcessIdentity, signal: NodeJS.Signals) => Promise<boolean>;
13
+ export declare const signalOwnedProcessIdentity: (identity: IOwnedProcessIdentity, signal: NodeJS.Signals) => Promise<boolean>;
14
+ export declare const delay: (milliseconds: number) => Promise<void>;
@@ -0,0 +1,132 @@
1
+ import * as plugins from './smartpuppeteer.plugins.js';
2
+ const maxOwnedGroupProcesses = 4096;
3
+ const maxVisibleProcesses = 65536;
4
+ const processReadBatchSize = 64;
5
+ const parseProcessStat = (pid, stat) => {
6
+ const commandEnd = stat.lastIndexOf(')');
7
+ if (commandEnd < 0) {
8
+ throw new Error(`Unable to parse process identity for PID ${pid}`);
9
+ }
10
+ const fields = stat.slice(commandEnd + 2).trim().split(/\s+/);
11
+ const state = fields[0];
12
+ const parentPid = Number(fields[1]);
13
+ const processGroupId = Number(fields[2]);
14
+ const sessionId = Number(fields[3]);
15
+ const startTime = fields[19];
16
+ if (typeof state !== 'string'
17
+ || state.length !== 1
18
+ || !Number.isInteger(parentPid)
19
+ || parentPid < 0
20
+ || !Number.isInteger(processGroupId)
21
+ || processGroupId < 0
22
+ || !Number.isInteger(sessionId)
23
+ || sessionId < 0
24
+ || typeof startTime !== 'string'
25
+ || !/^[0-9]+$/.test(startTime)) {
26
+ throw new Error(`Unable to parse process identity for PID ${pid}`);
27
+ }
28
+ return { pid, parentPid, processGroupId, sessionId, startTime, state };
29
+ };
30
+ export const readOwnedProcessIdentity = async (pid) => {
31
+ if (process.platform !== 'linux') {
32
+ throw new Error('Confirmed browser process ownership is supported on Linux only');
33
+ }
34
+ try {
35
+ const stat = await plugins.fs.promises.readFile(`/proc/${pid}/stat`, 'utf8');
36
+ return parseProcessStat(pid, stat);
37
+ }
38
+ catch (error) {
39
+ if (error.code === 'ENOENT'
40
+ || error.code === 'ESRCH') {
41
+ return undefined;
42
+ }
43
+ throw error;
44
+ }
45
+ };
46
+ const listProcessIdentities = async () => {
47
+ const entries = await plugins.fs.promises.readdir('/proc', { withFileTypes: true });
48
+ const pids = entries
49
+ .filter((entry) => entry.isDirectory() && /^[0-9]+$/.test(entry.name))
50
+ .map((entry) => Number(entry.name));
51
+ if (pids.length > maxVisibleProcesses) {
52
+ throw new Error(`Refusing to inspect more than ${maxVisibleProcesses} processes`);
53
+ }
54
+ const identities = [];
55
+ for (let index = 0; index < pids.length; index += processReadBatchSize) {
56
+ identities.push(...await Promise.all(pids.slice(index, index + processReadBatchSize).map((pid) => readOwnedProcessIdentity(pid))));
57
+ }
58
+ return identities.filter((identity) => Boolean(identity));
59
+ };
60
+ export const listOwnedProcessGroupMembers = async (rootIdentity) => {
61
+ const currentRoot = await readOwnedProcessIdentity(rootIdentity.pid);
62
+ if (currentRoot && currentRoot.startTime !== rootIdentity.startTime) {
63
+ throw new Error('The owned browser PID was reused by another process');
64
+ }
65
+ if (currentRoot
66
+ && (currentRoot.processGroupId !== rootIdentity.processGroupId
67
+ || currentRoot.sessionId !== rootIdentity.sessionId)) {
68
+ throw new Error('The owned browser process changed its process-group identity');
69
+ }
70
+ const members = (await listProcessIdentities()).filter((identity) => (identity.processGroupId === rootIdentity.processGroupId
71
+ && identity.sessionId === rootIdentity.sessionId));
72
+ if (members.length > maxOwnedGroupProcesses) {
73
+ throw new Error(`Owned browser process group exceeded ${maxOwnedGroupProcesses} processes`);
74
+ }
75
+ return members;
76
+ };
77
+ export const killFrozenOwnedProcessGroup = (rootIdentity) => {
78
+ try {
79
+ process.kill(-rootIdentity.processGroupId, 'SIGKILL');
80
+ return true;
81
+ }
82
+ catch (error) {
83
+ if (error.code === 'ESRCH') {
84
+ return false;
85
+ }
86
+ throw error;
87
+ }
88
+ };
89
+ export const signalOwnedProcessGroup = async (rootIdentity, signal) => {
90
+ const currentRoot = await readOwnedProcessIdentity(rootIdentity.pid);
91
+ if (!currentRoot) {
92
+ return false;
93
+ }
94
+ if (currentRoot.startTime !== rootIdentity.startTime
95
+ || currentRoot.processGroupId !== rootIdentity.processGroupId
96
+ || currentRoot.sessionId !== rootIdentity.sessionId) {
97
+ throw new Error('Refusing to signal a reused browser process-group leader');
98
+ }
99
+ try {
100
+ process.kill(-rootIdentity.processGroupId, signal);
101
+ return true;
102
+ }
103
+ catch (error) {
104
+ if (error.code === 'ESRCH') {
105
+ return false;
106
+ }
107
+ throw error;
108
+ }
109
+ };
110
+ export const signalOwnedProcessIdentity = async (identity, signal) => {
111
+ const currentIdentity = await readOwnedProcessIdentity(identity.pid);
112
+ if (!currentIdentity) {
113
+ return false;
114
+ }
115
+ if (currentIdentity.startTime !== identity.startTime) {
116
+ throw new Error(`Refusing to signal reused PID ${identity.pid}`);
117
+ }
118
+ try {
119
+ process.kill(identity.pid, signal);
120
+ return true;
121
+ }
122
+ catch (error) {
123
+ if (error.code === 'ESRCH') {
124
+ return false;
125
+ }
126
+ throw error;
127
+ }
128
+ };
129
+ export const delay = async (milliseconds) => {
130
+ await new Promise((resolve) => setTimeout(resolve, milliseconds));
131
+ };
132
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIuaGVscGVycy5wcm9jZXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRwdXBwZXRlZXIuaGVscGVycy5wcm9jZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sNkJBQTZCLENBQUM7QUFXdkQsTUFBTSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFDcEMsTUFBTSxtQkFBbUIsR0FBRyxLQUFLLENBQUM7QUFDbEMsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLENBQUM7QUFFaEMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLEdBQVcsRUFBRSxJQUFZLEVBQXlCLEVBQUU7SUFDNUUsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN6QyxJQUFJLFVBQVUsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNuQixNQUFNLElBQUksS0FBSyxDQUFDLDRDQUE0QyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDOUQsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3hCLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwQyxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekMsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BDLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM3QixJQUNFLE9BQU8sS0FBSyxLQUFL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
@@ -3,6 +3,8 @@ export type TLiveBrowserStatus = 'stopped' | 'starting' | 'running' | 'stopping'
3
3
  export type TLiveBrowserImageFormat = 'jpeg' | 'png';
4
4
  export type TLiveBrowserTabStatus = 'open' | 'crashed';
5
5
  export type TLiveBrowserWaitUntil = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
6
+ export declare const liveBrowserDefaultMaxOutstandingFrames = 3;
7
+ export declare const liveBrowserMaxOutstandingFrames = 64;
6
8
  export interface ILiveBrowserViewport {
7
9
  width: number;
8
10
  height: number;
@@ -14,12 +16,18 @@ export interface ILiveBrowserScreencastOptions {
14
16
  maxWidth?: number;
15
17
  maxHeight?: number;
16
18
  everyNthFrame?: number;
19
+ maxOutstandingFrames?: number;
17
20
  }
18
21
  export interface ILiveBrowserSecurityOptions {
19
22
  denyDownloads?: boolean;
20
23
  denyFileChoosers?: boolean;
21
24
  denyPermissions?: boolean;
22
25
  httpNavigationOnly?: boolean;
26
+ proxyCredentials?: ILiveBrowserProxyCredentials;
27
+ }
28
+ export interface ILiveBrowserProxyCredentials {
29
+ username: string;
30
+ password: string;
23
31
  }
24
32
  export interface ILiveBrowserOperationOptions {
25
33
  signal?: AbortSignal;
@@ -45,6 +53,24 @@ export interface ILiveBrowserSessionOptions extends Omit<IEnvAwareOptions, 'laun
45
53
  security?: ILiveBrowserSecurityOptions;
46
54
  allowEvaluation?: boolean;
47
55
  }
56
+ export interface ILiveBrowserProcessState {
57
+ generation: number;
58
+ pid: number | null;
59
+ processGroupId: number | null;
60
+ running: boolean;
61
+ exitCode: number | null;
62
+ signalCode: NodeJS.Signals | null;
63
+ }
64
+ export interface ILiveBrowserTerminationOptions {
65
+ gracefulTimeoutMs?: number;
66
+ forceTimeoutMs?: number;
67
+ }
68
+ export interface ILiveBrowserTerminationResult extends ILiveBrowserProcessState {
69
+ forced: boolean;
70
+ shutdownComplete: boolean;
71
+ confirmedDead: boolean;
72
+ errors: string[];
73
+ }
48
74
  export interface ILiveBrowserTabState {
49
75
  id: string;
50
76
  url: string;
@@ -1,2 +1,3 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIuaW50ZXJmYWNlcy5saXZlYnJvd3Nlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLmludGVyZmFjZXMubGl2ZWJyb3dzZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
1
+ export const liveBrowserDefaultMaxOutstandingFrames = 3;
2
+ export const liveBrowserMaxOutstandingFrames = 64;
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIuaW50ZXJmYWNlcy5saXZlYnJvd3Nlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLmludGVyZmFjZXMubGl2ZWJyb3dzZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBV0EsTUFBTSxDQUFDLE1BQU0sc0NBQXNDLEdBQUcsQ0FBQyxDQUFDO0FBQ3hELE1BQU0sQ0FBQyxNQUFNLCtCQUErQixHQUFHLEVBQUUsQ0FBQyJ9
@@ -1,6 +1,8 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import * as os from 'os';
3
- export { Buffer, os };
2
+ import * as fs from 'node:fs';
3
+ import * as http from 'node:http';
4
+ import * as os from 'node:os';
5
+ export { Buffer, fs, http, os };
4
6
  import * as smartdelay from '@push.rocks/smartdelay';
5
7
  import * as smartshell from '@push.rocks/smartshell';
6
8
  export { smartdelay, smartshell };
@@ -1,7 +1,9 @@
1
1
  // node native scope
2
2
  import { Buffer } from 'node:buffer';
3
- import * as os from 'os';
4
- export { Buffer, os };
3
+ import * as fs from 'node:fs';
4
+ import * as http from 'node:http';
5
+ import * as os from 'node:os';
6
+ export { Buffer, fs, http, os };
5
7
  // @pushrocks scope
6
8
  import * as smartdelay from '@push.rocks/smartdelay';
7
9
  import * as smartshell from '@push.rocks/smartshell';
@@ -10,4 +12,4 @@ export { smartdelay, smartshell };
10
12
  import * as puppeteer from 'puppeteer';
11
13
  import treeKill from 'tree-kill';
12
14
  export { puppeteer, treeKill };
13
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDckMsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFFekIsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsQ0FBQztBQUV0QixtQkFBbUI7QUFDbkIsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBRXJELE9BQU8sRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLENBQUM7QUFFbEMsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxTQUFTLE1BQU0sV0FBVyxDQUFDO0FBQ3ZDLE9BQU8sUUFBUSxNQUFNLFdBQVcsQ0FBQztBQUVqQyxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDIn0=
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDckMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFFOUIsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDO0FBRWhDLG1CQUFtQjtBQUNuQixPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFFckQsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsQ0FBQztBQUVsQyxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFNBQVMsTUFBTSxXQUFXLENBQUM7QUFDdkMsT0FBTyxRQUFRLE1BQU0sV0FBVyxDQUFDO0FBRWpDLE9BQU8sRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLENBQUMifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartpuppeteer",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "private": false,
5
5
  "description": "Provides simplified access to Puppeteer for automation and testing purposes.",
6
6
  "main": "dist_ts/index.js",
@@ -19,8 +19,8 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@push.rocks/smartdelay": "^3.0.1",
22
- "@push.rocks/smartshell": "^3.5.0",
23
- "puppeteer": "^25.4.0",
22
+ "@push.rocks/smartshell": "^4.1.0",
23
+ "puppeteer": "^25.5.0",
24
24
  "tree-kill": "^1.2.2"
25
25
  },
26
26
  "files": [
package/readme.hints.md CHANGED
@@ -3,7 +3,7 @@
3
3
  - `getEnvAwareBrowserInstance()` is the only Chromium launch path. Merge caller arguments before adding environment-required sandbox arguments, retain the pipe default, and do not run executable discovery when the caller selected a browser, channel, or executable.
4
4
  - `LiveBrowserSession` uses the browser's default context so all tabs and popups share one profile. Omitting both `launchOptions.userDataDir` and a `--user-data-dir` argument intentionally relies on Puppeteer's ephemeral profile lifecycle.
5
5
  - CDP is private to the live runtime. Public contracts contain transport-neutral values and `Uint8Array` image data, never `CDPSession`, raw CDP frame IDs, or base64 image strings.
6
- - Every published screencast frame has one private sequence-to-CDP acknowledgement entry. Public acknowledgement requires matching tab ID, sequence, generation, and viewport revision. Pending frames are bounded; drops and all stream invalidation paths must retire and acknowledge entries before detaching the CDP session.
6
+ - Every published screencast frame has one private sequence-to-CDP acknowledgement entry. Public acknowledgement requires matching tab ID, sequence, generation, and viewport revision. `screencast.maxOutstandingFrames` bounds those entries independently of any application transport window; overflow retires oldest-first. Drops and all stream invalidation paths must issue each CDP acknowledgement at most once and await in-flight acknowledgements before detaching the CDP session.
7
7
  - Activation, viewport changes, navigation, tab closure, snapshots, observations, semantic actions, and shutdown share one bounded operation scheduler. Raw input and frame acknowledgement remain direct, but must validate active tab, generation, and viewport revision. Repeated internal navigation/load state updates are coalesced per tab, and shutdown cancels queued work.
8
8
  - Retain the scheduler-owned launch `AbortController` for the full browser lifetime. Shutdown aborts both the active operation and Chromium itself so a non-signal-aware Puppeteer command or disabled protocol timeout cannot retain the browser ahead of queued cleanup.
9
9
  - Viewport revision starts at 1 and advances only after `Page.setViewport()` succeeds. Stop and flush the active screencast before applying a viewport or navigation mutation, then restart it with a new generation.
package/readme.md CHANGED
@@ -120,6 +120,8 @@ const session = new LiveBrowserSession({
120
120
  quality: 80,
121
121
  maxWidth: 1280,
122
122
  maxHeight: 720,
123
+ everyNthFrame: 1,
124
+ maxOutstandingFrames: 3,
123
125
  },
124
126
  launchOptions: {
125
127
  headless: true,
@@ -168,11 +170,13 @@ try {
168
170
  }
169
171
  ```
170
172
 
171
- Only the active tab is streamed. Frames carry a session-monotonic sequence, tab/CDP generation, viewport revision, viewport, MIME type, encoded dimensions, screencast metadata, and binary `Uint8Array` data. Every delivered frame must be acknowledged with all four identity fields. A delivered frame remains pending until it is acknowledged, dropped, or retired by the runtime; mismatched, duplicate, stale, retired, or operationally failed acknowledgements return `{ accepted: false }`. Operational acknowledgement failures also emit an `error` event whose code is `frame_acknowledgement_failed`. The runtime bounds pending frames and CDP-acknowledges an oldest frame when it must be dropped. Tab switches, navigation, resize, page cleanup, disconnect, and shutdown retire pending frames and acknowledge them while their CDP session remains available.
173
+ Only the active tab is streamed. Frames carry a session-monotonic sequence, tab/CDP generation, viewport revision, viewport, MIME type, encoded dimensions, screencast metadata, and binary `Uint8Array` data. Every delivered frame must be acknowledged with all four identity fields. A delivered frame remains pending until it is acknowledged, dropped, or retired by the runtime; mismatched, duplicate, stale, retired, or operationally failed acknowledgements return `{ accepted: false }`. Operational acknowledgement failures also emit an `error` event whose code is `frame_acknowledgement_failed`. `screencast.maxOutstandingFrames` is an integer from 1 through `liveBrowserMaxOutstandingFrames` (64) and defaults to `liveBrowserDefaultMaxOutstandingFrames` (3). At capacity, SmartPuppeteer retires and CDP-acknowledges the oldest pending frame before publishing the next one. Tab switches, navigation, resize, page cleanup, disconnect, and shutdown also retire pending frames and await their single CDP acknowledgement attempt before detaching the screencast session.
174
+
175
+ The SmartPuppeteer outstanding-frame bound covers only the private frame-to-`Page.screencastFrameAck` lifecycle. It is not a binary transport window, and `acknowledgeFrame()` is not an application transport acknowledgement. A higher-level runtime must maintain and bound its application frame window separately. Screencast `format` accepts `jpeg` or `png`, `quality` accepts integers from 0 through 100, `maxWidth` and `maxHeight` accept integers from 1 through 4096 subject to an 8,294,400-pixel combined ceiling, and `everyNthFrame` accepts integers from 1 through 100.
172
176
 
173
177
  The live API includes:
174
178
 
175
- - Lifecycle and state: `start()`, `stop()`, `onEvent()`, and `getState()`
179
+ - Lifecycle and state: `start()`, `stop()`, `terminate()`, `getProcessState()`, `onEvent()`, and `getState()`
176
180
  - Tabs and navigation: `createTab()`, `activateTab()`, `closeTab()`, `navigate()`, `back()`, `forward()`, and `reload()`
177
181
  - Viewport and raw input: `setViewport()`, `dispatchMouse()`, `dispatchWheel()`, `dispatchKey()`, and `insertText()`
178
182
  - Agent-oriented actions: `click()`, `fill()`, and `press()` with bounded selectors and timeouts
@@ -188,17 +192,31 @@ Optional browser guards can be enabled when composing a higher-level runtime:
188
192
  ```typescript
189
193
  const guardedSession = new LiveBrowserSession({
190
194
  requireSandbox: true,
195
+ launchOptions: {
196
+ args: [
197
+ '--proxy-server=http://127.0.0.1:8080',
198
+ '--proxy-bypass-list=<-loopback>',
199
+ ],
200
+ },
191
201
  security: {
192
202
  denyDownloads: true,
193
203
  denyFileChoosers: true,
194
204
  denyPermissions: true,
195
205
  httpNavigationOnly: true,
206
+ proxyCredentials: {
207
+ username: 'proxy-user',
208
+ password: 'proxy-password',
209
+ },
196
210
  },
197
211
  });
198
212
  ```
199
213
 
200
214
  `denyDownloads` installs a default-context download denial at launch. `denyPermissions` applies an empty browser-wide permission grant before the first page is exposed, causing unlisted permissions to be denied. `denyFileChoosers` installs persistent CDP cancellation on each registered page. `httpNavigationOnly` limits URLs passed to `createTab()` and `navigate()` to `http:` and `https:`; it does not inspect or rewrite renderer-initiated navigation.
201
215
 
216
+ `proxyCredentials` handles authenticated-proxy challenges for existing and future page and dedicated-worker traffic. SmartPuppeteer supplies credentials only when CDP identifies the challenge source as `Proxy`; origin-server and unknown challenges are cancelled without credentials, and a repeated challenge for the same request is cancelled. Setup commands are queued from Puppeteer's public CDP session-attachment event before Puppeteer resumes a new target. Setup failure rejects `start()` and closes the browser. During a running session, an unexpected browser security-session detach, a live target without a protected replacement session, or a proxy protocol failure is fatal and requests termination; Linux termination is confirmed only when `terminate()` resolves. The caller still owns proxy selection, Chromium proxy arguments, bypass rules, DNS behavior, egress policy, and proxy trust. Credentials remain in process memory for the session lifetime.
217
+
218
+ On Linux, Puppeteer launches Chromium as a dedicated process-group and session leader. `getProcessState()` reports the generation, root PID, process-group ID, and Node.js exit state. `terminate({ gracefulTimeoutMs, forceTimeoutMs })` is idempotent for concurrent callers, first requests normal shutdown, then freezes and kills the owned process group if the graceful deadline expires. It resolves with `confirmedDead: true` only after `/proc` contains no member of that owned process group, the Node.js child exit is observed, and session shutdown settles. PID reuse, permission failures, surviving members, or an unsettled shutdown reject confirmation and keep restart blocked. Confirmed process-group termination is Linux-only; use `stop()` for portable best-effort lifecycle cleanup.
219
+
202
220
  Evaluation is disabled by default. Once enabled, it accepts a JavaScript expression, runs it in a dedicated main-frame isolated world, awaits its result, and returns only JSON-compatible values:
203
221
 
204
222
  ```typescript
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartpuppeteer',
6
- version: '2.2.0',
6
+ version: '2.4.0',
7
7
  description: 'Provides simplified access to Puppeteer for automation and testing purposes.'
8
8
  }