browser-commander 0.21.2 → 0.22.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/.jscpd-baseline.json +5 -6
- package/CHANGELOG.md +32 -0
- package/package.json +1 -1
- package/src/browser/browser-cookie-credentials.js +15 -24
- package/src/browser/browser-cookies.js +219 -11
- package/src/browser/browser-profiles.js +145 -79
- package/src/browser/browser-sources.js +184 -0
- package/src/browser/browser-sources.json +399 -0
- package/src/browser/default-browser.js +176 -0
- package/src/browser/migration/cookies.js +3 -0
- package/src/browser/migration/index.js +41 -14
- package/src/cli/args.js +10 -0
- package/src/cli/commands.js +27 -0
- package/src/cli/high-level-methods.js +28 -2
- package/src/cli/modules.js +6 -0
- package/src/exports.js +4 -0
- package/tests/helpers/cli-fakes.js +21 -0
- package/tests/helpers/migration-fixtures.js +30 -1
- package/tests/unit/browser/browser-cookies-profiledir.test.js +36 -0
- package/tests/unit/browser/browser-cookies.test.js +2 -11
- package/tests/unit/browser/browser-profiles-sources.test.js +150 -0
- package/tests/unit/browser/browser-sources.test.js +154 -0
- package/tests/unit/browser/cookie-sources.test.js +146 -0
- package/tests/unit/browser/default-browser.test.js +121 -0
- package/tests/unit/browser/migration/index.test.js +62 -1
- package/tests/unit/cli/main.test.js +32 -5
- package/tests/unit/scripts/browser-support.test.js +56 -0
package/.jscpd-baseline.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"0fd23be8410c2c95": 1,
|
|
20
20
|
"0fe4f3b5e5e939b3": 1,
|
|
21
21
|
"1019ebbe7f07a5ad": 1,
|
|
22
|
+
"119f429fe4d23e4a": 1,
|
|
22
23
|
"1273365073edd790": 1,
|
|
23
24
|
"1296adc3c3128048": 1,
|
|
24
25
|
"13c4b58d3ea0719b": 1,
|
|
@@ -54,11 +55,11 @@
|
|
|
54
55
|
"2e70be24d2268673": 1,
|
|
55
56
|
"2f4c9810eae87d6f": 1,
|
|
56
57
|
"2f9e15467cc2f12a": 1,
|
|
58
|
+
"30da9d4e83f1c939": 1,
|
|
57
59
|
"3101802d3ea65339": 1,
|
|
58
60
|
"33357ce94e07cc4b": 1,
|
|
59
61
|
"35cf2769645f3234": 1,
|
|
60
62
|
"37bd4114d0089f49": 1,
|
|
61
|
-
"389df6fb4b8315a1": 1,
|
|
62
63
|
"39eaada6d1879f4a": 1,
|
|
63
64
|
"3a9f1672ecc1d55b": 1,
|
|
64
65
|
"3adca52a3f04837c": 1,
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
"4c3b94caa28045d0": 1,
|
|
78
79
|
"4e090d6bb30e6551": 1,
|
|
79
80
|
"4f3c541b3f3e3f23": 1,
|
|
81
|
+
"4f93b074bc2ac9e8": 1,
|
|
80
82
|
"5085765e1f103f3e": 1,
|
|
81
83
|
"5184c90c9f8406c3": 1,
|
|
82
84
|
"520a28ee2ced56c2": 1,
|
|
@@ -153,11 +155,9 @@
|
|
|
153
155
|
"92ddafa7e65be7eb": 1,
|
|
154
156
|
"94821a4d1b745dfd": 1,
|
|
155
157
|
"97c5a112fdde7be8": 1,
|
|
156
|
-
"98cc22079feaa004": 1,
|
|
157
158
|
"990d87f21678fa96": 1,
|
|
158
159
|
"9a72b1c2f22156ca": 1,
|
|
159
160
|
"9b6ef4df04562546": 1,
|
|
160
|
-
"9c055dfd90c369bf": 1,
|
|
161
161
|
"9ee57dcd5e7a6691": 1,
|
|
162
162
|
"9fbb8296259dedbe": 1,
|
|
163
163
|
"a0183465782b0b15": 1,
|
|
@@ -167,11 +167,11 @@
|
|
|
167
167
|
"a305c6cac16f3af5": 1,
|
|
168
168
|
"a4c8498d4f2d2191": 1,
|
|
169
169
|
"a6ad7ab6c05656dc": 1,
|
|
170
|
+
"a6ffab1aacd00af6": 1,
|
|
170
171
|
"a78d76908162699e": 1,
|
|
171
172
|
"a9bd4ffef6da5faf": 1,
|
|
172
173
|
"a9d71c2e50f1b832": 1,
|
|
173
174
|
"acc95d694eb2f581": 1,
|
|
174
|
-
"ad4469f2e8131535": 1,
|
|
175
175
|
"ad4cfa76daa1b6fc": 1,
|
|
176
176
|
"ad50b48938af8aef": 1,
|
|
177
177
|
"adae1329361a81a1": 1,
|
|
@@ -190,7 +190,6 @@
|
|
|
190
190
|
"bb269582b94ed42d": 1,
|
|
191
191
|
"bb3e6d0f7e7edccd": 1,
|
|
192
192
|
"bc9115be3a462930": 1,
|
|
193
|
-
"bccf0d5cc57c08df": 1,
|
|
194
193
|
"bd0ae42c13d5b4f9": 1,
|
|
195
194
|
"bd6c62a6e1c6a071": 1,
|
|
196
195
|
"bdb8ca169be36b80": 1,
|
|
@@ -207,10 +206,10 @@
|
|
|
207
206
|
"c9cf3c28bed33a5b": 1,
|
|
208
207
|
"cab590a1d04b5a05": 1,
|
|
209
208
|
"cbd444e9a6a9072e": 1,
|
|
210
|
-
"cd49f39b43d14635": 1,
|
|
211
209
|
"cebff7d1cc87f646": 1,
|
|
212
210
|
"cf6116d195199f8a": 1,
|
|
213
211
|
"cfd68135b744c27b": 1,
|
|
212
|
+
"d016035b3bf3340c": 1,
|
|
214
213
|
"d097f41a6f43e634": 1,
|
|
215
214
|
"d0da8714c4b7de30": 1,
|
|
216
215
|
"d17201496391f6cc": 1,
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f4ce84c: Add a shared catalogue of importable browsers and resolve the system default
|
|
8
|
+
(#114).
|
|
9
|
+
|
|
10
|
+
- `browser-sources.json` is a shared, data-driven catalogue of the browsers
|
|
11
|
+
Browser Commander can import from — Chrome and its Beta/Dev/Canary channels,
|
|
12
|
+
Edge channels, Brave variants, Chromium, Opera and Opera GX, Vivaldi, Arc,
|
|
13
|
+
Yandex, and Firefox with its LibreWolf, Waterfox, Zen, Floorp, Developer
|
|
14
|
+
Edition and Nightly forks. It records each browser's per-platform profile
|
|
15
|
+
roots, Chromium Safe Storage identity, and operating-system default
|
|
16
|
+
identifiers, and ships byte-identical to the Python and Rust packages.
|
|
17
|
+
- `browser: 'default'` (or `'auto'`) now resolves the operating-system default
|
|
18
|
+
web browser (macOS LaunchServices, Linux `xdg-settings`/`xdg-mime`, Windows
|
|
19
|
+
`UserChoice` ProgId) to a catalogue id, so an import follows whichever
|
|
20
|
+
browser a person actually uses.
|
|
21
|
+
- Profile discovery and migration classify a browser's engine family from the
|
|
22
|
+
catalogue instead of a hard-coded list, so every catalogued Chromium variant
|
|
23
|
+
and Firefox fork is recognised, and reading cookies honours a custom
|
|
24
|
+
`userDataDir`.
|
|
25
|
+
- New `cookies sources` and `profile sources` commands (and the
|
|
26
|
+
`cookies.sources`/`profile.sources` JSON-RPC methods) report which browsers
|
|
27
|
+
and profiles hold cookies — optionally for specific `--domain`s — as names
|
|
28
|
+
and counts only, never values.
|
|
29
|
+
- A `default`/`auto` migration scoped to `domains` falls back from a default
|
|
30
|
+
browser that holds no cookies for them to the installed profile holding the
|
|
31
|
+
most, and reports which one with a `default-browser-fallback` warning
|
|
32
|
+
(`resolveImportSource`). Migrating from Opera or Opera GX now reads their
|
|
33
|
+
single profile from the user data directory itself.
|
|
34
|
+
|
|
3
35
|
## 0.21.2
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,29 +1,20 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
|
|
3
3
|
import { runCommand as runSubprocess } from '../utilities/subprocess.js';
|
|
4
|
+
import { safeStorageIdentity } from './browser-sources.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
application: 'chromium',
|
|
18
|
-
folder: 'Chromium Keys',
|
|
19
|
-
service: 'Chromium Safe Storage',
|
|
20
|
-
},
|
|
21
|
-
edge: {
|
|
22
|
-
application: 'microsoft-edge',
|
|
23
|
-
folder: 'Microsoft Edge Keys',
|
|
24
|
-
service: 'Microsoft Edge Safe Storage',
|
|
25
|
-
},
|
|
26
|
-
};
|
|
6
|
+
/**
|
|
7
|
+
* The Chromium Safe Storage identity for a browser, from the shared
|
|
8
|
+
* `browser-sources.json` catalogue. Firefox-family browsers return
|
|
9
|
+
* `undefined` because they do not use OSCrypt.
|
|
10
|
+
*/
|
|
11
|
+
function safeStorageFor(browser) {
|
|
12
|
+
try {
|
|
13
|
+
return safeStorageIdentity(browser);
|
|
14
|
+
} catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
27
18
|
|
|
28
19
|
/**
|
|
29
20
|
* Run a credential tool (`security`, `secret-tool`, `kwallet-query`,
|
|
@@ -35,7 +26,7 @@ async function runCredentialCommand(command, args, environment) {
|
|
|
35
26
|
}
|
|
36
27
|
|
|
37
28
|
async function readLinuxSafeStoragePassword(browser, environment, runCommand) {
|
|
38
|
-
const identity =
|
|
29
|
+
const identity = safeStorageFor(browser);
|
|
39
30
|
try {
|
|
40
31
|
const password = await runCommand(
|
|
41
32
|
'secret-tool',
|
|
@@ -72,7 +63,7 @@ export async function readSafeStoragePassword({
|
|
|
72
63
|
environment = process.env,
|
|
73
64
|
runCredentialCommand: runCommand = runCredentialCommand,
|
|
74
65
|
}) {
|
|
75
|
-
const identity =
|
|
66
|
+
const identity = safeStorageFor(browser);
|
|
76
67
|
if (!identity) {
|
|
77
68
|
throw new Error(`No Safe Storage identity is known for ${browser}`);
|
|
78
69
|
}
|
|
@@ -24,12 +24,15 @@ import {
|
|
|
24
24
|
openSqliteDatabase,
|
|
25
25
|
preserveIntegerPrecision,
|
|
26
26
|
} from './browser-cookie-database.js';
|
|
27
|
+
import { browserFamily } from './browser-sources.js';
|
|
27
28
|
import {
|
|
28
29
|
findCookieDatabase,
|
|
30
|
+
isDefaultBrowserKeyword,
|
|
29
31
|
listBrowserProfiles,
|
|
30
|
-
normalizeCookieBrowser,
|
|
31
32
|
resolveBrowserProfile,
|
|
33
|
+
resolveSourceBrowser,
|
|
32
34
|
} from './browser-profiles.js';
|
|
35
|
+
import { resolveDefaultBrowser } from './default-browser.js';
|
|
33
36
|
|
|
34
37
|
const CHROME_EPOCH_OFFSET_SECONDS = 11_644_473_600n;
|
|
35
38
|
const MICROSECONDS_PER_SECOND = 1_000_000n;
|
|
@@ -266,20 +269,33 @@ export async function readBrowserCookiesWithDependencies(
|
|
|
266
269
|
if (!options || typeof options !== 'object') {
|
|
267
270
|
throw new TypeError('readBrowserCookies requires an options object');
|
|
268
271
|
}
|
|
269
|
-
const browser = normalizeCookieBrowser(options.browser);
|
|
270
272
|
const platform = dependencies.platform ?? process.platform;
|
|
271
273
|
const homeDir = dependencies.homeDir ?? os.homedir();
|
|
272
274
|
const environment = dependencies.environment ?? process.env;
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
profile: options.profile,
|
|
275
|
+
const runCommand = dependencies.runCommand;
|
|
276
|
+
const browser = await resolveSourceBrowser(options.browser, {
|
|
276
277
|
platform,
|
|
277
|
-
homeDir,
|
|
278
278
|
environment,
|
|
279
|
+
runCommand,
|
|
279
280
|
});
|
|
280
|
-
|
|
281
|
+
// A caller that already resolved the profile directory (for example a
|
|
282
|
+
// migration honouring a custom `userDataDir`) passes it as `profileDir`, so
|
|
283
|
+
// the reader does not re-resolve the default profile location.
|
|
284
|
+
const profilePath = options.profileDir
|
|
285
|
+
? options.profileDir
|
|
286
|
+
: (
|
|
287
|
+
await resolveBrowserProfile({
|
|
288
|
+
browser,
|
|
289
|
+
profile: options.profile,
|
|
290
|
+
platform,
|
|
291
|
+
homeDir,
|
|
292
|
+
environment,
|
|
293
|
+
runCommand,
|
|
294
|
+
})
|
|
295
|
+
).path;
|
|
296
|
+
const cookiePath = await findCookieDatabase(browser, profilePath, platform);
|
|
281
297
|
if (!cookiePath) {
|
|
282
|
-
throw new Error(`No cookie database exists in ${
|
|
298
|
+
throw new Error(`No cookie database exists in ${profilePath}`);
|
|
283
299
|
}
|
|
284
300
|
const cache = normalizeCookieCache(
|
|
285
301
|
options.cache,
|
|
@@ -288,7 +304,7 @@ export async function readBrowserCookiesWithDependencies(
|
|
|
288
304
|
);
|
|
289
305
|
const identity = JSON.stringify({
|
|
290
306
|
browser,
|
|
291
|
-
profile:
|
|
307
|
+
profile: profilePath,
|
|
292
308
|
domainFilter: options.domainFilter ?? null,
|
|
293
309
|
ignoreDecryptionErrors: options.ignoreDecryptionErrors === true,
|
|
294
310
|
});
|
|
@@ -306,7 +322,7 @@ export async function readBrowserCookiesWithDependencies(
|
|
|
306
322
|
const database = await openCookieDatabase(cookiePath);
|
|
307
323
|
let cookies;
|
|
308
324
|
try {
|
|
309
|
-
if (browser === 'firefox') {
|
|
325
|
+
if (browserFamily(browser) === 'firefox') {
|
|
310
326
|
cookies = mapFirefoxCookieRows(
|
|
311
327
|
readFirefoxRows(database, options.domainFilter)
|
|
312
328
|
);
|
|
@@ -323,7 +339,7 @@ export async function readBrowserCookiesWithDependencies(
|
|
|
323
339
|
ignoreDecryptionErrors: options.ignoreDecryptionErrors === true,
|
|
324
340
|
now,
|
|
325
341
|
platform,
|
|
326
|
-
profile,
|
|
342
|
+
profile: { path: profilePath },
|
|
327
343
|
readSafeStoragePassword:
|
|
328
344
|
dependencies.readSafeStoragePassword ?? readSafeStoragePassword,
|
|
329
345
|
readWindowsEncryptionKey:
|
|
@@ -343,6 +359,198 @@ export function readBrowserCookies(options) {
|
|
|
343
359
|
return readBrowserCookiesWithDependencies(options);
|
|
344
360
|
}
|
|
345
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Count cookies in a database by domain, without ever reading a cookie value.
|
|
364
|
+
* Only host names and row counts are touched, so this is safe to expose for a
|
|
365
|
+
* "which browser holds cookies for this domain" listing.
|
|
366
|
+
*/
|
|
367
|
+
function countCookiesByDomain(database, family, domains) {
|
|
368
|
+
const column = family === 'firefox' ? 'host' : 'host_key';
|
|
369
|
+
const table = family === 'firefox' ? 'moz_cookies' : 'cookies';
|
|
370
|
+
const countFor = (filter) => {
|
|
371
|
+
const query = filter
|
|
372
|
+
? `SELECT COUNT(*) AS n FROM ${table} WHERE ${column} LIKE ?`
|
|
373
|
+
: `SELECT COUNT(*) AS n FROM ${table}`;
|
|
374
|
+
const statement = database.prepare(query);
|
|
375
|
+
const row = filter ? statement.get(`%${filter}%`) : statement.get();
|
|
376
|
+
return Number(row?.n ?? 0);
|
|
377
|
+
};
|
|
378
|
+
const total = countFor(null);
|
|
379
|
+
if (!Array.isArray(domains) || domains.length === 0) {
|
|
380
|
+
return { total, byDomain: null };
|
|
381
|
+
}
|
|
382
|
+
const byDomain = {};
|
|
383
|
+
for (const domain of domains) {
|
|
384
|
+
byDomain[domain] = countFor(domain);
|
|
385
|
+
}
|
|
386
|
+
return { total, byDomain };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* List the installed browser profiles that hold cookies, with per-domain
|
|
391
|
+
* counts when `domains` is given. Values are never read or returned — this is
|
|
392
|
+
* the data behind the `cookies sources` command.
|
|
393
|
+
*
|
|
394
|
+
* @param {Object} [options]
|
|
395
|
+
* @param {string[]} [options.domains] - Restrict and count by these domains
|
|
396
|
+
* @param {string} [options.platform=process.platform]
|
|
397
|
+
* @param {string} [options.homeDir=os.homedir()]
|
|
398
|
+
* @param {Object} [options.environment=process.env]
|
|
399
|
+
* @returns {Promise<Array<Object>>} One entry per profile:
|
|
400
|
+
* `{browser, profile, path, isDefault, cookies, byDomain}`, plus `error`
|
|
401
|
+
* when the database could not be read
|
|
402
|
+
*/
|
|
403
|
+
export async function listCookieSources({
|
|
404
|
+
domains,
|
|
405
|
+
platform = process.platform,
|
|
406
|
+
homeDir = os.homedir(),
|
|
407
|
+
environment = process.env,
|
|
408
|
+
} = {}) {
|
|
409
|
+
const profiles = await listBrowserProfiles({
|
|
410
|
+
platform,
|
|
411
|
+
homeDir,
|
|
412
|
+
environment,
|
|
413
|
+
});
|
|
414
|
+
const sources = [];
|
|
415
|
+
for (const profile of profiles) {
|
|
416
|
+
const cookiePath = await findCookieDatabase(
|
|
417
|
+
profile.browser,
|
|
418
|
+
profile.path,
|
|
419
|
+
platform
|
|
420
|
+
);
|
|
421
|
+
if (!cookiePath) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const family = browserFamily(profile.browser);
|
|
425
|
+
let counts;
|
|
426
|
+
let error;
|
|
427
|
+
let database;
|
|
428
|
+
try {
|
|
429
|
+
database = await openCookieDatabase(cookiePath);
|
|
430
|
+
counts = countCookiesByDomain(database, family, domains);
|
|
431
|
+
} catch (openError) {
|
|
432
|
+
error = openError.message;
|
|
433
|
+
} finally {
|
|
434
|
+
database?.close();
|
|
435
|
+
}
|
|
436
|
+
if (error) {
|
|
437
|
+
sources.push({
|
|
438
|
+
browser: profile.browser,
|
|
439
|
+
profile: profile.name,
|
|
440
|
+
path: profile.path,
|
|
441
|
+
isDefault: profile.isDefault,
|
|
442
|
+
error,
|
|
443
|
+
});
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
// When filtering by domain, skip profiles that hold none of them.
|
|
447
|
+
const matchedCount = counts.byDomain
|
|
448
|
+
? Object.values(counts.byDomain).reduce((sum, n) => sum + n, 0)
|
|
449
|
+
: counts.total;
|
|
450
|
+
if (Array.isArray(domains) && domains.length > 0 && matchedCount === 0) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
sources.push({
|
|
454
|
+
browser: profile.browser,
|
|
455
|
+
profile: profile.name,
|
|
456
|
+
path: profile.path,
|
|
457
|
+
isDefault: profile.isDefault,
|
|
458
|
+
cookies: counts.total,
|
|
459
|
+
byDomain: counts.byDomain,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return sources;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function matchedCookies(source) {
|
|
466
|
+
return Object.values(source.byDomain ?? {}).reduce((sum, n) => sum + n, 0);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Pick the source browser for an import. A `default`/`auto` request scoped to
|
|
471
|
+
* `domains` uses the system default browser when it holds cookies for them
|
|
472
|
+
* and otherwise falls back to the installed browser profile holding the most,
|
|
473
|
+
* so "import my github.com sign-in" works whichever browser has it. Only
|
|
474
|
+
* names and counts are read (see `listCookieSources`), never cookie values.
|
|
475
|
+
*
|
|
476
|
+
* @param {Object} options
|
|
477
|
+
* @param {string} options.browser - Catalogue id, `default` or `auto`
|
|
478
|
+
* @param {string[]} [options.domains]
|
|
479
|
+
* @param {string} [options.platform=process.platform]
|
|
480
|
+
* @param {string} [options.homeDir=os.homedir()]
|
|
481
|
+
* @param {Object} [options.environment=process.env]
|
|
482
|
+
* @param {Function} [options.runCommand] - Injected for default-browser lookup
|
|
483
|
+
* @returns {Promise<Object>} `{browser, profile, warning}`: `profile` is the
|
|
484
|
+
* profile holding the cookies (when one was chosen) and `warning` is a
|
|
485
|
+
* migration-report warning explaining a fallback away from the system
|
|
486
|
+
* default (reason `default-browser-fallback` or `default-browser-unknown`),
|
|
487
|
+
* or `null`
|
|
488
|
+
*/
|
|
489
|
+
export async function resolveImportSource({
|
|
490
|
+
browser,
|
|
491
|
+
domains,
|
|
492
|
+
platform = process.platform,
|
|
493
|
+
homeDir = os.homedir(),
|
|
494
|
+
environment = process.env,
|
|
495
|
+
runCommand,
|
|
496
|
+
}) {
|
|
497
|
+
if (
|
|
498
|
+
!isDefaultBrowserKeyword(browser) ||
|
|
499
|
+
!Array.isArray(domains) ||
|
|
500
|
+
domains.length === 0
|
|
501
|
+
) {
|
|
502
|
+
return {
|
|
503
|
+
browser: await resolveSourceBrowser(browser, {
|
|
504
|
+
platform,
|
|
505
|
+
environment,
|
|
506
|
+
runCommand,
|
|
507
|
+
}),
|
|
508
|
+
profile: undefined,
|
|
509
|
+
warning: null,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
const systemDefault = await resolveDefaultBrowser({
|
|
513
|
+
platform,
|
|
514
|
+
environment,
|
|
515
|
+
runCommand,
|
|
516
|
+
});
|
|
517
|
+
const holders = (
|
|
518
|
+
await listCookieSources({ domains, platform, homeDir, environment })
|
|
519
|
+
).filter((source) => !source.error);
|
|
520
|
+
const fromDefault = holders.filter(
|
|
521
|
+
(source) => source.browser === systemDefault
|
|
522
|
+
);
|
|
523
|
+
const candidates = fromDefault.length > 0 ? fromDefault : holders;
|
|
524
|
+
if (candidates.length === 0) {
|
|
525
|
+
if (!systemDefault) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Could not determine the system default browser, and no installed browser holds cookies for ${domains.join(', ')}.`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
return { browser: systemDefault, profile: undefined, warning: null };
|
|
531
|
+
}
|
|
532
|
+
// The first profile with the most matching cookies; listing order breaks
|
|
533
|
+
// ties, so a browser's default profile wins over its others.
|
|
534
|
+
const best = candidates.reduce((chosen, source) =>
|
|
535
|
+
matchedCookies(source) > matchedCookies(chosen) ? source : chosen
|
|
536
|
+
);
|
|
537
|
+
let warning = null;
|
|
538
|
+
if (best.browser !== systemDefault) {
|
|
539
|
+
const reason = systemDefault
|
|
540
|
+
? `The default browser (${systemDefault}) holds no cookies for`
|
|
541
|
+
: 'Could not determine the default browser to read cookies for';
|
|
542
|
+
warning = {
|
|
543
|
+
type: 'source',
|
|
544
|
+
item: best.browser,
|
|
545
|
+
reason: systemDefault
|
|
546
|
+
? 'default-browser-fallback'
|
|
547
|
+
: 'default-browser-unknown',
|
|
548
|
+
detail: `${reason} ${domains.join(', ')}; imported from ${best.browser} instead.`,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
return { browser: best.browser, profile: best.profile, warning };
|
|
552
|
+
}
|
|
553
|
+
|
|
346
554
|
export {
|
|
347
555
|
clearBrowserCookieMemoryCache,
|
|
348
556
|
decryptChromiumCookie,
|