@steipete/oracle 0.7.6 → 0.8.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.
@@ -1,10 +1,11 @@
1
1
  import { COOKIE_URLS } from './constants.js';
2
- import { loadChromeCookies } from './chromeCookies.js';
2
+ import { getCookies } from '@steipete/sweet-cookie';
3
3
  export class ChromeCookieSyncError extends Error {
4
4
  }
5
5
  export async function syncCookies(Network, url, profile, logger, options = {}) {
6
6
  const { allowErrors = false, filterNames, inlineCookies, cookiePath } = options;
7
7
  try {
8
+ // Learned: inline cookies are the most deterministic (avoid Keychain + profile ambiguity).
8
9
  const cookies = inlineCookies?.length
9
10
  ? normalizeInlineCookies(inlineCookies, new URL(url).hostname)
10
11
  : await readChromeCookies(url, profile, filterNames ?? undefined, cookiePath ?? undefined);
@@ -15,6 +16,7 @@ export async function syncCookies(Network, url, profile, logger, options = {}) {
15
16
  for (const cookie of cookies) {
16
17
  const cookieWithUrl = attachUrl(cookie, url);
17
18
  try {
19
+ // Learned: CDP will silently drop cookies without a url; always attach one.
18
20
  const result = await Network.setCookie(cookieWithUrl);
19
21
  if (result?.success) {
20
22
  applied += 1;
@@ -37,23 +39,31 @@ export async function syncCookies(Network, url, profile, logger, options = {}) {
37
39
  }
38
40
  }
39
41
  async function readChromeCookies(url, profile, filterNames, cookiePath) {
40
- const urlsToCheck = Array.from(new Set([stripQuery(url), ...COOKIE_URLS]));
42
+ const origins = Array.from(new Set([stripQuery(url), ...COOKIE_URLS]));
43
+ const chromeProfile = cookiePath ?? profile ?? undefined;
44
+ const timeoutMs = readDuration('ORACLE_COOKIE_LOAD_TIMEOUT_MS', 5_000);
45
+ // Learned: read from multiple origins to capture auth cookies that land on chat.openai.com + atlas.
46
+ const { cookies, warnings } = await getCookies({
47
+ url,
48
+ origins,
49
+ names: filterNames?.length ? filterNames : undefined,
50
+ browsers: ['chrome'],
51
+ mode: 'merge',
52
+ chromeProfile,
53
+ timeoutMs,
54
+ });
55
+ if (process.env.ORACLE_DEBUG_COOKIES === '1' && warnings.length) {
56
+ // eslint-disable-next-line no-console
57
+ console.log(`[cookies] sweet-cookie warnings:\n- ${warnings.join('\n- ')}`);
58
+ }
41
59
  const merged = new Map();
42
- const allowlist = normalizeCookieNames(filterNames);
43
- for (const candidateUrl of urlsToCheck) {
44
- const cookies = await loadChromeCookies({
45
- targetUrl: candidateUrl,
46
- profile: profile ?? undefined,
47
- explicitCookiePath: cookiePath ?? undefined,
48
- filterNames: allowlist ?? undefined,
49
- });
50
- const fallbackHostname = new URL(candidateUrl).hostname;
51
- for (const cookie of cookies) {
52
- const key = `${cookie.domain ?? fallbackHostname}:${cookie.name}`;
53
- if (!merged.has(key)) {
54
- merged.set(key, cookie);
55
- }
56
- }
60
+ for (const cookie of cookies) {
61
+ const normalized = toCdpCookie(cookie);
62
+ if (!normalized)
63
+ continue;
64
+ const key = `${normalized.domain ?? ''}:${normalized.name}`;
65
+ if (!merged.has(key))
66
+ merged.set(key, normalized);
57
67
  }
58
68
  return Array.from(merged.values());
59
69
  }
@@ -62,15 +72,17 @@ function normalizeInlineCookies(rawCookies, fallbackHost) {
62
72
  for (const cookie of rawCookies) {
63
73
  if (!cookie?.name)
64
74
  continue;
75
+ // Learned: inline cookies may omit url/domain; default to current host with a safe path.
65
76
  const normalized = {
66
- ...cookie,
67
77
  name: cookie.name,
68
78
  value: cookie.value ?? '',
79
+ url: cookie.url,
69
80
  domain: cookie.domain ?? fallbackHost,
70
81
  path: cookie.path ?? '/',
71
82
  expires: normalizeExpiration(cookie.expires),
72
83
  secure: cookie.secure ?? true,
73
84
  httpOnly: cookie.httpOnly ?? false,
85
+ sameSite: cookie.sameSite,
74
86
  };
75
87
  const key = `${normalized.domain ?? fallbackHost}:${normalized.name}`;
76
88
  if (!merged.has(key)) {
@@ -79,11 +91,23 @@ function normalizeInlineCookies(rawCookies, fallbackHost) {
79
91
  }
80
92
  return Array.from(merged.values());
81
93
  }
82
- function normalizeCookieNames(names) {
83
- if (!names || names.length === 0) {
94
+ function toCdpCookie(cookie) {
95
+ if (!cookie?.name)
84
96
  return null;
97
+ const out = {
98
+ name: cookie.name,
99
+ value: cookie.value,
100
+ domain: cookie.domain,
101
+ path: cookie.path ?? '/',
102
+ secure: cookie.secure ?? true,
103
+ httpOnly: cookie.httpOnly ?? false,
104
+ };
105
+ if (typeof cookie.expires === 'number')
106
+ out.expires = cookie.expires;
107
+ if (cookie.sameSite === 'Lax' || cookie.sameSite === 'Strict' || cookie.sameSite === 'None') {
108
+ out.sameSite = cookie.sameSite;
85
109
  }
86
- return new Set(names.map((name) => name.trim()).filter(Boolean));
110
+ return out;
87
111
  }
88
112
  function attachUrl(cookie, fallbackUrl) {
89
113
  const cookieWithUrl = { ...cookie };
@@ -97,6 +121,7 @@ function attachUrl(cookie, fallbackUrl) {
97
121
  }
98
122
  // When url is present, let Chrome derive the host from it; keeping domain can trigger CDP sanitization errors.
99
123
  if (cookieWithUrl.url) {
124
+ // Learned: CDP rejects cookies with both url + domain in some cases; drop domain to avoid failures.
100
125
  delete cookieWithUrl.domain;
101
126
  }
102
127
  return cookieWithUrl;
@@ -121,10 +146,19 @@ function normalizeExpiration(expires) {
121
146
  return undefined;
122
147
  }
123
148
  if (value > 1_000_000_000_000) {
149
+ // Learned: Chrome may store WebKit microseconds since 1601; convert to Unix seconds.
124
150
  return Math.round(value / 1_000_000 - 11644473600);
125
151
  }
126
152
  if (value > 1_000_000_000) {
153
+ // Likely milliseconds; normalize to seconds for CDP.
127
154
  return Math.round(value / 1000);
128
155
  }
129
156
  return Math.round(value);
130
157
  }
158
+ function readDuration(envKey, defaultValueMs) {
159
+ const raw = process.env[envKey];
160
+ if (!raw)
161
+ return defaultValueMs;
162
+ const value = Number.parseInt(raw, 10);
163
+ return Number.isFinite(value) && value > 0 ? value : defaultValueMs;
164
+ }