doordash-cli 0.4.0 → 0.4.2
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/CHANGELOG.md +12 -0
- package/README.md +5 -5
- package/dist/cli.js +4 -3
- package/dist/cli.test.js +7 -5
- package/dist/direct-api.d.ts +20 -1
- package/dist/direct-api.js +535 -59
- package/dist/direct-api.test.js +230 -8
- package/docs/examples.md +2 -2
- package/docs/install.md +5 -5
- package/man/dd-cli.1 +19 -10
- package/package.json +1 -1
package/dist/direct-api.js
CHANGED
|
@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
5
6
|
import { chromium } from "playwright";
|
|
6
7
|
import { getBrowserImportBlockPath, getCookiesPath, getStorageStatePath } from "./session-storage.js";
|
|
7
8
|
const BASE_URL = "https://www.doordash.com";
|
|
@@ -9,7 +10,168 @@ const AUTH_BOOTSTRAP_URL = `${BASE_URL}/home`;
|
|
|
9
10
|
const AUTH_BOOTSTRAP_TIMEOUT_MS = 180_000;
|
|
10
11
|
const AUTH_BOOTSTRAP_POLL_INTERVAL_MS = 2_000;
|
|
11
12
|
const AUTH_BOOTSTRAP_NO_DISCOVERY_GRACE_MS = 10_000;
|
|
13
|
+
const ATTACHED_BROWSER_CDP_REACHABILITY_TIMEOUT_MS = 2_000;
|
|
14
|
+
const ATTACHED_BROWSER_CDP_CONNECT_TIMEOUT_MS = 5_000;
|
|
12
15
|
const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36";
|
|
16
|
+
const LINUX_CHROMIUM_COOKIE_IMPORT_SCRIPT = String.raw `
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import sqlite3
|
|
20
|
+
import hashlib
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import secretstorage
|
|
25
|
+
from Crypto.Cipher import AES
|
|
26
|
+
except Exception as exc:
|
|
27
|
+
raise SystemExit(f"missing python support for Chromium cookie import: {exc}")
|
|
28
|
+
|
|
29
|
+
browser_label = os.environ.get("DD_BROWSER_LABEL", "Chromium")
|
|
30
|
+
user_data_dir = os.environ.get("DD_BROWSER_USER_DATA_DIR", "")
|
|
31
|
+
safe_storage_application = os.environ.get("DD_BROWSER_SAFE_STORAGE_APP", "")
|
|
32
|
+
|
|
33
|
+
if not user_data_dir or not safe_storage_application:
|
|
34
|
+
raise SystemExit("missing browser metadata")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def chromium_utc_to_unix_seconds(value):
|
|
38
|
+
if not value:
|
|
39
|
+
return -1
|
|
40
|
+
return max(-1, (int(value) / 1000000) - 11644473600)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def samesite_to_playwright(value):
|
|
44
|
+
mapping = {
|
|
45
|
+
-1: "Lax",
|
|
46
|
+
0: "None",
|
|
47
|
+
1: "Lax",
|
|
48
|
+
2: "Strict",
|
|
49
|
+
}
|
|
50
|
+
try:
|
|
51
|
+
return mapping.get(int(value), "Lax")
|
|
52
|
+
except Exception:
|
|
53
|
+
return "Lax"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_safe_storage_secret(application):
|
|
57
|
+
bus = secretstorage.dbus_init()
|
|
58
|
+
for collection in secretstorage.get_all_collections(bus):
|
|
59
|
+
for item in collection.get_all_items():
|
|
60
|
+
attrs = item.get_attributes()
|
|
61
|
+
if attrs.get("xdg:schema") == "chrome_libsecret_os_crypt_password_v2" and attrs.get("application") == application:
|
|
62
|
+
secret = item.get_secret()
|
|
63
|
+
return secret.decode("utf-8") if isinstance(secret, bytes) else str(secret)
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def decrypt_cookie_value(encrypted_value, host_key, secret):
|
|
68
|
+
payload = encrypted_value[3:] if encrypted_value.startswith((b"v10", b"v11")) else encrypted_value
|
|
69
|
+
key = hashlib.pbkdf2_hmac("sha1", secret.encode("utf-8"), b"saltysalt", 1, dklen=16)
|
|
70
|
+
decrypted = AES.new(key, AES.MODE_CBC, b" " * 16).decrypt(payload)
|
|
71
|
+
pad = decrypted[-1]
|
|
72
|
+
if isinstance(pad, str):
|
|
73
|
+
pad = ord(pad)
|
|
74
|
+
if 1 <= pad <= 16:
|
|
75
|
+
decrypted = decrypted[:-pad]
|
|
76
|
+
host_hash = hashlib.sha256(host_key.encode("utf-8")).digest()
|
|
77
|
+
if decrypted.startswith(host_hash):
|
|
78
|
+
decrypted = decrypted[len(host_hash):]
|
|
79
|
+
return decrypted.decode("utf-8")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def profile_names_for_user_data_dir(root):
|
|
83
|
+
local_state_path = os.path.join(root, "Local State")
|
|
84
|
+
entries = []
|
|
85
|
+
try:
|
|
86
|
+
with open(local_state_path, "r", encoding="utf-8") as handle:
|
|
87
|
+
info_cache = ((json.load(handle).get("profile") or {}).get("info_cache") or {})
|
|
88
|
+
for profile_name, info in info_cache.items():
|
|
89
|
+
if not isinstance(profile_name, str) or not profile_name.strip():
|
|
90
|
+
continue
|
|
91
|
+
active_time = 0.0
|
|
92
|
+
if isinstance(info, dict):
|
|
93
|
+
try:
|
|
94
|
+
active_time = float(info.get("active_time") or 0)
|
|
95
|
+
except Exception:
|
|
96
|
+
active_time = 0.0
|
|
97
|
+
entries.append((0 if profile_name == "Default" else 1, -active_time, profile_name))
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
entries.sort()
|
|
102
|
+
names = [profile_name for _, _, profile_name in entries]
|
|
103
|
+
if "Default" not in names:
|
|
104
|
+
names.append("Default")
|
|
105
|
+
return names
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
safe_storage_secret = get_safe_storage_secret(safe_storage_application)
|
|
109
|
+
if not safe_storage_secret:
|
|
110
|
+
print("[]")
|
|
111
|
+
raise SystemExit(0)
|
|
112
|
+
|
|
113
|
+
imports = []
|
|
114
|
+
for profile_name in profile_names_for_user_data_dir(user_data_dir):
|
|
115
|
+
cookies_db_path = os.path.join(user_data_dir, profile_name, "Cookies")
|
|
116
|
+
if not os.path.exists(cookies_db_path):
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
connection = None
|
|
120
|
+
try:
|
|
121
|
+
connection = sqlite3.connect(f"file:{cookies_db_path}?mode=ro", uri=True)
|
|
122
|
+
cursor = connection.cursor()
|
|
123
|
+
rows = cursor.execute(
|
|
124
|
+
"""
|
|
125
|
+
select host_key, name, encrypted_value, path, expires_utc, is_secure, is_httponly, samesite
|
|
126
|
+
from cookies
|
|
127
|
+
where host_key like '%doordash%'
|
|
128
|
+
order by host_key, name
|
|
129
|
+
"""
|
|
130
|
+
).fetchall()
|
|
131
|
+
except Exception:
|
|
132
|
+
if connection is not None:
|
|
133
|
+
connection.close()
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
cookies = []
|
|
137
|
+
for host_key, name, encrypted_value, path, expires_utc, is_secure, is_httponly, samesite in rows:
|
|
138
|
+
try:
|
|
139
|
+
decrypted_value = decrypt_cookie_value(encrypted_value, host_key, safe_storage_secret)
|
|
140
|
+
except Exception:
|
|
141
|
+
continue
|
|
142
|
+
cookies.append({
|
|
143
|
+
"name": name,
|
|
144
|
+
"value": decrypted_value,
|
|
145
|
+
"domain": host_key,
|
|
146
|
+
"path": path or "/",
|
|
147
|
+
"expires": chromium_utc_to_unix_seconds(expires_utc),
|
|
148
|
+
"httpOnly": bool(is_httponly),
|
|
149
|
+
"secure": bool(is_secure),
|
|
150
|
+
"sameSite": samesite_to_playwright(samesite),
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
connection.close()
|
|
154
|
+
if cookies:
|
|
155
|
+
imports.append({
|
|
156
|
+
"browserLabel": browser_label,
|
|
157
|
+
"profileName": profile_name,
|
|
158
|
+
"cookies": cookies,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
print(json.dumps(imports))
|
|
162
|
+
`;
|
|
163
|
+
const LINUX_CHROMIUM_COOKIE_IMPORT_BROWSERS = [
|
|
164
|
+
{
|
|
165
|
+
browserLabel: "Brave",
|
|
166
|
+
safeStorageApplication: "brave",
|
|
167
|
+
userDataDir: join(homedir(), ".config", "BraveSoftware", "Brave-Browser"),
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
browserLabel: "Google Chrome",
|
|
171
|
+
safeStorageApplication: "chrome",
|
|
172
|
+
userDataDir: join(homedir(), ".config", "google-chrome"),
|
|
173
|
+
},
|
|
174
|
+
];
|
|
13
175
|
const GRAPHQL_HEADERS = {
|
|
14
176
|
accept: "*/*",
|
|
15
177
|
"content-type": "application/json",
|
|
@@ -864,6 +1026,42 @@ function buildAuthBootstrapFailure(auth, message) {
|
|
|
864
1026
|
message,
|
|
865
1027
|
};
|
|
866
1028
|
}
|
|
1029
|
+
function canPromptForManagedBrowserConfirmation() {
|
|
1030
|
+
return Boolean(process.stdin.isTTY);
|
|
1031
|
+
}
|
|
1032
|
+
function createManagedBrowserManualConfirmationHandle() {
|
|
1033
|
+
if (!canPromptForManagedBrowserConfirmation()) {
|
|
1034
|
+
return {
|
|
1035
|
+
isEnabled: false,
|
|
1036
|
+
consumeRequested: () => false,
|
|
1037
|
+
close: () => { },
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
const rl = createInterface({
|
|
1041
|
+
input: process.stdin,
|
|
1042
|
+
output: process.stderr,
|
|
1043
|
+
terminal: true,
|
|
1044
|
+
});
|
|
1045
|
+
let requested = false;
|
|
1046
|
+
const onLine = () => {
|
|
1047
|
+
requested = true;
|
|
1048
|
+
};
|
|
1049
|
+
rl.on("line", onLine);
|
|
1050
|
+
return {
|
|
1051
|
+
isEnabled: true,
|
|
1052
|
+
consumeRequested: () => {
|
|
1053
|
+
if (!requested) {
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
requested = false;
|
|
1057
|
+
return true;
|
|
1058
|
+
},
|
|
1059
|
+
close: () => {
|
|
1060
|
+
rl.off("line", onLine);
|
|
1061
|
+
rl.close();
|
|
1062
|
+
},
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
867
1065
|
export async function checkAuthDirect() {
|
|
868
1066
|
const data = await session.graphql("consumer", CONSUMER_QUERY, {});
|
|
869
1067
|
return buildAuthResult(data.consumer ?? null);
|
|
@@ -891,11 +1089,11 @@ export async function bootstrapAuthSessionWithDeps(deps) {
|
|
|
891
1089
|
});
|
|
892
1090
|
const openedDefaultBrowser = openedAttachedBrowser ? false : await deps.openUrlInDefaultBrowser(AUTH_BOOTSTRAP_URL);
|
|
893
1091
|
deps.log(openedAttachedBrowser
|
|
894
|
-
? `Opened DoorDash in the
|
|
1092
|
+
? `Opened DoorDash in the attachable browser session I'm watching: ${AUTH_BOOTSTRAP_URL}`
|
|
895
1093
|
: openedDefaultBrowser
|
|
896
|
-
? `Found
|
|
897
|
-
: `Detected
|
|
898
|
-
deps.log(`Detected ${reachableCandidates.length}
|
|
1094
|
+
? `Found an attachable browser session, but couldn't drive it directly, so I opened DoorDash in your default browser: ${AUTH_BOOTSTRAP_URL}`
|
|
1095
|
+
: `Detected an attachable browser session, but couldn't open DoorDash automatically. Open this URL in that watched browser to continue: ${AUTH_BOOTSTRAP_URL}`);
|
|
1096
|
+
deps.log(`Detected ${reachableCandidates.length} attachable browser session(s). Finish the sign-in in that browser window and I'll import it automatically for up to ${Math.round(AUTH_BOOTSTRAP_TIMEOUT_MS / 1000)} seconds.`);
|
|
899
1097
|
const importedAfterWait = await deps.waitForAttachedBrowserSessionImport({
|
|
900
1098
|
timeoutMs: AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
901
1099
|
pollIntervalMs: AUTH_BOOTSTRAP_POLL_INTERVAL_MS,
|
|
@@ -903,29 +1101,43 @@ export async function bootstrapAuthSessionWithDeps(deps) {
|
|
|
903
1101
|
const auth = await deps.checkAuthDirect();
|
|
904
1102
|
if (importedAfterWait) {
|
|
905
1103
|
return auth.isLoggedIn
|
|
906
|
-
? buildAuthBootstrapSuccess(auth, "Opened DoorDash in
|
|
1104
|
+
? buildAuthBootstrapSuccess(auth, "Opened DoorDash in an attachable browser session, detected the signed-in consumer state, and saved it for direct API use.")
|
|
907
1105
|
: buildAuthBootstrapFailure(auth, "Detected browser session state in the watched browser, but the consumer still appears logged out or guest-only.");
|
|
908
1106
|
}
|
|
909
1107
|
return buildAuthBootstrapFailure(auth, openedAttachedBrowser || openedDefaultBrowser
|
|
910
|
-
? `Opened DoorDash and watched
|
|
911
|
-
: `Watched
|
|
912
|
-
}
|
|
913
|
-
|
|
1108
|
+
? `Opened DoorDash and watched attachable browser sessions for ${Math.round(AUTH_BOOTSTRAP_TIMEOUT_MS / 1000)} seconds, but no signed-in DoorDash session was imported. Finish the login in that watched browser and rerun dd-cli login.`
|
|
1109
|
+
: `Watched attachable browser sessions for ${Math.round(AUTH_BOOTSTRAP_TIMEOUT_MS / 1000)} seconds without importing a signed-in DoorDash session. Open ${AUTH_BOOTSTRAP_URL} manually in the watched browser, finish signing in, then rerun dd-cli login.`);
|
|
1110
|
+
}
|
|
1111
|
+
const desktopBrowserReuseGap = await deps.describeDesktopBrowserReuseGap().catch(() => null);
|
|
1112
|
+
if (desktopBrowserReuseGap) {
|
|
1113
|
+
deps.log(desktopBrowserReuseGap);
|
|
1114
|
+
}
|
|
1115
|
+
const manualManagedConfirmationAvailable = deps.canPromptForManagedBrowserConfirmation();
|
|
1116
|
+
deps.log("I couldn't find an attachable browser session I can reuse, so I'm opening a temporary Chromium login window that the CLI can watch directly.");
|
|
1117
|
+
deps.log(manualManagedConfirmationAvailable
|
|
1118
|
+
? `Finish signing in in that window. I'll keep checking automatically for up to ${Math.round(AUTH_BOOTSTRAP_TIMEOUT_MS / 1000)} seconds. If the page already shows you're signed in and the CLI still hasn't finished, press Enter here to force an immediate recheck.`
|
|
1119
|
+
: `Finish signing in in that window. I'll keep checking automatically for up to ${Math.round(AUTH_BOOTSTRAP_TIMEOUT_MS / 1000)} seconds.`);
|
|
914
1120
|
const managedAuth = await deps.waitForManagedBrowserLogin({
|
|
915
1121
|
targetUrl: AUTH_BOOTSTRAP_URL,
|
|
916
1122
|
timeoutMs: AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
917
1123
|
pollIntervalMs: AUTH_BOOTSTRAP_POLL_INTERVAL_MS,
|
|
1124
|
+
log: deps.log,
|
|
918
1125
|
});
|
|
919
|
-
if (managedAuth) {
|
|
920
|
-
return managedAuth.
|
|
921
|
-
?
|
|
922
|
-
:
|
|
1126
|
+
if (managedAuth.status === "completed") {
|
|
1127
|
+
return buildAuthBootstrapSuccess(managedAuth.auth, managedAuth.completion === "manual"
|
|
1128
|
+
? "Opened a temporary Chromium login window. After you pressed Enter to confirm the browser login was complete, the CLI rechecked the signed-in session there and saved it for direct API use."
|
|
1129
|
+
: "Opened a temporary Chromium login window, detected the signed-in session there automatically, and saved it for direct API use.");
|
|
1130
|
+
}
|
|
1131
|
+
if (managedAuth.status === "timed-out") {
|
|
1132
|
+
return buildAuthBootstrapFailure(managedAuth.auth, manualManagedConfirmationAvailable
|
|
1133
|
+
? `Opened a temporary Chromium login window and watched for ${Math.round(AUTH_BOOTSTRAP_TIMEOUT_MS / 1000)} seconds, but I still couldn't prove an authenticated DoorDash session. If the browser already looks signed in, press Enter sooner next time to force an immediate recheck, or rerun dd-cli login.`
|
|
1134
|
+
: `Opened a temporary Chromium login window and watched for ${Math.round(AUTH_BOOTSTRAP_TIMEOUT_MS / 1000)} seconds, but no authenticated DoorDash session was established.`);
|
|
923
1135
|
}
|
|
924
1136
|
const openedBrowser = await deps.openUrlInDefaultBrowser(AUTH_BOOTSTRAP_URL);
|
|
925
1137
|
deps.log(openedBrowser
|
|
926
1138
|
? `I couldn't launch the temporary Chromium login window, so I opened DoorDash in your default browser instead: ${AUTH_BOOTSTRAP_URL}`
|
|
927
1139
|
: `Couldn't open the temporary Chromium login window or your default browser automatically. Open this URL to continue: ${AUTH_BOOTSTRAP_URL}`);
|
|
928
|
-
deps.log("This environment still isn't exposing
|
|
1140
|
+
deps.log("This environment still isn't exposing an attachable browser session the CLI can import, so I won't keep you waiting for the full login timeout. Once you've exposed an attachable browser session, rerun `dd-cli login`.");
|
|
929
1141
|
const importedAfterGrace = await deps.waitForAttachedBrowserSessionImport({
|
|
930
1142
|
timeoutMs: AUTH_BOOTSTRAP_NO_DISCOVERY_GRACE_MS,
|
|
931
1143
|
pollIntervalMs: AUTH_BOOTSTRAP_POLL_INTERVAL_MS,
|
|
@@ -933,12 +1145,12 @@ export async function bootstrapAuthSessionWithDeps(deps) {
|
|
|
933
1145
|
const auth = await deps.checkAuthDirect();
|
|
934
1146
|
if (importedAfterGrace) {
|
|
935
1147
|
return auth.isLoggedIn
|
|
936
|
-
? buildAuthBootstrapSuccess(auth, "
|
|
1148
|
+
? buildAuthBootstrapSuccess(auth, "An attachable browser session appeared a few seconds later and was saved for direct API use.")
|
|
937
1149
|
: buildAuthBootstrapFailure(auth, "Detected browser session state after opening the browser, but the consumer still appears logged out or guest-only.");
|
|
938
1150
|
}
|
|
939
1151
|
return buildAuthBootstrapFailure(auth, openedBrowser
|
|
940
|
-
? "Opened DoorDash in your default browser, but this environment still isn't exposing
|
|
941
|
-
: "Couldn't open a watchable browser automatically, and this environment still isn't exposing
|
|
1152
|
+
? "Opened DoorDash in your default browser, but this environment still isn't exposing an attachable browser session the CLI can import. Finish signing in there, make an attachable browser session discoverable, then rerun `dd-cli login`."
|
|
1153
|
+
: "Couldn't open a watchable browser automatically, and this environment still isn't exposing an attachable browser session the CLI can import. Open the DoorDash home page manually, make an attachable browser session discoverable, then rerun `dd-cli login`.");
|
|
942
1154
|
}
|
|
943
1155
|
export async function bootstrapAuthSession() {
|
|
944
1156
|
return bootstrapAuthSessionWithDeps({
|
|
@@ -948,10 +1160,12 @@ export async function bootstrapAuthSession() {
|
|
|
948
1160
|
markBrowserImportAttempted: () => session.markBrowserImportAttempted(),
|
|
949
1161
|
getAttachedBrowserCdpCandidates,
|
|
950
1162
|
getReachableCdpCandidates,
|
|
1163
|
+
describeDesktopBrowserReuseGap,
|
|
951
1164
|
openUrlInAttachedBrowser,
|
|
952
1165
|
openUrlInDefaultBrowser,
|
|
953
1166
|
waitForAttachedBrowserSessionImport,
|
|
954
1167
|
waitForManagedBrowserLogin,
|
|
1168
|
+
canPromptForManagedBrowserConfirmation,
|
|
955
1169
|
checkAuthDirect,
|
|
956
1170
|
log: (message) => console.error(message),
|
|
957
1171
|
});
|
|
@@ -1660,8 +1874,45 @@ export function selectAttachedBrowserImportMode(input) {
|
|
|
1660
1874
|
}
|
|
1661
1875
|
return "skip";
|
|
1662
1876
|
}
|
|
1877
|
+
export function preferredBrowserSessionImportStrategies(platform) {
|
|
1878
|
+
return platform === "linux"
|
|
1879
|
+
? ["local-linux-chromium-profile", "attached-browser-cdp"]
|
|
1880
|
+
: ["attached-browser-cdp"];
|
|
1881
|
+
}
|
|
1663
1882
|
async function importBrowserSessionIfAvailable() {
|
|
1664
|
-
|
|
1883
|
+
for (const strategy of preferredBrowserSessionImportStrategies(process.platform)) {
|
|
1884
|
+
if (strategy === "local-linux-chromium-profile") {
|
|
1885
|
+
if (await importBrowserSessionFromLocalChromiumProfiles()) {
|
|
1886
|
+
return true;
|
|
1887
|
+
}
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
if (await importBrowserSessionFromCdpCandidates(await getAttachedBrowserCdpCandidates())) {
|
|
1891
|
+
return true;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
return false;
|
|
1895
|
+
}
|
|
1896
|
+
async function importBrowserSessionFromLocalChromiumProfiles() {
|
|
1897
|
+
if (process.platform !== "linux") {
|
|
1898
|
+
return false;
|
|
1899
|
+
}
|
|
1900
|
+
const originalArtifacts = await snapshotStoredSessionArtifacts();
|
|
1901
|
+
for (const browser of LINUX_CHROMIUM_COOKIE_IMPORT_BROWSERS) {
|
|
1902
|
+
const profileImports = await readLinuxChromiumCookieImports(browser);
|
|
1903
|
+
for (const profileImport of profileImports) {
|
|
1904
|
+
if (!hasDoorDashCookies(profileImport.cookies)) {
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
await writeStoredSessionArtifacts(profileImport.cookies);
|
|
1908
|
+
const persistedAuth = await getPersistedAuthDirect();
|
|
1909
|
+
if (persistedAuth?.isLoggedIn) {
|
|
1910
|
+
return true;
|
|
1911
|
+
}
|
|
1912
|
+
await restoreStoredSessionArtifacts(originalArtifacts);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return false;
|
|
1665
1916
|
}
|
|
1666
1917
|
async function importBrowserSessionFromCdpCandidates(candidates) {
|
|
1667
1918
|
for (const cdpUrl of candidates) {
|
|
@@ -1671,7 +1922,7 @@ async function importBrowserSessionFromCdpCandidates(candidates) {
|
|
|
1671
1922
|
let browser = null;
|
|
1672
1923
|
let tempPage = null;
|
|
1673
1924
|
try {
|
|
1674
|
-
browser = await chromium.connectOverCDP(cdpUrl);
|
|
1925
|
+
browser = await chromium.connectOverCDP(cdpUrl, { timeout: ATTACHED_BROWSER_CDP_CONNECT_TIMEOUT_MS });
|
|
1675
1926
|
const context = browser.contexts()[0];
|
|
1676
1927
|
if (!context) {
|
|
1677
1928
|
continue;
|
|
@@ -1731,6 +1982,26 @@ async function fetchConsumerViaPage(page) {
|
|
|
1731
1982
|
});
|
|
1732
1983
|
return parseGraphQlResponse("attachedBrowserConsumerImport", raw.status, raw.text);
|
|
1733
1984
|
}
|
|
1985
|
+
async function readLiveManagedBrowserAuth(page) {
|
|
1986
|
+
if (!page || page.isClosed()) {
|
|
1987
|
+
return buildAuthResult(null);
|
|
1988
|
+
}
|
|
1989
|
+
const consumerData = await fetchConsumerViaPage(page).catch(() => null);
|
|
1990
|
+
return buildAuthResult(consumerData?.consumer ?? null);
|
|
1991
|
+
}
|
|
1992
|
+
async function confirmManagedBrowserAuth(context, page) {
|
|
1993
|
+
const liveAuth = await readLiveManagedBrowserAuth(page);
|
|
1994
|
+
if (liveAuth.isLoggedIn) {
|
|
1995
|
+
if (context) {
|
|
1996
|
+
await saveContextState(context).catch(() => { });
|
|
1997
|
+
}
|
|
1998
|
+
return liveAuth;
|
|
1999
|
+
}
|
|
2000
|
+
if (context) {
|
|
2001
|
+
await saveContextState(context).catch(() => { });
|
|
2002
|
+
}
|
|
2003
|
+
return (await getPersistedAuthDirect().catch(() => null)) ?? liveAuth;
|
|
2004
|
+
}
|
|
1734
2005
|
async function saveContextState(context, cookies = null) {
|
|
1735
2006
|
const storageStatePath = getStorageStatePath();
|
|
1736
2007
|
await ensureConfigDir();
|
|
@@ -1742,42 +2013,16 @@ async function getPersistedAuthDirect() {
|
|
|
1742
2013
|
if (!(await hasPersistedSessionArtifacts())) {
|
|
1743
2014
|
return null;
|
|
1744
2015
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
let page = null;
|
|
2016
|
+
await session.close().catch(() => { });
|
|
2017
|
+
session.markBrowserImportAttempted();
|
|
1748
2018
|
try {
|
|
1749
|
-
|
|
1750
|
-
headless: true,
|
|
1751
|
-
args: ["--disable-blink-features=AutomationControlled", "--no-sandbox", "--disable-setuid-sandbox"],
|
|
1752
|
-
});
|
|
1753
|
-
const storageStatePath = getStorageStatePath();
|
|
1754
|
-
const hasStorage = await hasStorageState();
|
|
1755
|
-
context = await browser.newContext({
|
|
1756
|
-
userAgent: DEFAULT_USER_AGENT,
|
|
1757
|
-
locale: "en-US",
|
|
1758
|
-
viewport: { width: 1280, height: 900 },
|
|
1759
|
-
...(hasStorage ? { storageState: storageStatePath } : {}),
|
|
1760
|
-
});
|
|
1761
|
-
if (!hasStorage) {
|
|
1762
|
-
const cookies = await readStoredCookies();
|
|
1763
|
-
if (cookies.length === 0) {
|
|
1764
|
-
return null;
|
|
1765
|
-
}
|
|
1766
|
-
await context.addCookies(cookies);
|
|
1767
|
-
}
|
|
1768
|
-
page = await context.newPage();
|
|
1769
|
-
await page.goto(`${BASE_URL}/home`, { waitUntil: "domcontentloaded", timeout: 90_000 }).catch(() => { });
|
|
1770
|
-
await page.waitForTimeout(1_000);
|
|
1771
|
-
const consumerData = await fetchConsumerViaPage(page).catch(() => null);
|
|
1772
|
-
return buildAuthResult(consumerData?.consumer ?? null);
|
|
2019
|
+
return await checkAuthDirect();
|
|
1773
2020
|
}
|
|
1774
2021
|
catch {
|
|
1775
2022
|
return null;
|
|
1776
2023
|
}
|
|
1777
2024
|
finally {
|
|
1778
|
-
await
|
|
1779
|
-
await context?.close().catch(() => { });
|
|
1780
|
-
await browser?.close().catch(() => { });
|
|
2025
|
+
await session.close().catch(() => { });
|
|
1781
2026
|
}
|
|
1782
2027
|
}
|
|
1783
2028
|
async function validatePersistedDirectSessionArtifacts() {
|
|
@@ -1869,7 +2114,7 @@ async function openUrlInDefaultBrowser(targetUrl) {
|
|
|
1869
2114
|
async function openUrlInAttachedBrowser(input) {
|
|
1870
2115
|
let browser = null;
|
|
1871
2116
|
try {
|
|
1872
|
-
browser = await chromium.connectOverCDP(input.cdpUrl);
|
|
2117
|
+
browser = await chromium.connectOverCDP(input.cdpUrl, { timeout: ATTACHED_BROWSER_CDP_CONNECT_TIMEOUT_MS });
|
|
1873
2118
|
const context = browser.contexts()[0];
|
|
1874
2119
|
if (!context) {
|
|
1875
2120
|
return false;
|
|
@@ -1893,6 +2138,7 @@ async function waitForManagedBrowserLogin(input) {
|
|
|
1893
2138
|
let browser = null;
|
|
1894
2139
|
let context = null;
|
|
1895
2140
|
let page = null;
|
|
2141
|
+
const manualConfirmation = createManagedBrowserManualConfirmationHandle();
|
|
1896
2142
|
try {
|
|
1897
2143
|
browser = await chromium.launch({
|
|
1898
2144
|
headless: false,
|
|
@@ -1917,10 +2163,25 @@ async function waitForManagedBrowserLogin(input) {
|
|
|
1917
2163
|
await page.bringToFront().catch(() => { });
|
|
1918
2164
|
const deadline = Date.now() + input.timeoutMs;
|
|
1919
2165
|
while (Date.now() <= deadline) {
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
return
|
|
2166
|
+
const liveAuth = await readLiveManagedBrowserAuth(page);
|
|
2167
|
+
if (liveAuth.isLoggedIn) {
|
|
2168
|
+
await saveContextState(context).catch(() => { });
|
|
2169
|
+
return {
|
|
2170
|
+
status: "completed",
|
|
2171
|
+
completion: "automatic",
|
|
2172
|
+
auth: liveAuth,
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
if (manualConfirmation.consumeRequested()) {
|
|
2176
|
+
const confirmedAuth = await confirmManagedBrowserAuth(context, page);
|
|
2177
|
+
if (confirmedAuth.isLoggedIn) {
|
|
2178
|
+
return {
|
|
2179
|
+
status: "completed",
|
|
2180
|
+
completion: "manual",
|
|
2181
|
+
auth: confirmedAuth,
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
input.log("Still not seeing an authenticated DoorDash session yet. Finish signing in in the opened browser window, then press Enter again or keep waiting for automatic detection.");
|
|
1924
2185
|
}
|
|
1925
2186
|
if (page.isClosed()) {
|
|
1926
2187
|
break;
|
|
@@ -1930,16 +2191,27 @@ async function waitForManagedBrowserLogin(input) {
|
|
|
1930
2191
|
}
|
|
1931
2192
|
await wait(input.pollIntervalMs);
|
|
1932
2193
|
}
|
|
1933
|
-
await
|
|
1934
|
-
|
|
2194
|
+
const finalAuth = await confirmManagedBrowserAuth(context, page);
|
|
2195
|
+
if (finalAuth.isLoggedIn) {
|
|
2196
|
+
return {
|
|
2197
|
+
status: "completed",
|
|
2198
|
+
completion: "automatic",
|
|
2199
|
+
auth: finalAuth,
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
return {
|
|
2203
|
+
status: "timed-out",
|
|
2204
|
+
auth: finalAuth,
|
|
2205
|
+
};
|
|
1935
2206
|
}
|
|
1936
2207
|
catch (error) {
|
|
1937
2208
|
if (isPlaywrightBrowserInstallMissingError(error)) {
|
|
1938
|
-
return
|
|
2209
|
+
return { status: "launch-failed" };
|
|
1939
2210
|
}
|
|
1940
|
-
return
|
|
2211
|
+
return { status: "launch-failed" };
|
|
1941
2212
|
}
|
|
1942
2213
|
finally {
|
|
2214
|
+
manualConfirmation.close();
|
|
1943
2215
|
await page?.close().catch(() => { });
|
|
1944
2216
|
await context?.close().catch(() => { });
|
|
1945
2217
|
await browser?.close().catch(() => { });
|
|
@@ -1952,6 +2224,40 @@ function isPlaywrightBrowserInstallMissingError(error) {
|
|
|
1952
2224
|
const message = error.message.toLowerCase();
|
|
1953
2225
|
return message.includes("executable doesn't exist") || message.includes("please run the following command") || message.includes("playwright install");
|
|
1954
2226
|
}
|
|
2227
|
+
const KNOWN_DESKTOP_BROWSERS = [
|
|
2228
|
+
{
|
|
2229
|
+
label: "Brave",
|
|
2230
|
+
processMatchers: [/\bbrave-browser(?:-stable)?\b/i, /\/brave(?:$|\s)/i],
|
|
2231
|
+
devToolsActivePortPaths: [
|
|
2232
|
+
join(homedir(), ".config", "BraveSoftware", "Brave-Browser", "DevToolsActivePort"),
|
|
2233
|
+
join(homedir(), "Library", "Application Support", "BraveSoftware", "Brave-Browser", "DevToolsActivePort"),
|
|
2234
|
+
],
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
label: "Google Chrome",
|
|
2238
|
+
processMatchers: [/\bgoogle-chrome(?:-stable|-beta|-unstable)?\b/i, /\/google-chrome(?:$|\s)/i],
|
|
2239
|
+
devToolsActivePortPaths: [
|
|
2240
|
+
join(homedir(), ".config", "google-chrome", "DevToolsActivePort"),
|
|
2241
|
+
join(homedir(), "Library", "Application Support", "Google", "Chrome", "DevToolsActivePort"),
|
|
2242
|
+
],
|
|
2243
|
+
},
|
|
2244
|
+
{
|
|
2245
|
+
label: "Microsoft Edge",
|
|
2246
|
+
processMatchers: [/\bmicrosoft-edge(?:-stable|-beta|-dev)?\b/i, /\/microsoft-edge(?:$|\s)/i],
|
|
2247
|
+
devToolsActivePortPaths: [
|
|
2248
|
+
join(homedir(), ".config", "microsoft-edge", "DevToolsActivePort"),
|
|
2249
|
+
join(homedir(), "Library", "Application Support", "Microsoft Edge", "DevToolsActivePort"),
|
|
2250
|
+
],
|
|
2251
|
+
},
|
|
2252
|
+
{
|
|
2253
|
+
label: "Chromium",
|
|
2254
|
+
processMatchers: [/\bchromium-browser\b/i, /\bchromium\b/i],
|
|
2255
|
+
devToolsActivePortPaths: [
|
|
2256
|
+
join(homedir(), ".config", "chromium", "DevToolsActivePort"),
|
|
2257
|
+
join(homedir(), "Library", "Application Support", "Chromium", "DevToolsActivePort"),
|
|
2258
|
+
],
|
|
2259
|
+
},
|
|
2260
|
+
];
|
|
1955
2261
|
function normalizeCdpCandidate(value) {
|
|
1956
2262
|
return value.trim().replace(/\/$/, "");
|
|
1957
2263
|
}
|
|
@@ -2001,6 +2307,91 @@ function wait(ms) {
|
|
|
2001
2307
|
setTimeout(resolve, ms);
|
|
2002
2308
|
});
|
|
2003
2309
|
}
|
|
2310
|
+
function detectRunningDesktopBrowser(processCommands) {
|
|
2311
|
+
const filteredCommands = processCommands
|
|
2312
|
+
.map((command) => command.trim())
|
|
2313
|
+
.filter((command) => command.length > 0)
|
|
2314
|
+
.filter((command) => !/chrome-devtools-mcp/i.test(command));
|
|
2315
|
+
for (const browser of KNOWN_DESKTOP_BROWSERS) {
|
|
2316
|
+
const preferredMatch = filteredCommands.some((command) => !/--type=|crashpad|zygote/i.test(command) && browser.processMatchers.some((matcher) => matcher.test(command)));
|
|
2317
|
+
if (preferredMatch) {
|
|
2318
|
+
return browser;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
for (const browser of KNOWN_DESKTOP_BROWSERS) {
|
|
2322
|
+
if (filteredCommands.some((command) => browser.processMatchers.some((matcher) => matcher.test(command)))) {
|
|
2323
|
+
return browser;
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
function hasRemoteDebuggingSignal(processCommands) {
|
|
2329
|
+
return processCommands.some((command) => /--remote-debugging-(?:port|pipe)(?:=|\b)/i.test(command));
|
|
2330
|
+
}
|
|
2331
|
+
export function summarizeDesktopBrowserReuseGap(input) {
|
|
2332
|
+
const browser = detectRunningDesktopBrowser(input.processCommands);
|
|
2333
|
+
if (!browser) {
|
|
2334
|
+
return null;
|
|
2335
|
+
}
|
|
2336
|
+
if (hasRemoteDebuggingSignal(input.processCommands) || input.hasAnyDevToolsActivePort) {
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2339
|
+
return `I can see ${browser.label} is already running on this desktop, but dd-cli still couldn't reuse it automatically. It is not exposing an attachable browser automation session right now, and no importable signed-in DoorDash browser profile state was found.`;
|
|
2340
|
+
}
|
|
2341
|
+
async function captureCommandStdout(command, args, options = {}) {
|
|
2342
|
+
return await new Promise((resolve, reject) => {
|
|
2343
|
+
const child = spawn(command, args, {
|
|
2344
|
+
env: options.env,
|
|
2345
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2346
|
+
});
|
|
2347
|
+
const stdout = [];
|
|
2348
|
+
const stderr = [];
|
|
2349
|
+
child.once("error", reject);
|
|
2350
|
+
child.stdout?.on("data", (chunk) => {
|
|
2351
|
+
stdout.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2352
|
+
});
|
|
2353
|
+
child.stderr?.on("data", (chunk) => {
|
|
2354
|
+
stderr.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2355
|
+
});
|
|
2356
|
+
child.stdin?.end(options.stdin ?? "");
|
|
2357
|
+
child.once("close", (code) => {
|
|
2358
|
+
if (code === 0) {
|
|
2359
|
+
resolve(Buffer.concat(stdout).toString("utf8"));
|
|
2360
|
+
return;
|
|
2361
|
+
}
|
|
2362
|
+
const stderrText = Buffer.concat(stderr).toString("utf8").trim();
|
|
2363
|
+
reject(new Error(`${command} exited with code ${code ?? "null"}${stderrText ? `: ${stderrText}` : ""}`));
|
|
2364
|
+
});
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2367
|
+
async function listProcessCommands(targetPlatform = process.platform) {
|
|
2368
|
+
if (targetPlatform === "win32") {
|
|
2369
|
+
return [];
|
|
2370
|
+
}
|
|
2371
|
+
const args = targetPlatform === "darwin" ? ["-axo", "command="] : ["-eo", "command="];
|
|
2372
|
+
const stdout = await captureCommandStdout("ps", args).catch(() => "");
|
|
2373
|
+
return stdout
|
|
2374
|
+
.split(/\r?\n/)
|
|
2375
|
+
.map((command) => command.trim())
|
|
2376
|
+
.filter((command) => command.length > 0);
|
|
2377
|
+
}
|
|
2378
|
+
async function hasAnyKnownDevToolsActivePort() {
|
|
2379
|
+
for (const browser of KNOWN_DESKTOP_BROWSERS) {
|
|
2380
|
+
for (const path of browser.devToolsActivePortPaths) {
|
|
2381
|
+
if ((await readFile(path, "utf8").catch(() => null)) !== null) {
|
|
2382
|
+
return true;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
return false;
|
|
2387
|
+
}
|
|
2388
|
+
async function describeDesktopBrowserReuseGap() {
|
|
2389
|
+
const processCommands = await listProcessCommands();
|
|
2390
|
+
return summarizeDesktopBrowserReuseGap({
|
|
2391
|
+
processCommands,
|
|
2392
|
+
hasAnyDevToolsActivePort: await hasAnyKnownDevToolsActivePort(),
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2004
2395
|
async function readOpenClawBrowserConfigCandidates(input) {
|
|
2005
2396
|
try {
|
|
2006
2397
|
const raw = await readFile(join(homedir(), ".openclaw", "openclaw.json"), "utf8");
|
|
@@ -2021,7 +2412,9 @@ async function readOpenClawBrowserConfigCandidates(input) {
|
|
|
2021
2412
|
}
|
|
2022
2413
|
async function isCdpEndpointReachable(cdpUrl) {
|
|
2023
2414
|
try {
|
|
2024
|
-
const response = await fetch(`${cdpUrl.replace(/\/$/, "")}/json/version
|
|
2415
|
+
const response = await fetch(`${cdpUrl.replace(/\/$/, "")}/json/version`, {
|
|
2416
|
+
signal: AbortSignal.timeout(ATTACHED_BROWSER_CDP_REACHABILITY_TIMEOUT_MS),
|
|
2417
|
+
});
|
|
2025
2418
|
return response.ok;
|
|
2026
2419
|
}
|
|
2027
2420
|
catch {
|
|
@@ -2656,6 +3049,89 @@ function truncate(value, length) {
|
|
|
2656
3049
|
async function ensureConfigDir() {
|
|
2657
3050
|
await mkdir(dirname(getCookiesPath()), { recursive: true });
|
|
2658
3051
|
}
|
|
3052
|
+
async function snapshotStoredSessionArtifacts() {
|
|
3053
|
+
const [cookiesRaw, storageStateRaw] = await Promise.all([
|
|
3054
|
+
readFile(getCookiesPath(), "utf8").catch(() => null),
|
|
3055
|
+
readFile(getStorageStatePath(), "utf8").catch(() => null),
|
|
3056
|
+
]);
|
|
3057
|
+
return { cookiesRaw, storageStateRaw };
|
|
3058
|
+
}
|
|
3059
|
+
async function restoreStoredSessionArtifacts(snapshot) {
|
|
3060
|
+
await ensureConfigDir();
|
|
3061
|
+
if (snapshot.cookiesRaw === null) {
|
|
3062
|
+
await rm(getCookiesPath(), { force: true }).catch(() => { });
|
|
3063
|
+
}
|
|
3064
|
+
else {
|
|
3065
|
+
await writeFile(getCookiesPath(), snapshot.cookiesRaw);
|
|
3066
|
+
}
|
|
3067
|
+
if (snapshot.storageStateRaw === null) {
|
|
3068
|
+
await rm(getStorageStatePath(), { force: true }).catch(() => { });
|
|
3069
|
+
}
|
|
3070
|
+
else {
|
|
3071
|
+
await writeFile(getStorageStatePath(), snapshot.storageStateRaw);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
async function writeStoredSessionArtifacts(cookies) {
|
|
3075
|
+
await ensureConfigDir();
|
|
3076
|
+
await writeFile(getCookiesPath(), JSON.stringify(cookies, null, 2));
|
|
3077
|
+
await writeFile(getStorageStatePath(), JSON.stringify({ cookies, origins: [] }, null, 2));
|
|
3078
|
+
}
|
|
3079
|
+
async function readLinuxChromiumCookieImports(input) {
|
|
3080
|
+
try {
|
|
3081
|
+
const stdout = await captureCommandStdout("python3", ["-c", LINUX_CHROMIUM_COOKIE_IMPORT_SCRIPT], {
|
|
3082
|
+
env: {
|
|
3083
|
+
...process.env,
|
|
3084
|
+
DD_BROWSER_LABEL: input.browserLabel,
|
|
3085
|
+
DD_BROWSER_SAFE_STORAGE_APP: input.safeStorageApplication,
|
|
3086
|
+
DD_BROWSER_USER_DATA_DIR: input.userDataDir,
|
|
3087
|
+
},
|
|
3088
|
+
});
|
|
3089
|
+
const parsed = JSON.parse(stdout);
|
|
3090
|
+
if (!Array.isArray(parsed)) {
|
|
3091
|
+
return [];
|
|
3092
|
+
}
|
|
3093
|
+
return parsed.flatMap((entry) => {
|
|
3094
|
+
const object = asObject(entry);
|
|
3095
|
+
const browserLabel = typeof object.browserLabel === "string" ? object.browserLabel.trim() : "";
|
|
3096
|
+
const profileName = typeof object.profileName === "string" ? object.profileName.trim() : "";
|
|
3097
|
+
if (!browserLabel || !profileName || !Array.isArray(object.cookies)) {
|
|
3098
|
+
return [];
|
|
3099
|
+
}
|
|
3100
|
+
const cookies = object.cookies.flatMap((cookie) => {
|
|
3101
|
+
const parsedCookie = asObject(cookie);
|
|
3102
|
+
const name = typeof parsedCookie.name === "string" ? parsedCookie.name : "";
|
|
3103
|
+
const value = typeof parsedCookie.value === "string" ? parsedCookie.value : "";
|
|
3104
|
+
const domain = typeof parsedCookie.domain === "string" ? parsedCookie.domain : "";
|
|
3105
|
+
const path = typeof parsedCookie.path === "string" && parsedCookie.path ? parsedCookie.path : "/";
|
|
3106
|
+
const sameSiteRaw = typeof parsedCookie.sameSite === "string" ? parsedCookie.sameSite : "Lax";
|
|
3107
|
+
const sameSite = sameSiteRaw === "Strict" || sameSiteRaw === "None" || sameSiteRaw === "Lax" ? sameSiteRaw : "Lax";
|
|
3108
|
+
const expires = typeof parsedCookie.expires === "number" && Number.isFinite(parsedCookie.expires) ? parsedCookie.expires : -1;
|
|
3109
|
+
if (!name || !domain) {
|
|
3110
|
+
return [];
|
|
3111
|
+
}
|
|
3112
|
+
return [
|
|
3113
|
+
{
|
|
3114
|
+
name,
|
|
3115
|
+
value,
|
|
3116
|
+
domain,
|
|
3117
|
+
path,
|
|
3118
|
+
expires,
|
|
3119
|
+
httpOnly: Boolean(parsedCookie.httpOnly),
|
|
3120
|
+
secure: Boolean(parsedCookie.secure),
|
|
3121
|
+
sameSite,
|
|
3122
|
+
},
|
|
3123
|
+
];
|
|
3124
|
+
});
|
|
3125
|
+
if (cookies.length === 0) {
|
|
3126
|
+
return [];
|
|
3127
|
+
}
|
|
3128
|
+
return [{ browserLabel, profileName, cookies }];
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
catch {
|
|
3132
|
+
return [];
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
2659
3135
|
async function hasBlockedBrowserImport() {
|
|
2660
3136
|
try {
|
|
2661
3137
|
await readFile(getBrowserImportBlockPath(), "utf8");
|