@steipete/oracle 0.14.1 → 0.15.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.
Files changed (65) hide show
  1. package/dist/bin/oracle-cli.js +2 -0
  2. package/dist/bin/oracle.js +569 -0
  3. package/dist/docs-site/.nojekyll +0 -0
  4. package/dist/docs-site/CNAME +1 -0
  5. package/dist/docs-site/RELEASING.html +410 -0
  6. package/dist/docs-site/agents.html +374 -0
  7. package/dist/docs-site/anthropic.html +368 -0
  8. package/dist/docs-site/bridge.html +400 -0
  9. package/dist/docs-site/browser-mode.html +594 -0
  10. package/dist/docs-site/chromium-forks.html +347 -0
  11. package/dist/docs-site/cli-reference.html +346 -0
  12. package/dist/docs-site/configuration.html +452 -0
  13. package/dist/docs-site/favicon.svg +14 -0
  14. package/dist/docs-site/followup.html +375 -0
  15. package/dist/docs-site/gemini.html +383 -0
  16. package/dist/docs-site/grok.html +325 -0
  17. package/dist/docs-site/index.html +360 -0
  18. package/dist/docs-site/install.html +335 -0
  19. package/dist/docs-site/linux.html +321 -0
  20. package/dist/docs-site/llms.txt +43 -0
  21. package/dist/docs-site/manual-tests.html +596 -0
  22. package/dist/docs-site/mcp.html +391 -0
  23. package/dist/docs-site/multimodel.html +364 -0
  24. package/dist/docs-site/mythical-pro-agents.html +360 -0
  25. package/dist/docs-site/notifier.html +338 -0
  26. package/dist/docs-site/openai-endpoints.html +387 -0
  27. package/dist/docs-site/openrouter.html +344 -0
  28. package/dist/docs-site/quickstart.html +369 -0
  29. package/dist/docs-site/refactor/ux.html +532 -0
  30. package/dist/docs-site/sessions.html +388 -0
  31. package/dist/docs-site/social-card.png +0 -0
  32. package/dist/docs-site/social-card.svg +79 -0
  33. package/dist/docs-site/spec.html +363 -0
  34. package/dist/docs-site/testing.html +320 -0
  35. package/dist/docs-site/tui-debug.html +326 -0
  36. package/dist/docs-site/windows-work.html +323 -0
  37. package/dist/docs-site/windows.html +320 -0
  38. package/dist/src/browser/actions/deepResearch.js +132 -61
  39. package/dist/src/browser/actions/modelSelection.js +45 -2
  40. package/dist/src/browser/actions/thinkingTime.js +65 -20
  41. package/dist/src/browser/artifacts.js +2 -8
  42. package/dist/src/browser/chatgptFiles.js +198 -49
  43. package/dist/src/browser/chromeCookies.js +312 -0
  44. package/dist/src/browser/chromeLifecycle.js +35 -4
  45. package/dist/src/browser/deepResearchResult.js +23 -0
  46. package/dist/src/browser/index.js +82 -11
  47. package/dist/src/browser/keytarShim.js +56 -0
  48. package/dist/src/browser/profileCopy.js +93 -0
  49. package/dist/src/browser/windowsCookies.js +219 -0
  50. package/dist/src/cli/browserConfig.js +17 -1
  51. package/dist/src/cli/sessionRunner.js +13 -7
  52. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  53. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +20 -0
  54. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  55. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  56. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +128 -0
  57. package/dist/vendor/oracle-notifier/build-notifier.sh +0 -0
  58. package/package.json +33 -31
  59. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  60. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +20 -0
  61. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  62. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  63. package/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +128 -0
  64. package/vendor/oracle-notifier/README.md +26 -0
  65. package/vendor/oracle-notifier/build-notifier.sh +0 -0
@@ -0,0 +1,219 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs/promises';
3
+ import { existsSync, promises as fsp } from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import sqlite3 from 'sqlite3';
7
+ import { createRequire } from 'node:module';
8
+ import { spawnSync } from 'node:child_process';
9
+ let cachedUnprotect = null;
10
+ function getUnprotectData() {
11
+ if (cachedUnprotect) {
12
+ return cachedUnprotect;
13
+ }
14
+ try {
15
+ // win-dpapi is CommonJS; require it explicitly and support both named/default shapes.
16
+ const dpapiModule = createRequire(import.meta.url)('win-dpapi');
17
+ const unprotect = dpapiModule?.Dpapi?.unprotectData ??
18
+ dpapiModule?.default?.unprotectData ??
19
+ ((_) => {
20
+ throw new Error('win-dpapi unprotectData not available');
21
+ });
22
+ cachedUnprotect = unprotect;
23
+ return unprotect;
24
+ }
25
+ catch (error) {
26
+ if (process.platform !== 'win32') {
27
+ // On macOS/Linux we don't need DPAPI; return a function that makes misuse obvious.
28
+ cachedUnprotect = () => {
29
+ throw new Error('win-dpapi is unavailable on non-Windows platforms');
30
+ };
31
+ return cachedUnprotect;
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+ export async function loadWindowsCookies(dbPath, filterNames) {
37
+ if (process.platform !== 'win32') {
38
+ throw new Error('loadWindowsCookies is only supported on Windows');
39
+ }
40
+ const localStatePath = await locateLocalState(dbPath);
41
+ const cookiesCopy = await copyLockedFile(dbPath, 'Cookies'); // name the copy exactly "Cookies"
42
+ const cookiesDirForFallback = await ensureCookiesDirForFallback(cookiesCopy);
43
+ const localStateCopy = await copyLockedFile(localStatePath, 'Local State');
44
+ const aesKey = await extractWindowsAesKey(localStateCopy);
45
+ const rows = await readChromeCookiesDb(cookiesCopy, filterNames);
46
+ const cookies = [];
47
+ for (const row of rows) {
48
+ const enc = row.encrypted_value && row.encrypted_value.length > 0 ? row.encrypted_value : Buffer.from(row.value ?? '', 'utf8');
49
+ const decrypted = enc.length > 0 ? decryptCookie(enc, aesKey) : '';
50
+ cookies.push({
51
+ name: row.name,
52
+ value: decrypted,
53
+ domain: row.host_key?.startsWith('.') ? row.host_key.slice(1) : row.host_key,
54
+ path: row.path ?? '/',
55
+ expires: normalizeExpiration(row.expires_utc),
56
+ secure: Boolean(row.is_secure),
57
+ httpOnly: Boolean(row.is_httponly),
58
+ });
59
+ }
60
+ if (process.env.ORACLE_DEBUG_COOKIES === '1') {
61
+ // eslint-disable-next-line no-console
62
+ console.log(`[cookies] windows decrypt decoded ${cookies.length} cookies from ${cookiesCopy} (fallback dir ${cookiesDirForFallback})`);
63
+ }
64
+ return cookies.filter((c) => c.value);
65
+ }
66
+ function decryptCookie(value, aesKey) {
67
+ const prefix = value.slice(0, 3).toString();
68
+ // Chrome prefixes AES-GCM encrypted cookies with version markers like v10/v11/v20; treat all v** the same.
69
+ if (/^v\d{2}$/u.test(prefix)) {
70
+ const iv = value.slice(3, 15);
71
+ const tag = value.slice(value.length - 16);
72
+ const data = value.slice(15, value.length - 16);
73
+ const decipher = crypto.createDecipheriv('aes-256-gcm', aesKey, iv);
74
+ decipher.setAuthTag(tag);
75
+ const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
76
+ return decrypted.toString('utf8');
77
+ }
78
+ const unprotectData = getUnprotectData();
79
+ const unprotected = unprotectData(value, undefined, 'CurrentUser');
80
+ return Buffer.from(unprotected).toString('utf8');
81
+ }
82
+ async function locateLocalState(dbPath) {
83
+ // Prefer sibling Local State near the profile path; fall back to default location.
84
+ const guess = path.resolve(path.join(path.dirname(dbPath), '..', 'Local State'));
85
+ if (existsSync(guess))
86
+ return guess;
87
+ const localState = path.join(os.homedir(), 'AppData', 'Local', 'Google', 'Chrome', 'User Data', 'Local State');
88
+ if (existsSync(localState))
89
+ return localState;
90
+ throw new Error('Chrome Local State not found for AES key');
91
+ }
92
+ async function extractWindowsAesKey(localStatePath) {
93
+ const raw = await fs.readFile(localStatePath, 'utf8');
94
+ const state = JSON.parse(raw);
95
+ const encKeyB64 = state?.os_crypt?.encrypted_key;
96
+ if (!encKeyB64)
97
+ throw new Error('encrypted_key missing in Local State');
98
+ const encKey = Buffer.from(encKeyB64, 'base64');
99
+ const dpapiBlob = encKey.slice(5); // strip "DPAPI"
100
+ const unprotectData = getUnprotectData();
101
+ const unprotected = unprotectData(dpapiBlob, undefined, 'CurrentUser');
102
+ return Buffer.from(unprotected);
103
+ }
104
+ async function readChromeCookiesDb(dbPath, filterNames) {
105
+ const db = await openSqlite(dbPath);
106
+ const placeholders = filterNames && filterNames.size > 0 ? `AND name IN (${Array.from(filterNames).map(() => '?').join(',')})` : '';
107
+ const params = filterNames ? Array.from(filterNames) : [];
108
+ const sql = `SELECT name,value,encrypted_value,host_key,path,expires_utc,is_secure,is_httponly FROM cookies WHERE host_key LIKE '%chatgpt.com%' ${placeholders}`;
109
+ try {
110
+ return await allSqlite(db, sql, params);
111
+ }
112
+ finally {
113
+ db.close();
114
+ }
115
+ }
116
+ function openSqlite(dbPath) {
117
+ return new Promise((resolve, reject) => {
118
+ const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (err) => {
119
+ if (err)
120
+ reject(err);
121
+ else
122
+ resolve(db);
123
+ });
124
+ });
125
+ }
126
+ async function copyLockedFile(sourcePath, targetName) {
127
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'oracle-cookie-'));
128
+ const tempPath = targetName ? path.join(tempDir, targetName) : path.join(tempDir, path.basename(sourcePath));
129
+ const psScript = `
130
+ $src = '${sourcePath.replace(/'/g, "''")}'
131
+ $dst = '${tempPath.replace(/'/g, "''")}'
132
+ try {
133
+ $fs = [System.IO.File]::Open($src, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
134
+ $bytes = New-Object byte[] $fs.Length
135
+ $null = $fs.Read($bytes, 0, $fs.Length)
136
+ $fs.Close()
137
+ [System.IO.File]::WriteAllBytes($dst, $bytes)
138
+ exit 0
139
+ } catch {
140
+ exit 1
141
+ }
142
+ `;
143
+ const encoded = Buffer.from(psScript, 'utf16le').toString('base64');
144
+ const result = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-EncodedCommand', encoded], {
145
+ timeout: 15000,
146
+ encoding: 'utf8',
147
+ });
148
+ if (result.status !== 0) {
149
+ // Fallback: best-effort normal copy
150
+ await fs.copyFile(sourcePath, tempPath);
151
+ }
152
+ if (process.env.ORACLE_DEBUG_COOKIES === '1') {
153
+ // eslint-disable-next-line no-console
154
+ console.log(`[cookies] copied locked file ${sourcePath} -> ${tempPath}`);
155
+ }
156
+ return tempPath;
157
+ }
158
+ // Create a directory containing a file named "Cookies" for chrome-cookies-secure fallback
159
+ export async function ensureCookiesDirForFallback(cookieFile) {
160
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'oracle-cookies-secure-'));
161
+ const target = path.join(dir, 'Cookies');
162
+ await fs.copyFile(cookieFile, target);
163
+ return dir;
164
+ }
165
+ function allSqlite(db, sql, params) {
166
+ return new Promise((resolve, reject) => {
167
+ db.all(sql, params, (err, rows) => {
168
+ if (err)
169
+ reject(err);
170
+ else
171
+ resolve((rows ?? []));
172
+ });
173
+ });
174
+ }
175
+ function normalizeExpiration(expires) {
176
+ if (!expires || Number.isNaN(expires)) {
177
+ return undefined;
178
+ }
179
+ const value = Number(expires);
180
+ if (value <= 0)
181
+ return undefined;
182
+ if (value > 1_000_000_000_000) {
183
+ return Math.round(value / 1_000_000 - 11644473600);
184
+ }
185
+ if (value > 1_000_000_000) {
186
+ return Math.round(value / 1000);
187
+ }
188
+ return Math.round(value);
189
+ }
190
+ export async function materializeCookieFile(sourcePath) {
191
+ if (process.platform !== 'win32')
192
+ return sourcePath;
193
+ const resolved = await resolveDirectoryCandidate(sourcePath);
194
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'oracle-cookies-'));
195
+ const tempPath = path.join(tempDir, 'Cookies');
196
+ await fs.copyFile(resolved, tempPath);
197
+ return tempPath;
198
+ }
199
+ async function resolveDirectoryCandidate(inputPath) {
200
+ const stat = await fs.stat(inputPath).catch(() => null);
201
+ if (!stat?.isDirectory())
202
+ return inputPath;
203
+ const network = path.join(inputPath, 'Network', 'Cookies');
204
+ const direct = path.join(inputPath, 'Cookies');
205
+ if (await fileExists(network))
206
+ return network;
207
+ if (await fileExists(direct))
208
+ return direct;
209
+ return inputPath;
210
+ }
211
+ async function fileExists(candidate) {
212
+ try {
213
+ const stat = await fsp.stat(candidate);
214
+ return stat.isFile();
215
+ }
216
+ catch {
217
+ return false;
218
+ }
219
+ }
@@ -64,6 +64,18 @@ export function normalizeChatGptModelForBrowser(model) {
64
64
  return model;
65
65
  }
66
66
  export async function buildBrowserConfig(options) {
67
+ if (options.copyProfile && options.browserKeepBrowser) {
68
+ throw new Error("--copy-profile cannot be combined with --browser-keep-browser: the copied profile is a throwaway that is deleted after the run, so it must not be retained.");
69
+ }
70
+ if (options.copyProfile && options.browserManualLogin) {
71
+ throw new Error("--copy-profile cannot be combined with --browser-manual-login: choose either a throwaway copied profile or the persistent manual-login profile.");
72
+ }
73
+ if (options.copyProfile && options.remoteChrome) {
74
+ throw new Error("--copy-profile cannot be combined with --remote-chrome: copied profiles require a locally launched Chrome instance.");
75
+ }
76
+ if (options.copyProfile && options.remoteHost) {
77
+ throw new Error("--copy-profile cannot be combined with --remote-host: the local profile source is not available to the remote browser service.");
78
+ }
67
79
  const desiredModelOverride = options.browserModelLabel?.trim();
68
80
  const normalizedOverride = desiredModelOverride?.toLowerCase() ?? "";
69
81
  const baseModel = options.model.toLowerCase();
@@ -98,7 +110,9 @@ export async function buildBrowserConfig(options) {
98
110
  ? desiredModelOverride
99
111
  : mapModelToBrowserLabel(options.model);
100
112
  return {
101
- chromeProfile: options.browserChromeProfile ?? DEFAULT_CHROME_PROFILE,
113
+ chromeProfile: options.copyProfile
114
+ ? (options.browserChromeProfile ?? null)
115
+ : (options.browserChromeProfile ?? DEFAULT_CHROME_PROFILE),
102
116
  chromePath: options.browserChromePath ?? null,
103
117
  chromeCookiePath: options.browserCookiePath ?? null,
104
118
  attachRunning,
@@ -146,6 +160,7 @@ export async function buildBrowserConfig(options) {
146
160
  keepBrowser: options.browserKeepBrowser ? true : undefined,
147
161
  manualLogin: options.browserManualLogin === undefined ? undefined : options.browserManualLogin,
148
162
  manualLoginProfileDir: options.browserManualLoginProfileDir ?? undefined,
163
+ copyProfileSource: options.copyProfile ?? undefined,
149
164
  hideWindow: options.browserHideWindow ? true : undefined,
150
165
  desiredModel,
151
166
  modelStrategy,
@@ -171,6 +186,7 @@ function validateAttachRunningOptions(options, { attachRunning, hasInlineCookies
171
186
  options.browserKeepBrowser ? "--browser-keep-browser" : null,
172
187
  options.browserManualLogin ? "--browser-manual-login" : null,
173
188
  options.browserManualLoginProfileDir ? "--browser-manual-login-profile-dir" : null,
189
+ options.copyProfile ? "--copy-profile" : null,
174
190
  hasInlineCookies ? "--browser-inline-cookies/--browser-inline-cookies-file" : null,
175
191
  options.browserPort != null || options.browserDebugPort != null
176
192
  ? "--browser-port/--browser-debug-port"
@@ -389,6 +389,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
389
389
  userError.details?.stage === "assistant-timeout";
390
390
  const cloudflareChallenge = userError?.category === "browser-automation" &&
391
391
  userError.details?.stage === "cloudflare-challenge";
392
+ const browserCanReattach = !browserConfig?.copyProfileSource;
392
393
  let reattachGuidanceLogged = false;
393
394
  const logBrowserReattachGuidance = (runtime) => {
394
395
  if (reattachGuidanceLogged || mode !== "browser")
@@ -399,7 +400,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
399
400
  reattachGuidanceLogged = true;
400
401
  log(formatBrowserReattachGuidance(sessionMeta.id));
401
402
  };
402
- if (connectionLost && mode === "browser") {
403
+ if (connectionLost && mode === "browser" && browserCanReattach) {
403
404
  const runtime = userError.details
404
405
  ?.runtime;
405
406
  const recoverableRuntime = runtime ?? sessionMeta.browser?.runtime;
@@ -456,7 +457,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
456
457
  logBrowserReattachGuidance(recoverableRuntime);
457
458
  return;
458
459
  }
459
- if (assistantTimeout && mode === "browser") {
460
+ if (assistantTimeout && mode === "browser" && browserCanReattach) {
460
461
  const runtime = userError.details
461
462
  ?.runtime;
462
463
  log(dim("Assistant response timed out; marking capture incomplete for reattach."));
@@ -509,9 +510,14 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
509
510
  }
510
511
  if (cloudflareChallenge && mode === "browser") {
511
512
  const details = userError.details;
512
- log(dim("Cloudflare challenge detected; browser left running so you can complete the check."));
513
- if (details?.reuseProfileHint) {
514
- log(dim(`Reuse this browser profile with: ${details.reuseProfileHint}`));
513
+ if (browserCanReattach) {
514
+ log(dim("Cloudflare challenge detected; browser left running so you can complete the check."));
515
+ if (details?.reuseProfileHint) {
516
+ log(dim(`Reuse this browser profile with: ${details.reuseProfileHint}`));
517
+ }
518
+ }
519
+ else {
520
+ log(dim("Cloudflare challenge detected; copied profile closed and removed."));
515
521
  }
516
522
  }
517
523
  if (userError) {
@@ -527,10 +533,10 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
527
533
  if (transportLine) {
528
534
  log(dim(`Transport: ${transportLine}`));
529
535
  }
530
- const browserRuntime = mode === "browser"
536
+ const browserRuntime = mode === "browser" && browserCanReattach
531
537
  ? userError?.details?.runtime
532
538
  : undefined;
533
- if (!cloudflareChallenge) {
539
+ if (!cloudflareChallenge && browserCanReattach) {
534
540
  logBrowserReattachGuidance(browserRuntime ?? sessionMeta.browser?.runtime);
535
541
  }
536
542
  await sessionStore.updateSession(sessionMeta.id, {
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleIdentifier</key>
6
+ <string>com.steipete.oracle.notifier</string>
7
+ <key>CFBundleName</key>
8
+ <string>OracleNotifier</string>
9
+ <key>CFBundleDisplayName</key>
10
+ <string>Oracle Notifier</string>
11
+ <key>CFBundleExecutable</key>
12
+ <string>OracleNotifier</string>
13
+ <key>CFBundleIconFile</key>
14
+ <string>OracleIcon</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>APPL</string>
17
+ <key>LSMinimumSystemVersion</key>
18
+ <string>13.0</string>
19
+ </dict>
20
+ </plist>
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict>
7
+ <key>Resources/OracleIcon.icns</key>
8
+ <data>
9
+ edUHAMetayIv3xtc3Vb92VXRLfM=
10
+ </data>
11
+ </dict>
12
+ <key>files2</key>
13
+ <dict>
14
+ <key>Resources/OracleIcon.icns</key>
15
+ <dict>
16
+ <key>hash2</key>
17
+ <data>
18
+ AVPJK/6w6IOsDLmZTW4hL+Za+/4wHMxZiIp0t6m3NRA=
19
+ </data>
20
+ </dict>
21
+ </dict>
22
+ <key>rules</key>
23
+ <dict>
24
+ <key>^Resources/</key>
25
+ <true/>
26
+ <key>^Resources/.*\.lproj/</key>
27
+ <dict>
28
+ <key>optional</key>
29
+ <true/>
30
+ <key>weight</key>
31
+ <real>1000</real>
32
+ </dict>
33
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
34
+ <dict>
35
+ <key>omit</key>
36
+ <true/>
37
+ <key>weight</key>
38
+ <real>1100</real>
39
+ </dict>
40
+ <key>^Resources/Base\.lproj/</key>
41
+ <dict>
42
+ <key>weight</key>
43
+ <real>1010</real>
44
+ </dict>
45
+ <key>^version.plist$</key>
46
+ <true/>
47
+ </dict>
48
+ <key>rules2</key>
49
+ <dict>
50
+ <key>.*\.dSYM($|/)</key>
51
+ <dict>
52
+ <key>weight</key>
53
+ <real>11</real>
54
+ </dict>
55
+ <key>^(.*/)?\.DS_Store$</key>
56
+ <dict>
57
+ <key>omit</key>
58
+ <true/>
59
+ <key>weight</key>
60
+ <real>2000</real>
61
+ </dict>
62
+ <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
63
+ <dict>
64
+ <key>nested</key>
65
+ <true/>
66
+ <key>weight</key>
67
+ <real>10</real>
68
+ </dict>
69
+ <key>^.*</key>
70
+ <true/>
71
+ <key>^Info\.plist$</key>
72
+ <dict>
73
+ <key>omit</key>
74
+ <true/>
75
+ <key>weight</key>
76
+ <real>20</real>
77
+ </dict>
78
+ <key>^PkgInfo$</key>
79
+ <dict>
80
+ <key>omit</key>
81
+ <true/>
82
+ <key>weight</key>
83
+ <real>20</real>
84
+ </dict>
85
+ <key>^Resources/</key>
86
+ <dict>
87
+ <key>weight</key>
88
+ <real>20</real>
89
+ </dict>
90
+ <key>^Resources/.*\.lproj/</key>
91
+ <dict>
92
+ <key>optional</key>
93
+ <true/>
94
+ <key>weight</key>
95
+ <real>1000</real>
96
+ </dict>
97
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
98
+ <dict>
99
+ <key>omit</key>
100
+ <true/>
101
+ <key>weight</key>
102
+ <real>1100</real>
103
+ </dict>
104
+ <key>^Resources/Base\.lproj/</key>
105
+ <dict>
106
+ <key>weight</key>
107
+ <real>1010</real>
108
+ </dict>
109
+ <key>^[^/]+$</key>
110
+ <dict>
111
+ <key>nested</key>
112
+ <true/>
113
+ <key>weight</key>
114
+ <real>10</real>
115
+ </dict>
116
+ <key>^embedded\.provisionprofile$</key>
117
+ <dict>
118
+ <key>weight</key>
119
+ <real>20</real>
120
+ </dict>
121
+ <key>^version\.plist$</key>
122
+ <dict>
123
+ <key>weight</key>
124
+ <real>20</real>
125
+ </dict>
126
+ </dict>
127
+ </dict>
128
+ </plist>
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steipete/oracle",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "CLI wrapper around OpenAI Responses API with GPT-5.5 Pro, GPT-5.5, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
5
5
  "keywords": [],
6
6
  "homepage": "https://askoracle.sh",
@@ -28,6 +28,34 @@
28
28
  ],
29
29
  "type": "module",
30
30
  "main": "dist/bin/oracle-cli.js",
31
+ "scripts": {
32
+ "docs:list": "tsx scripts/docs-list.ts",
33
+ "docs:check": "node --no-deprecation --import tsx bin/oracle-cli.ts docs check",
34
+ "docs:site": "node scripts/build-docs-site.mjs",
35
+ "build": "tsgo -p tsconfig.build.json && pnpm run build:vendor",
36
+ "build:vendor": "node -e \"const fs=require('fs'); const path=require('path'); const vendorRoot=path.join('dist','vendor'); fs.rmSync(vendorRoot,{recursive:true,force:true}); const vendors=[['oracle-notifier']]; vendors.forEach(([name])=>{const src=path.join('vendor',name); const dest=path.join(vendorRoot,name); fs.mkdirSync(dest,{recursive:true}); if(fs.existsSync(src)){fs.cpSync(src,dest,{recursive:true,force:true});}});\"",
37
+ "start": "pnpm run build && node ./dist/scripts/run-cli.js",
38
+ "oracle": "pnpm start",
39
+ "check": "pnpm run format:check && pnpm run lint",
40
+ "typecheck": "tsgo --noEmit",
41
+ "format": "oxfmt --write .",
42
+ "format:check": "oxfmt --check .",
43
+ "format:diff": "oxfmt --write . && git --no-pager diff",
44
+ "lint": "pnpm run typecheck && oxlint",
45
+ "lint:fix": "oxlint --fix && pnpm run format",
46
+ "test": "vitest run",
47
+ "test:mcp": "pnpm run build && pnpm run test:mcp:unit && pnpm run test:mcp:mcporter",
48
+ "test:mcp:unit": "vitest run tests/mcp*.test.ts tests/mcp/**/*.test.ts",
49
+ "test:mcp:mcporter": "CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter list oracle-local --schema --config config/mcporter.json && CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter call oracle-local.sessions limit:1 --config config/mcporter.json",
50
+ "test:browser": "pnpm run build && tsx scripts/test-browser.ts && ./scripts/browser-smoke.sh",
51
+ "test:packed-cli": "node scripts/packed-cli-smoke.mjs",
52
+ "test:live": "ORACLE_LIVE_TEST=1 vitest run tests/live --exclude tests/live/openai-live.test.ts",
53
+ "test:live:fast": "ORACLE_LIVE_TEST=1 ORACLE_LIVE_TEST_FAST=1 vitest run tests/live/browser-fast-live.test.ts",
54
+ "test:pro": "ORACLE_LIVE_TEST=1 vitest run tests/live/openai-live.test.ts",
55
+ "test:coverage": "vitest run --coverage",
56
+ "prepare": "pnpm run build",
57
+ "mcp": "pnpm run build && node ./dist/bin/oracle-mcp.js"
58
+ },
31
59
  "dependencies": {
32
60
  "@anthropic-ai/tokenizer": "^0.0.4",
33
61
  "@google/genai": "^2.8.0",
@@ -60,7 +88,7 @@
60
88
  "@types/inquirer": "^9.0.9",
61
89
  "@types/node": "^25.9.2",
62
90
  "@typescript/native-preview": "7.0.0-dev.20260612.1",
63
- "@vitest/coverage-v8": "4.1.8",
91
+ "@vitest/coverage-v8": "4.1.9",
64
92
  "devtools-protocol": "0.0.1643702",
65
93
  "es-toolkit": "^1.47.0",
66
94
  "esbuild": "^0.28.0",
@@ -69,7 +97,7 @@
69
97
  "puppeteer-core": "^25.1.0",
70
98
  "tsx": "^4.22.4",
71
99
  "typescript": "^6.0.3",
72
- "vitest": "^4.1.8"
100
+ "vitest": "^4.1.9"
73
101
  },
74
102
  "devEngines": {
75
103
  "runtime": [
@@ -82,31 +110,5 @@
82
110
  "engines": {
83
111
  "node": ">=24"
84
112
  },
85
- "scripts": {
86
- "docs:list": "tsx scripts/docs-list.ts",
87
- "docs:check": "node --no-deprecation --import tsx bin/oracle-cli.ts docs check",
88
- "docs:site": "node scripts/build-docs-site.mjs",
89
- "build": "tsgo -p tsconfig.build.json && pnpm run build:vendor",
90
- "build:vendor": "node -e \"const fs=require('fs'); const path=require('path'); const vendorRoot=path.join('dist','vendor'); fs.rmSync(vendorRoot,{recursive:true,force:true}); const vendors=[['oracle-notifier']]; vendors.forEach(([name])=>{const src=path.join('vendor',name); const dest=path.join(vendorRoot,name); fs.mkdirSync(dest,{recursive:true}); if(fs.existsSync(src)){fs.cpSync(src,dest,{recursive:true,force:true});}});\"",
91
- "start": "pnpm run build && node ./dist/scripts/run-cli.js",
92
- "oracle": "pnpm start",
93
- "check": "pnpm run format:check && pnpm run lint",
94
- "typecheck": "tsgo --noEmit",
95
- "format": "oxfmt --write .",
96
- "format:check": "oxfmt --check .",
97
- "format:diff": "oxfmt --write . && git --no-pager diff",
98
- "lint": "pnpm run typecheck && oxlint",
99
- "lint:fix": "oxlint --fix && pnpm run format",
100
- "test": "vitest run",
101
- "test:mcp": "pnpm run build && pnpm run test:mcp:unit && pnpm run test:mcp:mcporter",
102
- "test:mcp:unit": "vitest run tests/mcp*.test.ts tests/mcp/**/*.test.ts",
103
- "test:mcp:mcporter": "CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter list oracle-local --schema --config config/mcporter.json && CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter call oracle-local.sessions limit:1 --config config/mcporter.json",
104
- "test:browser": "pnpm run build && tsx scripts/test-browser.ts && ./scripts/browser-smoke.sh",
105
- "test:packed-cli": "node scripts/packed-cli-smoke.mjs",
106
- "test:live": "ORACLE_LIVE_TEST=1 vitest run tests/live --exclude tests/live/openai-live.test.ts",
107
- "test:live:fast": "ORACLE_LIVE_TEST=1 ORACLE_LIVE_TEST_FAST=1 vitest run tests/live/browser-fast-live.test.ts",
108
- "test:pro": "ORACLE_LIVE_TEST=1 vitest run tests/live/openai-live.test.ts",
109
- "test:coverage": "vitest run --coverage",
110
- "mcp": "pnpm run build && node ./dist/bin/oracle-mcp.js"
111
- }
112
- }
113
+ "packageManager": "pnpm@10.33.2"
114
+ }
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleIdentifier</key>
6
+ <string>com.steipete.oracle.notifier</string>
7
+ <key>CFBundleName</key>
8
+ <string>OracleNotifier</string>
9
+ <key>CFBundleDisplayName</key>
10
+ <string>Oracle Notifier</string>
11
+ <key>CFBundleExecutable</key>
12
+ <string>OracleNotifier</string>
13
+ <key>CFBundleIconFile</key>
14
+ <string>OracleIcon</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>APPL</string>
17
+ <key>LSMinimumSystemVersion</key>
18
+ <string>13.0</string>
19
+ </dict>
20
+ </plist>