@steipete/oracle 0.9.0 → 0.10.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 (177) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +61 -48
  3. package/dist/bin/oracle-cli.js +455 -402
  4. package/dist/bin/oracle-mcp.js +2 -2
  5. package/dist/bin/oracle.js +165 -279
  6. package/dist/scripts/agent-send.js +31 -31
  7. package/dist/scripts/check.js +6 -6
  8. package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
  9. package/dist/scripts/docs-list.js +30 -30
  10. package/dist/scripts/git-policy.js +25 -23
  11. package/dist/scripts/run-cli.js +8 -8
  12. package/dist/scripts/runner.js +203 -195
  13. package/dist/scripts/test-browser.js +21 -18
  14. package/dist/scripts/test-remote-chrome.js +20 -20
  15. package/dist/src/bridge/connection.js +18 -18
  16. package/dist/src/bridge/userConfigFile.js +7 -7
  17. package/dist/src/browser/actions/assistantResponse.js +149 -101
  18. package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
  19. package/dist/src/browser/actions/attachments.js +246 -150
  20. package/dist/src/browser/actions/domEvents.js +2 -2
  21. package/dist/src/browser/actions/modelSelection.js +275 -117
  22. package/dist/src/browser/actions/navigation.js +161 -137
  23. package/dist/src/browser/actions/promptComposer.js +100 -64
  24. package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
  25. package/dist/src/browser/actions/thinkingTime.js +207 -110
  26. package/dist/src/browser/chromeLifecycle.js +62 -60
  27. package/dist/src/browser/config.js +34 -15
  28. package/dist/src/browser/constants.js +17 -12
  29. package/dist/src/browser/cookies.js +19 -19
  30. package/dist/src/browser/detect.js +62 -62
  31. package/dist/src/browser/domDebug.js +1 -1
  32. package/dist/src/browser/index.js +390 -295
  33. package/dist/src/browser/modelStrategy.js +1 -1
  34. package/dist/src/browser/pageActions.js +5 -5
  35. package/dist/src/browser/policies.js +16 -13
  36. package/dist/src/browser/profileState.js +44 -39
  37. package/dist/src/browser/prompt.js +72 -42
  38. package/dist/src/browser/promptSummary.js +5 -5
  39. package/dist/src/browser/providerDomFlow.js +1 -1
  40. package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
  41. package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
  42. package/dist/src/browser/providers/index.js +2 -2
  43. package/dist/src/browser/reattach.js +67 -34
  44. package/dist/src/browser/reattachHelpers.js +31 -26
  45. package/dist/src/browser/sessionRunner.js +37 -25
  46. package/dist/src/browser/utils.js +9 -9
  47. package/dist/src/browserMode.js +1 -1
  48. package/dist/src/cli/bridge/claudeConfig.js +16 -16
  49. package/dist/src/cli/bridge/client.js +28 -20
  50. package/dist/src/cli/bridge/codexConfig.js +16 -16
  51. package/dist/src/cli/bridge/doctor.js +47 -39
  52. package/dist/src/cli/bridge/host.js +58 -56
  53. package/dist/src/cli/browserConfig.js +62 -48
  54. package/dist/src/cli/browserDefaults.js +27 -26
  55. package/dist/src/cli/bundleWarnings.js +1 -1
  56. package/dist/src/cli/clipboard.js +11 -2
  57. package/dist/src/cli/detach.js +2 -2
  58. package/dist/src/cli/dryRun.js +29 -25
  59. package/dist/src/cli/duplicatePromptGuard.js +3 -3
  60. package/dist/src/cli/engine.js +9 -9
  61. package/dist/src/cli/errorUtils.js +1 -1
  62. package/dist/src/cli/fileSize.js +3 -3
  63. package/dist/src/cli/format.js +2 -2
  64. package/dist/src/cli/help.js +28 -28
  65. package/dist/src/cli/hiddenAliases.js +3 -3
  66. package/dist/src/cli/markdownBundle.js +7 -7
  67. package/dist/src/cli/markdownRenderer.js +15 -15
  68. package/dist/src/cli/notifier.js +77 -67
  69. package/dist/src/cli/options.js +127 -106
  70. package/dist/src/cli/oscUtils.js +1 -1
  71. package/dist/src/cli/promptRequirement.js +2 -2
  72. package/dist/src/cli/renderOutput.js +1 -1
  73. package/dist/src/cli/rootAlias.js +1 -1
  74. package/dist/src/cli/runOptions.js +32 -28
  75. package/dist/src/cli/sessionCommand.js +31 -21
  76. package/dist/src/cli/sessionDisplay.js +95 -81
  77. package/dist/src/cli/sessionLineage.js +6 -2
  78. package/dist/src/cli/sessionRunner.js +103 -93
  79. package/dist/src/cli/sessionTable.js +26 -23
  80. package/dist/src/cli/stdin.js +22 -0
  81. package/dist/src/cli/tagline.js +121 -124
  82. package/dist/src/cli/tui/index.js +139 -128
  83. package/dist/src/cli/writeOutputPath.js +5 -5
  84. package/dist/src/config.js +7 -7
  85. package/dist/src/gemini-web/browserSessionManager.js +19 -15
  86. package/dist/src/gemini-web/client.js +76 -70
  87. package/dist/src/gemini-web/executionMode.js +6 -8
  88. package/dist/src/gemini-web/executor.js +98 -93
  89. package/dist/src/gemini-web/index.js +1 -1
  90. package/dist/src/mcp/server.js +16 -12
  91. package/dist/src/mcp/tools/consult.js +51 -47
  92. package/dist/src/mcp/tools/sessionResources.js +12 -12
  93. package/dist/src/mcp/tools/sessions.js +26 -17
  94. package/dist/src/mcp/types.js +5 -5
  95. package/dist/src/mcp/utils.js +15 -7
  96. package/dist/src/oracle/background.js +15 -15
  97. package/dist/src/oracle/claude.js +53 -25
  98. package/dist/src/oracle/client.js +50 -41
  99. package/dist/src/oracle/config.js +96 -66
  100. package/dist/src/oracle/errors.js +38 -38
  101. package/dist/src/oracle/files.js +55 -46
  102. package/dist/src/oracle/finishLine.js +10 -8
  103. package/dist/src/oracle/format.js +3 -3
  104. package/dist/src/oracle/gemini.js +37 -33
  105. package/dist/src/oracle/logging.js +7 -7
  106. package/dist/src/oracle/markdown.js +28 -28
  107. package/dist/src/oracle/modelResolver.js +16 -16
  108. package/dist/src/oracle/multiModelRunner.js +12 -12
  109. package/dist/src/oracle/oscProgress.js +8 -8
  110. package/dist/src/oracle/promptAssembly.js +6 -3
  111. package/dist/src/oracle/request.js +16 -13
  112. package/dist/src/oracle/run.js +156 -134
  113. package/dist/src/oracle/runUtils.js +8 -5
  114. package/dist/src/oracle/tokenEstimate.js +6 -6
  115. package/dist/src/oracle/tokenStats.js +5 -5
  116. package/dist/src/oracle/tokenStringifier.js +5 -5
  117. package/dist/src/oracle.js +12 -12
  118. package/dist/src/oracleHome.js +3 -3
  119. package/dist/src/remote/client.js +25 -25
  120. package/dist/src/remote/health.js +20 -20
  121. package/dist/src/remote/remoteServiceConfig.js +9 -9
  122. package/dist/src/remote/server.js +129 -118
  123. package/dist/src/sessionManager.js +77 -75
  124. package/dist/src/sessionStore.js +3 -3
  125. package/dist/src/version.js +10 -10
  126. package/dist/vendor/oracle-notifier/README.md +2 -0
  127. package/package.json +66 -62
  128. package/vendor/oracle-notifier/README.md +2 -0
  129. package/dist/markdansi/types/index.js +0 -4
  130. package/dist/oracle/bin/oracle-cli.js +0 -472
  131. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  132. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  133. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  134. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  135. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  136. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  137. package/dist/oracle/src/browser/config.js +0 -33
  138. package/dist/oracle/src/browser/constants.js +0 -40
  139. package/dist/oracle/src/browser/cookies.js +0 -210
  140. package/dist/oracle/src/browser/domDebug.js +0 -36
  141. package/dist/oracle/src/browser/index.js +0 -331
  142. package/dist/oracle/src/browser/pageActions.js +0 -5
  143. package/dist/oracle/src/browser/prompt.js +0 -88
  144. package/dist/oracle/src/browser/promptSummary.js +0 -20
  145. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  146. package/dist/oracle/src/browser/types.js +0 -1
  147. package/dist/oracle/src/browser/utils.js +0 -62
  148. package/dist/oracle/src/browserMode.js +0 -1
  149. package/dist/oracle/src/cli/browserConfig.js +0 -44
  150. package/dist/oracle/src/cli/dryRun.js +0 -59
  151. package/dist/oracle/src/cli/engine.js +0 -17
  152. package/dist/oracle/src/cli/errorUtils.js +0 -9
  153. package/dist/oracle/src/cli/help.js +0 -70
  154. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  155. package/dist/oracle/src/cli/options.js +0 -103
  156. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  157. package/dist/oracle/src/cli/rootAlias.js +0 -30
  158. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  159. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  160. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  161. package/dist/oracle/src/heartbeat.js +0 -43
  162. package/dist/oracle/src/oracle/client.js +0 -48
  163. package/dist/oracle/src/oracle/config.js +0 -29
  164. package/dist/oracle/src/oracle/errors.js +0 -101
  165. package/dist/oracle/src/oracle/files.js +0 -220
  166. package/dist/oracle/src/oracle/format.js +0 -33
  167. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  168. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  169. package/dist/oracle/src/oracle/request.js +0 -48
  170. package/dist/oracle/src/oracle/run.js +0 -444
  171. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  172. package/dist/oracle/src/oracle/types.js +0 -1
  173. package/dist/oracle/src/oracle.js +0 -9
  174. package/dist/oracle/src/sessionManager.js +0 -205
  175. package/dist/oracle/src/version.js +0 -39
  176. package/dist/scripts/chrome/browser-tools.js +0 -295
  177. package/dist/src/browser/profileSync.js +0 -141
@@ -1,9 +1,9 @@
1
- import chalk from 'chalk';
2
- import { formatTokenCount } from '../oracle/runUtils.js';
3
- import { formatFinishLine } from '../oracle/finishLine.js';
4
- import { runBrowserMode } from '../browserMode.js';
5
- import { assembleBrowserPrompt } from './prompt.js';
6
- import { BrowserAutomationError } from '../oracle/errors.js';
1
+ import chalk from "chalk";
2
+ import { formatTokenCount } from "../oracle/runUtils.js";
3
+ import { formatFinishLine } from "../oracle/finishLine.js";
4
+ import { runBrowserMode } from "../browserMode.js";
5
+ import { assembleBrowserPrompt } from "./prompt.js";
6
+ import { BrowserAutomationError } from "../oracle/errors.js";
7
7
  export async function runBrowserSessionExecution({ runOptions, browserConfig, cwd, log }, deps = {}) {
8
8
  const assemblePrompt = deps.assemblePrompt ?? assembleBrowserPrompt;
9
9
  const executeBrowser = deps.executeBrowser ?? runBrowserMode;
@@ -14,14 +14,18 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
14
14
  })}`));
15
15
  log(chalk.dim(`[verbose] Browser prompt length: ${promptArtifacts.composerText.length} chars`));
16
16
  if (promptArtifacts.attachments.length > 0) {
17
- const attachmentList = promptArtifacts.attachments.map((attachment) => attachment.displayPath).join(', ');
17
+ const attachmentList = promptArtifacts.attachments
18
+ .map((attachment) => attachment.displayPath)
19
+ .join(", ");
18
20
  log(chalk.dim(`[verbose] Browser attachments: ${attachmentList}`));
19
21
  if (promptArtifacts.bundled) {
20
22
  log(chalk.yellow(`[browser] Bundled ${promptArtifacts.bundled.originalCount} files into ${promptArtifacts.bundled.bundlePath}.`));
21
23
  }
22
24
  }
23
- else if (runOptions.file && runOptions.file.length > 0 && promptArtifacts.attachmentMode === 'inline') {
24
- log(chalk.dim('[verbose] Browser will paste file contents inline (no uploads).'));
25
+ else if (runOptions.file &&
26
+ runOptions.file.length > 0 &&
27
+ promptArtifacts.attachmentMode === "inline") {
28
+ log(chalk.dim("[verbose] Browser will paste file contents inline (no uploads)."));
25
29
  }
26
30
  }
27
31
  if (promptArtifacts.bundled) {
@@ -29,19 +33,19 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
29
33
  }
30
34
  const headerLine = `Launching browser mode (${runOptions.model}) with ~${promptArtifacts.estimatedInputTokens.toLocaleString()} tokens.`;
31
35
  const automationLogger = ((message) => {
32
- if (typeof message !== 'string')
36
+ if (typeof message !== "string")
33
37
  return;
34
- const shouldAlwaysPrint = message.startsWith('[browser] ') && /fallback|retry/i.test(message);
38
+ const shouldAlwaysPrint = message.startsWith("[browser] ") && /fallback|retry/i.test(message);
35
39
  if (!runOptions.verbose && !shouldAlwaysPrint)
36
40
  return;
37
41
  log(message);
38
42
  });
39
43
  automationLogger.verbose = Boolean(runOptions.verbose);
40
- automationLogger.sessionLog = runOptions.verbose ? log : (() => { });
44
+ automationLogger.sessionLog = runOptions.verbose ? log : () => { };
41
45
  log(headerLine);
42
- log(chalk.dim('This run can take up to an hour (usually ~10 minutes).'));
46
+ log(chalk.dim("This run can take up to an hour (usually ~10 minutes)."));
43
47
  if (runOptions.verbose) {
44
- log(chalk.dim('Chrome automation does not stream output; this may take a minute...'));
48
+ log(chalk.dim("Chrome automation does not stream output; this may take a minute..."));
45
49
  }
46
50
  const persistRuntimeHint = deps.persistRuntimeHint ?? (() => { });
47
51
  let browserResult;
@@ -50,14 +54,20 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
50
54
  prompt: promptArtifacts.composerText,
51
55
  attachments: promptArtifacts.attachments,
52
56
  fallbackSubmission: promptArtifacts.fallback
53
- ? { prompt: promptArtifacts.fallback.composerText, attachments: promptArtifacts.fallback.attachments }
57
+ ? {
58
+ prompt: promptArtifacts.fallback.composerText,
59
+ attachments: promptArtifacts.fallback.attachments,
60
+ }
54
61
  : undefined,
55
62
  config: browserConfig,
56
63
  log: automationLogger,
57
64
  heartbeatIntervalMs: runOptions.heartbeatIntervalMs,
58
65
  verbose: runOptions.verbose,
59
66
  runtimeHintCb: async (runtime) => {
60
- await persistRuntimeHint({ ...runtime, controllerPid: runtime.controllerPid ?? process.pid });
67
+ await persistRuntimeHint({
68
+ ...runtime,
69
+ controllerPid: runtime.controllerPid ?? process.pid,
70
+ });
61
71
  },
62
72
  });
63
73
  }
@@ -65,15 +75,15 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
65
75
  if (error instanceof BrowserAutomationError) {
66
76
  throw error;
67
77
  }
68
- const message = error instanceof Error ? error.message : 'Browser automation failed.';
69
- throw new BrowserAutomationError(message, { stage: 'execute-browser' }, error);
78
+ const message = error instanceof Error ? error.message : "Browser automation failed.";
79
+ throw new BrowserAutomationError(message, { stage: "execute-browser" }, error);
70
80
  }
71
81
  if (!runOptions.silent) {
72
- log(chalk.bold('Answer:'));
73
- log(browserResult.answerMarkdown || browserResult.answerText || chalk.dim('(no text output)'));
74
- log('');
82
+ log(chalk.bold("Answer:"));
83
+ log(browserResult.answerMarkdown || browserResult.answerText || chalk.dim("(no text output)"));
84
+ log("");
75
85
  }
76
- const answerText = browserResult.answerMarkdown || browserResult.answerText || '';
86
+ const answerText = browserResult.answerMarkdown || browserResult.answerText || "";
77
87
  const usage = {
78
88
  inputTokens: promptArtifacts.estimatedInputTokens,
79
89
  outputTokens: browserResult.answerTokens,
@@ -87,9 +97,9 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
87
97
  usage.totalTokens,
88
98
  ]
89
99
  .map((value) => formatTokenCount(value))
90
- .join('/');
100
+ .join("/");
91
101
  const tokensPart = (() => {
92
- const parts = tokensDisplay.split('/');
102
+ const parts = tokensDisplay.split("/");
93
103
  if (parts.length !== 4)
94
104
  return tokensDisplay;
95
105
  return `↑${parts[0]} ↓${parts[1]} ↻${parts[2]} Δ${parts[3]}`;
@@ -98,7 +108,9 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
98
108
  elapsedMs: browserResult.tookMs,
99
109
  model: `${runOptions.model}[browser]`,
100
110
  tokensPart,
101
- detailParts: [runOptions.file && runOptions.file.length > 0 ? `files=${runOptions.file.length}` : null],
111
+ detailParts: [
112
+ runOptions.file && runOptions.file.length > 0 ? `files=${runOptions.file.length}` : null,
113
+ ],
102
114
  });
103
115
  log(chalk.blue(line1));
104
116
  if (line2) {
@@ -10,7 +10,7 @@ export function parseDuration(input, fallback) {
10
10
  if (/^[0-9]+$/.test(lowercase)) {
11
11
  return Number(lowercase);
12
12
  }
13
- const normalized = lowercase.replace(/\s+/g, '');
13
+ const normalized = lowercase.replace(/\s+/g, "");
14
14
  const singleMatch = /^([0-9]+)(ms|s|m|h)$/i.exec(normalized);
15
15
  if (singleMatch && singleMatch[0].length === normalized.length) {
16
16
  const value = Number(singleMatch[1]);
@@ -33,13 +33,13 @@ export function parseDuration(input, fallback) {
33
33
  function convertUnit(value, unitRaw) {
34
34
  const unit = unitRaw?.toLowerCase();
35
35
  switch (unit) {
36
- case 'ms':
36
+ case "ms":
37
37
  return value;
38
- case 's':
38
+ case "s":
39
39
  return value * 1000;
40
- case 'm':
40
+ case "m":
41
41
  return value * 60_000;
42
- case 'h':
42
+ case "h":
43
43
  return value * 3_600_000;
44
44
  default:
45
45
  return value;
@@ -72,11 +72,11 @@ export async function withRetries(task, options = {}) {
72
72
  await delay(delayMs * attempt);
73
73
  }
74
74
  }
75
- throw new Error('withRetries exhausted without result');
75
+ throw new Error("withRetries exhausted without result");
76
76
  }
77
77
  export function formatBytes(size) {
78
78
  if (!Number.isFinite(size) || size < 0) {
79
- return 'n/a';
79
+ return "n/a";
80
80
  }
81
81
  if (size < 1024) {
82
82
  return `${size} B`;
@@ -113,8 +113,8 @@ export function normalizeChatgptUrl(raw, fallback) {
113
113
  export function isTemporaryChatUrl(url) {
114
114
  try {
115
115
  const parsed = new URL(url);
116
- const value = (parsed.searchParams.get('temporary-chat') ?? '').trim().toLowerCase();
117
- return value === 'true' || value === '1' || value === 'yes';
116
+ const value = (parsed.searchParams.get("temporary-chat") ?? "").trim().toLowerCase();
117
+ return value === "true" || value === "1" || value === "yes";
118
118
  }
119
119
  catch {
120
120
  return false;
@@ -1 +1 @@
1
- export { runBrowserMode, CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, parseDuration, normalizeChatgptUrl, isTemporaryChatUrl, } from './browser/index.js';
1
+ export { runBrowserMode, CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, parseDuration, normalizeChatgptUrl, isTemporaryChatUrl, } from "./browser/index.js";
@@ -1,8 +1,8 @@
1
- import chalk from 'chalk';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { loadUserConfig } from '../../config.js';
5
- import { resolveRemoteServiceConfig } from '../../remote/remoteServiceConfig.js';
1
+ import chalk from "chalk";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { loadUserConfig } from "../../config.js";
5
+ import { resolveRemoteServiceConfig } from "../../remote/remoteServiceConfig.js";
6
6
  export async function runBridgeClaudeConfig(options) {
7
7
  const { config: userConfig } = await loadUserConfig();
8
8
  const resolved = resolveRemoteServiceConfig({
@@ -12,40 +12,40 @@ export async function runBridgeClaudeConfig(options) {
12
12
  env: process.env,
13
13
  });
14
14
  const snippet = formatClaudeMcpConfig({
15
- oracleHomeDir: process.env.ORACLE_HOME_DIR ?? path.join(os.homedir(), '.oracle-local'),
15
+ oracleHomeDir: process.env.ORACLE_HOME_DIR ?? path.join(os.homedir(), ".oracle-local"),
16
16
  browserProfileDir: process.env.ORACLE_BROWSER_PROFILE_DIR ??
17
- path.join(os.homedir(), '.oracle-local', 'browser-profile'),
17
+ path.join(os.homedir(), ".oracle-local", "browser-profile"),
18
18
  remoteHost: resolved.host,
19
19
  remoteToken: resolved.token,
20
20
  includeToken: Boolean(options.printToken),
21
21
  });
22
22
  console.log(snippet);
23
23
  if (!options.printToken) {
24
- console.log('');
25
- console.log(chalk.dim('Tip: rerun with --print-token to include ORACLE_REMOTE_TOKEN in the snippet.'));
24
+ console.log("");
25
+ console.log(chalk.dim("Tip: rerun with --print-token to include ORACLE_REMOTE_TOKEN in the snippet."));
26
26
  }
27
27
  }
28
28
  export function formatClaudeMcpConfig({ oracleHomeDir, browserProfileDir, remoteHost, remoteToken, includeToken, }) {
29
29
  const env = {};
30
30
  // biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
31
- env['ORACLE_ENGINE'] = 'browser';
31
+ env["ORACLE_ENGINE"] = "browser";
32
32
  // biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
33
- env['ORACLE_HOME_DIR'] = oracleHomeDir;
33
+ env["ORACLE_HOME_DIR"] = oracleHomeDir;
34
34
  // biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
35
- env['ORACLE_BROWSER_PROFILE_DIR'] = browserProfileDir;
35
+ env["ORACLE_BROWSER_PROFILE_DIR"] = browserProfileDir;
36
36
  if (remoteHost) {
37
37
  // biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
38
- env['ORACLE_REMOTE_HOST'] = remoteHost;
38
+ env["ORACLE_REMOTE_HOST"] = remoteHost;
39
39
  // biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
40
- env['ORACLE_REMOTE_TOKEN'] = includeToken ? remoteToken ?? '<YOUR_TOKEN>' : '<YOUR_TOKEN>';
40
+ env["ORACLE_REMOTE_TOKEN"] = includeToken ? (remoteToken ?? "<YOUR_TOKEN>") : "<YOUR_TOKEN>";
41
41
  }
42
42
  // Claude Code supports project-scoped `.mcp.json` config files:
43
43
  // https://docs.anthropic.com/en/docs/claude-code/mcp
44
44
  return JSON.stringify({
45
45
  mcpServers: {
46
46
  oracle: {
47
- type: 'stdio',
48
- command: 'oracle-mcp',
47
+ type: "stdio",
48
+ command: "oracle-mcp",
49
49
  args: [],
50
50
  env,
51
51
  },
@@ -1,29 +1,33 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import chalk from 'chalk';
4
- import { configPath as defaultConfigPath } from '../../config.js';
5
- import { parseBridgeConnectionString, readBridgeConnectionArtifact, looksLikePath, } from '../../bridge/connection.js';
6
- import { readUserConfigFile, writeUserConfigFile } from '../../bridge/userConfigFile.js';
7
- import { checkRemoteHealth } from '../../remote/health.js';
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import chalk from "chalk";
4
+ import { configPath as defaultConfigPath } from "../../config.js";
5
+ import { parseBridgeConnectionString, readBridgeConnectionArtifact, looksLikePath, } from "../../bridge/connection.js";
6
+ import { readUserConfigFile, writeUserConfigFile } from "../../bridge/userConfigFile.js";
7
+ import { checkRemoteHealth } from "../../remote/health.js";
8
8
  export async function runBridgeClient(options) {
9
9
  const connectRaw = options.connect?.trim();
10
10
  if (!connectRaw) {
11
- throw new Error('Missing --connect. Provide a connection string or a bridge-connection.json path.');
11
+ throw new Error("Missing --connect. Provide a connection string or a bridge-connection.json path.");
12
12
  }
13
13
  const { remoteHost, remoteToken, tunnel } = await resolveConnection(connectRaw);
14
14
  if (options.test !== false) {
15
- const health = await checkRemoteHealth({ host: remoteHost, token: remoteToken, timeoutMs: 5000 });
15
+ const health = await checkRemoteHealth({
16
+ host: remoteHost,
17
+ token: remoteToken,
18
+ timeoutMs: 5000,
19
+ });
16
20
  if (!health.ok) {
17
- const suffix = health.statusCode ? ` (HTTP ${health.statusCode})` : '';
18
- throw new Error(`Remote service health check failed: ${health.error ?? 'unknown error'}${suffix}`);
21
+ const suffix = health.statusCode ? ` (HTTP ${health.statusCode})` : "";
22
+ throw new Error(`Remote service health check failed: ${health.error ?? "unknown error"}${suffix}`);
19
23
  }
20
- console.log(chalk.green(`Remote service OK (${remoteHost})${health.version ? ` — oracle ${health.version}` : ''}`));
24
+ console.log(chalk.green(`Remote service OK (${remoteHost})${health.version ? ` — oracle ${health.version}` : ""}`));
21
25
  }
22
26
  const configFilePath = options.config?.trim() || defaultConfigPath();
23
27
  if (options.writeConfig !== false) {
24
28
  const { config } = await readUserConfigFile(configFilePath);
25
- const next = { ...config, browser: { ...(config.browser ?? {}) } };
26
- next.browser = { ...(next.browser ?? {}) };
29
+ const next = { ...config, browser: { ...config.browser } };
30
+ next.browser = { ...next.browser };
27
31
  next.browser.remoteHost = remoteHost;
28
32
  next.browser.remoteToken = remoteToken;
29
33
  if (tunnel) {
@@ -38,19 +42,19 @@ export async function runBridgeClient(options) {
38
42
  await writeUserConfigFile(configFilePath, next);
39
43
  console.log(chalk.green(`Wrote remote config to ${configFilePath}`));
40
44
  }
41
- console.log('');
42
- console.log('Next:');
45
+ console.log("");
46
+ console.log("Next:");
43
47
  console.log(chalk.dim(`- oracle --engine browser -p "hello" --file README.md`));
44
48
  if (options.printEnv) {
45
- console.log('');
46
- console.log('# Optional env overrides (paste into your shell):');
49
+ console.log("");
50
+ console.log("# Optional env overrides (paste into your shell):");
47
51
  console.log(`export ORACLE_ENGINE=browser`);
48
52
  console.log(`export ORACLE_REMOTE_HOST=${shellQuote(remoteHost)}`);
49
53
  console.log(`export ORACLE_REMOTE_TOKEN=${shellQuote(remoteToken)}`);
50
54
  }
51
55
  }
52
56
  async function resolveConnection(input) {
53
- if (input.includes('://')) {
57
+ if (input.includes("://")) {
54
58
  return { ...parseBridgeConnectionString(input) };
55
59
  }
56
60
  const resolvedPath = looksLikePath(input) ? path.resolve(process.cwd(), input) : null;
@@ -58,7 +62,11 @@ async function resolveConnection(input) {
58
62
  const stat = await fs.stat(resolvedPath).catch(() => null);
59
63
  if (stat?.isFile()) {
60
64
  const artifact = await readBridgeConnectionArtifact(resolvedPath);
61
- return { remoteHost: artifact.remoteHost, remoteToken: artifact.remoteToken, tunnel: artifact.tunnel };
65
+ return {
66
+ remoteHost: artifact.remoteHost,
67
+ remoteToken: artifact.remoteToken,
68
+ tunnel: artifact.tunnel,
69
+ };
62
70
  }
63
71
  if (stat) {
64
72
  throw new Error(`--connect points to ${resolvedPath}, but it is not a file.`);
@@ -1,6 +1,6 @@
1
- import chalk from 'chalk';
2
- import { loadUserConfig } from '../../config.js';
3
- import { resolveRemoteServiceConfig } from '../../remote/remoteServiceConfig.js';
1
+ import chalk from "chalk";
2
+ import { loadUserConfig } from "../../config.js";
3
+ import { resolveRemoteServiceConfig } from "../../remote/remoteServiceConfig.js";
4
4
  export async function runBridgeCodexConfig(options) {
5
5
  const { config: userConfig } = await loadUserConfig();
6
6
  const resolved = resolveRemoteServiceConfig({
@@ -16,28 +16,28 @@ export async function runBridgeCodexConfig(options) {
16
16
  });
17
17
  console.log(snippet);
18
18
  if (!options.printToken) {
19
- console.log('');
20
- console.log(chalk.dim('Tip: rerun with --print-token to include ORACLE_REMOTE_TOKEN in the snippet.'));
19
+ console.log("");
20
+ console.log(chalk.dim("Tip: rerun with --print-token to include ORACLE_REMOTE_TOKEN in the snippet."));
21
21
  }
22
22
  }
23
23
  export function formatCodexMcpSnippet({ remoteHost, remoteToken, includeToken, }) {
24
- const hostValue = remoteHost ?? '127.0.0.1:9473';
25
- const tokenValue = includeToken ? remoteToken ?? '<YOUR_TOKEN>' : '<YOUR_TOKEN>';
24
+ const hostValue = remoteHost ?? "127.0.0.1:9473";
25
+ const tokenValue = includeToken ? (remoteToken ?? "<YOUR_TOKEN>") : "<YOUR_TOKEN>";
26
26
  return [
27
- '# ~/.codex/config.toml',
28
- '',
29
- '[mcp.servers.oracle]',
27
+ "# ~/.codex/config.toml",
28
+ "",
29
+ "[mcp.servers.oracle]",
30
30
  'command = "oracle-mcp"',
31
- 'args = []',
31
+ "args = []",
32
32
  `env = { ORACLE_ENGINE = "browser", ORACLE_REMOTE_HOST = "${escapeTomlString(hostValue)}", ORACLE_REMOTE_TOKEN = "${escapeTomlString(tokenValue)}" }`,
33
- '',
34
- '# If you prefer npx:',
35
- '# [mcp.servers.oracle]',
33
+ "",
34
+ "# If you prefer npx:",
35
+ "# [mcp.servers.oracle]",
36
36
  '# command = "npx"',
37
37
  '# args = ["-y", "@steipete/oracle", "oracle-mcp"]',
38
38
  `# env = { ORACLE_ENGINE = "browser", ORACLE_REMOTE_HOST = "${escapeTomlString(hostValue)}", ORACLE_REMOTE_TOKEN = "${escapeTomlString(tokenValue)}" }`,
39
- ].join('\n');
39
+ ].join("\n");
40
40
  }
41
41
  function escapeTomlString(value) {
42
- return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
42
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
43
43
  }
@@ -1,11 +1,11 @@
1
- import os from 'node:os';
2
- import chalk from 'chalk';
3
- import { getCliVersion } from '../../version.js';
4
- import { loadUserConfig } from '../../config.js';
5
- import { resolveRemoteServiceConfig } from '../../remote/remoteServiceConfig.js';
6
- import { checkTcpConnection, checkRemoteHealth } from '../../remote/health.js';
7
- import { detectChromeBinary, detectChromeCookieDb } from '../../browser/detect.js';
8
- import { formatCodexMcpSnippet } from './codexConfig.js';
1
+ import os from "node:os";
2
+ import chalk from "chalk";
3
+ import { getCliVersion } from "../../version.js";
4
+ import { loadUserConfig } from "../../config.js";
5
+ import { resolveRemoteServiceConfig } from "../../remote/remoteServiceConfig.js";
6
+ import { checkTcpConnection, checkRemoteHealth } from "../../remote/health.js";
7
+ import { detectChromeBinary, detectChromeCookieDb } from "../../browser/detect.js";
8
+ import { formatCodexMcpSnippet } from "./codexConfig.js";
9
9
  export async function runBridgeDoctor(_options) {
10
10
  const { config: userConfig, path: configPath, loaded } = await loadUserConfig();
11
11
  const version = getCliVersion();
@@ -18,90 +18,98 @@ export async function runBridgeDoctor(_options) {
18
18
  const lines = [];
19
19
  const fail = [];
20
20
  const warn = [];
21
- lines.push(chalk.bold('Bridge doctor'));
21
+ lines.push(chalk.bold("Bridge doctor"));
22
22
  lines.push(chalk.dim(`OS: ${process.platform} ${os.release()} (${process.arch})`));
23
23
  lines.push(chalk.dim(`Node: ${process.version}`));
24
24
  lines.push(chalk.dim(`Oracle: ${version}`));
25
- lines.push(chalk.dim(`Config: ${loaded ? configPath : '(missing)'}`));
25
+ lines.push(chalk.dim(`Config: ${loaded ? configPath : "(missing)"}`));
26
26
  if (userConfig.engine) {
27
27
  lines.push(chalk.dim(`Default engine: ${userConfig.engine}`));
28
28
  }
29
29
  if (userConfig.model) {
30
30
  lines.push(chalk.dim(`Default model: ${userConfig.model}`));
31
31
  }
32
- lines.push('');
33
- lines.push(chalk.bold('Browser mode'));
32
+ lines.push("");
33
+ lines.push(chalk.bold("Browser mode"));
34
34
  if (resolvedRemote.host) {
35
- lines.push(`Remote service: ${chalk.green('configured')}`);
35
+ lines.push(`Remote service: ${chalk.green("configured")}`);
36
36
  lines.push(chalk.dim(`remoteHost: ${resolvedRemote.host} (${resolvedRemote.sources.host})`));
37
- lines.push(chalk.dim(`remoteToken: ${resolvedRemote.token ? 'set' : 'missing'} (${resolvedRemote.sources.token})`));
37
+ lines.push(chalk.dim(`remoteToken: ${resolvedRemote.token ? "set" : "missing"} (${resolvedRemote.sources.token})`));
38
38
  const tcp = await checkTcpConnection(resolvedRemote.host, 2000);
39
39
  if (tcp.ok) {
40
- lines.push(chalk.dim(`TCP connect: ${chalk.green('ok')}`));
40
+ lines.push(chalk.dim(`TCP connect: ${chalk.green("ok")}`));
41
41
  }
42
42
  else {
43
- fail.push(`Cannot reach ${resolvedRemote.host} (${tcp.error ?? 'unknown error'}).`);
44
- lines.push(chalk.dim(`TCP connect: ${chalk.red(`failed (${tcp.error ?? 'unknown error'})`)}`));
43
+ fail.push(`Cannot reach ${resolvedRemote.host} (${tcp.error ?? "unknown error"}).`);
44
+ lines.push(chalk.dim(`TCP connect: ${chalk.red(`failed (${tcp.error ?? "unknown error"})`)}`));
45
45
  }
46
46
  if (!resolvedRemote.token) {
47
- fail.push('Remote token is missing. Run `oracle bridge client --connect <...> --write-config` or set ORACLE_REMOTE_TOKEN.');
47
+ fail.push("Remote token is missing. Run `oracle bridge client --connect <...> --write-config` or set ORACLE_REMOTE_TOKEN.");
48
48
  }
49
49
  else if (tcp.ok) {
50
- const health = await checkRemoteHealth({ host: resolvedRemote.host, token: resolvedRemote.token, timeoutMs: 5000 });
50
+ const health = await checkRemoteHealth({
51
+ host: resolvedRemote.host,
52
+ token: resolvedRemote.token,
53
+ timeoutMs: 5000,
54
+ });
51
55
  if (health.ok) {
52
- const meta = health.version ? `oracle ${health.version}` : 'ok';
56
+ const meta = health.version ? `oracle ${health.version}` : "ok";
53
57
  lines.push(chalk.dim(`Auth (/health): ${chalk.green(meta)}`));
54
58
  }
55
59
  else {
56
- const detail = health.error ?? 'unknown error';
60
+ const detail = health.error ?? "unknown error";
57
61
  fail.push(`Remote auth failed: ${detail}`);
58
- const suffix = health.statusCode ? `HTTP ${health.statusCode}` : 'network';
62
+ const suffix = health.statusCode ? `HTTP ${health.statusCode}` : "network";
59
63
  lines.push(chalk.dim(`Auth (/health): ${chalk.red(`${suffix} (${detail})`)}`));
60
64
  }
61
65
  }
62
66
  }
63
67
  else {
64
- lines.push(`Remote service: ${chalk.yellow('not configured')}`);
68
+ lines.push(`Remote service: ${chalk.yellow("not configured")}`);
65
69
  const chrome = await detectChromeBinary();
66
70
  if (chrome.path) {
67
71
  lines.push(chalk.dim(`Chrome: ${chalk.green(chrome.path)}`));
68
72
  }
69
73
  else {
70
- fail.push('No Chrome installation detected. Install Chrome/Chromium or set --browser-chrome-path.');
71
- lines.push(chalk.dim(`Chrome: ${chalk.red('not found')}`));
74
+ fail.push("No Chrome installation detected. Install Chrome/Chromium or set --browser-chrome-path.");
75
+ lines.push(chalk.dim(`Chrome: ${chalk.red("not found")}`));
72
76
  }
73
- if (process.platform === 'win32') {
74
- warn.push('Cookie sync is disabled on Windows; use --browser-manual-login or run browser automation on another host.');
75
- lines.push(chalk.dim('Cookies: (cookie sync disabled on Windows)'));
77
+ if (process.platform === "win32") {
78
+ warn.push("Cookie sync is disabled on Windows; use --browser-manual-login or run browser automation on another host.");
79
+ lines.push(chalk.dim("Cookies: (cookie sync disabled on Windows)"));
76
80
  }
77
81
  else {
78
- const cookieDb = await detectChromeCookieDb({ profile: 'Default' });
82
+ const cookieDb = await detectChromeCookieDb({ profile: "Default" });
79
83
  if (cookieDb) {
80
84
  lines.push(chalk.dim(`Cookies DB: ${chalk.green(cookieDb)}`));
81
85
  }
82
86
  else {
83
- warn.push('Chrome cookies DB not detected. You may need --browser-cookie-path or --browser-manual-login.');
84
- lines.push(chalk.dim(`Cookies DB: ${chalk.yellow('not found')}`));
87
+ warn.push("Chrome cookies DB not detected. You may need --browser-cookie-path or --browser-manual-login.");
88
+ lines.push(chalk.dim(`Cookies DB: ${chalk.yellow("not found")}`));
85
89
  }
86
90
  }
87
91
  }
88
- lines.push('');
89
- lines.push(chalk.bold('Codex MCP'));
90
- lines.push(formatCodexMcpSnippet({ remoteHost: resolvedRemote.host, remoteToken: resolvedRemote.token, includeToken: false }));
92
+ lines.push("");
93
+ lines.push(chalk.bold("Codex MCP"));
94
+ lines.push(formatCodexMcpSnippet({
95
+ remoteHost: resolvedRemote.host,
96
+ remoteToken: resolvedRemote.token,
97
+ includeToken: false,
98
+ }));
91
99
  if (warn.length) {
92
- lines.push('');
93
- lines.push(chalk.yellowBright('Warnings:'));
100
+ lines.push("");
101
+ lines.push(chalk.yellowBright("Warnings:"));
94
102
  for (const message of warn) {
95
103
  lines.push(chalk.yellow(`- ${message}`));
96
104
  }
97
105
  }
98
106
  if (fail.length) {
99
- lines.push('');
100
- lines.push(chalk.redBright('Problems:'));
107
+ lines.push("");
108
+ lines.push(chalk.redBright("Problems:"));
101
109
  for (const message of fail) {
102
110
  lines.push(chalk.red(`- ${message}`));
103
111
  }
104
112
  }
105
- console.log(lines.join('\n'));
113
+ console.log(lines.join("\n"));
106
114
  process.exitCode = fail.length ? 1 : 0;
107
115
  }