pi-browser-use 0.6.0 → 0.7.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.
- package/README.md +39 -11
- package/dist/auth-verifiers.d.ts +60 -0
- package/dist/auth-verifiers.d.ts.map +1 -0
- package/dist/auth-verifiers.js +92 -0
- package/dist/auth-verifiers.js.map +1 -0
- package/dist/chrome-launcher.d.ts +100 -0
- package/dist/chrome-launcher.d.ts.map +1 -0
- package/dist/chrome-launcher.js +268 -0
- package/dist/chrome-launcher.js.map +1 -0
- package/dist/config.d.ts +3 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -5
- package/dist/config.js.map +1 -1
- package/dist/doctor.d.ts +8 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +16 -6
- package/dist/doctor.js.map +1 -1
- package/dist/existing-flow.d.ts +68 -0
- package/dist/existing-flow.d.ts.map +1 -0
- package/dist/existing-flow.js +128 -0
- package/dist/existing-flow.js.map +1 -0
- package/dist/focus-policy.d.ts +28 -0
- package/dist/focus-policy.d.ts.map +1 -0
- package/dist/focus-policy.js +28 -0
- package/dist/focus-policy.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +497 -39
- package/dist/index.js.map +1 -1
- package/dist/named-profile.d.ts +47 -0
- package/dist/named-profile.d.ts.map +1 -0
- package/dist/named-profile.js +123 -0
- package/dist/named-profile.js.map +1 -0
- package/dist/persistent-backend.d.ts +77 -0
- package/dist/persistent-backend.d.ts.map +1 -0
- package/dist/persistent-backend.js +166 -0
- package/dist/persistent-backend.js.map +1 -0
- package/dist/persistent-store.d.ts +35 -0
- package/dist/persistent-store.d.ts.map +1 -0
- package/dist/persistent-store.js +98 -0
- package/dist/persistent-store.js.map +1 -0
- package/dist/profile-lock.d.ts +40 -0
- package/dist/profile-lock.d.ts.map +1 -0
- package/dist/profile-lock.js +165 -0
- package/dist/profile-lock.js.map +1 -0
- package/dist/profile.d.ts +3 -1
- package/dist/profile.d.ts.map +1 -1
- package/dist/profile.js +15 -6
- package/dist/profile.js.map +1 -1
- package/dist/session-manager.d.ts +125 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +275 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/session.d.ts +92 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +29 -0
- package/dist/session.js.map +1 -0
- package/dist/setup-flow.d.ts +78 -0
- package/dist/setup-flow.d.ts.map +1 -0
- package/dist/setup-flow.js +97 -0
- package/dist/setup-flow.js.map +1 -0
- package/dist/tab-bridge.d.ts +71 -0
- package/dist/tab-bridge.d.ts.map +1 -0
- package/dist/tab-bridge.js +198 -0
- package/dist/tab-bridge.js.map +1 -0
- package/extension/README.md +33 -0
- package/extension/background.js +185 -0
- package/extension/manifest.json +12 -0
- package/package.json +2 -1
- package/skills/auth-bootstrap/SKILL.md +13 -0
- package/skills/browser-policy/SKILL.md +29 -4
- package/skills/gmail-auth/SKILL.md +51 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Persistent-mode metadata (spec section 2).
|
|
3
|
+
*
|
|
4
|
+
* Only durable state is persisted: initialization flag, profile path, last
|
|
5
|
+
* working launch mode, and last bootstrap timestamp. Passwords, cookies,
|
|
6
|
+
* tokens, or copied browser credentials must never be stored here — Chrome
|
|
7
|
+
* owns those inside its profile directory.
|
|
8
|
+
*
|
|
9
|
+
* Default location is a sibling of the profile directory:
|
|
10
|
+
* `<profileDir>.meta.json` (e.g. `~/.pi/browser-profile.meta.json`), so a
|
|
11
|
+
* profile move/rename never orphans its metadata.
|
|
12
|
+
*/
|
|
13
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
14
|
+
import { dirname } from 'node:path';
|
|
15
|
+
import { DEFAULT_PROFILE_DIR } from './config.js';
|
|
16
|
+
export function metadataPathFor(profileDir = DEFAULT_PROFILE_DIR) {
|
|
17
|
+
return `${profileDir}.meta.json`;
|
|
18
|
+
}
|
|
19
|
+
export function defaultMetadata(profileDir = DEFAULT_PROFILE_DIR) {
|
|
20
|
+
return { initialized: false, profilePath: profileDir };
|
|
21
|
+
}
|
|
22
|
+
/** Load metadata; returns uninitialized defaults when absent or corrupt. */
|
|
23
|
+
export function loadPersistentMetadata(profileDir = DEFAULT_PROFILE_DIR) {
|
|
24
|
+
const path = metadataPathFor(profileDir);
|
|
25
|
+
try {
|
|
26
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
27
|
+
return {
|
|
28
|
+
initialized: raw.initialized === true,
|
|
29
|
+
profilePath: typeof raw.profilePath === 'string' ? raw.profilePath : profileDir,
|
|
30
|
+
lastSuccessfulMode: raw.lastSuccessfulMode === 'headless' || raw.lastSuccessfulMode === 'headed'
|
|
31
|
+
? raw.lastSuccessfulMode
|
|
32
|
+
: undefined,
|
|
33
|
+
lastBootstrapAt: typeof raw.lastBootstrapAt === 'string' ? raw.lastBootstrapAt : undefined,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return defaultMetadata(profileDir);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Atomically persist metadata (write-then-rename via temp file). */
|
|
41
|
+
export function savePersistentMetadata(meta) {
|
|
42
|
+
const path = metadataPathFor(meta.profilePath);
|
|
43
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
44
|
+
writeFileSync(path, `${JSON.stringify(meta, null, 2)}\n`, 'utf8');
|
|
45
|
+
}
|
|
46
|
+
/** Record a completed bootstrap: initialized now, timestamped. */
|
|
47
|
+
export function markBootstrapped(profileDir = DEFAULT_PROFILE_DIR, at = new Date().toISOString()) {
|
|
48
|
+
const meta = {
|
|
49
|
+
...loadPersistentMetadata(profileDir),
|
|
50
|
+
initialized: true,
|
|
51
|
+
profilePath: profileDir,
|
|
52
|
+
lastBootstrapAt: at,
|
|
53
|
+
};
|
|
54
|
+
savePersistentMetadata(meta);
|
|
55
|
+
return meta;
|
|
56
|
+
}
|
|
57
|
+
/** Record which launch mode last automated successfully. */
|
|
58
|
+
export function markAutomationResult(profileDir, mode) {
|
|
59
|
+
const meta = {
|
|
60
|
+
...loadPersistentMetadata(profileDir),
|
|
61
|
+
profilePath: profileDir,
|
|
62
|
+
lastSuccessfulMode: mode,
|
|
63
|
+
};
|
|
64
|
+
savePersistentMetadata(meta);
|
|
65
|
+
return meta;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Per-origin headed-background preferences (spec section 8). Stored beside
|
|
69
|
+
* the metadata file (`<profileDir>.sites.json`); origins only, never
|
|
70
|
+
* credentials. A site pinned here starts headed-background instead of
|
|
71
|
+
* globally downgrading Persistent because one site rejects headless.
|
|
72
|
+
*/
|
|
73
|
+
export function sitePreferencesPathFor(profileDir = DEFAULT_PROFILE_DIR) {
|
|
74
|
+
return `${profileDir}.sites.json`;
|
|
75
|
+
}
|
|
76
|
+
/** Load per-origin preferences; empty when absent or corrupt. */
|
|
77
|
+
export function loadSitePreferences(profileDir = DEFAULT_PROFILE_DIR) {
|
|
78
|
+
try {
|
|
79
|
+
const raw = JSON.parse(readFileSync(sitePreferencesPathFor(profileDir), 'utf8'));
|
|
80
|
+
if (!Array.isArray(raw))
|
|
81
|
+
return [];
|
|
82
|
+
return raw.filter((entry) => typeof entry === 'object' &&
|
|
83
|
+
entry !== null &&
|
|
84
|
+
typeof entry.origin === 'string' &&
|
|
85
|
+
(entry.executionMode === 'headless' ||
|
|
86
|
+
entry.executionMode === 'headed-background'));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Persist per-origin preferences. */
|
|
93
|
+
export function saveSitePreferences(profileDir, preferences) {
|
|
94
|
+
const path = sitePreferencesPathFor(profileDir);
|
|
95
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
96
|
+
writeFileSync(path, `${JSON.stringify(preferences, null, 2)}\n`, 'utf8');
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=persistent-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistent-store.js","sourceRoot":"","sources":["../src/persistent-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAGjD,MAAM,UAAU,eAAe,CAAC,aAAqB,mBAAmB;IACtE,OAAO,GAAG,UAAU,YAAY,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,aAAqB,mBAAmB;IAExC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,CAAA;AACxD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,sBAAsB,CACpC,aAAqB,mBAAmB;IAExC,MAAM,IAAI,GAAG,eAAe,CAAC,UAAU,CAAC,CAAA;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAuC,CAAA;QACxF,OAAO;YACL,WAAW,EAAE,GAAG,CAAC,WAAW,KAAK,IAAI;YACrC,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU;YAC/E,kBAAkB,EAChB,GAAG,CAAC,kBAAkB,KAAK,UAAU,IAAI,GAAG,CAAC,kBAAkB,KAAK,QAAQ;gBAC1E,CAAC,CAAC,GAAG,CAAC,kBAAkB;gBACxB,CAAC,CAAC,SAAS;YACf,eAAe,EAAE,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;SAC3F,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,CAAC,UAAU,CAAC,CAAA;IACpC,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,sBAAsB,CAAC,IAA+B;IACpE,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC9C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AACnE,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAC9B,aAAqB,mBAAmB,EACxC,KAAa,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;IAErC,MAAM,IAAI,GAA8B;QACtC,GAAG,sBAAsB,CAAC,UAAU,CAAC;QACrC,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,UAAU;QACvB,eAAe,EAAE,EAAE;KACpB,CAAA;IACD,sBAAsB,CAAC,IAAI,CAAC,CAAA;IAC5B,OAAO,IAAI,CAAA;AACb,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,oBAAoB,CAClC,UAAkB,EAClB,IAA2B;IAE3B,MAAM,IAAI,GAA8B;QACtC,GAAG,sBAAsB,CAAC,UAAU,CAAC;QACrC,WAAW,EAAE,UAAU;QACvB,kBAAkB,EAAE,IAAI;KACzB,CAAA;IACD,sBAAsB,CAAC,IAAI,CAAC,CAAA;IAC5B,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,aAAqB,mBAAmB;IAC7E,OAAO,GAAG,UAAU,aAAa,CAAA;AACnC,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,mBAAmB,CACjC,aAAqB,mBAAmB;IAExC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,sBAAsB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAY,CAAA;QAC3F,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAA;QAClC,OAAO,GAAG,CAAC,MAAM,CACf,CAAC,KAAK,EAAkC,EAAE,CACxC,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACd,OAAQ,KAA+B,CAAC,MAAM,KAAK,QAAQ;YAC3D,CAAE,KAA+B,CAAC,aAAa,KAAK,UAAU;gBAC3D,KAA+B,CAAC,aAAa,KAAK,mBAAmB,CAAC,CAC5E,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,mBAAmB,CACjC,UAAkB,EAClB,WAAoC;IAEpC,MAAM,IAAI,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAA;IAC/C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AAC1E,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-process guard for a Chrome user-data directory (spec section 4).
|
|
3
|
+
*
|
|
4
|
+
* Chrome corrupts state when two processes share one user-data-dir, so every
|
|
5
|
+
* Pi-owned launch/shutdown path must hold this lock:
|
|
6
|
+
*
|
|
7
|
+
* ```text
|
|
8
|
+
* one Chrome process <-> one persistent profile
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* Implemented as a sibling `<profileDir>.lock` file holding `{ pid,
|
|
12
|
+
* createdAt }`. Stale locks (dead pid or age beyond `staleMs`) are reclaimed;
|
|
13
|
+
* live locks throw {@link ProfileLockedError}.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ProfileLockedError extends Error {
|
|
16
|
+
readonly profileDir: string;
|
|
17
|
+
readonly holderPid?: number;
|
|
18
|
+
constructor(profileDir: string, holderPid?: number);
|
|
19
|
+
}
|
|
20
|
+
export interface ProfileLockHandle {
|
|
21
|
+
readonly profileDir: string;
|
|
22
|
+
readonly lockPath: string;
|
|
23
|
+
released: boolean;
|
|
24
|
+
release(): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function lockPathFor(profileDir: string): string;
|
|
27
|
+
export interface AcquireLockOptions {
|
|
28
|
+
/** Locks older than this with a dead holder are reclaimed. Default 5 min. */
|
|
29
|
+
staleMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Acquire the exclusive lock for `profileDir`. Throws ProfileLockedError when
|
|
33
|
+
* a live Chrome (or another Pi process) holds it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function acquireProfileLock(profileDir: string, options?: AcquireLockOptions): ProfileLockHandle;
|
|
36
|
+
/** True when a live holder currently owns the profile lock. */
|
|
37
|
+
export declare function isProfileLocked(profileDir: string): boolean;
|
|
38
|
+
/** Run `fn` while holding the profile lock, always releasing afterwards. */
|
|
39
|
+
export declare function withProfileLock<T>(profileDir: string, fn: (lock: ProfileLockHandle) => Promise<T>, options?: AcquireLockOptions): Promise<T>;
|
|
40
|
+
//# sourceMappingURL=profile-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-lock.d.ts","sourceRoot":"","sources":["../src/profile-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;gBAEf,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAUnD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD;AAmDD,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,iBAAiB,CAgCnB;AAoBD,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAc3D;AAED,4EAA4E;AAC5E,wBAAsB,eAAe,CAAC,CAAC,EACrC,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,EAC3C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-process guard for a Chrome user-data directory (spec section 4).
|
|
3
|
+
*
|
|
4
|
+
* Chrome corrupts state when two processes share one user-data-dir, so every
|
|
5
|
+
* Pi-owned launch/shutdown path must hold this lock:
|
|
6
|
+
*
|
|
7
|
+
* ```text
|
|
8
|
+
* one Chrome process <-> one persistent profile
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* Implemented as a sibling `<profileDir>.lock` file holding `{ pid,
|
|
12
|
+
* createdAt }`. Stale locks (dead pid or age beyond `staleMs`) are reclaimed;
|
|
13
|
+
* live locks throw {@link ProfileLockedError}.
|
|
14
|
+
*/
|
|
15
|
+
import { closeSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { dirname } from 'node:path';
|
|
17
|
+
export class ProfileLockedError extends Error {
|
|
18
|
+
profileDir;
|
|
19
|
+
holderPid;
|
|
20
|
+
constructor(profileDir, holderPid) {
|
|
21
|
+
super(holderPid !== undefined
|
|
22
|
+
? `Chrome profile is already in use by pid ${holderPid}: ${profileDir}`
|
|
23
|
+
: `Chrome profile is already in use: ${profileDir}`);
|
|
24
|
+
this.name = 'ProfileLockedError';
|
|
25
|
+
this.profileDir = profileDir;
|
|
26
|
+
this.holderPid = holderPid;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function lockPathFor(profileDir) {
|
|
30
|
+
return `${profileDir}.lock`;
|
|
31
|
+
}
|
|
32
|
+
function readLockPid(lockPath) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(readFileSync(lockPath, 'utf8'));
|
|
35
|
+
return typeof raw.pid === 'number' && Number.isInteger(raw.pid) ? raw.pid : undefined;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function readLockAgeMs(lockPath) {
|
|
42
|
+
try {
|
|
43
|
+
const raw = JSON.parse(readFileSync(lockPath, 'utf8'));
|
|
44
|
+
if (typeof raw.createdAt !== 'string')
|
|
45
|
+
return undefined;
|
|
46
|
+
const age = Date.now() - Date.parse(raw.createdAt);
|
|
47
|
+
return Number.isFinite(age) ? age : undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isPidAlive(pid) {
|
|
54
|
+
try {
|
|
55
|
+
process.kill(pid, 0);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function tryCreateLockFile(lockPath) {
|
|
63
|
+
let fd;
|
|
64
|
+
try {
|
|
65
|
+
fd = openSync(lockPath, 'wx');
|
|
66
|
+
writeFileSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error.code === 'EEXIST')
|
|
71
|
+
return false;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
if (fd !== undefined) {
|
|
76
|
+
try {
|
|
77
|
+
closeSync(fd);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Best effort; the lock content is already written.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Acquire the exclusive lock for `profileDir`. Throws ProfileLockedError when
|
|
87
|
+
* a live Chrome (or another Pi process) holds it.
|
|
88
|
+
*/
|
|
89
|
+
export function acquireProfileLock(profileDir, options) {
|
|
90
|
+
const staleMs = options?.staleMs ?? 5 * 60 * 1000;
|
|
91
|
+
const lockPath = lockPathFor(profileDir);
|
|
92
|
+
// Ensure the parent exists so the sibling lock file can be created.
|
|
93
|
+
try {
|
|
94
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Parent creation failure surfaces on lock creation below.
|
|
98
|
+
}
|
|
99
|
+
if (tryCreateLockFile(lockPath)) {
|
|
100
|
+
return makeHandle(profileDir, lockPath);
|
|
101
|
+
}
|
|
102
|
+
const holderPid = readLockPid(lockPath);
|
|
103
|
+
const ageMs = readLockAgeMs(lockPath);
|
|
104
|
+
const holderAlive = holderPid !== undefined && isPidAlive(holderPid);
|
|
105
|
+
const isStale = !holderAlive || (ageMs !== undefined && ageMs > staleMs);
|
|
106
|
+
if (isStale) {
|
|
107
|
+
try {
|
|
108
|
+
rmSync(lockPath, { force: true });
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Fall through to the error below if removal failed.
|
|
112
|
+
}
|
|
113
|
+
if (tryCreateLockFile(lockPath)) {
|
|
114
|
+
return makeHandle(profileDir, lockPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw new ProfileLockedError(profileDir, holderPid);
|
|
118
|
+
}
|
|
119
|
+
function makeHandle(profileDir, lockPath) {
|
|
120
|
+
const handle = {
|
|
121
|
+
profileDir,
|
|
122
|
+
lockPath,
|
|
123
|
+
released: false,
|
|
124
|
+
release() {
|
|
125
|
+
if (handle.released)
|
|
126
|
+
return;
|
|
127
|
+
handle.released = true;
|
|
128
|
+
try {
|
|
129
|
+
rmSync(lockPath, { force: true });
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Lock files are best-effort on shutdown.
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
return handle;
|
|
137
|
+
}
|
|
138
|
+
/** True when a live holder currently owns the profile lock. */
|
|
139
|
+
export function isProfileLocked(profileDir) {
|
|
140
|
+
const lockPath = lockPathFor(profileDir);
|
|
141
|
+
const holderPid = readLockPid(lockPath);
|
|
142
|
+
if (holderPid === undefined) {
|
|
143
|
+
// Unparseable/missing lock file: treat a present file as locked to stay
|
|
144
|
+
// safe, absent as unlocked.
|
|
145
|
+
try {
|
|
146
|
+
readFileSync(lockPath);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return isPidAlive(holderPid);
|
|
154
|
+
}
|
|
155
|
+
/** Run `fn` while holding the profile lock, always releasing afterwards. */
|
|
156
|
+
export async function withProfileLock(profileDir, fn, options) {
|
|
157
|
+
const lock = acquireProfileLock(profileDir, options);
|
|
158
|
+
try {
|
|
159
|
+
return await fn(lock);
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
lock.release();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=profile-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-lock.js","sourceRoot":"","sources":["../src/profile-lock.ts"],"names":[],"mappings":"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"}
|
package/dist/profile.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { type BrowserUseConfig } from './config.js';
|
|
|
5
5
|
* moved aside so Chrome starts fresh instead of showing a "can't read your
|
|
6
6
|
* preferences" dialog on every launch. An explicit custom userDataDir in the
|
|
7
7
|
* same state fails fast with a remediation hint instead of silently
|
|
8
|
-
* discarding the user's data.
|
|
8
|
+
* discarding the user's data. Persistent launches additionally pin the named
|
|
9
|
+
* `pi-browser-use` profile (one-time legacy migration, refuses while an
|
|
10
|
+
* external Chrome holds the root).
|
|
9
11
|
*/
|
|
10
12
|
export declare function prepareBrowserProfile(config: BrowserUseConfig): void;
|
|
11
13
|
//# sourceMappingURL=profile.d.ts.map
|
package/dist/profile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAyBxE;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CA+BpE"}
|
package/dist/profile.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { accessSync, constants, renameSync, statSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { DEFAULT_PROFILE_DIR } from './config.js';
|
|
4
|
+
import { ensureNamedProfile } from './named-profile.js';
|
|
4
5
|
function isRootOwned(path) {
|
|
5
6
|
try {
|
|
6
7
|
const stat = statSync(path);
|
|
@@ -30,20 +31,23 @@ function isUnusableDir(path) {
|
|
|
30
31
|
* moved aside so Chrome starts fresh instead of showing a "can't read your
|
|
31
32
|
* preferences" dialog on every launch. An explicit custom userDataDir in the
|
|
32
33
|
* same state fails fast with a remediation hint instead of silently
|
|
33
|
-
* discarding the user's data.
|
|
34
|
+
* discarding the user's data. Persistent launches additionally pin the named
|
|
35
|
+
* `pi-browser-use` profile (one-time legacy migration, refuses while an
|
|
36
|
+
* external Chrome holds the root).
|
|
34
37
|
*/
|
|
35
38
|
export function prepareBrowserProfile(config) {
|
|
36
|
-
if (config.browserUrl || config.wsEndpoint)
|
|
39
|
+
if (config.browserUrl || config.wsEndpoint || config.autoConnect)
|
|
37
40
|
return;
|
|
38
41
|
const dir = config.userDataDir ?? (config.sessionMode === 'persistent' ? DEFAULT_PROFILE_DIR : undefined);
|
|
39
|
-
if (!dir
|
|
40
|
-
|
|
42
|
+
if (!dir)
|
|
43
|
+
return;
|
|
44
|
+
if (dir !== DEFAULT_PROFILE_DIR) {
|
|
45
|
+
if (isUnusableDir(dir)) {
|
|
41
46
|
throw new Error(`Browser profile at ${dir} is not accessible (likely root-owned from running under sudo). ` +
|
|
42
47
|
`chown it back with: sudo chown -R $(id -un):$(id -gn) ${JSON.stringify(dir)}`);
|
|
43
48
|
}
|
|
44
|
-
return;
|
|
45
49
|
}
|
|
46
|
-
if (isUnusableDir(dir)) {
|
|
50
|
+
else if (isUnusableDir(dir)) {
|
|
47
51
|
const aside = join(dirname(DEFAULT_PROFILE_DIR), `browser-profile.inaccessible-${Date.now()}`);
|
|
48
52
|
console.error(`[pi-browser-use] default browser profile is inaccessible; moving it aside to ${aside} and starting fresh.`);
|
|
49
53
|
try {
|
|
@@ -54,5 +58,10 @@ export function prepareBrowserProfile(config) {
|
|
|
54
58
|
`Fix ownership with: sudo chown -R $(id -un):$(id -gn) ${JSON.stringify(dir)}`);
|
|
55
59
|
}
|
|
56
60
|
}
|
|
61
|
+
// Pin the named Pi profile for persistent launches (fresh migrations
|
|
62
|
+
// happen here, under no running Chrome by construction of the callers).
|
|
63
|
+
if (config.sessionMode === 'persistent') {
|
|
64
|
+
ensureNamedProfile(dir);
|
|
65
|
+
}
|
|
57
66
|
}
|
|
58
67
|
//# sourceMappingURL=profile.js.map
|
package/dist/profile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACrE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,mBAAmB,EAAyB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACrE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,mBAAmB,EAAyB,MAAM,aAAa,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAEvD,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC3B,MAAM,MAAM,GAAI,OAAgD,CAAC,MAAM,CAAA;QACvE,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,MAAM,EAAE,KAAK,CAAC,CAAA;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,CAAC;QACH,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QAClC,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;QACjD,OAAO,KAAK,CAAA;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,uEAAuE;QACvE,8BAA8B;QAC9B,OAAQ,KAA+B,CAAC,IAAI,KAAK,QAAQ,CAAA;IAC3D,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAwB;IAC5D,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,WAAW;QAAE,OAAM;IACxE,MAAM,GAAG,GACP,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,YAAY,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC/F,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,IAAI,GAAG,KAAK,mBAAmB,EAAE,CAAC;QAChC,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,sBAAsB,GAAG,kEAAkE;gBACzF,yDAAyD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CACjF,CAAA;QACH,CAAC;IACH,CAAC;SAAM,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,gCAAgC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;QAC9F,OAAO,CAAC,KAAK,CACX,gFAAgF,KAAK,sBAAsB,CAC5G,CAAA;QACD,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,sBAAsB,GAAG,mDAAmD;gBAC1E,yDAAyD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CACjF,CAAA;QACH,CAAC;IACH,CAAC;IACD,qEAAqE;IACrE,wEAAwE;IACxE,IAAI,MAAM,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;QACxC,kBAAkB,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrowserSessionManager: mode-specific behavior behind one stable API
|
|
3
|
+
* (spec sections 1, 8, 21, 24, 25).
|
|
4
|
+
*
|
|
5
|
+
* ```text
|
|
6
|
+
* Pi Skills / Agent → BrowserSessionManager → Fresh | Persistent | Existing
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* - Fresh: temporary profile, headless, deleted afterwards (spec 12).
|
|
10
|
+
* - Persistent: Pi-owned profile with headless-first automation and a
|
|
11
|
+
* headed-background fallback per origin (spec 2, 4, 7, 8, 24).
|
|
12
|
+
* - Existing: the user's own Chrome via autoConnect; Pi tabs are brokered by
|
|
13
|
+
* the Pi extension into the collapsed `pi-browser-use` group (spec 13-20).
|
|
14
|
+
*
|
|
15
|
+
* This module holds the state machines, capability resolution, and fallback
|
|
16
|
+
* hierarchies. Process launching (chrome-launcher), locking (profile-lock),
|
|
17
|
+
* and durable metadata (persistent-store) are injected collaborators so the
|
|
18
|
+
* logic stays unit-testable without a real Chrome.
|
|
19
|
+
*/
|
|
20
|
+
import type { BrowserCapabilityRequest, BrowserSession, BrowserSessionMode, BrowserStatus, PageHandle, PersistentBrowserMetadata, PersistentExecutionMode, PersistentState, SiteBrowserPreference } from './session.js';
|
|
21
|
+
export type { BrowserSession, BrowserStatus, PageHandle } from './session.js';
|
|
22
|
+
/** Minimal transport SPI a session uses to drive pages (MCP/CDP/extension). */
|
|
23
|
+
export interface SessionTransport {
|
|
24
|
+
openPage(url: string): Promise<PageHandle>;
|
|
25
|
+
listPages(): Promise<PageHandle[]>;
|
|
26
|
+
closePage(page: PageHandle): Promise<void>;
|
|
27
|
+
requestVisibleBrowser?(reason: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/** In-memory transport for tests and for wiring custom backends. */
|
|
30
|
+
export declare function createMemoryTransport(): SessionTransport & {
|
|
31
|
+
pages: PageHandle[];
|
|
32
|
+
};
|
|
33
|
+
declare abstract class BaseSession implements BrowserSession {
|
|
34
|
+
readonly mode: BrowserSessionMode;
|
|
35
|
+
protected readonly transport: SessionTransport;
|
|
36
|
+
constructor(mode: BrowserSessionMode, transport: SessionTransport);
|
|
37
|
+
ensureReady(): Promise<void>;
|
|
38
|
+
openPage(url: string): Promise<PageHandle>;
|
|
39
|
+
listPages(): Promise<PageHandle[]>;
|
|
40
|
+
closePage(page: PageHandle): Promise<void>;
|
|
41
|
+
requestVisibleBrowser(reason: string): Promise<void>;
|
|
42
|
+
abstract getStatus(): Promise<BrowserStatus>;
|
|
43
|
+
shutdown(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
/** Fresh: anonymous, stateless, headless. Never inherits auth (spec 12). */
|
|
46
|
+
export declare class FreshSession extends BaseSession {
|
|
47
|
+
constructor(transport: SessionTransport);
|
|
48
|
+
getStatus(): Promise<BrowserStatus>;
|
|
49
|
+
}
|
|
50
|
+
/** Events driving the Persistent state machine (spec section 2). */
|
|
51
|
+
export type PersistentEvent = 'bootstrap-needed' | 'bootstrap-opened' | 'bootstrap-closed' | 'automation-started' | 'automation-succeeded' | 'auth-required' | 'reauth-opened' | 'reauth-completed' | 'headless-incompatible';
|
|
52
|
+
/** Pure Persistent lifecycle transition. Returns the state unchanged for unknown events. */
|
|
53
|
+
export declare function nextPersistentState(state: PersistentState, event: PersistentEvent): PersistentState;
|
|
54
|
+
/** Normalize a URL or origin to a bare origin for preference lookup. */
|
|
55
|
+
export declare function normalizeOrigin(urlOrOrigin: string): string;
|
|
56
|
+
/** Resolve the execution mode for an origin or full URL (spec section 8). */
|
|
57
|
+
export declare function resolveExecutionForOrigin(preferences: SiteBrowserPreference[], urlOrOrigin: string, fallback?: PersistentExecutionMode): PersistentExecutionMode;
|
|
58
|
+
/** Remember/overwrite the per-origin preference; never downgrades globally. */
|
|
59
|
+
export declare function rememberExecutionPreference(preferences: SiteBrowserPreference[], urlOrOrigin: string, executionMode: PersistentExecutionMode): SiteBrowserPreference[];
|
|
60
|
+
export interface PersistentSessionOptions {
|
|
61
|
+
transport: SessionTransport;
|
|
62
|
+
metadata: PersistentBrowserMetadata;
|
|
63
|
+
initialState?: PersistentState;
|
|
64
|
+
executionMode?: PersistentExecutionMode;
|
|
65
|
+
sitePreferences?: SiteBrowserPreference[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Persistent: Pi's browser identity. Headless first; per-origin
|
|
69
|
+
* headed-background fallback; reauth via headed browser (spec 2, 7, 8).
|
|
70
|
+
*/
|
|
71
|
+
export declare class PersistentSession extends BaseSession {
|
|
72
|
+
private persistentState;
|
|
73
|
+
private executionMode;
|
|
74
|
+
private sitePreferences;
|
|
75
|
+
private metadata;
|
|
76
|
+
constructor(options: PersistentSessionOptions);
|
|
77
|
+
get persistentStatus(): PersistentState;
|
|
78
|
+
get currentExecutionMode(): PersistentExecutionMode;
|
|
79
|
+
get preferences(): SiteBrowserPreference[];
|
|
80
|
+
send(event: PersistentEvent): PersistentState;
|
|
81
|
+
executionFor(urlOrOrigin: string): PersistentExecutionMode;
|
|
82
|
+
/** A site failed headless while headed works: pin headed-background there. */
|
|
83
|
+
markHeadlessIncompatible(urlOrOrigin: string): void;
|
|
84
|
+
getStatus(): Promise<BrowserStatus>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Existing: the user's browser identity via autoConnect (spec 13).
|
|
88
|
+
* All Pi-created tabs must go through the extension broker into the
|
|
89
|
+
* collapsed `pi-browser-use` group — never raw foreground tabs.
|
|
90
|
+
*/
|
|
91
|
+
export declare class ExistingSession extends BaseSession {
|
|
92
|
+
constructor(transport: SessionTransport);
|
|
93
|
+
getStatus(): Promise<BrowserStatus>;
|
|
94
|
+
}
|
|
95
|
+
export interface ManagedSessions {
|
|
96
|
+
fresh: BrowserSession;
|
|
97
|
+
persistent: BrowserSession;
|
|
98
|
+
existing: BrowserSession;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve a capability request to a mode (spec section 21):
|
|
102
|
+
* persistence/auth → persistent; explicit existing opt-in → existing;
|
|
103
|
+
* otherwise fresh. Callers escalate along the fallback hierarchies
|
|
104
|
+
* (sections 24/25) when the resolved mode cannot serve.
|
|
105
|
+
*/
|
|
106
|
+
export declare function resolveModeForCapabilities(request: BrowserCapabilityRequest, persistentInitialized: boolean): BrowserSessionMode;
|
|
107
|
+
/** Persistent escalation sequence (spec section 24). */
|
|
108
|
+
export type PersistentEscalation = 'headless' | 'headed-auth-then-headless' | 'headed-background' | 'suggest-existing';
|
|
109
|
+
/** Next step when persistent headless cannot proceed. */
|
|
110
|
+
export declare function nextPersistentEscalation(reason: 'works' | 'login-needed' | 'headless-incompatible'): PersistentEscalation;
|
|
111
|
+
/** Existing-mode creation contract (spec section 25): fail loudly when the
|
|
112
|
+
* extension broker is unavailable rather than opening unmanaged tabs. */
|
|
113
|
+
export declare function assertExistingBrokerAvailable(available: boolean): void;
|
|
114
|
+
export declare class BrowserSessionManager {
|
|
115
|
+
private readonly sessions;
|
|
116
|
+
private current;
|
|
117
|
+
constructor(sessions: ManagedSessions);
|
|
118
|
+
getMode(): BrowserSessionMode;
|
|
119
|
+
getSession(mode?: BrowserSessionMode): BrowserSession;
|
|
120
|
+
switchTo(mode: BrowserSessionMode): Promise<BrowserSession>;
|
|
121
|
+
/** Capability-based selection (spec 21): skills ask, manager resolves. */
|
|
122
|
+
require(request: BrowserCapabilityRequest): Promise<BrowserSession>;
|
|
123
|
+
shutdownAll(): Promise<void>;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"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"}
|