@indykish/oracle 0.9.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 (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +215 -0
  3. package/assets-oracle-icon.png +0 -0
  4. package/dist/bin/oracle-cli.js +1252 -0
  5. package/dist/bin/oracle-mcp.js +6 -0
  6. package/dist/scripts/agent-send.js +147 -0
  7. package/dist/scripts/browser-tools.js +536 -0
  8. package/dist/scripts/check.js +21 -0
  9. package/dist/scripts/debug/extract-chatgpt-response.js +53 -0
  10. package/dist/scripts/docs-list.js +110 -0
  11. package/dist/scripts/git-policy.js +125 -0
  12. package/dist/scripts/run-cli.js +14 -0
  13. package/dist/scripts/runner.js +1378 -0
  14. package/dist/scripts/test-browser.js +103 -0
  15. package/dist/scripts/test-remote-chrome.js +68 -0
  16. package/dist/src/bridge/connection.js +103 -0
  17. package/dist/src/bridge/userConfigFile.js +28 -0
  18. package/dist/src/browser/actions/assistantResponse.js +1067 -0
  19. package/dist/src/browser/actions/attachmentDataTransfer.js +138 -0
  20. package/dist/src/browser/actions/attachments.js +1910 -0
  21. package/dist/src/browser/actions/domEvents.js +19 -0
  22. package/dist/src/browser/actions/modelSelection.js +485 -0
  23. package/dist/src/browser/actions/navigation.js +445 -0
  24. package/dist/src/browser/actions/promptComposer.js +485 -0
  25. package/dist/src/browser/actions/remoteFileTransfer.js +37 -0
  26. package/dist/src/browser/actions/thinkingTime.js +206 -0
  27. package/dist/src/browser/chromeLifecycle.js +344 -0
  28. package/dist/src/browser/config.js +103 -0
  29. package/dist/src/browser/constants.js +71 -0
  30. package/dist/src/browser/cookies.js +191 -0
  31. package/dist/src/browser/detect.js +164 -0
  32. package/dist/src/browser/domDebug.js +36 -0
  33. package/dist/src/browser/index.js +1741 -0
  34. package/dist/src/browser/modelStrategy.js +13 -0
  35. package/dist/src/browser/pageActions.js +5 -0
  36. package/dist/src/browser/policies.js +43 -0
  37. package/dist/src/browser/profileState.js +280 -0
  38. package/dist/src/browser/prompt.js +152 -0
  39. package/dist/src/browser/promptSummary.js +20 -0
  40. package/dist/src/browser/reattach.js +186 -0
  41. package/dist/src/browser/reattachHelpers.js +382 -0
  42. package/dist/src/browser/sessionRunner.js +119 -0
  43. package/dist/src/browser/types.js +1 -0
  44. package/dist/src/browser/utils.js +122 -0
  45. package/dist/src/browserMode.js +1 -0
  46. package/dist/src/cli/bridge/claudeConfig.js +54 -0
  47. package/dist/src/cli/bridge/client.js +73 -0
  48. package/dist/src/cli/bridge/codexConfig.js +43 -0
  49. package/dist/src/cli/bridge/doctor.js +107 -0
  50. package/dist/src/cli/bridge/host.js +259 -0
  51. package/dist/src/cli/browserConfig.js +278 -0
  52. package/dist/src/cli/browserDefaults.js +81 -0
  53. package/dist/src/cli/bundleWarnings.js +9 -0
  54. package/dist/src/cli/clipboard.js +10 -0
  55. package/dist/src/cli/detach.js +11 -0
  56. package/dist/src/cli/dryRun.js +105 -0
  57. package/dist/src/cli/duplicatePromptGuard.js +14 -0
  58. package/dist/src/cli/engine.js +41 -0
  59. package/dist/src/cli/errorUtils.js +9 -0
  60. package/dist/src/cli/format.js +13 -0
  61. package/dist/src/cli/help.js +77 -0
  62. package/dist/src/cli/hiddenAliases.js +22 -0
  63. package/dist/src/cli/markdownBundle.js +17 -0
  64. package/dist/src/cli/markdownRenderer.js +97 -0
  65. package/dist/src/cli/notifier.js +306 -0
  66. package/dist/src/cli/options.js +281 -0
  67. package/dist/src/cli/oscUtils.js +2 -0
  68. package/dist/src/cli/promptRequirement.js +17 -0
  69. package/dist/src/cli/renderFlags.js +9 -0
  70. package/dist/src/cli/renderOutput.js +26 -0
  71. package/dist/src/cli/rootAlias.js +30 -0
  72. package/dist/src/cli/runOptions.js +78 -0
  73. package/dist/src/cli/sessionCommand.js +111 -0
  74. package/dist/src/cli/sessionDisplay.js +567 -0
  75. package/dist/src/cli/sessionRunner.js +602 -0
  76. package/dist/src/cli/sessionTable.js +92 -0
  77. package/dist/src/cli/tagline.js +258 -0
  78. package/dist/src/cli/tui/index.js +486 -0
  79. package/dist/src/cli/writeOutputPath.js +21 -0
  80. package/dist/src/config.js +26 -0
  81. package/dist/src/gemini-web/client.js +328 -0
  82. package/dist/src/gemini-web/executor.js +285 -0
  83. package/dist/src/gemini-web/index.js +1 -0
  84. package/dist/src/gemini-web/types.js +1 -0
  85. package/dist/src/heartbeat.js +43 -0
  86. package/dist/src/mcp/server.js +40 -0
  87. package/dist/src/mcp/tools/consult.js +290 -0
  88. package/dist/src/mcp/tools/sessionResources.js +75 -0
  89. package/dist/src/mcp/tools/sessions.js +105 -0
  90. package/dist/src/mcp/types.js +22 -0
  91. package/dist/src/mcp/utils.js +37 -0
  92. package/dist/src/oracle/background.js +141 -0
  93. package/dist/src/oracle/claude.js +101 -0
  94. package/dist/src/oracle/client.js +197 -0
  95. package/dist/src/oracle/config.js +227 -0
  96. package/dist/src/oracle/errors.js +132 -0
  97. package/dist/src/oracle/files.js +378 -0
  98. package/dist/src/oracle/finishLine.js +32 -0
  99. package/dist/src/oracle/format.js +30 -0
  100. package/dist/src/oracle/fsAdapter.js +10 -0
  101. package/dist/src/oracle/gemini.js +195 -0
  102. package/dist/src/oracle/logging.js +36 -0
  103. package/dist/src/oracle/markdown.js +46 -0
  104. package/dist/src/oracle/modelResolver.js +183 -0
  105. package/dist/src/oracle/multiModelRunner.js +153 -0
  106. package/dist/src/oracle/oscProgress.js +24 -0
  107. package/dist/src/oracle/promptAssembly.js +13 -0
  108. package/dist/src/oracle/request.js +50 -0
  109. package/dist/src/oracle/run.js +596 -0
  110. package/dist/src/oracle/runUtils.js +31 -0
  111. package/dist/src/oracle/tokenEstimate.js +37 -0
  112. package/dist/src/oracle/tokenStats.js +39 -0
  113. package/dist/src/oracle/tokenStringifier.js +24 -0
  114. package/dist/src/oracle/types.js +1 -0
  115. package/dist/src/oracle.js +12 -0
  116. package/dist/src/oracleHome.js +13 -0
  117. package/dist/src/remote/client.js +129 -0
  118. package/dist/src/remote/health.js +113 -0
  119. package/dist/src/remote/remoteServiceConfig.js +31 -0
  120. package/dist/src/remote/server.js +533 -0
  121. package/dist/src/remote/types.js +1 -0
  122. package/dist/src/sessionManager.js +637 -0
  123. package/dist/src/sessionStore.js +56 -0
  124. package/dist/src/version.js +39 -0
  125. package/dist/vendor/oracle-notifier/OracleNotifier.swift +45 -0
  126. package/dist/vendor/oracle-notifier/README.md +24 -0
  127. package/dist/vendor/oracle-notifier/build-notifier.sh +93 -0
  128. package/package.json +115 -0
  129. package/vendor/oracle-notifier/OracleNotifier.swift +45 -0
  130. package/vendor/oracle-notifier/README.md +24 -0
  131. package/vendor/oracle-notifier/build-notifier.sh +93 -0
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Lightweight browser connectivity smoke test.
4
+ * - Launches Chrome headful with a fixed DevTools port (default 45871 or env ORACLE_BROWSER_PORT/ORACLE_BROWSER_DEBUG_PORT).
5
+ * - Verifies the DevTools /json/version endpoint responds.
6
+ * - Prints a WSL-friendly firewall hint if the port is unreachable.
7
+ */
8
+ import { setTimeout as sleep } from 'node:timers/promises';
9
+ import { launch } from 'chrome-launcher';
10
+ import os from 'node:os';
11
+ import { readFileSync } from 'node:fs';
12
+ const DEFAULT_PORT = 45871;
13
+ const port = normalizePort(process.env.ORACLE_BROWSER_PORT ?? process.env.ORACLE_BROWSER_DEBUG_PORT) ?? DEFAULT_PORT;
14
+ const hostHint = resolveWslHost();
15
+ const targetHost = hostHint ?? '127.0.0.1';
16
+ function normalizePort(raw) {
17
+ if (!raw)
18
+ return null;
19
+ const value = Number.parseInt(raw, 10);
20
+ if (!Number.isFinite(value) || value <= 0 || value > 65535)
21
+ return null;
22
+ return value;
23
+ }
24
+ function isWsl() {
25
+ if (process.platform !== 'linux')
26
+ return false;
27
+ if (process.env.WSL_DISTRO_NAME)
28
+ return true;
29
+ return os.release().toLowerCase().includes('microsoft');
30
+ }
31
+ function resolveWslHost() {
32
+ if (!isWsl())
33
+ return null;
34
+ try {
35
+ const resolv = readFileSync('/etc/resolv.conf', 'utf8');
36
+ for (const line of resolv.split('\n')) {
37
+ const match = line.match(/^nameserver\s+([0-9.]+)/);
38
+ if (match?.[1])
39
+ return match[1];
40
+ }
41
+ }
42
+ catch {
43
+ // ignore
44
+ }
45
+ return null;
46
+ }
47
+ function firewallHint(host, devtoolsPort) {
48
+ if (!isWsl())
49
+ return null;
50
+ return [
51
+ `DevTools port ${host}:${devtoolsPort} is blocked from WSL.`,
52
+ '',
53
+ 'PowerShell (admin):',
54
+ `New-NetFirewallRule -DisplayName 'Chrome DevTools ${devtoolsPort}' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${devtoolsPort}`,
55
+ "New-NetFirewallRule -DisplayName 'Chrome DevTools (chrome.exe)' -Direction Inbound -Action Allow -Program 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' -Protocol TCP",
56
+ '',
57
+ 'Re-run ./runner pnpm test:browser after adding the rule.',
58
+ ].join('\n');
59
+ }
60
+ async function fetchVersion(host, devtoolsPort) {
61
+ const controller = new AbortController();
62
+ const timer = setTimeout(() => controller.abort(), 5000);
63
+ try {
64
+ const res = await fetch(`http://${host}:${devtoolsPort}/json/version`, { signal: controller.signal });
65
+ if (!res.ok)
66
+ return false;
67
+ const json = (await res.json());
68
+ return Boolean(json.webSocketDebuggerUrl);
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ finally {
74
+ clearTimeout(timer);
75
+ }
76
+ }
77
+ async function main() {
78
+ console.log(`[browser-test] launching Chrome on ${targetHost}:${port} (headful)…`);
79
+ const chrome = await launch({
80
+ port,
81
+ chromeFlags: ['--remote-debugging-address=0.0.0.0'],
82
+ });
83
+ let ok = await fetchVersion(targetHost, chrome.port);
84
+ if (!ok) {
85
+ await sleep(500);
86
+ ok = await fetchVersion(targetHost, chrome.port);
87
+ }
88
+ await chrome.kill();
89
+ if (ok) {
90
+ console.log(`[browser-test] PASS: DevTools responding on ${targetHost}:${chrome.port}`);
91
+ process.exit(0);
92
+ }
93
+ const hint = firewallHint(targetHost, chrome.port);
94
+ console.error(`[browser-test] FAIL: DevTools not reachable at ${targetHost}:${chrome.port}`);
95
+ if (hint) {
96
+ console.error(hint);
97
+ }
98
+ process.exit(1);
99
+ }
100
+ main().catch((error) => {
101
+ console.error('[browser-test] Unexpected failure:', error instanceof Error ? error.message : String(error));
102
+ process.exit(1);
103
+ });
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * POC: Test connecting to remote Chrome instance
4
+ *
5
+ * On remote machine with display, run:
6
+ * google-chrome --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0
7
+ *
8
+ * Then run this script:
9
+ * npx tsx scripts/test-remote-chrome.ts <remote-host> [port]
10
+ */
11
+ import CDP from 'chrome-remote-interface';
12
+ async function main() {
13
+ const host = process.argv[2] || 'localhost';
14
+ const port = parseInt(process.argv[3] || '9222', 10);
15
+ console.log(`Attempting to connect to Chrome at ${host}:${port}...`);
16
+ try {
17
+ // Test connection
18
+ const client = await CDP({ host, port });
19
+ console.log('✓ Connected to Chrome DevTools Protocol');
20
+ const { Network, Page, Runtime } = client;
21
+ // Enable domains
22
+ await Promise.all([Network.enable(), Page.enable()]);
23
+ console.log('✓ Enabled Network and Page domains');
24
+ // Get browser version info
25
+ const version = await CDP.Version({ host, port });
26
+ console.log(`✓ Browser: ${version.Browser}`);
27
+ console.log(`✓ Protocol: ${version['Protocol-Version']}`);
28
+ // Navigate to ChatGPT
29
+ console.log('\nNavigating to ChatGPT...');
30
+ await Page.navigate({ url: 'https://chatgpt.com/' });
31
+ await Page.loadEventFired();
32
+ console.log('✓ Page loaded');
33
+ // Check current URL
34
+ const evalResult = await Runtime.evaluate({ expression: 'window.location.href' });
35
+ console.log(`✓ Current URL: ${evalResult.result.value}`);
36
+ // Check if logged in (look for specific elements)
37
+ const checkLogin = await Runtime.evaluate({
38
+ expression: `
39
+ // Check for composer textarea (indicates logged in)
40
+ const composer = document.querySelector('textarea, [contenteditable="true"]');
41
+ const hasComposer = !!composer;
42
+
43
+ // Check for login button (indicates logged out)
44
+ const loginBtn = document.querySelector('a[href*="login"], button[data-testid*="login"]');
45
+ const hasLogin = !!loginBtn;
46
+
47
+ ({ hasComposer, hasLogin, loggedIn: hasComposer && !hasLogin })
48
+ `,
49
+ });
50
+ console.log(`✓ Login status: ${JSON.stringify(checkLogin.result.value)}`);
51
+ await client.close();
52
+ console.log('\n✓ POC successful! Remote Chrome connection works.');
53
+ console.log('\nTo use Oracle with remote Chrome, you would need to:');
54
+ console.log('1. Ensure cookies are loaded in remote Chrome');
55
+ console.log('2. Configure Oracle with --remote-chrome <host:port> to use this instance');
56
+ console.log('3. Ensure Oracle skips local Chrome launch when --remote-chrome is specified');
57
+ }
58
+ catch (error) {
59
+ console.error('✗ Connection failed:', error instanceof Error ? error.message : error);
60
+ console.log('\nTroubleshooting:');
61
+ console.log('1. Ensure Chrome is running on remote machine with:');
62
+ console.log(` google-chrome --remote-debugging-port=${port} --remote-debugging-address=0.0.0.0`);
63
+ console.log('2. Check firewall allows connections to port', port);
64
+ console.log('3. Verify network connectivity to', host);
65
+ process.exit(1);
66
+ }
67
+ }
68
+ void main();
@@ -0,0 +1,103 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export function normalizeHostPort(hostname, port) {
4
+ const trimmed = hostname.trim();
5
+ const unwrapped = trimmed.startsWith('[') && trimmed.endsWith(']') ? trimmed.slice(1, -1) : trimmed;
6
+ if (unwrapped.includes(':')) {
7
+ return `[${unwrapped}]:${port}`;
8
+ }
9
+ return `${unwrapped}:${port}`;
10
+ }
11
+ export function parseHostPort(raw) {
12
+ const target = raw.trim();
13
+ if (!target) {
14
+ throw new Error('Expected host:port but received an empty value.');
15
+ }
16
+ const ipv6Match = target.match(/^\[(.+)]:(\d+)$/);
17
+ let hostname;
18
+ let portSegment;
19
+ if (ipv6Match) {
20
+ hostname = ipv6Match[1]?.trim();
21
+ portSegment = ipv6Match[2]?.trim();
22
+ }
23
+ else {
24
+ const lastColon = target.lastIndexOf(':');
25
+ if (lastColon === -1) {
26
+ throw new Error(`Invalid host:port format: ${target}. Expected host:port (IPv6 must use [host]:port notation).`);
27
+ }
28
+ hostname = target.slice(0, lastColon).trim();
29
+ portSegment = target.slice(lastColon + 1).trim();
30
+ if (hostname.includes(':')) {
31
+ throw new Error(`Invalid host:port format: ${target}. Wrap IPv6 addresses in brackets, e.g. "[2001:db8::1]:9473".`);
32
+ }
33
+ }
34
+ if (!hostname) {
35
+ throw new Error(`Invalid host:port format: ${target}. Host portion is missing.`);
36
+ }
37
+ const port = Number.parseInt(portSegment ?? '', 10);
38
+ if (!Number.isFinite(port) || port <= 0 || port > 65_535) {
39
+ throw new Error(`Invalid port: "${portSegment ?? ''}". Expected a number between 1 and 65535.`);
40
+ }
41
+ return { hostname, port };
42
+ }
43
+ export function parseBridgeConnectionString(input) {
44
+ const raw = input.trim();
45
+ if (!raw) {
46
+ throw new Error('Missing connection string.');
47
+ }
48
+ let url;
49
+ try {
50
+ url = raw.includes('://') ? new URL(raw) : new URL(`oracle+tcp://${raw}`);
51
+ }
52
+ catch (error) {
53
+ throw new Error(`Invalid connection string: ${error instanceof Error ? error.message : String(error)}`);
54
+ }
55
+ const hostname = url.hostname?.trim();
56
+ const port = Number.parseInt(url.port ?? '', 10);
57
+ if (!hostname || !Number.isFinite(port) || port <= 0 || port > 65_535) {
58
+ throw new Error(`Invalid connection string host: ${raw}. Expected host:port.`);
59
+ }
60
+ const token = url.searchParams.get('token')?.trim() ?? '';
61
+ if (!token) {
62
+ throw new Error('Connection string is missing token. Expected "?token=...".');
63
+ }
64
+ const remoteHost = normalizeHostPort(hostname, port);
65
+ return { remoteHost, remoteToken: token };
66
+ }
67
+ export function formatBridgeConnectionString(connection, options = {}) {
68
+ const { hostname, port } = parseHostPort(connection.remoteHost);
69
+ const base = `oracle+tcp://${normalizeHostPort(hostname, port)}`;
70
+ if (!options.includeToken) {
71
+ return base;
72
+ }
73
+ const params = new URLSearchParams({ token: connection.remoteToken });
74
+ return `${base}?${params.toString()}`;
75
+ }
76
+ export function looksLikePath(value) {
77
+ return value.includes('/') || value.includes('\\') || value.endsWith('.json');
78
+ }
79
+ export async function readBridgeConnectionArtifact(filePath) {
80
+ const resolved = path.resolve(process.cwd(), filePath);
81
+ const raw = await fs.readFile(resolved, 'utf8');
82
+ let parsed;
83
+ try {
84
+ parsed = JSON.parse(raw);
85
+ }
86
+ catch (error) {
87
+ throw new Error(`Failed to parse connection artifact JSON at ${resolved}: ${error instanceof Error ? error.message : String(error)}`);
88
+ }
89
+ if (!parsed || typeof parsed !== 'object') {
90
+ throw new Error(`Invalid connection artifact at ${resolved}: expected an object.`);
91
+ }
92
+ const remoteHost = parsed.remoteHost;
93
+ const remoteToken = parsed.remoteToken;
94
+ if (typeof remoteHost !== 'string' || remoteHost.trim().length === 0) {
95
+ throw new Error(`Invalid connection artifact at ${resolved}: remoteHost is missing.`);
96
+ }
97
+ if (typeof remoteToken !== 'string' || remoteToken.trim().length === 0) {
98
+ throw new Error(`Invalid connection artifact at ${resolved}: remoteToken is missing.`);
99
+ }
100
+ // Validate host formatting early so downstream checks don't crash.
101
+ parseHostPort(remoteHost);
102
+ return parsed;
103
+ }
@@ -0,0 +1,28 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import JSON5 from 'json5';
4
+ export async function readUserConfigFile(configPath) {
5
+ try {
6
+ const raw = await fs.readFile(configPath, 'utf8');
7
+ const parsed = JSON5.parse(raw);
8
+ return { config: parsed ?? {}, loaded: true };
9
+ }
10
+ catch (error) {
11
+ const code = error.code;
12
+ if (code === 'ENOENT') {
13
+ return { config: {}, loaded: false };
14
+ }
15
+ throw new Error(`Failed to read ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
16
+ }
17
+ }
18
+ export async function writeUserConfigFile(configPath, config) {
19
+ const dir = path.dirname(configPath);
20
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
21
+ const contents = `${JSON.stringify(config, null, 2)}\n`;
22
+ const tempPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
23
+ await fs.writeFile(tempPath, contents, { encoding: 'utf8', mode: 0o600 });
24
+ await fs.rename(tempPath, configPath);
25
+ if (process.platform !== 'win32') {
26
+ await fs.chmod(configPath, 0o600).catch(() => undefined);
27
+ }
28
+ }