pi-oracle 0.6.16 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- // Purpose: Define the allowlist/drop policy for importing ChatGPT/OpenAI auth cookies into the isolated oracle browser profile.
1
+ // Purpose: Define the allowlist/drop policy for importing provider auth cookies into the isolated oracle browser profile.
2
2
  // Responsibilities: Recognize required auth cookies, drop noisy/irrelevant cookies, and normalize cookie import decisions.
3
3
  // Scope: Pure cookie-policy logic only; reading cookies from Chrome and writing them into the isolated profile happen elsewhere.
4
4
  // Usage: Imported by auth-bootstrap and sanity tests to keep cookie import behavior deterministic and reviewable.
@@ -14,6 +14,17 @@ const AUTH_COOKIE_NAME_PATTERNS = [
14
14
  /^auth-session-minimized(?:-client-checksum)?$/,
15
15
  /^(?:login_session|auth_provider|hydra_redirect|iss_context|rg_context)$/,
16
16
  /^cf_clearance$/,
17
+ /^__(?:cf_bm|cflb)$/,
18
+ /^_cfuvid$/,
19
+ ];
20
+
21
+ const GROK_AUTH_COOKIE_NAME_PATTERNS = [
22
+ /^sso(?:-rw)?$/,
23
+ /^auth_token$/,
24
+ /^ct0$/,
25
+ /^cf_clearance$/,
26
+ /^__(?:cf_bm|cflb)$/,
27
+ /^_cfuvid$/,
17
28
  ];
18
29
 
19
30
  const DROPPED_COOKIE_NAME_PATTERNS = [
@@ -21,9 +32,6 @@ const DROPPED_COOKIE_NAME_PATTERNS = [
21
32
  /^_uet/,
22
33
  /^_rdt_uuid$/,
23
34
  /^(?:marketing|analytics)_consent$/,
24
- /^__cf_bm$/,
25
- /^__cflb$/,
26
- /^_cfuvid$/,
27
35
  /^_dd_s$/,
28
36
  /^g_state$/,
29
37
  /^country$/,
@@ -41,6 +49,9 @@ const BASE_ALLOWED_COOKIE_HOSTS = new Set([
41
49
  'sentinel.openai.com',
42
50
  'atlas.openai.com',
43
51
  'ws.chatgpt.com',
52
+ 'grok.com',
53
+ 'x.ai',
54
+ 'x.com',
44
55
  ]);
45
56
 
46
57
  function normalizeSameSite(value) {
@@ -101,6 +112,9 @@ export function normalizeImportedCookie(cookie, fallbackHost) {
101
112
  export function classifyImportedCookie(cookie, chatUrl) {
102
113
  if (matchesAny(DROPPED_COOKIE_NAME_PATTERNS, cookie.name)) return 'noise';
103
114
  if (!isAllowedCookieDomain(cookie.domain, chatUrl)) return 'foreign-domain';
115
+ if (['grok.com', 'x.ai', 'x.com'].includes(cookie.domain)) {
116
+ return matchesAny(GROK_AUTH_COOKIE_NAME_PATTERNS, cookie.name) ? 'keep' : 'non-auth';
117
+ }
104
118
  if (!matchesAny(AUTH_COOKIE_NAME_PATTERNS, cookie.name)) return 'non-auth';
105
119
  return 'keep';
106
120
  }
@@ -82,6 +82,9 @@ export function classifyChatAuthPage(args) {
82
82
  /we detect suspicious activity/i,
83
83
  ];
84
84
  if (challengePatterns.some((pattern) => pattern.test(text))) {
85
+ if (/verification successful|waiting for chatgpt\.com to respond/i.test(text)) {
86
+ return { state: "unknown", message: "ChatGPT verification is still settling." };
87
+ }
85
88
  return {
86
89
  state: "challenge_blocking",
87
90
  message:
@@ -109,7 +112,9 @@ export function classifyChatAuthPage(args) {
109
112
  return { state: "transient_outage_error", message: `ChatGPT is showing a transient outage/error page. Logs: ${args.logPath}` };
110
113
  }
111
114
 
112
- if (args.probe?.status === 401 || (args.probe?.status === 403 && (!onAllowedOrigin || !hasUsableComposer || args.probe?.domLoginCta))) {
115
+ const probeHasAccountIdentity = args.probe?.bodyHasId === true || args.probe?.bodyHasEmail === true;
116
+
117
+ if (args.probe?.status === 401 || (args.probe?.status === 403 && (!onAllowedOrigin || !hasUsableComposer))) {
113
118
  return {
114
119
  state: "login_required",
115
120
  message:
@@ -120,7 +125,7 @@ export function classifyChatAuthPage(args) {
120
125
  }
121
126
 
122
127
  if (args.probe?.onAuthPage) {
123
- if (args.probe?.bodyHasId || args.probe?.bodyHasEmail) {
128
+ if (probeHasAccountIdentity) {
124
129
  return {
125
130
  state: "auth_transitioning",
126
131
  message:
@@ -137,6 +142,16 @@ export function classifyChatAuthPage(args) {
137
142
  };
138
143
  }
139
144
 
145
+ if (onAllowedOrigin && hasUsableComposer && args.probe?.domLoginCta && !probeHasAccountIdentity) {
146
+ return {
147
+ state: "login_required",
148
+ message:
149
+ `ChatGPT still shows public login controls after syncing cookies from ${args.cookieSourceLabel}. ` +
150
+ `The cookie DB may be stale, from the wrong browser profile, or for an account that is logged out. ` +
151
+ `Check auth.chromeProfile/auth.chromeCookiePath/auth.chromiumKeychain and inspect ${args.logPath}.`,
152
+ };
153
+ }
154
+
140
155
  if (onAllowedOrigin && (args.probe?.status === 200 || args.probe?.status === 403) && hasUsableComposer) {
141
156
  if (!args.probe?.domLoginCta) {
142
157
  return {
@@ -145,12 +160,12 @@ export function classifyChatAuthPage(args) {
145
160
  };
146
161
  }
147
162
 
163
+ // The public logged-out composer case returned above, so a remaining visible login CTA here still has account-like probe data.
148
164
  return {
149
165
  state: "auth_transitioning",
150
166
  message:
151
- args.probe?.bodyHasId || args.probe?.bodyHasEmail
152
- ? `ChatGPT backend session is authenticated but the shell still shows public CTA chrome. Logs: ${args.logPath}`
153
- : `ChatGPT accepted cookies but is still hydrating/auth-selecting. Logs: ${args.logPath}`,
167
+ `ChatGPT backend probe returned account-like fields, but the shell still shows public login controls. ` +
168
+ `Trying to resolve the account shell. Logs: ${args.logPath}`,
154
169
  };
155
170
  }
156
171
 
@@ -1,4 +1,4 @@
1
- // Purpose: Provide pure ChatGPT conversation-state helpers used by the oracle worker.
1
+ // Purpose: Provide pure provider conversation-state helpers used by the oracle worker.
2
2
  // Responsibilities: Slice assistant snapshot regions, normalize URLs, and track stable conversation URL observations.
3
3
  // Scope: Pure worker flow logic only; browser I/O and polling loops stay in run-job.mjs.
4
4
  // Usage: Imported by run-job.mjs and sanity tests to validate conversation-state heuristics without driving a browser.
@@ -53,7 +53,7 @@ export function stripUrlQueryAndHash(url) {
53
53
  */
54
54
  export function isConversationPathUrl(url) {
55
55
  try {
56
- return /\/c\/[A-Za-z0-9-]+$/i.test(new URL(url).pathname);
56
+ return /\/(?:c|chat)\/[A-Za-z0-9-]+$/i.test(new URL(url).pathname);
57
57
  } catch {
58
58
  return false;
59
59
  }
@@ -30,6 +30,7 @@ const THINKING_EFFORT_COMBOBOX_LABEL = "Thinking effort";
30
30
  const PRO_THINKING_EFFORT_COMBOBOX_LABEL = "Pro thinking effort";
31
31
  const EFFORT_LABELS = new Set(["Light", "Standard", "Extended", "Heavy"]);
32
32
  const BARE_EFFORT_PATTERN = /^(light|standard|extended|heavy)(?:, click to remove)?$/i;
33
+ const INSTANT_CHIP_PATTERN = /^instant(?:, click to remove)?$/i;
33
34
  const THINKING_CHIP_PATTERN = /^(?:(light|standard|extended|heavy)\s+)?thinking(?:, click to remove)?$/i;
34
35
  const PRO_CHIP_PATTERN = /^(?:(light|standard|extended|heavy)\s+)?pro(?:, click to remove)?$/i;
35
36
  const MODEL_FAMILY_CONTROL_KINDS = new Set(["button", "radio", "menuitemradio"]);
@@ -125,6 +126,12 @@ function parseComposerChipSelection(label) {
125
126
  };
126
127
  }
127
128
 
129
+ if (INSTANT_CHIP_PATTERN.test(normalized)) {
130
+ return {
131
+ modelFamily: /** @type {OracleUiModelFamily} */ ("instant"),
132
+ };
133
+ }
134
+
128
135
  const thinkingMatch = normalized.match(THINKING_CHIP_PATTERN);
129
136
  if (thinkingMatch) {
130
137
  return {
@@ -240,11 +247,20 @@ export function snapshotHasModelConfigurationUi(snapshot) {
240
247
  .filter((family) => matchesModelFamilyLabel(entry.label, family)),
241
248
  ),
242
249
  );
250
+ const visibleRadioFamilies = new Set(
251
+ entries
252
+ .filter((entry) => entry.kind === "radio" && typeof entry.label === "string")
253
+ .flatMap((entry) =>
254
+ /** @type {OracleUiModelFamily[]} */ (["instant", "thinking", "pro"])
255
+ .filter((family) => matchesModelFamilyLabel(entry.label, family)),
256
+ ),
257
+ );
243
258
  const hasCloseButton = entries.some((entry) => entry.kind === "button" && entry.label === "Close" && !entry.disabled);
259
+ const hasIntelligenceHeading = entries.some((entry) => entry.kind === "heading" && normalizeText(entry.label) === "Intelligence" && !entry.disabled);
244
260
  const hasEffortCombobox = entries.some(
245
261
  (entry) => entry.kind === "combobox" && EFFORT_LABELS.has(entry.value || "") && !entry.disabled,
246
262
  );
247
- return visibleFamilies.size >= 2 || hasCloseButton || hasEffortCombobox;
263
+ return visibleFamilies.size >= 2 || visibleRadioFamilies.size >= 2 || hasCloseButton || hasIntelligenceHeading || hasEffortCombobox;
248
264
  }
249
265
 
250
266
  /**
@@ -1,4 +1,4 @@
1
- // Purpose: Read ChatGPT cookies from arbitrary macOS Chromium-family cookie stores when sweet-cookie's built-in browser list is too narrow.
1
+ // Purpose: Read provider cookies from arbitrary macOS Chromium-family cookie stores when sweet-cookie's built-in browser list is too narrow.
2
2
  // Responsibilities: Snapshot a Chromium Cookies SQLite DB, decrypt AES-CBC cookie values with a configured Keychain item, and return sweet-cookie-shaped cookie objects.
3
3
  // Scope: macOS Chromium cookie extraction only; auth policy filtering and browser seeding stay in auth-bootstrap.mjs.
4
4
  // Usage: auth-bootstrap.mjs uses this when auth.chromiumKeychain is configured alongside auth.chromeCookiePath.
@@ -117,7 +117,7 @@ function buildHostWhereClause(origins) {
117
117
  try {
118
118
  for (const domain of parentCookieDomains(new URL(origin).hostname)) domains.add(domain);
119
119
  } catch {
120
- // Ignore malformed origins; validated ChatGPT config supplies the real set.
120
+ // Ignore malformed origins; validated provider config supplies the real set.
121
121
  }
122
122
  }
123
123
  if (domains.size === 0) return "0";