@agentproto/browser-profiles 0.1.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/LICENSE +202 -0
- package/README.md +72 -0
- package/dist/index.d.ts +1272 -0
- package/dist/index.mjs +1946 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +68 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1272 @@
|
|
|
1
|
+
import { ToolError } from '@agentproto/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { BrowserDriver, BrowserCookie } from '@agentproto/driver-browser';
|
|
4
|
+
export { BrowserProfileRefusedError } from '@agentproto/driver-browser';
|
|
5
|
+
|
|
6
|
+
declare const SESSION_SOURCE_UNKNOWN_CODE: "browser-profiles:unknown-source";
|
|
7
|
+
declare const SESSION_SOURCE_DUPLICATE_CODE: "browser-profiles:duplicate-source";
|
|
8
|
+
declare const SESSION_DESCRIPTOR_INVALID_CODE: "browser-profiles:invalid-descriptor";
|
|
9
|
+
/** Thrown when a descriptor names a session source kind nobody registered. */
|
|
10
|
+
declare class SessionSourceUnknownError extends ToolError {
|
|
11
|
+
readonly kind: string;
|
|
12
|
+
readonly registered: readonly string[];
|
|
13
|
+
constructor(kind: string, registered: readonly string[]);
|
|
14
|
+
}
|
|
15
|
+
/** Thrown when a session source kind is registered twice. */
|
|
16
|
+
declare class SessionSourceDuplicateError extends ToolError {
|
|
17
|
+
readonly kind: string;
|
|
18
|
+
constructor(kind: string);
|
|
19
|
+
}
|
|
20
|
+
/** Thrown when a JSON document is not a readable session descriptor. */
|
|
21
|
+
declare class SessionDescriptorInvalidError extends ToolError {
|
|
22
|
+
readonly issues: readonly string[];
|
|
23
|
+
constructor(issues: readonly string[]);
|
|
24
|
+
}
|
|
25
|
+
declare function isSessionSourceUnknownError(value: unknown): value is SessionSourceUnknownError;
|
|
26
|
+
declare const SESSION_SOURCE_RESERVED_CODE: "browser-profiles:reserved-source";
|
|
27
|
+
declare const ACCOUNT_SWITCHER_MISSING_CODE: "browser-profiles:account-switcher-missing";
|
|
28
|
+
declare const CHROME_IDENTITY_CODE: "browser-profiles:chrome-identity";
|
|
29
|
+
declare const SESSION_RESOLVE_CODE: "browser-profiles:resolve";
|
|
30
|
+
/** Thrown when a host tries to register a session source under a built-in strategy kind. */
|
|
31
|
+
declare class SessionSourceReservedError extends ToolError {
|
|
32
|
+
readonly kind: string;
|
|
33
|
+
constructor(kind: string);
|
|
34
|
+
}
|
|
35
|
+
/** Thrown when a descriptor pins a sub-account but no `accountSwitcher` hook was provided. */
|
|
36
|
+
declare class AccountSwitcherMissingError extends ToolError {
|
|
37
|
+
constructor(platform: string);
|
|
38
|
+
}
|
|
39
|
+
/** A Chrome profile could not be confirmed as the pinned, logged-in identity. */
|
|
40
|
+
declare class ChromeIdentityError extends ToolError {
|
|
41
|
+
constructor(message: string);
|
|
42
|
+
}
|
|
43
|
+
/** A session descriptor could not be turned into a live session. */
|
|
44
|
+
declare class SessionResolveError extends ToolError {
|
|
45
|
+
constructor(message: string, cause?: unknown);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A value a read might not be able to produce: either a `known` result or an
|
|
50
|
+
* `unknown` with a human reason. A cookie read that threw (sqlite3 missing, the
|
|
51
|
+
* DB copy failed, Chrome holding the Cookies lock) is `{ unknown }`, so a caller
|
|
52
|
+
* can never mistake a failed read for a confident empty or zero result.
|
|
53
|
+
*/
|
|
54
|
+
type Known<T> = {
|
|
55
|
+
known: T;
|
|
56
|
+
} | {
|
|
57
|
+
unknown: string;
|
|
58
|
+
};
|
|
59
|
+
declare function isKnown<T>(k: Known<T>): k is {
|
|
60
|
+
known: T;
|
|
61
|
+
};
|
|
62
|
+
/** Collapse a {@link Known} to a plain value with an explicit fallback (a deliberate, greppable decision). */
|
|
63
|
+
declare function knownOr<T>(k: Known<T>, fallback: T): T;
|
|
64
|
+
|
|
65
|
+
/** A cookie to inject into a browser session (camofox and Playwright shape). `value` is a secret: never log it. */
|
|
66
|
+
interface SessionCookie {
|
|
67
|
+
name: string;
|
|
68
|
+
value: string;
|
|
69
|
+
domain: string;
|
|
70
|
+
path?: string;
|
|
71
|
+
httpOnly?: boolean;
|
|
72
|
+
secure?: boolean;
|
|
73
|
+
sameSite?: string;
|
|
74
|
+
expires?: number;
|
|
75
|
+
}
|
|
76
|
+
/** The loose shape a cookie file or a decrypted jar yields before normalising. */
|
|
77
|
+
interface RawCookie {
|
|
78
|
+
name: string;
|
|
79
|
+
value: string;
|
|
80
|
+
domain: string;
|
|
81
|
+
path?: string;
|
|
82
|
+
httpOnly?: boolean;
|
|
83
|
+
secure?: boolean;
|
|
84
|
+
sameSite?: string;
|
|
85
|
+
expires?: number;
|
|
86
|
+
expirationDate?: number;
|
|
87
|
+
session?: boolean;
|
|
88
|
+
}
|
|
89
|
+
declare function domainMatches(domain: string | undefined, domains: readonly string[]): boolean;
|
|
90
|
+
declare function toSessionCookie(c: RawCookie): SessionCookie;
|
|
91
|
+
/** Drop duplicates by (name, domain, path); aliased domains otherwise yield the same cookie twice. */
|
|
92
|
+
declare function dedupeCookies(cookies: readonly SessionCookie[]): SessionCookie[];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Per-site "am I signed in" detectors, injected. The package ships none: which
|
|
96
|
+
* cookie name proves a live login on a given site is site knowledge, so the host
|
|
97
|
+
* registers it. A detector sees cookie NAMES and expiry only, never values.
|
|
98
|
+
*/
|
|
99
|
+
/** Cookie metadata a detector may look at. No value, by design. */
|
|
100
|
+
interface AuthCookieMeta {
|
|
101
|
+
name: string;
|
|
102
|
+
/** Host key without the leading dot, lowercased. */
|
|
103
|
+
host: string;
|
|
104
|
+
/** Unix seconds; `undefined` means a session cookie (valid while the browser is open). */
|
|
105
|
+
expiresUnix: number | undefined;
|
|
106
|
+
}
|
|
107
|
+
interface SiteAuthSignal {
|
|
108
|
+
/** Registrable domain the detector is about, e.g. `"example.com"`. */
|
|
109
|
+
readonly domain: string;
|
|
110
|
+
/** Other hosts whose cookies count as this domain (e.g. a renamed site). */
|
|
111
|
+
readonly aliases?: readonly string[];
|
|
112
|
+
/** Cookie NAME(s) that prove a live login. Any one present and unexpired counts. Ignored when `detect` is set. */
|
|
113
|
+
readonly cookieNames?: readonly string[];
|
|
114
|
+
/** Custom detector over this domain's cookie metadata; overrides the `cookieNames` rule. */
|
|
115
|
+
readonly detect?: (cookies: readonly AuthCookieMeta[], nowMs: number) => boolean;
|
|
116
|
+
}
|
|
117
|
+
interface AuthSignalRegistry {
|
|
118
|
+
register(signal: SiteAuthSignal): void;
|
|
119
|
+
/** Domains with a registered detector: the KNOWN side of the known/unknown split. */
|
|
120
|
+
knownDomains(): readonly string[];
|
|
121
|
+
isKnownDomain(domain: string): boolean;
|
|
122
|
+
/** Map an alias host onto its registered domain; anything else passes through lowercased. */
|
|
123
|
+
canonicalDomain(domain: string): string;
|
|
124
|
+
/** Other hosts that count as the same site as `domain` (its aliases, or its canonical domain plus siblings). */
|
|
125
|
+
equivalentDomains(domain: string): readonly string[];
|
|
126
|
+
/** Whether `host` belongs to `domain` (or one of its aliases). */
|
|
127
|
+
hostBelongsTo(host: string, domain: string): boolean;
|
|
128
|
+
/** Run the detector for `domain` over the profile's cookie metadata. `false` when no detector is registered. */
|
|
129
|
+
isAuthed(domain: string, cookies: readonly AuthCookieMeta[], nowMs: number): boolean;
|
|
130
|
+
}
|
|
131
|
+
declare function createAuthSignalRegistry(initial?: readonly SiteAuthSignal[]): AuthSignalRegistry;
|
|
132
|
+
/** Process-wide default registry (empty). Hosts register their detectors here or pass their own. */
|
|
133
|
+
declare const defaultAuthSignalRegistry: AuthSignalRegistry;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Chrome `Local State` parsing: the one implementation, shared by the Chrome
|
|
137
|
+
* profile scanner here and by `@agentproto/plugin-local-browser`.
|
|
138
|
+
*
|
|
139
|
+
* macOS: ~/Library/Application Support/Google/Chrome/Local State
|
|
140
|
+
* Linux: ~/.config/google-chrome/Local State
|
|
141
|
+
* Win: %LOCALAPPDATA%\Google\Chrome\User Data\Local State
|
|
142
|
+
*/
|
|
143
|
+
/** Resolves the platform-specific Chrome user-data-dir root. */
|
|
144
|
+
declare function chromeUserDataDir(home?: string): string;
|
|
145
|
+
declare function chromeLocalStatePath(home?: string): string;
|
|
146
|
+
/** One `profile.info_cache` entry, reduced to the fields callers use. */
|
|
147
|
+
interface LocalStateProfile {
|
|
148
|
+
/** Directory name under the user-data-dir (`Default`, `Profile 1`). */
|
|
149
|
+
directory: string;
|
|
150
|
+
/** Friendly profile name. */
|
|
151
|
+
name: string | undefined;
|
|
152
|
+
/** Signed-in Google account email (`user_name`). */
|
|
153
|
+
userName: string | undefined;
|
|
154
|
+
/** Google display name. */
|
|
155
|
+
gaiaName: string | undefined;
|
|
156
|
+
/** Raw Chrome activity stamp (`active_time`, else `last_active_time`), unconverted. */
|
|
157
|
+
activeTime: unknown;
|
|
158
|
+
}
|
|
159
|
+
interface ParsedLocalState {
|
|
160
|
+
profiles: LocalStateProfile[];
|
|
161
|
+
/** `profile.last_used`, or `null` when absent. */
|
|
162
|
+
lastUsed: string | null;
|
|
163
|
+
}
|
|
164
|
+
/** Parse Local State JSON text. Throws on malformed JSON. */
|
|
165
|
+
declare function parseLocalState(text: string): ParsedLocalState;
|
|
166
|
+
/** Lenient read of `<chromeRoot>/Local State`: a missing or unreadable file is an empty result. */
|
|
167
|
+
declare function readLocalState(chromeRoot: string): ParsedLocalState;
|
|
168
|
+
/**
|
|
169
|
+
* Chrome stores timestamps as microseconds since the Windows epoch (1601-01-01
|
|
170
|
+
* UTC). Convert to ISO-8601, or return "" when the field is missing or out of
|
|
171
|
+
* plausible range (so callers can't surface 1601 in a UI).
|
|
172
|
+
*/
|
|
173
|
+
declare function chromeTimeToIso(raw: unknown): string;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The session descriptor model: a saveable, named description of a browser
|
|
177
|
+
* session and EXACTLY which cookies to inject (by domain, never the whole jar).
|
|
178
|
+
*
|
|
179
|
+
* Wire compatibility is the contract: descriptors already on disk (written by
|
|
180
|
+
* the earlier studio code) load unchanged. The schemas below validate shape but
|
|
181
|
+
* never rewrite it; {@link parseSessionDescriptor} returns the parsed JSON as
|
|
182
|
+
* written, so a load followed by a save is byte-stable.
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
/** Built-in strategy kinds. Any other kind is a registered {@link SessionSource}. */
|
|
186
|
+
declare const BUILTIN_STRATEGY_KINDS: readonly ["chrome-cookie", "stored-credential", "authed-storageState"];
|
|
187
|
+
type BuiltinStrategyKind = (typeof BUILTIN_STRATEGY_KINDS)[number];
|
|
188
|
+
/** Built-in `inject.from` tags. Any other tag is a registered {@link SessionSource}. */
|
|
189
|
+
declare const BUILTIN_INJECT_FROM: readonly ["chrome-profile", "file", "camofox-native"];
|
|
190
|
+
type BuiltinInjectFrom = (typeof BUILTIN_INJECT_FROM)[number];
|
|
191
|
+
interface ChromeProfileInject {
|
|
192
|
+
from: "chrome-profile";
|
|
193
|
+
domains: string[];
|
|
194
|
+
/** Required: with several profiles on one site, "first match" would pick at random. */
|
|
195
|
+
profile: string;
|
|
196
|
+
/** Sub-account selector for multi-account profiles; needs an `accountSwitcher` hook. */
|
|
197
|
+
account?: {
|
|
198
|
+
platform: string;
|
|
199
|
+
userId: string;
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
interface FileInject {
|
|
203
|
+
from: "file";
|
|
204
|
+
domains: string[];
|
|
205
|
+
path: string;
|
|
206
|
+
}
|
|
207
|
+
/** The session owns its login (camofox storageState under the descriptor id); nothing is injected. */
|
|
208
|
+
interface CamofoxNativeInject {
|
|
209
|
+
from: "camofox-native";
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Cookies materialised at resolve time by a registered {@link SessionSource}.
|
|
213
|
+
* The descriptor carries only the `sessionRef` and `domains`, never cookies.
|
|
214
|
+
* `from` is the source kind (the studio's private store registers `"guilde"`).
|
|
215
|
+
*/
|
|
216
|
+
interface SourceInject {
|
|
217
|
+
from: string;
|
|
218
|
+
domains: string[];
|
|
219
|
+
sessionRef: string;
|
|
220
|
+
}
|
|
221
|
+
type SessionInject = ChromeProfileInject | FileInject | CamofoxNativeInject | SourceInject;
|
|
222
|
+
type SessionInjectFrom = SessionInject["from"];
|
|
223
|
+
interface ChromeCookieStrategy {
|
|
224
|
+
kind: "chrome-cookie";
|
|
225
|
+
domains: string[];
|
|
226
|
+
/** Chrome profile dir ("Default", "Profile 1"). */
|
|
227
|
+
profile: string;
|
|
228
|
+
profileName?: string;
|
|
229
|
+
profileEmail?: string;
|
|
230
|
+
account?: {
|
|
231
|
+
platform: string;
|
|
232
|
+
userId: string;
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/** A re-login CAPABILITY: only the credential reference, never the secret. Not a resolve source. */
|
|
236
|
+
interface StoredCredentialStrategy {
|
|
237
|
+
kind: "stored-credential";
|
|
238
|
+
platform: string;
|
|
239
|
+
account: string;
|
|
240
|
+
}
|
|
241
|
+
interface OwnedStorageStateStrategy {
|
|
242
|
+
kind: "authed-storageState";
|
|
243
|
+
storageStatePath: string;
|
|
244
|
+
capturedAt?: string;
|
|
245
|
+
}
|
|
246
|
+
/** A registered-source strategy: `kind` is the source kind. */
|
|
247
|
+
interface SourceStrategy {
|
|
248
|
+
kind: string;
|
|
249
|
+
domains: string[];
|
|
250
|
+
sessionRef: string;
|
|
251
|
+
}
|
|
252
|
+
type Strategy = ChromeCookieStrategy | StoredCredentialStrategy | OwnedStorageStateStrategy | SourceStrategy;
|
|
253
|
+
declare function isBuiltinStrategyKind(kind: string): kind is BuiltinStrategyKind;
|
|
254
|
+
declare const isChromeCookieStrategy: (s: Strategy) => s is ChromeCookieStrategy;
|
|
255
|
+
declare const isStoredCredentialStrategy: (s: Strategy) => s is StoredCredentialStrategy;
|
|
256
|
+
declare const isOwnedStorageStateStrategy: (s: Strategy) => s is OwnedStorageStateStrategy;
|
|
257
|
+
declare const isSourceStrategy: (s: Strategy) => s is SourceStrategy;
|
|
258
|
+
declare const isChromeProfileInject: (i: SessionInject) => i is ChromeProfileInject;
|
|
259
|
+
declare const isFileInject: (i: SessionInject) => i is FileInject;
|
|
260
|
+
declare const isCamofoxNativeInject: (i: SessionInject) => i is CamofoxNativeInject;
|
|
261
|
+
declare const isSourceInject: (i: SessionInject) => i is SourceInject;
|
|
262
|
+
/** The account a session is pinned to: for naming and drift detection. */
|
|
263
|
+
interface SessionIdentity {
|
|
264
|
+
profile?: string;
|
|
265
|
+
account?: string;
|
|
266
|
+
platform?: string;
|
|
267
|
+
profileName?: string;
|
|
268
|
+
profileEmail?: string;
|
|
269
|
+
}
|
|
270
|
+
type AuthStatus = "authenticated" | "auth-wall" | "stale" | "unknown";
|
|
271
|
+
interface SessionDescriptor {
|
|
272
|
+
/** Stable name the agent resolves by. */
|
|
273
|
+
id: string;
|
|
274
|
+
/** `camofox`: a stealth browser we inject curated cookies into. `chrome`: the user's own browser over a driver, no injection. */
|
|
275
|
+
backend: "camofox" | "chrome";
|
|
276
|
+
identity?: SessionIdentity;
|
|
277
|
+
/** Legacy single-source seam; {@link strategies} is the multi-source model. */
|
|
278
|
+
inject?: SessionInject;
|
|
279
|
+
strategies?: Strategy[];
|
|
280
|
+
base?: string;
|
|
281
|
+
url?: string;
|
|
282
|
+
savedAt?: string;
|
|
283
|
+
lastVerifiedAt?: string;
|
|
284
|
+
lastAuthStatus?: AuthStatus;
|
|
285
|
+
lastCookieRefreshAt?: string;
|
|
286
|
+
}
|
|
287
|
+
declare const sessionInjectSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
288
|
+
from: z.ZodLiteral<"chrome-profile">;
|
|
289
|
+
domains: z.ZodArray<z.ZodString>;
|
|
290
|
+
profile: z.ZodString;
|
|
291
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
292
|
+
platform: z.ZodString;
|
|
293
|
+
userId: z.ZodString;
|
|
294
|
+
}, z.core.$loose>>;
|
|
295
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
296
|
+
from: z.ZodLiteral<"file">;
|
|
297
|
+
domains: z.ZodArray<z.ZodString>;
|
|
298
|
+
path: z.ZodString;
|
|
299
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
300
|
+
from: z.ZodLiteral<"camofox-native">;
|
|
301
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
302
|
+
from: z.ZodString;
|
|
303
|
+
domains: z.ZodArray<z.ZodString>;
|
|
304
|
+
sessionRef: z.ZodString;
|
|
305
|
+
}, z.core.$loose>]>;
|
|
306
|
+
declare const strategySchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
307
|
+
kind: z.ZodLiteral<"chrome-cookie">;
|
|
308
|
+
domains: z.ZodArray<z.ZodString>;
|
|
309
|
+
profile: z.ZodString;
|
|
310
|
+
profileName: z.ZodOptional<z.ZodString>;
|
|
311
|
+
profileEmail: z.ZodOptional<z.ZodString>;
|
|
312
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
313
|
+
platform: z.ZodString;
|
|
314
|
+
userId: z.ZodString;
|
|
315
|
+
}, z.core.$loose>>;
|
|
316
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
317
|
+
kind: z.ZodLiteral<"stored-credential">;
|
|
318
|
+
platform: z.ZodString;
|
|
319
|
+
account: z.ZodString;
|
|
320
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
321
|
+
kind: z.ZodLiteral<"authed-storageState">;
|
|
322
|
+
storageStatePath: z.ZodString;
|
|
323
|
+
capturedAt: z.ZodOptional<z.ZodString>;
|
|
324
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
325
|
+
kind: z.ZodString;
|
|
326
|
+
domains: z.ZodArray<z.ZodString>;
|
|
327
|
+
sessionRef: z.ZodString;
|
|
328
|
+
}, z.core.$loose>]>;
|
|
329
|
+
declare const sessionIdentitySchema: z.ZodObject<{
|
|
330
|
+
profile: z.ZodOptional<z.ZodString>;
|
|
331
|
+
account: z.ZodOptional<z.ZodString>;
|
|
332
|
+
platform: z.ZodOptional<z.ZodString>;
|
|
333
|
+
profileName: z.ZodOptional<z.ZodString>;
|
|
334
|
+
profileEmail: z.ZodOptional<z.ZodString>;
|
|
335
|
+
}, z.core.$loose>;
|
|
336
|
+
declare const authStatusSchema: z.ZodEnum<{
|
|
337
|
+
authenticated: "authenticated";
|
|
338
|
+
"auth-wall": "auth-wall";
|
|
339
|
+
stale: "stale";
|
|
340
|
+
unknown: "unknown";
|
|
341
|
+
}>;
|
|
342
|
+
declare const sessionDescriptorSchema: z.ZodObject<{
|
|
343
|
+
id: z.ZodString;
|
|
344
|
+
backend: z.ZodEnum<{
|
|
345
|
+
camofox: "camofox";
|
|
346
|
+
chrome: "chrome";
|
|
347
|
+
}>;
|
|
348
|
+
identity: z.ZodOptional<z.ZodObject<{
|
|
349
|
+
profile: z.ZodOptional<z.ZodString>;
|
|
350
|
+
account: z.ZodOptional<z.ZodString>;
|
|
351
|
+
platform: z.ZodOptional<z.ZodString>;
|
|
352
|
+
profileName: z.ZodOptional<z.ZodString>;
|
|
353
|
+
profileEmail: z.ZodOptional<z.ZodString>;
|
|
354
|
+
}, z.core.$loose>>;
|
|
355
|
+
inject: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
356
|
+
from: z.ZodLiteral<"chrome-profile">;
|
|
357
|
+
domains: z.ZodArray<z.ZodString>;
|
|
358
|
+
profile: z.ZodString;
|
|
359
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
360
|
+
platform: z.ZodString;
|
|
361
|
+
userId: z.ZodString;
|
|
362
|
+
}, z.core.$loose>>;
|
|
363
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
364
|
+
from: z.ZodLiteral<"file">;
|
|
365
|
+
domains: z.ZodArray<z.ZodString>;
|
|
366
|
+
path: z.ZodString;
|
|
367
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
368
|
+
from: z.ZodLiteral<"camofox-native">;
|
|
369
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
370
|
+
from: z.ZodString;
|
|
371
|
+
domains: z.ZodArray<z.ZodString>;
|
|
372
|
+
sessionRef: z.ZodString;
|
|
373
|
+
}, z.core.$loose>]>>;
|
|
374
|
+
strategies: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
375
|
+
kind: z.ZodLiteral<"chrome-cookie">;
|
|
376
|
+
domains: z.ZodArray<z.ZodString>;
|
|
377
|
+
profile: z.ZodString;
|
|
378
|
+
profileName: z.ZodOptional<z.ZodString>;
|
|
379
|
+
profileEmail: z.ZodOptional<z.ZodString>;
|
|
380
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
381
|
+
platform: z.ZodString;
|
|
382
|
+
userId: z.ZodString;
|
|
383
|
+
}, z.core.$loose>>;
|
|
384
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
385
|
+
kind: z.ZodLiteral<"stored-credential">;
|
|
386
|
+
platform: z.ZodString;
|
|
387
|
+
account: z.ZodString;
|
|
388
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
389
|
+
kind: z.ZodLiteral<"authed-storageState">;
|
|
390
|
+
storageStatePath: z.ZodString;
|
|
391
|
+
capturedAt: z.ZodOptional<z.ZodString>;
|
|
392
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
393
|
+
kind: z.ZodString;
|
|
394
|
+
domains: z.ZodArray<z.ZodString>;
|
|
395
|
+
sessionRef: z.ZodString;
|
|
396
|
+
}, z.core.$loose>]>>>;
|
|
397
|
+
base: z.ZodOptional<z.ZodString>;
|
|
398
|
+
url: z.ZodOptional<z.ZodString>;
|
|
399
|
+
savedAt: z.ZodOptional<z.ZodString>;
|
|
400
|
+
lastVerifiedAt: z.ZodOptional<z.ZodString>;
|
|
401
|
+
lastAuthStatus: z.ZodOptional<z.ZodEnum<{
|
|
402
|
+
authenticated: "authenticated";
|
|
403
|
+
"auth-wall": "auth-wall";
|
|
404
|
+
stale: "stale";
|
|
405
|
+
unknown: "unknown";
|
|
406
|
+
}>>;
|
|
407
|
+
lastCookieRefreshAt: z.ZodOptional<z.ZodString>;
|
|
408
|
+
}, z.core.$loose>;
|
|
409
|
+
/**
|
|
410
|
+
* Validate a parsed JSON document as a {@link SessionDescriptor} and return it AS
|
|
411
|
+
* WRITTEN (not the zod output, which reorders keys), so a load then a save is
|
|
412
|
+
* byte-stable. Throws {@link SessionDescriptorInvalidError} on a bad shape.
|
|
413
|
+
*/
|
|
414
|
+
declare function parseSessionDescriptor(json: unknown): SessionDescriptor;
|
|
415
|
+
/** Persisted storageState of an owned camofox session: `<CAMOFOX_PROFILES_DIR or ~/.agentproto/camofox-profiles>/<id>.json`. */
|
|
416
|
+
declare function ownedStorageStatePath(id: string): string;
|
|
417
|
+
/** inject.from to strategy kind: file collapses onto chrome-cookie; a source tag is its own kind. */
|
|
418
|
+
declare function injectKindOf(from: SessionInjectFrom): Strategy["kind"];
|
|
419
|
+
/** strategy kind to inject.from; `stored-credential` has no transport. */
|
|
420
|
+
declare function injectFromOf(kind: Strategy["kind"]): SessionInjectFrom | undefined;
|
|
421
|
+
declare function chromeCookieStrategyOf(inject: ChromeProfileInject, identity?: SessionIdentity): ChromeCookieStrategy;
|
|
422
|
+
interface NormalizeOptions {
|
|
423
|
+
/** (platform, account) credential index: a match synthesizes a `stored-credential` strategy. Never secrets. */
|
|
424
|
+
credentials?: ReadonlyArray<{
|
|
425
|
+
platform: string;
|
|
426
|
+
account: string;
|
|
427
|
+
}>;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* The single migration seam: when `strategies` is absent, derive them from the
|
|
431
|
+
* legacy `inject`/`identity` (plus a credential match); when present, return the
|
|
432
|
+
* descriptor unchanged. Never drops or rewrites legacy fields.
|
|
433
|
+
*/
|
|
434
|
+
declare function normalizeDescriptor(raw: SessionDescriptor, opts?: NormalizeOptions): SessionDescriptor;
|
|
435
|
+
/**
|
|
436
|
+
* Resolution precedence: `authed-storageState` (unless it last hit an auth wall),
|
|
437
|
+
* then `chrome-cookie`, then any registered-source strategy. `stored-credential`
|
|
438
|
+
* is never selected: it is a recovery capability, not a resolve source.
|
|
439
|
+
*/
|
|
440
|
+
declare function selectResolutionStrategy(desc: SessionDescriptor): Strategy | undefined;
|
|
441
|
+
/** The effective `SessionInject` for resolution (see the studio's `resolveInjectFromStrategies`). */
|
|
442
|
+
declare function resolveInjectFromStrategies(desc: SessionDescriptor): SessionInject | undefined;
|
|
443
|
+
/** Default staleness TTL for owned cookies (7 days). */
|
|
444
|
+
declare const COOKIE_STALE_TTL_MS: number;
|
|
445
|
+
/**
|
|
446
|
+
* Pure staleness verdict computed at read time with no probe. Chrome-cookie and
|
|
447
|
+
* file sources are always fresh (re-read each resolve); owned and registered
|
|
448
|
+
* sources go by `lastCookieRefreshAt`.
|
|
449
|
+
*/
|
|
450
|
+
declare function cookieFreshness(desc: SessionDescriptor, now: number, ttlMs?: number): "fresh" | "stale" | "unknown";
|
|
451
|
+
declare function priorChromeStrategy(prev?: SessionDescriptor): ChromeCookieStrategy | undefined;
|
|
452
|
+
interface BuildOwnedDescriptorInput {
|
|
453
|
+
id: string;
|
|
454
|
+
platform: string;
|
|
455
|
+
reuseBase: string;
|
|
456
|
+
account?: string;
|
|
457
|
+
url?: string;
|
|
458
|
+
/** ISO timestamp for `savedAt` and the storageState `capturedAt` (injected, not read from the clock). */
|
|
459
|
+
now: string;
|
|
460
|
+
/** Prior descriptor whose chrome provenance is preserved (augment, not replace). */
|
|
461
|
+
prev?: SessionDescriptor;
|
|
462
|
+
}
|
|
463
|
+
/** Build the `camofox-native` descriptor for an owned login, preserving any prior chrome provenance. */
|
|
464
|
+
declare function buildOwnedSessionDescriptor(input: BuildOwnedDescriptorInput): SessionDescriptor;
|
|
465
|
+
|
|
466
|
+
/** Saveable store of descriptors, injected so it can be local files or a remote adapter. */
|
|
467
|
+
interface SessionStorePort {
|
|
468
|
+
save(desc: SessionDescriptor): Promise<void>;
|
|
469
|
+
load(id: string): Promise<SessionDescriptor | null>;
|
|
470
|
+
list(): Promise<SessionDescriptor[]>;
|
|
471
|
+
remove(id: string): Promise<void>;
|
|
472
|
+
}
|
|
473
|
+
interface FileSessionStoreOptions {
|
|
474
|
+
/** Live credential-index reader (a thunk so it reflects the current index). */
|
|
475
|
+
credentials?: () => ReadonlyArray<{
|
|
476
|
+
platform: string;
|
|
477
|
+
account: string;
|
|
478
|
+
}>;
|
|
479
|
+
}
|
|
480
|
+
/** Local-file adapter: one `<id>.json` per descriptor under `dir` (dir 0700, file 0600). */
|
|
481
|
+
declare function fileSessionStore(dir: string, opts?: FileSessionStoreOptions): SessionStorePort;
|
|
482
|
+
/**
|
|
483
|
+
* Resolve a session by name across several stores. `load` returns the first
|
|
484
|
+
* hit; `list` unions by id (earlier stores win). Writes go to `writeStore`, or
|
|
485
|
+
* throw when none is given.
|
|
486
|
+
*/
|
|
487
|
+
declare function compositeSessionStore(readStores: SessionStorePort[], writeStore?: SessionStorePort): SessionStorePort;
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* The `SessionSource` registry: how a host supplies cookies that live somewhere
|
|
491
|
+
* this package knows nothing about (a managed central store, a vault, a device
|
|
492
|
+
* bridge). The descriptor carries only a `kind`, a `sessionRef` and `domains`;
|
|
493
|
+
* the registered source materialises the cookies at resolve time.
|
|
494
|
+
*
|
|
495
|
+
* Replaces the hard-wired `guilde` inject: the studio registers a `"guilde"`
|
|
496
|
+
* source, the open package never mentions it.
|
|
497
|
+
*/
|
|
498
|
+
|
|
499
|
+
interface SessionSourceRef {
|
|
500
|
+
sessionRef: string;
|
|
501
|
+
/** Only these domains' cookies may be returned, never the whole jar. */
|
|
502
|
+
domains: readonly string[];
|
|
503
|
+
}
|
|
504
|
+
interface SessionSource {
|
|
505
|
+
/** The `inject.from` / `strategy.kind` this source answers to. Not a built-in kind. */
|
|
506
|
+
readonly kind: string;
|
|
507
|
+
materialize(ref: SessionSourceRef): Promise<SessionCookie[]>;
|
|
508
|
+
}
|
|
509
|
+
interface SessionSourceRegistry {
|
|
510
|
+
/** Register a source. Throws on a duplicate kind or a built-in kind. */
|
|
511
|
+
register(source: SessionSource): void;
|
|
512
|
+
list(): readonly SessionSource[];
|
|
513
|
+
has(kind: string): boolean;
|
|
514
|
+
/** The source for `kind`, or a typed {@link SessionSourceUnknownError}. */
|
|
515
|
+
resolve(kind: string): SessionSource;
|
|
516
|
+
materialize(kind: string, ref: SessionSourceRef): Promise<SessionCookie[]>;
|
|
517
|
+
}
|
|
518
|
+
declare function createSessionSourceRegistry(initial?: readonly SessionSource[]): SessionSourceRegistry;
|
|
519
|
+
/** Catalog row of a centrally managed session. Carries NO cookies. */
|
|
520
|
+
interface SessionSourceMeta {
|
|
521
|
+
ref: string;
|
|
522
|
+
domains: string[];
|
|
523
|
+
identity?: SessionIdentity;
|
|
524
|
+
url?: string;
|
|
525
|
+
}
|
|
526
|
+
interface SessionSourceCatalog {
|
|
527
|
+
list(): Promise<SessionSourceMeta[]>;
|
|
528
|
+
get(ref: string): Promise<SessionSourceMeta | null>;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Read-only descriptor store over a catalog of managed sessions, resolving them
|
|
532
|
+
* into `from: <kind>` descriptors. `save` and `remove` throw: sessions are
|
|
533
|
+
* captured elsewhere, so the contract stays loud.
|
|
534
|
+
*/
|
|
535
|
+
declare function catalogSessionStore(kind: string, catalog: SessionSourceCatalog): SessionStorePort;
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Read the logged-in browser on THIS machine, on demand and in memory. Nothing
|
|
539
|
+
* is persisted: the browser is the source of truth. Cookie DB access is a
|
|
540
|
+
* read-only copy to a temp dir; only cookie NAMES and expiry are read for the
|
|
541
|
+
* auth signal, values are decrypted only by {@link decryptChromeCookies}, and
|
|
542
|
+
* never logged.
|
|
543
|
+
*
|
|
544
|
+
* Backend: macOS Chrome/Chromium (Keychain "Chrome Safe Storage", PBKDF2-SHA1,
|
|
545
|
+
* AES-128-CBC, `v10` prefix, a 32-byte SHA-256(domain) prefix stripped on newer
|
|
546
|
+
* Chrome). Pass `safeStoragePassword` and `chromeRoot` for anything else.
|
|
547
|
+
*/
|
|
548
|
+
|
|
549
|
+
interface LocalBrowserSessionOptions {
|
|
550
|
+
/** Root of the Chrome/Chromium user-data dir. Default: the macOS Chrome path. */
|
|
551
|
+
chromeRoot?: string;
|
|
552
|
+
/** Safe-Storage password (e.g. for Chromium's own Keychain service). Default reads the macOS Keychain. */
|
|
553
|
+
safeStoragePassword?: () => string;
|
|
554
|
+
/** Per-domain cookie aliases. Default: derived from the auth signal registry's aliases. */
|
|
555
|
+
domainAliases?: Record<string, string[]>;
|
|
556
|
+
/** Extra domains for the host-presence scan beyond the registry's known domains. */
|
|
557
|
+
extraDomains?: readonly string[];
|
|
558
|
+
/** Per-site "signed in" detectors. Default: {@link defaultAuthSignalRegistry}. */
|
|
559
|
+
authSignals?: AuthSignalRegistry;
|
|
560
|
+
}
|
|
561
|
+
/** Profiles live as sibling dirs (`Default`, `Profile 1`, ...) each with a `Cookies` DB. */
|
|
562
|
+
declare function profileDirs(chromeRoot: string): string[];
|
|
563
|
+
/** The Chrome user-data root the scanner reads. Reading it is fine; launching Chrome against it is refused by the kit (F11). */
|
|
564
|
+
declare function chromeUserDataRoot(opts?: LocalBrowserSessionOptions): string;
|
|
565
|
+
/** Read the Chrome Safe Storage key from the macOS Keychain. Touches the Keychain: call only on an explicit user action. */
|
|
566
|
+
declare function readSafeStorageKey(): Buffer;
|
|
567
|
+
/** Decrypt one hex-dumped Chrome `encrypted_value`. Exported for the synthetic-data tests. */
|
|
568
|
+
declare function decryptChromeValue(hex: string, key: Buffer): string;
|
|
569
|
+
/** Derive the AES key from a Safe-Storage password (Chrome's constants). */
|
|
570
|
+
declare function safeStorageKey(password: string): Buffer;
|
|
571
|
+
/** Total cookie count (no decryption). A failed read is `{ unknown }`, never 0. */
|
|
572
|
+
declare function countCookies(profileDir: string): Known<number>;
|
|
573
|
+
/** Cookie counts per requested domain (host and subdomains), no decryption. A failed read is `{ unknown }`, never zeros. */
|
|
574
|
+
declare function countCookiesByDomain(profileDir: string, domains: readonly string[]): Known<Record<string, number>>;
|
|
575
|
+
/** Distinct cookie hosts (no decryption) mapped onto the registry's known domains plus `extraDomains`. */
|
|
576
|
+
declare function profileLoggedInDomains(profileDir: string, registry?: AuthSignalRegistry, extraDomains?: readonly string[]): Known<string[]>;
|
|
577
|
+
/**
|
|
578
|
+
* Known domains the profile holds a valid, non-expired AUTH signal for. Reads
|
|
579
|
+
* cookie NAME and expiry only (no value decryption), then asks the registry's
|
|
580
|
+
* detector. A failed read is `{ unknown }`.
|
|
581
|
+
*/
|
|
582
|
+
declare function profileAuthedDomains(profileDir: string, registry?: AuthSignalRegistry, nowMs?: number): Known<string[]>;
|
|
583
|
+
/** A logged-in identity behind a Chrome profile: the unit a person reasons about, not the dir name. */
|
|
584
|
+
interface ChromeIdentity {
|
|
585
|
+
/** On-disk dir: the addressable key passed back as `label`/`profile`. */
|
|
586
|
+
profile: string;
|
|
587
|
+
name: string | null;
|
|
588
|
+
email: string | null;
|
|
589
|
+
gaiaName: string | null;
|
|
590
|
+
lastUsed: boolean;
|
|
591
|
+
/** Display only; a failed read collapses to 0 here (see `domainsAuthed` for the honest value). */
|
|
592
|
+
cookieCount: number;
|
|
593
|
+
/** Weak host-presence signal: any cookie on the domain counts. */
|
|
594
|
+
domainsLoggedIn: string[];
|
|
595
|
+
/** Strong signal: a valid, non-expired auth cookie, by name and expiry. `Known` so a failed read is never "authed nowhere". */
|
|
596
|
+
domainsAuthed: Known<string[]>;
|
|
597
|
+
}
|
|
598
|
+
/** Enumerate the machine's Chrome profiles as named identities. Pure read: Local State JSON plus a host-key scan. */
|
|
599
|
+
declare function scanChromeIdentities(opts?: LocalBrowserSessionOptions): ChromeIdentity[];
|
|
600
|
+
/** Decrypt cookies for `domains` from a single Chrome profile, in memory (the DB copy is removed). */
|
|
601
|
+
declare function decryptChromeCookies(profileDir: string, domains: readonly string[], key: Buffer): SessionCookie[];
|
|
602
|
+
interface LocalBrowserSession {
|
|
603
|
+
/** Decrypted cookies for `domain` (and its aliases) from `profile`, or the first profile that has one. `null` when none. */
|
|
604
|
+
getDecryptedForDomain(domain: string, profile?: string): Promise<{
|
|
605
|
+
cookies: SessionCookie[];
|
|
606
|
+
capturedAt: string;
|
|
607
|
+
} | null>;
|
|
608
|
+
/** One summary per Chrome profile (no decryption). */
|
|
609
|
+
listProfiles(): Array<{
|
|
610
|
+
profile: string;
|
|
611
|
+
cookieCount: Known<number>;
|
|
612
|
+
}>;
|
|
613
|
+
}
|
|
614
|
+
/** A local, no-persistence provider reading this machine's browser. */
|
|
615
|
+
declare function createLocalBrowserSession(opts?: LocalBrowserSessionOptions): LocalBrowserSession;
|
|
616
|
+
|
|
617
|
+
interface ChromeIdentityOptions {
|
|
618
|
+
/** Pre-scanned identities. Injectable so tests never scan a real profile. Scanned lazily when absent. */
|
|
619
|
+
scan?: ChromeIdentity[];
|
|
620
|
+
authSignals?: AuthSignalRegistry;
|
|
621
|
+
/** Options for the lazy scan (`chromeRoot` etc). */
|
|
622
|
+
scanOptions?: LocalBrowserSessionOptions;
|
|
623
|
+
/** Receives non-fatal warnings (never cookie values). Default: dropped. */
|
|
624
|
+
warn?: (message: string) => void;
|
|
625
|
+
}
|
|
626
|
+
/** Three-valued verdict for one domain. `unknown` must never be treated as `authed`. */
|
|
627
|
+
type DomainAuthSignal = "authed" | "not-authed" | "unknown";
|
|
628
|
+
/** {@link DomainAuthSignal} plus `not-checked`: the state where no live auth guard applies at all. */
|
|
629
|
+
type SessionAuthState = DomainAuthSignal | "not-checked";
|
|
630
|
+
interface SessionAuthDescription {
|
|
631
|
+
state: SessionAuthState;
|
|
632
|
+
message: string;
|
|
633
|
+
}
|
|
634
|
+
/** Classify `domain` for `found`: `unknown` when no detector is registered or the cookie read failed. */
|
|
635
|
+
declare function domainAuthSignal(domain: string, found: ChromeIdentity, registry?: AuthSignalRegistry): DomainAuthSignal;
|
|
636
|
+
/** Human label for a Chrome identity: friendly name, email, and the dir key. */
|
|
637
|
+
declare function chromeIdentityLabel(s: ChromeIdentity): string;
|
|
638
|
+
/**
|
|
639
|
+
* Which Chrome profile a chrome-profile session's cookies should actually be
|
|
640
|
+
* read from: a still-valid pin wins; otherwise a profile authed for the domain
|
|
641
|
+
* AND on the same Google account; otherwise the pin unchanged. Never heals to a
|
|
642
|
+
* different account.
|
|
643
|
+
*/
|
|
644
|
+
declare function resolveChromeProfile(inject: SessionInject, identity?: SessionIdentity, opts?: ChromeIdentityOptions): string | undefined;
|
|
645
|
+
/**
|
|
646
|
+
* Pin guard for chrome-sourced sessions: the profile exists and carries a valid
|
|
647
|
+
* auth signal for the declared domain. Throws {@link ChromeIdentityError} on a
|
|
648
|
+
* hard mismatch. A domain with no registered detector is `unknown`: it falls
|
|
649
|
+
* back to host presence and is never reported as authed.
|
|
650
|
+
*/
|
|
651
|
+
declare function verifyChromeIdentity(inject: SessionInject, identity?: SessionIdentity, opts?: ChromeIdentityOptions): void;
|
|
652
|
+
/**
|
|
653
|
+
* What {@link verifyChromeIdentity} established, without throwing, so a renderer
|
|
654
|
+
* can tell "confirmed" from every shade of "not confirmed". Only `authed` should
|
|
655
|
+
* earn a checkmark. A heuristic (cookie name and expiry, no network).
|
|
656
|
+
*/
|
|
657
|
+
declare function describeSessionAuth(inject: SessionInject, identity?: SessionIdentity, opts?: ChromeIdentityOptions): SessionAuthDescription;
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Gate a Chrome launch against `userDataDir` before anything spawns. Delegates
|
|
661
|
+
* to the kit's F11 refusal (`assertSpawnArgsSafe`); nothing is re-implemented
|
|
662
|
+
* here. Throws {@link BrowserProfileRefusedError} (`browser:profile-refused`)
|
|
663
|
+
* when `userDataDir` is, or sits inside, a default Chrome user-data-dir.
|
|
664
|
+
*
|
|
665
|
+
* The scanner in this package only READS cookies from a profile. A host that
|
|
666
|
+
* launches Chrome per profile must pass its `--user-data-dir` through this first.
|
|
667
|
+
*/
|
|
668
|
+
declare function assertChromeLaunchDirSafe(userDataDir: string, providerId: string): void;
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* A reused camofox tab per `userId`. A camofox `userId` owns one browser context
|
|
672
|
+
* (cookies); `POST /tabs` always creates a NEW tab, so reuse is the client's job:
|
|
673
|
+
* the tab id is remembered in a small state file and re-navigated on the next run.
|
|
674
|
+
*
|
|
675
|
+
* Public half only: cookie injection, tab reuse, navigation, evaluation, basic
|
|
676
|
+
* input, screenshots and server-side network capture. Human-mode pacing,
|
|
677
|
+
* anti-bot challenge handling and passive in-page capture stay private.
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
/** Where the reused tab ids are remembered. Computed on call, never at import. */
|
|
681
|
+
declare function camofoxStatePath(): string;
|
|
682
|
+
/** The camofox instance a session resolves against when no base is pinned: `CAMOFOX_URL`, else `localhost:$CAMOFOX_PORT` (9377). */
|
|
683
|
+
declare function defaultCamofoxBase(): string;
|
|
684
|
+
interface OpenSessionOptions {
|
|
685
|
+
/** The camofox `userId` that owns the browser context. */
|
|
686
|
+
userId: string;
|
|
687
|
+
base?: string;
|
|
688
|
+
/** Curated cookies to inject: the declared domains only, never the whole jar. */
|
|
689
|
+
cookies?: readonly SessionCookie[];
|
|
690
|
+
/** Initial URL to land the tab on. Omitted: no landing navigation. */
|
|
691
|
+
url?: string;
|
|
692
|
+
/** Inject cookies before use (default true). */
|
|
693
|
+
injectCookies?: boolean;
|
|
694
|
+
/** Absolute path to a Playwright storageState JSON loaded when creating a new tab. */
|
|
695
|
+
storageState?: string;
|
|
696
|
+
/** Exempt the tab from camofox's idle reaper. */
|
|
697
|
+
keepAlive?: boolean;
|
|
698
|
+
/** If a read returns 401, POST this URL in-page (a token-refresh endpoint) and retry once. */
|
|
699
|
+
refreshUrl?: string;
|
|
700
|
+
/** Where tab ids are remembered. Default: {@link camofoxStatePath}. */
|
|
701
|
+
stateFile?: string;
|
|
702
|
+
/** Injectable transport, for tests. Default: global `fetch`. */
|
|
703
|
+
fetch?: typeof fetch;
|
|
704
|
+
/** Injectable settle delay after navigation (ms). Default 2500. */
|
|
705
|
+
settleMs?: number;
|
|
706
|
+
}
|
|
707
|
+
interface ApiRawResult {
|
|
708
|
+
status: number;
|
|
709
|
+
body: unknown;
|
|
710
|
+
}
|
|
711
|
+
interface CamofoxSession {
|
|
712
|
+
tabId: string;
|
|
713
|
+
userId: string;
|
|
714
|
+
/** In-page authed JSON GET; returns the parsed body, throws on non-2xx. */
|
|
715
|
+
api(pathAndQuery: string): Promise<unknown>;
|
|
716
|
+
/** In-page GET returning `{ status, body }` without throwing. */
|
|
717
|
+
apiRaw(pathAndQuery: string): Promise<ApiRawResult>;
|
|
718
|
+
evaluate<T = unknown>(expression: string): Promise<T>;
|
|
719
|
+
screenshot(opts?: {
|
|
720
|
+
selector?: string;
|
|
721
|
+
format?: "png" | "jpeg";
|
|
722
|
+
quality?: number;
|
|
723
|
+
}): Promise<{
|
|
724
|
+
imageBase64: string;
|
|
725
|
+
mimeType: string;
|
|
726
|
+
}>;
|
|
727
|
+
goto(url: string, waitUntil?: string): Promise<void>;
|
|
728
|
+
click(selector: string): Promise<void>;
|
|
729
|
+
type(selector: string, text: string): Promise<void>;
|
|
730
|
+
press(key: string): Promise<void>;
|
|
731
|
+
/** Arm server-side network capture (Playwright request events). */
|
|
732
|
+
startNetCapture(urlPattern?: string, max?: number): Promise<void>;
|
|
733
|
+
readNetCapture(): Promise<Array<{
|
|
734
|
+
method: string;
|
|
735
|
+
url: string;
|
|
736
|
+
resourceType: string;
|
|
737
|
+
postData: string | null;
|
|
738
|
+
headers: Record<string, string>;
|
|
739
|
+
}>>;
|
|
740
|
+
/** Close and forget the tab. */
|
|
741
|
+
close(): Promise<void>;
|
|
742
|
+
}
|
|
743
|
+
declare function openSession(opts: OpenSessionOptions): Promise<CamofoxSession>;
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Optional hook that rewrites a gathered jar so `userId` is the active
|
|
747
|
+
* sub-account on `platform` (for profiles logged into several accounts of one
|
|
748
|
+
* site). Policy about HOW to switch lives with the host; the default is none.
|
|
749
|
+
*/
|
|
750
|
+
type AccountSwitcher = (platform: string, jar: SessionCookie[], userId: string) => SessionCookie[];
|
|
751
|
+
interface ResolveSessionDeps {
|
|
752
|
+
/** Single driver, used when a chrome session names no profile. */
|
|
753
|
+
driver?: BrowserDriver;
|
|
754
|
+
/** Per-profile driver factory (the multi-account seam). Preferred over `driver`. */
|
|
755
|
+
driverFor?: (profile: string) => BrowserDriver | Promise<BrowserDriver>;
|
|
756
|
+
/** Where registered-source cookies come from. Default: an empty registry. */
|
|
757
|
+
sources?: SessionSourceRegistry;
|
|
758
|
+
/** Optional sub-account switcher. Never called unless a chrome-profile inject pins an `account`. */
|
|
759
|
+
accountSwitcher?: AccountSwitcher;
|
|
760
|
+
/** Per-site "signed in" detectors. Default: {@link defaultAuthSignalRegistry}. */
|
|
761
|
+
authSignals?: AuthSignalRegistry;
|
|
762
|
+
/** Chrome scanning options (`chromeRoot`, `safeStoragePassword`, ...). */
|
|
763
|
+
local?: LocalBrowserSessionOptions;
|
|
764
|
+
/** Camofox opener. Default: {@link openSession}. Injectable so tests need no network. */
|
|
765
|
+
openCamofox?: (opts: OpenSessionOptions) => Promise<CamofoxSession>;
|
|
766
|
+
/** Non-fatal warnings (never cookie values). Default: dropped. */
|
|
767
|
+
warn?: (message: string) => void;
|
|
768
|
+
}
|
|
769
|
+
/** What a descriptor resolves to: a chrome driver, or a live camofox session. */
|
|
770
|
+
type ResolvedSession = {
|
|
771
|
+
backend: "chrome";
|
|
772
|
+
driver: BrowserDriver;
|
|
773
|
+
} | {
|
|
774
|
+
backend: "camofox";
|
|
775
|
+
session: CamofoxSession;
|
|
776
|
+
};
|
|
777
|
+
/**
|
|
778
|
+
* Cookies for a `chrome-cookie` strategy. A file-sourced descriptor keeps its
|
|
779
|
+
* cookie path in the legacy `file` inject, so `desc` is consulted to recover it.
|
|
780
|
+
*/
|
|
781
|
+
declare function gatherCookiesForStrategy(strategy: Strategy, desc: SessionDescriptor, deps?: ResolveSessionDeps): Promise<SessionCookie[]>;
|
|
782
|
+
/** Gather cookies for a legacy `SessionInject` (chrome-profile or file only). */
|
|
783
|
+
declare function gatherCookies(inject: SessionInject, deps?: ResolveSessionDeps): Promise<SessionCookie[]>;
|
|
784
|
+
/** Turn a saved descriptor into a live session. */
|
|
785
|
+
declare function resolveSession(desc: SessionDescriptor, deps?: ResolveSessionDeps): Promise<ResolvedSession>;
|
|
786
|
+
|
|
787
|
+
declare const GRANT_DOMAIN_INVALID_CODE: "browser-profiles:grant-domain-invalid";
|
|
788
|
+
declare const CONSENT_REQUIRED_CODE: "browser:consent_required";
|
|
789
|
+
declare const CONSENT_FLAGS_REQUIRED_CODE: "browser-profiles:consent-flags-required";
|
|
790
|
+
declare const CONSENT_DECLINED_CODE: "browser-profiles:consent-declined";
|
|
791
|
+
declare const SINK_ACK_REQUIRED_CODE: "browser-profiles:sink-ack-required";
|
|
792
|
+
declare const AGENT_GRANT_REFUSED_CODE: "browser-profiles:agent-grant-refused";
|
|
793
|
+
declare const FULL_PROFILE_REFUSED_CODE: "browser-profiles:full-profile-refused";
|
|
794
|
+
declare const LEDGER_RECORD_INVALID_CODE: "browser-profiles:ledger-record-invalid";
|
|
795
|
+
declare const GRANT_STORE_INVALID_CODE: "browser-profiles:grant-store-invalid";
|
|
796
|
+
type GrantDomainReason = "empty" | "wildcard" | "leading-dot" | "bare-tld" | "public-suffix" | "invalid";
|
|
797
|
+
/** A grant's domain list broke AIP-63 C2 (wildcard, empty, bare TLD, leading dot, public suffix). */
|
|
798
|
+
declare class GrantDomainError extends ToolError {
|
|
799
|
+
readonly reason: GrantDomainReason;
|
|
800
|
+
readonly domain: string | undefined;
|
|
801
|
+
constructor(reason: GrantDomainReason, domain?: string);
|
|
802
|
+
}
|
|
803
|
+
/** The caller has no usable grant for this. Also what a device sees for another device's grant (C12). */
|
|
804
|
+
declare class ConsentRequiredError extends ToolError {
|
|
805
|
+
constructor(message?: string);
|
|
806
|
+
}
|
|
807
|
+
/** A non-interactive request lacked `--domains` plus `--yes`, or the named full-profile request (C4). */
|
|
808
|
+
declare class NonInteractiveConsentError extends ToolError {
|
|
809
|
+
readonly missing: readonly string[];
|
|
810
|
+
constructor(missing: readonly string[]);
|
|
811
|
+
}
|
|
812
|
+
/** The human declined at the prompt. */
|
|
813
|
+
declare class ConsentDeclinedError extends ToolError {
|
|
814
|
+
constructor(what: string);
|
|
815
|
+
}
|
|
816
|
+
/** Material would go to a remote sink nobody acknowledged (C6). */
|
|
817
|
+
declare class SinkAckRequiredError extends ToolError {
|
|
818
|
+
readonly sinkId: string;
|
|
819
|
+
constructor(sinkId: string);
|
|
820
|
+
}
|
|
821
|
+
/** An agent tried something only a human consent path may do (C7). */
|
|
822
|
+
declare class AgentGrantRefusedError extends ToolError {
|
|
823
|
+
readonly attempt: string;
|
|
824
|
+
constructor(attempt: string);
|
|
825
|
+
}
|
|
826
|
+
/** A full-profile grant was requested the wrong way (not by name, or with a remote sink) (C3). */
|
|
827
|
+
declare class FullProfileGrantError extends ToolError {
|
|
828
|
+
constructor(message: string);
|
|
829
|
+
}
|
|
830
|
+
/** A ledger row broke the AIP-63 `ledgerRecord` shape; nothing was written. */
|
|
831
|
+
declare class LedgerRecordInvalidError extends ToolError {
|
|
832
|
+
readonly issues: readonly string[];
|
|
833
|
+
constructor(issues: readonly string[]);
|
|
834
|
+
}
|
|
835
|
+
/** The grants file is not a readable grant list. */
|
|
836
|
+
declare class GrantStoreInvalidError extends ToolError {
|
|
837
|
+
constructor(message: string);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/** The AIP-63 `domain` pattern: lowercase, ASCII or punycode, at least two labels. */
|
|
841
|
+
declare const GRANT_DOMAIN_PATTERN: RegExp;
|
|
842
|
+
interface GrantDomainPolicy {
|
|
843
|
+
/** Extra public-suffix check (for example one backed by the full Public Suffix List). */
|
|
844
|
+
isPublicSuffix?: (domain: string) => boolean;
|
|
845
|
+
}
|
|
846
|
+
/** True for a suffix in the embedded subset. */
|
|
847
|
+
declare function isKnownPublicSuffix(domain: string): boolean;
|
|
848
|
+
/**
|
|
849
|
+
* Validate a grant's domain list against AIP-63 C2 and return it lowercased and
|
|
850
|
+
* de-duplicated. Rejects `*`, `*.x`, a leading dot, an empty list, a bare TLD,
|
|
851
|
+
* a public suffix and anything that is not a plain host, each with a typed
|
|
852
|
+
* {@link GrantDomainError}. A grant covers the named domain and its subdomains.
|
|
853
|
+
*/
|
|
854
|
+
declare function validateGrantDomains(input: readonly string[], policy?: GrantDomainPolicy): string[];
|
|
855
|
+
/** True when `host` is `domain` or one of its subdomains. */
|
|
856
|
+
declare function hostCoveredBy(host: string, domain: string): boolean;
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* The consent ledger (AIP-63 C10, C11): an append-only JSONL file, one record per
|
|
860
|
+
* grant, refresh, revoke, sink-ack or deny. The record shape is closed on purpose
|
|
861
|
+
* (see `ledgerRecordSchema`): there is no field a cookie value could go in.
|
|
862
|
+
*/
|
|
863
|
+
|
|
864
|
+
/** `~/.agentproto/bureau/consent.jsonl`. Resolved on call, never at import. */
|
|
865
|
+
declare function defaultConsentLedgerPath(home?: string): string;
|
|
866
|
+
declare const ledgerEventSchema: z.ZodEnum<{
|
|
867
|
+
grant: "grant";
|
|
868
|
+
refresh: "refresh";
|
|
869
|
+
revoke: "revoke";
|
|
870
|
+
"sink-ack": "sink-ack";
|
|
871
|
+
deny: "deny";
|
|
872
|
+
}>;
|
|
873
|
+
type LedgerEvent = z.infer<typeof ledgerEventSchema>;
|
|
874
|
+
declare const ledgerActorSchema: z.ZodObject<{
|
|
875
|
+
kind: z.ZodEnum<{
|
|
876
|
+
human: "human";
|
|
877
|
+
agent: "agent";
|
|
878
|
+
system: "system";
|
|
879
|
+
}>;
|
|
880
|
+
via: z.ZodOptional<z.ZodString>;
|
|
881
|
+
deviceId: z.ZodOptional<z.ZodString>;
|
|
882
|
+
}, z.core.$strict>;
|
|
883
|
+
type LedgerActor = z.infer<typeof ledgerActorSchema>;
|
|
884
|
+
declare const ledgerRecordSchema: z.ZodObject<{
|
|
885
|
+
seq: z.ZodNumber;
|
|
886
|
+
at: z.ZodISODateTime;
|
|
887
|
+
event: z.ZodEnum<{
|
|
888
|
+
grant: "grant";
|
|
889
|
+
refresh: "refresh";
|
|
890
|
+
revoke: "revoke";
|
|
891
|
+
"sink-ack": "sink-ack";
|
|
892
|
+
deny: "deny";
|
|
893
|
+
}>;
|
|
894
|
+
actor: z.ZodObject<{
|
|
895
|
+
kind: z.ZodEnum<{
|
|
896
|
+
human: "human";
|
|
897
|
+
agent: "agent";
|
|
898
|
+
system: "system";
|
|
899
|
+
}>;
|
|
900
|
+
via: z.ZodOptional<z.ZodString>;
|
|
901
|
+
deviceId: z.ZodOptional<z.ZodString>;
|
|
902
|
+
}, z.core.$strict>;
|
|
903
|
+
grantId: z.ZodOptional<z.ZodString>;
|
|
904
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
905
|
+
domains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
906
|
+
fullProfile: z.ZodOptional<z.ZodLiteral<true>>;
|
|
907
|
+
cookieCount: z.ZodOptional<z.ZodNumber>;
|
|
908
|
+
nameHashes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
909
|
+
sinkId: z.ZodOptional<z.ZodString>;
|
|
910
|
+
derived: z.ZodOptional<z.ZodObject<{
|
|
911
|
+
local: z.ZodEnum<{
|
|
912
|
+
none: "none";
|
|
913
|
+
deleted: "deleted";
|
|
914
|
+
}>;
|
|
915
|
+
remote: z.ZodEnum<{
|
|
916
|
+
confirmed: "confirmed";
|
|
917
|
+
unavailable: "unavailable";
|
|
918
|
+
failed: "failed";
|
|
919
|
+
"not-applicable": "not-applicable";
|
|
920
|
+
}>;
|
|
921
|
+
}, z.core.$strict>>;
|
|
922
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
923
|
+
prev: z.ZodOptional<z.ZodString>;
|
|
924
|
+
}, z.core.$strict>;
|
|
925
|
+
type LedgerRecord = z.infer<typeof ledgerRecordSchema>;
|
|
926
|
+
/** A record before the ledger stamps `seq`, `at` and `prev`. */
|
|
927
|
+
type LedgerEntry = Omit<LedgerRecord, "seq" | "at" | "prev">;
|
|
928
|
+
/** SHA-256 of the grant id, a NUL byte and a cookie name, lowercase hex. Never a value. */
|
|
929
|
+
declare function cookieNameHash(grantId: string, cookieName: string): string;
|
|
930
|
+
interface ConsentLedgerOptions {
|
|
931
|
+
/** Ledger file. Default {@link defaultConsentLedgerPath}. */
|
|
932
|
+
path?: string;
|
|
933
|
+
now?: () => Date;
|
|
934
|
+
}
|
|
935
|
+
interface ConsentLedger {
|
|
936
|
+
readonly path: string;
|
|
937
|
+
/** Validate, stamp and append one record. One writer process only: concurrent processes can duplicate `seq` and break the chain, which `verifyChain` reports. Throws {@link LedgerRecordInvalidError} and writes nothing on a bad record. */
|
|
938
|
+
append(entry: LedgerEntry): LedgerRecord;
|
|
939
|
+
read(): LedgerRecord[];
|
|
940
|
+
/** True when every `prev` matches the SHA-256 of the line before it and `seq` counts up from 0. */
|
|
941
|
+
verifyChain(): boolean;
|
|
942
|
+
}
|
|
943
|
+
declare function createConsentLedger(opts?: ConsentLedgerOptions): ConsentLedger;
|
|
944
|
+
|
|
945
|
+
/** The grant model of AIP-63 (`$defs/grant`) and where grants are kept. Grants hold no cookie values. */
|
|
946
|
+
|
|
947
|
+
type GrantSink = {
|
|
948
|
+
kind: "local";
|
|
949
|
+
} | {
|
|
950
|
+
kind: "remote";
|
|
951
|
+
providerId: string;
|
|
952
|
+
ackSeq: number;
|
|
953
|
+
};
|
|
954
|
+
type GrantSourceKind = "chrome" | "firefox" | "file" | "native-login";
|
|
955
|
+
interface Grant {
|
|
956
|
+
id: string;
|
|
957
|
+
sessionId: string;
|
|
958
|
+
/** Paired device (AIP-59) this grant is scoped to. Absent: usable by any device the host serves (C12). */
|
|
959
|
+
deviceId?: string;
|
|
960
|
+
source: {
|
|
961
|
+
kind: GrantSourceKind;
|
|
962
|
+
profile: string;
|
|
963
|
+
};
|
|
964
|
+
/** Explicit registrable domains. Exactly one of `domains` and `fullProfile`. */
|
|
965
|
+
domains?: string[];
|
|
966
|
+
/** The recorded full-profile grant (C3). Not a wildcard domain. */
|
|
967
|
+
fullProfile?: true;
|
|
968
|
+
sinks: GrantSink[];
|
|
969
|
+
grantedAt: string;
|
|
970
|
+
grantedVia: "interactive" | "flag";
|
|
971
|
+
expiresAt?: string;
|
|
972
|
+
cookieCount: number;
|
|
973
|
+
revokedAt?: string;
|
|
974
|
+
}
|
|
975
|
+
declare const grantSchema: z.ZodObject<{
|
|
976
|
+
id: z.ZodString;
|
|
977
|
+
sessionId: z.ZodString;
|
|
978
|
+
deviceId: z.ZodOptional<z.ZodString>;
|
|
979
|
+
source: z.ZodObject<{
|
|
980
|
+
kind: z.ZodEnum<{
|
|
981
|
+
file: "file";
|
|
982
|
+
chrome: "chrome";
|
|
983
|
+
firefox: "firefox";
|
|
984
|
+
"native-login": "native-login";
|
|
985
|
+
}>;
|
|
986
|
+
profile: z.ZodString;
|
|
987
|
+
}, z.core.$strict>;
|
|
988
|
+
domains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
989
|
+
fullProfile: z.ZodOptional<z.ZodLiteral<true>>;
|
|
990
|
+
sinks: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
991
|
+
kind: z.ZodLiteral<"local">;
|
|
992
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
993
|
+
kind: z.ZodLiteral<"remote">;
|
|
994
|
+
providerId: z.ZodString;
|
|
995
|
+
ackSeq: z.ZodNumber;
|
|
996
|
+
}, z.core.$strict>]>>;
|
|
997
|
+
grantedAt: z.ZodISODateTime;
|
|
998
|
+
grantedVia: z.ZodEnum<{
|
|
999
|
+
interactive: "interactive";
|
|
1000
|
+
flag: "flag";
|
|
1001
|
+
}>;
|
|
1002
|
+
expiresAt: z.ZodOptional<z.ZodISODateTime>;
|
|
1003
|
+
cookieCount: z.ZodNumber;
|
|
1004
|
+
revokedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
1005
|
+
}, z.core.$strict>;
|
|
1006
|
+
/** True while the grant is not revoked and not past `expiresAt`. */
|
|
1007
|
+
declare function isGrantActive(grant: Grant, now?: Date): boolean;
|
|
1008
|
+
/** True when a call from `deviceId` may use the grant (C12): unscoped grants serve any device. */
|
|
1009
|
+
declare function grantServesDevice(grant: Grant, deviceId: string | undefined): boolean;
|
|
1010
|
+
/** Where grants live. Synchronous on purpose: a launch asks "is this grant still active" inline. */
|
|
1011
|
+
interface GrantStore {
|
|
1012
|
+
list(): Grant[];
|
|
1013
|
+
get(id: string): Grant | undefined;
|
|
1014
|
+
put(grant: Grant): void;
|
|
1015
|
+
}
|
|
1016
|
+
declare function memoryGrantStore(initial?: readonly Grant[]): GrantStore;
|
|
1017
|
+
/** `~/.agentproto/bureau/grants.json`. Resolved on call, never at import. */
|
|
1018
|
+
declare function defaultGrantStorePath(home?: string): string;
|
|
1019
|
+
/** One JSON file, dir 0700, file 0600, replaced atomically on every write. */
|
|
1020
|
+
declare function fileGrantStore(path?: string): GrantStore;
|
|
1021
|
+
|
|
1022
|
+
/** Injected seams of the consent host: the human prompt and the Chrome profile reader. */
|
|
1023
|
+
|
|
1024
|
+
/** What the human is being asked. Counts only: a prompt never carries a cookie value. */
|
|
1025
|
+
type ConsentQuestion = {
|
|
1026
|
+
kind: "domain";
|
|
1027
|
+
domain: string;
|
|
1028
|
+
profile: string;
|
|
1029
|
+
presentCount: number | undefined;
|
|
1030
|
+
deviceId?: string;
|
|
1031
|
+
} | {
|
|
1032
|
+
kind: "full-profile";
|
|
1033
|
+
profile: string;
|
|
1034
|
+
warning: string;
|
|
1035
|
+
deviceId?: string;
|
|
1036
|
+
} | {
|
|
1037
|
+
kind: "sink";
|
|
1038
|
+
providerId: string;
|
|
1039
|
+
sessionId: string;
|
|
1040
|
+
};
|
|
1041
|
+
/** The interactive consent port. A CLI wires it to a terminal, a desktop app to a dialog, a test to a fake. */
|
|
1042
|
+
interface ConsentPrompt {
|
|
1043
|
+
confirm(question: ConsentQuestion): Promise<boolean>;
|
|
1044
|
+
}
|
|
1045
|
+
/** Reads a Chrome profile. Every method that decrypts values is only called after consent. */
|
|
1046
|
+
interface ChromeProfilePort {
|
|
1047
|
+
/** Presence scan (C5): counts per domain, no values, no Keychain. */
|
|
1048
|
+
countByDomain(profile: string, domains: readonly string[]): Known<Record<string, number>>;
|
|
1049
|
+
/** Whole-profile cookie count, no values. */
|
|
1050
|
+
countAll(profile: string): Known<number>;
|
|
1051
|
+
/** Decrypt the cookies of `domains`. Touches the Keychain: only for an explicit, consented import. */
|
|
1052
|
+
readCookies(profile: string, domains: readonly string[]): SessionCookie[];
|
|
1053
|
+
}
|
|
1054
|
+
interface LocalChromePortOptions {
|
|
1055
|
+
/** Chrome user-data root. Required: this package never guesses the real one here. */
|
|
1056
|
+
chromeRoot: string;
|
|
1057
|
+
/** Safe-Storage password source. Default reads the macOS Keychain at import time. */
|
|
1058
|
+
safeStoragePassword?: () => string;
|
|
1059
|
+
}
|
|
1060
|
+
/** The default reader over a Chrome user-data dir. The profile must be a real `Default` or `Profile N` dir. */
|
|
1061
|
+
declare function localChromePort(opts: LocalChromePortOptions): ChromeProfilePort;
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* The consent host: the one place a grant is created, refreshed or revoked.
|
|
1065
|
+
* Human consent paths live here; agents get the narrower surface in `agent.ts`.
|
|
1066
|
+
* No cookie value ever reaches the ledger, a thrown error or a returned result:
|
|
1067
|
+
* values exist only in the per-grant jar file (derived material) and in the
|
|
1068
|
+
* arrays handed to a provider's cookie source.
|
|
1069
|
+
*/
|
|
1070
|
+
|
|
1071
|
+
/** Returned with every full-profile grant. Shown to the human verbatim. */
|
|
1072
|
+
declare const FULL_PROFILE_WARNING: string;
|
|
1073
|
+
type SinkRequest = {
|
|
1074
|
+
kind: "local";
|
|
1075
|
+
} | {
|
|
1076
|
+
kind: "remote";
|
|
1077
|
+
providerId: string;
|
|
1078
|
+
acknowledged?: boolean;
|
|
1079
|
+
};
|
|
1080
|
+
interface ImportRequest {
|
|
1081
|
+
sessionId: string;
|
|
1082
|
+
/** Chrome profile dir (`Default`, `Profile 1`). */
|
|
1083
|
+
profile: string;
|
|
1084
|
+
/** Explicit registrable domains. There is no "all". */
|
|
1085
|
+
domains?: readonly string[];
|
|
1086
|
+
/** `--yes`: the human confirmed the named domains without a prompt. */
|
|
1087
|
+
yes?: boolean;
|
|
1088
|
+
deviceId?: string;
|
|
1089
|
+
sinks?: readonly SinkRequest[];
|
|
1090
|
+
expiresAt?: string;
|
|
1091
|
+
/** Defaults to a human. An `agent` here is refused and recorded as a deny row. */
|
|
1092
|
+
actor?: LedgerActor;
|
|
1093
|
+
}
|
|
1094
|
+
interface FullProfileRequest {
|
|
1095
|
+
sessionId: string;
|
|
1096
|
+
profile: string;
|
|
1097
|
+
yes?: boolean;
|
|
1098
|
+
deviceId?: string;
|
|
1099
|
+
expiresAt?: string;
|
|
1100
|
+
actor?: LedgerActor;
|
|
1101
|
+
}
|
|
1102
|
+
interface GrantResult {
|
|
1103
|
+
grant: Grant;
|
|
1104
|
+
/** Cookie counts per domain from the presence scan; `undefined` when the scan failed. */
|
|
1105
|
+
presence: Record<string, number> | undefined;
|
|
1106
|
+
warnings: string[];
|
|
1107
|
+
}
|
|
1108
|
+
interface FullProfileResult {
|
|
1109
|
+
grant: Grant;
|
|
1110
|
+
/** The loud warning to show the human. */
|
|
1111
|
+
warning: string;
|
|
1112
|
+
warnings: string[];
|
|
1113
|
+
}
|
|
1114
|
+
interface RevokeResult {
|
|
1115
|
+
grant: Grant;
|
|
1116
|
+
derived: {
|
|
1117
|
+
local: "deleted" | "none";
|
|
1118
|
+
remote: "confirmed" | "unavailable" | "failed" | "not-applicable";
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
interface ConsentHostOptions {
|
|
1122
|
+
grants: GrantStore;
|
|
1123
|
+
ledger: ConsentLedger;
|
|
1124
|
+
/** L5a store holding the session descriptor this host derives. */
|
|
1125
|
+
store: SessionStorePort;
|
|
1126
|
+
/** Directory of per-grant cookie jars (0700). Derived material: deleted on revoke. */
|
|
1127
|
+
jarDir: string;
|
|
1128
|
+
chrome: ChromeProfilePort;
|
|
1129
|
+
/** Present: a human can be asked. Absent: non-interactive, so `--domains` and `--yes` are mandatory (C4). */
|
|
1130
|
+
prompt?: ConsentPrompt;
|
|
1131
|
+
domainPolicy?: GrantDomainPolicy;
|
|
1132
|
+
/** C13: a device-scoped grant is honored only while its device is still paired. */
|
|
1133
|
+
isDevicePaired?: (deviceId: string) => boolean;
|
|
1134
|
+
/** Asks a remote sink to delete what it received. Absent: `remote` is recorded as `unavailable`. */
|
|
1135
|
+
revokeRemote?: (grant: Grant) => Promise<"confirmed" | "failed" | "unavailable">;
|
|
1136
|
+
/** Removes material this host does not own, such as a full-profile clone. True when something was deleted. */
|
|
1137
|
+
deleteExtraDerived?: (grant: Grant) => Promise<boolean>;
|
|
1138
|
+
now?: () => Date;
|
|
1139
|
+
newId?: () => string;
|
|
1140
|
+
}
|
|
1141
|
+
interface FullProfileSelector {
|
|
1142
|
+
grantId?: string;
|
|
1143
|
+
sessionId?: string;
|
|
1144
|
+
profile?: string;
|
|
1145
|
+
deviceId?: string;
|
|
1146
|
+
}
|
|
1147
|
+
interface FullProfileProof {
|
|
1148
|
+
readonly grantId: string;
|
|
1149
|
+
isActive(): boolean;
|
|
1150
|
+
}
|
|
1151
|
+
interface ConsentHost {
|
|
1152
|
+
importFromChrome(req: ImportRequest): Promise<GrantResult>;
|
|
1153
|
+
grantFullProfile(req: FullProfileRequest): Promise<FullProfileResult>;
|
|
1154
|
+
/** Re-read the grant's own domains from its own profile. Never widens. Allowed for an agent (C7). */
|
|
1155
|
+
refresh(grantId: string, opts?: {
|
|
1156
|
+
actor?: LedgerActor;
|
|
1157
|
+
}): Promise<{
|
|
1158
|
+
grant: Grant;
|
|
1159
|
+
cookieCount: number;
|
|
1160
|
+
}>;
|
|
1161
|
+
revoke(grantId: string, opts?: {
|
|
1162
|
+
actor?: LedgerActor;
|
|
1163
|
+
}): Promise<RevokeResult>;
|
|
1164
|
+
/** Proof for the kit's `fullProfile` unlock. Throws {@link FullProfileGrantError} when no active full-profile grant matches. */
|
|
1165
|
+
fullProfileProof(selector: FullProfileSelector): FullProfileProof;
|
|
1166
|
+
/** A provider cookie source that serves only this device's active, granted domains. */
|
|
1167
|
+
cookieSourceFor(scope?: {
|
|
1168
|
+
deviceId?: string;
|
|
1169
|
+
}): (request: {
|
|
1170
|
+
providerId: string;
|
|
1171
|
+
profile: string;
|
|
1172
|
+
}) => readonly BrowserCookie[];
|
|
1173
|
+
/** Throws `browser:consent_required` unless an active grant of this session covers `host` for this device. */
|
|
1174
|
+
assertCovered(input: {
|
|
1175
|
+
sessionId: string;
|
|
1176
|
+
host: string;
|
|
1177
|
+
deviceId?: string;
|
|
1178
|
+
}): Grant;
|
|
1179
|
+
/** Record a refused agent attempt and throw {@link AgentGrantRefusedError}. */
|
|
1180
|
+
refuseAgent(attempt: string, ctx: {
|
|
1181
|
+
actor: LedgerActor;
|
|
1182
|
+
grantId?: string;
|
|
1183
|
+
sessionId?: string;
|
|
1184
|
+
domains?: readonly string[];
|
|
1185
|
+
}): never;
|
|
1186
|
+
getGrant(id: string): Grant | undefined;
|
|
1187
|
+
listGrants(): Grant[];
|
|
1188
|
+
}
|
|
1189
|
+
declare function createConsentHost(opts: ConsentHostOptions): ConsentHost;
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* The agent-path surface (AIP-63 C7, plan F4). An agent may refresh inside a
|
|
1193
|
+
* grant and revoke. It cannot grant, add a domain, change the profile or add a
|
|
1194
|
+
* sink: each attempt is refused with a typed error and appended as a `deny` row.
|
|
1195
|
+
*/
|
|
1196
|
+
|
|
1197
|
+
interface AgentSyncInput {
|
|
1198
|
+
grantId: string;
|
|
1199
|
+
/** Accepted only when identical to the grant's own domains. Anything else is a refused widening. */
|
|
1200
|
+
domains?: readonly string[];
|
|
1201
|
+
/** Accepted only when identical to the grant's own profile. */
|
|
1202
|
+
profile?: string;
|
|
1203
|
+
}
|
|
1204
|
+
interface AgentConsentSurface {
|
|
1205
|
+
/** `session_sync_from_chrome`: refresh within the grant. Cannot add a domain or change profile. */
|
|
1206
|
+
sync(input: AgentSyncInput): Promise<{
|
|
1207
|
+
grantId: string;
|
|
1208
|
+
cookieCount: number;
|
|
1209
|
+
}>;
|
|
1210
|
+
revoke(grantId: string): Promise<void>;
|
|
1211
|
+
/** Always refused: only a human consent path creates a grant. */
|
|
1212
|
+
grant(input: {
|
|
1213
|
+
sessionId: string;
|
|
1214
|
+
domains?: readonly string[];
|
|
1215
|
+
}): Promise<never>;
|
|
1216
|
+
addSink(input: {
|
|
1217
|
+
grantId: string;
|
|
1218
|
+
providerId: string;
|
|
1219
|
+
}): Promise<never>;
|
|
1220
|
+
}
|
|
1221
|
+
declare function createAgentConsentSurface(host: ConsentHost, who?: {
|
|
1222
|
+
deviceId?: string;
|
|
1223
|
+
via?: string;
|
|
1224
|
+
}): AgentConsentSurface;
|
|
1225
|
+
|
|
1226
|
+
/**
|
|
1227
|
+
* `doctor`: why can't Bureau read this Chrome profile? Every probe goes through
|
|
1228
|
+
* an injected port, so tests use fakes and never touch a real profile. The
|
|
1229
|
+
* Keychain is probed only when the caller asks for it explicitly.
|
|
1230
|
+
*/
|
|
1231
|
+
type DoctorFailure = "missing" | "full-disk-access" | "unreadable" | "keychain-denied";
|
|
1232
|
+
interface DoctorCheck {
|
|
1233
|
+
id: "local-state" | "cookies-db" | "keychain";
|
|
1234
|
+
status: "ok" | "fail" | "skipped";
|
|
1235
|
+
detail: string;
|
|
1236
|
+
failure?: DoctorFailure;
|
|
1237
|
+
/** What to do about it. */
|
|
1238
|
+
fix?: string;
|
|
1239
|
+
}
|
|
1240
|
+
interface DoctorReport {
|
|
1241
|
+
ok: boolean;
|
|
1242
|
+
checks: DoctorCheck[];
|
|
1243
|
+
/** Set when any check failed: the way around needing the profile at all. */
|
|
1244
|
+
recommendation?: string;
|
|
1245
|
+
}
|
|
1246
|
+
/** Probes the doctor runs. Each throws a Node-style error (with `code`) on failure. */
|
|
1247
|
+
interface DoctorPort {
|
|
1248
|
+
/** Raw text of `Local State`. */
|
|
1249
|
+
readLocalState(): string;
|
|
1250
|
+
/** Copy the profile's Cookies db somewhere temporary and remove the copy. */
|
|
1251
|
+
copyCookiesDb(profile: string): void;
|
|
1252
|
+
/** Ask the Keychain for the Safe Storage key. Only called when `checkKeychain` is true. */
|
|
1253
|
+
probeKeychain?(): void;
|
|
1254
|
+
/** The exact executable that needs Full Disk Access. */
|
|
1255
|
+
binary: string;
|
|
1256
|
+
}
|
|
1257
|
+
interface DoctorOptions {
|
|
1258
|
+
port: DoctorPort;
|
|
1259
|
+
profile: string;
|
|
1260
|
+
/** Explicit action: touch the Keychain. Default false. */
|
|
1261
|
+
checkKeychain?: boolean;
|
|
1262
|
+
}
|
|
1263
|
+
declare function runDoctor(opts: DoctorOptions): DoctorReport;
|
|
1264
|
+
interface LocalDoctorPortOptions {
|
|
1265
|
+
chromeRoot: string;
|
|
1266
|
+
binary?: string;
|
|
1267
|
+
probeKeychain?: () => void;
|
|
1268
|
+
}
|
|
1269
|
+
/** The real-filesystem port. The Cookies copy lives in a temp dir and is removed at once. */
|
|
1270
|
+
declare function localDoctorPort(opts: LocalDoctorPortOptions): DoctorPort;
|
|
1271
|
+
|
|
1272
|
+
export { ACCOUNT_SWITCHER_MISSING_CODE, AGENT_GRANT_REFUSED_CODE, type AccountSwitcher, AccountSwitcherMissingError, type AgentConsentSurface, AgentGrantRefusedError, type AgentSyncInput, type ApiRawResult, type AuthCookieMeta, type AuthSignalRegistry, type AuthStatus, BUILTIN_INJECT_FROM, BUILTIN_STRATEGY_KINDS, type BuildOwnedDescriptorInput, type BuiltinInjectFrom, type BuiltinStrategyKind, CHROME_IDENTITY_CODE, CONSENT_DECLINED_CODE, CONSENT_FLAGS_REQUIRED_CODE, CONSENT_REQUIRED_CODE, COOKIE_STALE_TTL_MS, type CamofoxNativeInject, type CamofoxSession, type ChromeCookieStrategy, type ChromeIdentity, ChromeIdentityError, type ChromeIdentityOptions, type ChromeProfileInject, type ChromeProfilePort, ConsentDeclinedError, type ConsentHost, type ConsentHostOptions, type ConsentLedger, type ConsentLedgerOptions, type ConsentPrompt, type ConsentQuestion, ConsentRequiredError, type DoctorCheck, type DoctorFailure, type DoctorOptions, type DoctorPort, type DoctorReport, type DomainAuthSignal, FULL_PROFILE_REFUSED_CODE, FULL_PROFILE_WARNING, type FileInject, type FileSessionStoreOptions, FullProfileGrantError, type FullProfileProof, type FullProfileRequest, type FullProfileResult, type FullProfileSelector, GRANT_DOMAIN_INVALID_CODE, GRANT_DOMAIN_PATTERN, GRANT_STORE_INVALID_CODE, type Grant, GrantDomainError, type GrantDomainPolicy, type GrantDomainReason, type GrantResult, type GrantSink, type GrantSourceKind, type GrantStore, GrantStoreInvalidError, type ImportRequest, type Known, LEDGER_RECORD_INVALID_CODE, type LedgerActor, type LedgerEntry, type LedgerEvent, type LedgerRecord, LedgerRecordInvalidError, type LocalBrowserSession, type LocalBrowserSessionOptions, type LocalChromePortOptions, type LocalDoctorPortOptions, type LocalStateProfile, NonInteractiveConsentError, type NormalizeOptions, type OpenSessionOptions, type OwnedStorageStateStrategy, type ParsedLocalState, type RawCookie, type ResolveSessionDeps, type ResolvedSession, type RevokeResult, SESSION_DESCRIPTOR_INVALID_CODE, SESSION_RESOLVE_CODE, SESSION_SOURCE_DUPLICATE_CODE, SESSION_SOURCE_RESERVED_CODE, SESSION_SOURCE_UNKNOWN_CODE, SINK_ACK_REQUIRED_CODE, type SessionAuthDescription, type SessionAuthState, type SessionCookie, type SessionDescriptor, SessionDescriptorInvalidError, type SessionIdentity, type SessionInject, type SessionInjectFrom, SessionResolveError, type SessionSource, type SessionSourceCatalog, SessionSourceDuplicateError, type SessionSourceMeta, type SessionSourceRef, type SessionSourceRegistry, SessionSourceReservedError, SessionSourceUnknownError, type SessionStorePort, SinkAckRequiredError, type SinkRequest, type SiteAuthSignal, type SourceInject, type SourceStrategy, type StoredCredentialStrategy, type Strategy, assertChromeLaunchDirSafe, authStatusSchema, buildOwnedSessionDescriptor, camofoxStatePath, catalogSessionStore, chromeCookieStrategyOf, chromeIdentityLabel, chromeLocalStatePath, chromeTimeToIso, chromeUserDataDir, chromeUserDataRoot, compositeSessionStore, cookieFreshness, cookieNameHash, countCookies, countCookiesByDomain, createAgentConsentSurface, createAuthSignalRegistry, createConsentHost, createConsentLedger, createLocalBrowserSession, createSessionSourceRegistry, decryptChromeCookies, decryptChromeValue, dedupeCookies, defaultAuthSignalRegistry, defaultCamofoxBase, defaultConsentLedgerPath, defaultGrantStorePath, describeSessionAuth, domainAuthSignal, domainMatches, fileGrantStore, fileSessionStore, gatherCookies, gatherCookiesForStrategy, grantSchema, grantServesDevice, hostCoveredBy, injectFromOf, injectKindOf, isBuiltinStrategyKind, isCamofoxNativeInject, isChromeCookieStrategy, isChromeProfileInject, isFileInject, isGrantActive, isKnown, isKnownPublicSuffix, isOwnedStorageStateStrategy, isSessionSourceUnknownError, isSourceInject, isSourceStrategy, isStoredCredentialStrategy, knownOr, ledgerActorSchema, ledgerEventSchema, ledgerRecordSchema, localChromePort, localDoctorPort, memoryGrantStore, normalizeDescriptor, openSession, ownedStorageStatePath, parseLocalState, parseSessionDescriptor, priorChromeStrategy, profileAuthedDomains, profileDirs, profileLoggedInDomains, readLocalState, readSafeStorageKey, resolveChromeProfile, resolveInjectFromStrategies, resolveSession, runDoctor, safeStorageKey, scanChromeIdentities, selectResolutionStrategy, sessionDescriptorSchema, sessionIdentitySchema, sessionInjectSchema, strategySchema, toSessionCookie, validateGrantDomains, verifyChromeIdentity };
|