@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,26 +1,28 @@
1
- import { spawn } from 'node:child_process';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { randomBytes } from 'node:crypto';
5
- import chalk from 'chalk';
6
- import { getOracleHomeDir } from '../../oracleHome.js';
7
- import { parseHostPort, normalizeHostPort, formatBridgeConnectionString } from '../../bridge/connection.js';
8
- import { serveRemote } from '../../remote/server.js';
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { randomBytes } from "node:crypto";
5
+ import chalk from "chalk";
6
+ import { getOracleHomeDir } from "../../oracleHome.js";
7
+ import { parseHostPort, normalizeHostPort, formatBridgeConnectionString, } from "../../bridge/connection.js";
8
+ import { serveRemote } from "../../remote/server.js";
9
9
  export async function runBridgeHost(options) {
10
- const bindRaw = options.bind?.trim() || '127.0.0.1:9473';
10
+ const bindRaw = options.bind?.trim() || "127.0.0.1:9473";
11
11
  const { hostname: bindHost, port: bindPort } = parseHostPort(bindRaw);
12
- const tokenRaw = options.token?.trim() || 'auto';
13
- const token = tokenRaw === 'auto' ? randomBytes(16).toString('hex') : tokenRaw;
12
+ const tokenRaw = options.token?.trim() || "auto";
13
+ const token = tokenRaw === "auto" ? randomBytes(16).toString("hex") : tokenRaw;
14
14
  if (!token.trim()) {
15
- throw new Error('Token is required (use --token auto to generate one).');
15
+ throw new Error("Token is required (use --token auto to generate one).");
16
16
  }
17
- const writeConnectionPath = options.writeConnection?.trim() || path.join(getOracleHomeDir(), 'bridge-connection.json');
17
+ const writeConnectionPath = options.writeConnection?.trim() || path.join(getOracleHomeDir(), "bridge-connection.json");
18
18
  const sshTarget = options.ssh?.trim();
19
- const sshRemotePort = typeof options.sshRemotePort === 'number' ? options.sshRemotePort : bindPort;
19
+ const sshRemotePort = typeof options.sshRemotePort === "number" ? options.sshRemotePort : bindPort;
20
20
  if (sshRemotePort <= 0 || sshRemotePort > 65_535) {
21
21
  throw new Error(`Invalid --ssh-remote-port: ${sshRemotePort}. Expected 1-65535.`);
22
22
  }
23
- const connectionHostForClient = sshTarget ? normalizeHostPort('127.0.0.1', sshRemotePort) : normalizeHostPort(bindHost === '0.0.0.0' || bindHost === '::' ? '127.0.0.1' : bindHost, bindPort);
23
+ const connectionHostForClient = sshTarget
24
+ ? normalizeHostPort("127.0.0.1", sshRemotePort)
25
+ : normalizeHostPort(bindHost === "0.0.0.0" || bindHost === "::" ? "127.0.0.1" : bindHost, bindPort);
24
26
  const artifact = await upsertConnectionArtifact(writeConnectionPath, {
25
27
  remoteHost: connectionHostForClient,
26
28
  remoteToken: token,
@@ -52,11 +54,11 @@ export async function runBridgeHost(options) {
52
54
  });
53
55
  return;
54
56
  }
55
- console.log(chalk.cyanBright('Bridge host starting...'));
57
+ console.log(chalk.cyanBright("Bridge host starting..."));
56
58
  console.log(chalk.dim(`- Local bind: ${normalizeHostPort(bindHost, bindPort)}`));
57
59
  console.log(chalk.dim(`- Connection artifact: ${writeConnectionPath}`));
58
60
  console.log(chalk.dim(`- Client remoteHost: ${artifact.remoteHost}`));
59
- console.log(chalk.dim('Token stored in connection artifact (not printed). Use --print or --print-token if needed.'));
61
+ console.log(chalk.dim("Token stored in connection artifact (not printed). Use --print or --print-token if needed."));
60
62
  let tunnel = null;
61
63
  if (sshTarget) {
62
64
  tunnel = startReverseTunnel({
@@ -70,7 +72,7 @@ export async function runBridgeHost(options) {
70
72
  console.log(chalk.dim(`Reverse SSH tunnel active (remote 127.0.0.1:${sshRemotePort} -> local 127.0.0.1:${bindPort})`));
71
73
  }
72
74
  const filteredServeLogger = (message) => {
73
- if (message.includes('Access token:')) {
75
+ if (message.includes("Access token:")) {
74
76
  return;
75
77
  }
76
78
  console.log(message);
@@ -91,12 +93,12 @@ async function upsertConnectionArtifact(filePath, input) {
91
93
  const dir = path.dirname(filePath);
92
94
  await fs.mkdir(dir, { recursive: true, mode: 0o700 });
93
95
  const now = new Date().toISOString();
94
- const existing = await fs.readFile(filePath, 'utf8').catch(() => null);
96
+ const existing = await fs.readFile(filePath, "utf8").catch(() => null);
95
97
  let createdAt = now;
96
98
  if (existing) {
97
99
  try {
98
100
  const parsed = JSON.parse(existing);
99
- if (typeof parsed.createdAt === 'string' && parsed.createdAt.trim().length > 0) {
101
+ if (typeof parsed.createdAt === "string" && parsed.createdAt.trim().length > 0) {
100
102
  createdAt = parsed.createdAt;
101
103
  }
102
104
  }
@@ -113,9 +115,9 @@ async function upsertConnectionArtifact(filePath, input) {
113
115
  };
114
116
  const contents = `${JSON.stringify(artifact, null, 2)}\n`;
115
117
  const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
116
- await fs.writeFile(tempPath, contents, { encoding: 'utf8', mode: 0o600 });
118
+ await fs.writeFile(tempPath, contents, { encoding: "utf8", mode: 0o600 });
117
119
  await fs.rename(tempPath, filePath);
118
- if (process.platform !== 'win32') {
120
+ if (process.platform !== "win32") {
119
121
  await fs.chmod(filePath, 0o600).catch(() => undefined);
120
122
  }
121
123
  return artifact;
@@ -129,27 +131,27 @@ function startReverseTunnel({ sshTarget, remotePort, localPort, identity, extraA
129
131
  if (stopped)
130
132
  return;
131
133
  const args = [
132
- '-N',
133
- '-R',
134
+ "-N",
135
+ "-R",
134
136
  `${remotePort}:127.0.0.1:${localPort}`,
135
- '-o',
136
- 'ExitOnForwardFailure=yes',
137
- '-o',
138
- 'ServerAliveInterval=30',
139
- '-o',
140
- 'ServerAliveCountMax=3',
137
+ "-o",
138
+ "ExitOnForwardFailure=yes",
139
+ "-o",
140
+ "ServerAliveInterval=30",
141
+ "-o",
142
+ "ServerAliveCountMax=3",
141
143
  ];
142
144
  if (identity) {
143
- args.push('-i', identity);
145
+ args.push("-i", identity);
144
146
  }
145
147
  if (extraArgs) {
146
148
  args.push(...splitArgs(extraArgs));
147
149
  }
148
150
  args.push(sshTarget);
149
- child = spawn('ssh', args, { stdio: 'ignore' });
151
+ child = spawn("ssh", args, { stdio: "ignore" });
150
152
  const pid = child.pid;
151
- log(`[bridge host] ssh tunnel started${pid ? ` (pid ${pid})` : ''}: ${sshTarget}`);
152
- child.once('exit', (code, signal) => {
153
+ log(`[bridge host] ssh tunnel started${pid ? ` (pid ${pid})` : ""}: ${sshTarget}`);
154
+ child.once("exit", (code, signal) => {
153
155
  child = null;
154
156
  if (stopped)
155
157
  return;
@@ -179,16 +181,16 @@ function startReverseTunnel({ sshTarget, remotePort, localPort, identity, extraA
179
181
  }
180
182
  function splitArgs(input) {
181
183
  const args = [];
182
- let current = '';
184
+ let current = "";
183
185
  let quote = null;
184
186
  const push = () => {
185
187
  const trimmed = current.trim();
186
188
  if (trimmed.length)
187
189
  args.push(trimmed);
188
- current = '';
190
+ current = "";
189
191
  };
190
192
  for (let i = 0; i < input.length; i += 1) {
191
- const ch = input[i] ?? '';
193
+ const ch = input[i] ?? "";
192
194
  if (quote) {
193
195
  if (ch === quote) {
194
196
  quote = null;
@@ -214,46 +216,46 @@ function splitArgs(input) {
214
216
  async function spawnBridgeHostInBackground({ bind, token, writeConnectionPath, sshTarget, sshRemotePort, sshIdentity, sshExtraArgs, }) {
215
217
  const oracleHome = getOracleHomeDir();
216
218
  await fs.mkdir(oracleHome, { recursive: true, mode: 0o700 });
217
- const logPath = path.join(oracleHome, 'bridge-host.log');
218
- const pidPath = path.join(oracleHome, 'bridge-host.pid');
219
- const logHandle = await fs.open(logPath, 'a');
220
- const stdio = ['ignore', logHandle.fd, logHandle.fd];
219
+ const logPath = path.join(oracleHome, "bridge-host.log");
220
+ const pidPath = path.join(oracleHome, "bridge-host.pid");
221
+ const logHandle = await fs.open(logPath, "a");
222
+ const stdio = ["ignore", logHandle.fd, logHandle.fd];
221
223
  const scriptPath = process.argv[1];
222
224
  if (!scriptPath) {
223
- throw new Error('Unable to determine CLI entrypoint for background mode.');
225
+ throw new Error("Unable to determine CLI entrypoint for background mode.");
224
226
  }
225
227
  const args = [
226
228
  scriptPath,
227
- 'bridge',
228
- 'host',
229
- '--foreground',
230
- '--bind',
229
+ "bridge",
230
+ "host",
231
+ "--foreground",
232
+ "--bind",
231
233
  bind,
232
- '--token',
234
+ "--token",
233
235
  token,
234
- '--write-connection',
236
+ "--write-connection",
235
237
  writeConnectionPath,
236
238
  ];
237
239
  if (sshTarget) {
238
- args.push('--ssh', sshTarget);
240
+ args.push("--ssh", sshTarget);
239
241
  }
240
- if (typeof sshRemotePort === 'number') {
241
- args.push('--ssh-remote-port', String(sshRemotePort));
242
+ if (typeof sshRemotePort === "number") {
243
+ args.push("--ssh-remote-port", String(sshRemotePort));
242
244
  }
243
245
  if (sshIdentity) {
244
- args.push('--ssh-identity', sshIdentity);
246
+ args.push("--ssh-identity", sshIdentity);
245
247
  }
246
248
  if (sshExtraArgs) {
247
- args.push('--ssh-extra-args', sshExtraArgs);
249
+ args.push("--ssh-extra-args", sshExtraArgs);
248
250
  }
249
251
  const child = spawn(process.execPath, args, { detached: true, stdio });
250
252
  child.unref();
251
- await fs.writeFile(pidPath, `${child.pid ?? ''}\n`, { encoding: 'utf8', mode: 0o600 });
252
- if (process.platform !== 'win32') {
253
+ await fs.writeFile(pidPath, `${child.pid ?? ""}\n`, { encoding: "utf8", mode: 0o600 });
254
+ if (process.platform !== "win32") {
253
255
  await fs.chmod(pidPath, 0o600).catch(() => undefined);
254
256
  }
255
257
  await logHandle.close();
256
- console.log(chalk.green(`Bridge host running in background (pid ${child.pid ?? '?'})`));
258
+ console.log(chalk.green(`Bridge host running in background (pid ${child.pid ?? "?"})`));
257
259
  console.log(chalk.dim(`- Log: ${logPath}`));
258
260
  console.log(chalk.dim(`- PID: ${pidPath}`));
259
261
  }
@@ -1,57 +1,62 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, isTemporaryChatUrl, normalizeChatgptUrl, parseDuration } from '../browserMode.js';
4
- import { normalizeBrowserModelStrategy } from '../browser/modelStrategy.js';
5
- import { getOracleHomeDir } from '../oracleHome.js';
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, isTemporaryChatUrl, normalizeChatgptUrl, parseDuration, } from "../browserMode.js";
4
+ import { normalizeBrowserModelStrategy } from "../browser/modelStrategy.js";
5
+ import { getOracleHomeDir } from "../oracleHome.js";
6
6
  const DEFAULT_BROWSER_TIMEOUT_MS = 1_200_000;
7
7
  const DEFAULT_BROWSER_INPUT_TIMEOUT_MS = 60_000;
8
8
  const DEFAULT_BROWSER_RECHECK_TIMEOUT_MS = 120_000;
9
9
  const DEFAULT_BROWSER_AUTO_REATTACH_TIMEOUT_MS = 120_000;
10
- const DEFAULT_CHROME_PROFILE = 'Default';
10
+ const DEFAULT_CHROME_PROFILE = "Default";
11
11
  // Ordered array: most specific models first to ensure correct selection.
12
12
  // The browser label is passed to the model picker which fuzzy-matches against ChatGPT's UI.
13
13
  const BROWSER_MODEL_LABELS = [
14
14
  // Most specific first (e.g., "gpt-5.2-thinking" before "gpt-5.2")
15
- ['gpt-5.4-pro', 'GPT-5.4 Pro'],
16
- ['gpt-5.2-thinking', 'GPT-5.2 Thinking'],
17
- ['gpt-5.2-instant', 'GPT-5.2 Instant'],
18
- ['gpt-5.2-pro', 'GPT-5.4 Pro'],
19
- ['gpt-5.1-pro', 'GPT-5.4 Pro'],
20
- ['gpt-5-pro', 'GPT-5.4 Pro'],
15
+ ["gpt-5.5-pro", "GPT-5.5 Pro"],
16
+ ["gpt-5.5", "Thinking 5.5"],
17
+ ["gpt-5.4-pro", "GPT-5.4 Pro"],
18
+ ["gpt-5.2-thinking", "GPT-5.2 Thinking"],
19
+ ["gpt-5.2-instant", "GPT-5.2 Instant"],
20
+ ["gpt-5.2-pro", "GPT-5.5 Pro"],
21
+ ["gpt-5.1-pro", "GPT-5.5 Pro"],
22
+ ["gpt-5-pro", "GPT-5.5 Pro"],
21
23
  // Base models last (least specific)
22
- ['gpt-5.4', 'Thinking 5.4'],
23
- ['gpt-5.2', 'GPT-5.2'], // Selects "Auto" in ChatGPT UI
24
- ['gpt-5.1', 'GPT-5.2'], // Legacy alias → Auto
25
- ['gemini-3-pro', 'Gemini 3 Pro'],
26
- ['gemini-3-pro-deep-think', 'gemini-3-deep-think'],
24
+ ["gpt-5.4", "Thinking 5.4"],
25
+ ["gpt-5.2", "GPT-5.2"], // Selects "Auto" in ChatGPT UI
26
+ ["gpt-5.1", "GPT-5.2"], // Legacy alias → Auto
27
+ ["gemini-3-pro", "Gemini 3 Pro"],
28
+ ["gemini-3-pro-deep-think", "gemini-3-deep-think"],
27
29
  ];
28
30
  export function normalizeChatGptModelForBrowser(model) {
29
31
  const normalized = model.toLowerCase();
30
- if (!normalized.startsWith('gpt-') || normalized.includes('codex')) {
32
+ if (!normalized.startsWith("gpt-") || normalized.includes("codex")) {
31
33
  return model;
32
34
  }
33
- if (normalized === 'gpt-5.4-pro' || normalized === 'gpt-5.4') {
35
+ if (normalized === "gpt-5.5-pro" ||
36
+ normalized === "gpt-5.5" ||
37
+ normalized === "gpt-5.4-pro" ||
38
+ normalized === "gpt-5.4") {
34
39
  return normalized;
35
40
  }
36
41
  // Pro variants: resolve to the latest Pro model in ChatGPT.
37
- if (normalized === 'gpt-5-pro' || normalized === 'gpt-5.1-pro' || normalized === 'gpt-5.2-pro') {
38
- return 'gpt-5.4-pro';
42
+ if (normalized === "gpt-5-pro" || normalized === "gpt-5.1-pro" || normalized === "gpt-5.2-pro") {
43
+ return "gpt-5.5-pro";
39
44
  }
40
45
  // Explicit model variants: keep as-is (they have their own browser labels)
41
- if (normalized === 'gpt-5.2-thinking' || normalized === 'gpt-5.2-instant') {
46
+ if (normalized === "gpt-5.2-thinking" || normalized === "gpt-5.2-instant") {
42
47
  return normalized;
43
48
  }
44
49
  // Legacy aliases: map to base GPT-5.2 (Auto)
45
- if (normalized === 'gpt-5.1') {
46
- return 'gpt-5.2';
50
+ if (normalized === "gpt-5.1") {
51
+ return "gpt-5.2";
47
52
  }
48
53
  return model;
49
54
  }
50
55
  export async function buildBrowserConfig(options) {
51
56
  const desiredModelOverride = options.browserModelLabel?.trim();
52
- const normalizedOverride = desiredModelOverride?.toLowerCase() ?? '';
57
+ const normalizedOverride = desiredModelOverride?.toLowerCase() ?? "";
53
58
  const baseModel = options.model.toLowerCase();
54
- const isChatGptModel = baseModel.startsWith('gpt-') && !baseModel.includes('codex');
59
+ const isChatGptModel = baseModel.startsWith("gpt-") && !baseModel.includes("codex");
55
60
  const shouldUseOverride = !isChatGptModel && normalizedOverride.length > 0 && normalizedOverride !== baseModel;
56
61
  const modelStrategy = normalizeBrowserModelStrategy(options.browserModelStrategy) ?? DEFAULT_MODEL_STRATEGY;
57
62
  const cookieNames = parseCookieNames(options.browserCookieNames ?? process.env.ORACLE_BROWSER_COOKIE_NAMES);
@@ -62,7 +67,7 @@ export async function buildBrowserConfig(options) {
62
67
  envFile: process.env.ORACLE_BROWSER_COOKIES_FILE,
63
68
  cwd: process.cwd(),
64
69
  });
65
- if (inline?.source?.startsWith('home:') && options.browserNoCookieSync !== true) {
70
+ if (inline?.source?.startsWith("home:") && options.browserNoCookieSync !== true) {
66
71
  inline = undefined;
67
72
  }
68
73
  let remoteChrome;
@@ -76,8 +81,11 @@ export async function buildBrowserConfig(options) {
76
81
  : shouldUseOverride
77
82
  ? desiredModelOverride
78
83
  : mapModelToBrowserLabel(options.model);
79
- if (modelStrategy === 'select' && url && isTemporaryChatUrl(url) && /\bpro\b/i.test(desiredModel ?? '')) {
80
- throw new Error('Temporary Chat mode does not expose Pro models in the ChatGPT model picker. ' +
84
+ if (modelStrategy === "select" &&
85
+ url &&
86
+ isTemporaryChatUrl(url) &&
87
+ /\bpro\b/i.test(desiredModel ?? "")) {
88
+ throw new Error("Temporary Chat mode does not expose Pro models in the ChatGPT model picker. " +
81
89
  'Remove "temporary-chat=true" from --chatgpt-url (or omit --chatgpt-url), or use a non-Pro model (e.g. --model gpt-5.2).');
82
90
  }
83
91
  return {
@@ -86,7 +94,9 @@ export async function buildBrowserConfig(options) {
86
94
  chromeCookiePath: options.browserCookiePath ?? null,
87
95
  url,
88
96
  debugPort: selectBrowserPort(options),
89
- timeoutMs: options.browserTimeout ? parseDuration(options.browserTimeout, DEFAULT_BROWSER_TIMEOUT_MS) : undefined,
97
+ timeoutMs: options.browserTimeout
98
+ ? parseDuration(options.browserTimeout, DEFAULT_BROWSER_TIMEOUT_MS)
99
+ : undefined,
90
100
  inputTimeoutMs: options.browserInputTimeout
91
101
  ? parseDuration(options.browserInputTimeout, DEFAULT_BROWSER_INPUT_TIMEOUT_MS)
92
102
  : undefined,
@@ -96,7 +106,9 @@ export async function buildBrowserConfig(options) {
96
106
  assistantRecheckTimeoutMs: options.browserRecheckTimeout
97
107
  ? parseDuration(options.browserRecheckTimeout, DEFAULT_BROWSER_RECHECK_TIMEOUT_MS)
98
108
  : undefined,
99
- reuseChromeWaitMs: options.browserReuseWait ? parseDuration(options.browserReuseWait, 0) : undefined,
109
+ reuseChromeWaitMs: options.browserReuseWait
110
+ ? parseDuration(options.browserReuseWait, 0)
111
+ : undefined,
100
112
  profileLockTimeoutMs: options.browserProfileLockTimeout
101
113
  ? parseDuration(options.browserProfileLockTimeout, 0)
102
114
  : undefined,
@@ -109,7 +121,9 @@ export async function buildBrowserConfig(options) {
109
121
  autoReattachTimeoutMs: options.browserAutoReattachTimeout
110
122
  ? parseDuration(options.browserAutoReattachTimeout, DEFAULT_BROWSER_AUTO_REATTACH_TIMEOUT_MS)
111
123
  : undefined,
112
- cookieSyncWaitMs: options.browserCookieWait ? parseDuration(options.browserCookieWait, 0) : undefined,
124
+ cookieSyncWaitMs: options.browserCookieWait
125
+ ? parseDuration(options.browserCookieWait, 0)
126
+ : undefined,
113
127
  cookieSync: options.browserNoCookieSync ? false : undefined,
114
128
  cookieNames,
115
129
  inlineCookies: inline?.cookies,
@@ -148,7 +162,7 @@ export function mapModelToBrowserLabel(model) {
148
162
  return DEFAULT_MODEL_TARGET;
149
163
  }
150
164
  export function resolveBrowserModelLabel(input, model) {
151
- const trimmed = input?.trim?.() ?? '';
165
+ const trimmed = input?.trim?.() ?? "";
152
166
  if (!trimmed) {
153
167
  return mapModelToBrowserLabel(model);
154
168
  }
@@ -161,7 +175,7 @@ export function resolveBrowserModelLabel(input, model) {
161
175
  function parseRemoteChromeTarget(raw) {
162
176
  const target = raw.trim();
163
177
  if (!target) {
164
- throw new Error('Invalid remote-chrome value: expected host:port but received an empty string.');
178
+ throw new Error("Invalid remote-chrome value: expected host:port but received an empty string.");
165
179
  }
166
180
  const ipv6Match = target.match(/^\[(.+)]:(\d+)$/);
167
181
  let host;
@@ -171,22 +185,22 @@ function parseRemoteChromeTarget(raw) {
171
185
  portSegment = ipv6Match[2]?.trim();
172
186
  }
173
187
  else {
174
- const lastColon = target.lastIndexOf(':');
188
+ const lastColon = target.lastIndexOf(":");
175
189
  if (lastColon === -1) {
176
190
  throw new Error(`Invalid remote-chrome format: ${target}. Expected host:port (IPv6 must use [host]:port notation).`);
177
191
  }
178
192
  host = target.slice(0, lastColon).trim();
179
193
  portSegment = target.slice(lastColon + 1).trim();
180
- if (host.includes(':')) {
194
+ if (host.includes(":")) {
181
195
  throw new Error(`Invalid remote-chrome format: ${target}. Wrap IPv6 addresses in brackets, e.g. --remote-chrome "[2001:db8::1]:9222".`);
182
196
  }
183
197
  }
184
198
  if (!host) {
185
199
  throw new Error(`Invalid remote-chrome format: ${target}. Host portion is missing; expected host:port.`);
186
200
  }
187
- const port = Number.parseInt(portSegment ?? '', 10);
201
+ const port = Number.parseInt(portSegment ?? "", 10);
188
202
  if (!Number.isFinite(port) || port <= 0 || port > 65_535) {
189
- throw new Error(`Invalid remote-chrome port: "${portSegment ?? ''}". Expected a number between 1 and 65535.`);
203
+ throw new Error(`Invalid remote-chrome port: "${portSegment ?? ""}". Expected a number between 1 and 65535.`);
190
204
  }
191
205
  return { host, port };
192
206
  }
@@ -194,7 +208,7 @@ function parseCookieNames(raw) {
194
208
  if (!raw)
195
209
  return undefined;
196
210
  const names = raw
197
- .split(',')
211
+ .split(",")
198
212
  .map((entry) => entry.trim())
199
213
  .filter(Boolean);
200
214
  return names.length ? names : undefined;
@@ -211,7 +225,7 @@ async function resolveInlineCookies({ inlineArg, inlineFileArg, envPayload, envF
211
225
  try {
212
226
  const stat = await fs.stat(resolved);
213
227
  if (stat.isFile()) {
214
- const fileContent = await fs.readFile(resolved, 'utf8');
228
+ const fileContent = await fs.readFile(resolved, "utf8");
215
229
  const parsed = parseInlineCookiesPayload(fileContent);
216
230
  if (parsed)
217
231
  return parsed;
@@ -224,10 +238,10 @@ async function resolveInlineCookies({ inlineArg, inlineFileArg, envPayload, envF
224
238
  return parseInlineCookiesPayload(trimmed);
225
239
  };
226
240
  const sources = [
227
- { value: inlineFileArg, allowPath: true, source: 'inline-file' },
228
- { value: inlineArg, allowPath: true, source: 'inline-arg' },
229
- { value: envFile, allowPath: true, source: 'env-file' },
230
- { value: envPayload, allowPath: false, source: 'env-payload' },
241
+ { value: inlineFileArg, allowPath: true, source: "inline-file" },
242
+ { value: inlineArg, allowPath: true, source: "inline-arg" },
243
+ { value: envFile, allowPath: true, source: "env-file" },
244
+ { value: envPayload, allowPath: false, source: "env-payload" },
231
245
  ];
232
246
  for (const { value, allowPath, source } of sources) {
233
247
  const parsed = await tryLoad(value, allowPath);
@@ -236,14 +250,14 @@ async function resolveInlineCookies({ inlineArg, inlineFileArg, envPayload, envF
236
250
  }
237
251
  // fallback: ~/.oracle/cookies.{json,base64}
238
252
  const oracleHome = getOracleHomeDir();
239
- const candidates = ['cookies.json', 'cookies.base64'];
253
+ const candidates = ["cookies.json", "cookies.base64"];
240
254
  for (const file of candidates) {
241
255
  const fullPath = path.join(oracleHome, file);
242
256
  try {
243
257
  const stat = await fs.stat(fullPath);
244
258
  if (!stat.isFile())
245
259
  continue;
246
- const content = await fs.readFile(fullPath, 'utf8');
260
+ const content = await fs.readFile(fullPath, "utf8");
247
261
  const parsed = parseInlineCookiesPayload(content);
248
262
  if (parsed)
249
263
  return { cookies: parsed, source: `home:${file}` };
@@ -263,8 +277,8 @@ function parseInlineCookiesPayload(raw) {
263
277
  let jsonPayload = text;
264
278
  // Attempt base64 decode first; fall back to raw text on failure.
265
279
  try {
266
- const decoded = Buffer.from(text, 'base64').toString('utf8');
267
- if (decoded.trim().startsWith('[')) {
280
+ const decoded = Buffer.from(text, "base64").toString("utf8");
281
+ if (decoded.trim().startsWith("[")) {
268
282
  jsonPayload = decoded;
269
283
  }
270
284
  }
@@ -1,81 +1,82 @@
1
- import { normalizeChatgptUrl, CHATGPT_URL } from '../browserMode.js';
1
+ import { normalizeChatgptUrl, CHATGPT_URL } from "../browserMode.js";
2
2
  export function applyBrowserDefaultsFromConfig(options, config, getSource) {
3
3
  const browser = config.browser;
4
4
  if (!browser)
5
5
  return;
6
6
  const isUnset = (key) => {
7
7
  const source = getSource(key);
8
- return source === undefined || source === 'default';
8
+ return source === undefined || source === "default";
9
9
  };
10
10
  const configuredChatgptUrl = browser.chatgptUrl ?? browser.url;
11
11
  const cliChatgptSet = options.chatgptUrl !== undefined || options.browserUrl !== undefined;
12
- if (isUnset('chatgptUrl') && !cliChatgptSet && configuredChatgptUrl !== undefined) {
13
- options.chatgptUrl = normalizeChatgptUrl(configuredChatgptUrl ?? '', CHATGPT_URL);
12
+ if (isUnset("chatgptUrl") && !cliChatgptSet && configuredChatgptUrl !== undefined) {
13
+ options.chatgptUrl = normalizeChatgptUrl(configuredChatgptUrl ?? "", CHATGPT_URL);
14
14
  }
15
- if (isUnset('browserChromeProfile') && browser.chromeProfile !== undefined) {
15
+ if (isUnset("browserChromeProfile") && browser.chromeProfile !== undefined) {
16
16
  options.browserChromeProfile = browser.chromeProfile ?? undefined;
17
17
  }
18
- if (isUnset('browserChromePath') && browser.chromePath !== undefined) {
18
+ if (isUnset("browserChromePath") && browser.chromePath !== undefined) {
19
19
  options.browserChromePath = browser.chromePath ?? undefined;
20
20
  }
21
- if (isUnset('browserCookiePath') && browser.chromeCookiePath !== undefined) {
21
+ if (isUnset("browserCookiePath") && browser.chromeCookiePath !== undefined) {
22
22
  options.browserCookiePath = browser.chromeCookiePath ?? undefined;
23
23
  }
24
- if (isUnset('browserUrl') && options.browserUrl === undefined && browser.url !== undefined) {
24
+ if (isUnset("browserUrl") && options.browserUrl === undefined && browser.url !== undefined) {
25
25
  options.browserUrl = browser.url;
26
26
  }
27
- if (isUnset('browserTimeout') && typeof browser.timeoutMs === 'number') {
27
+ if (isUnset("browserTimeout") && typeof browser.timeoutMs === "number") {
28
28
  options.browserTimeout = String(browser.timeoutMs);
29
29
  }
30
- if (isUnset('browserPort') && typeof browser.debugPort === 'number') {
30
+ if (isUnset("browserPort") && typeof browser.debugPort === "number") {
31
31
  options.browserPort = browser.debugPort;
32
32
  }
33
- if (isUnset('browserInputTimeout') && typeof browser.inputTimeoutMs === 'number') {
33
+ if (isUnset("browserInputTimeout") && typeof browser.inputTimeoutMs === "number") {
34
34
  options.browserInputTimeout = String(browser.inputTimeoutMs);
35
35
  }
36
- if (isUnset('browserRecheckDelay') && typeof browser.assistantRecheckDelayMs === 'number') {
36
+ if (isUnset("browserRecheckDelay") && typeof browser.assistantRecheckDelayMs === "number") {
37
37
  options.browserRecheckDelay = String(browser.assistantRecheckDelayMs);
38
38
  }
39
- if (isUnset('browserRecheckTimeout') && typeof browser.assistantRecheckTimeoutMs === 'number') {
39
+ if (isUnset("browserRecheckTimeout") && typeof browser.assistantRecheckTimeoutMs === "number") {
40
40
  options.browserRecheckTimeout = String(browser.assistantRecheckTimeoutMs);
41
41
  }
42
- if (isUnset('browserReuseWait') && typeof browser.reuseChromeWaitMs === 'number') {
42
+ if (isUnset("browserReuseWait") && typeof browser.reuseChromeWaitMs === "number") {
43
43
  options.browserReuseWait = String(browser.reuseChromeWaitMs);
44
44
  }
45
- if (isUnset('browserProfileLockTimeout') && typeof browser.profileLockTimeoutMs === 'number') {
45
+ if (isUnset("browserProfileLockTimeout") && typeof browser.profileLockTimeoutMs === "number") {
46
46
  options.browserProfileLockTimeout = String(browser.profileLockTimeoutMs);
47
47
  }
48
- if (isUnset('browserAutoReattachDelay') && typeof browser.autoReattachDelayMs === 'number') {
48
+ if (isUnset("browserAutoReattachDelay") && typeof browser.autoReattachDelayMs === "number") {
49
49
  options.browserAutoReattachDelay = String(browser.autoReattachDelayMs);
50
50
  }
51
- if (isUnset('browserAutoReattachInterval') && typeof browser.autoReattachIntervalMs === 'number') {
51
+ if (isUnset("browserAutoReattachInterval") &&
52
+ typeof browser.autoReattachIntervalMs === "number") {
52
53
  options.browserAutoReattachInterval = String(browser.autoReattachIntervalMs);
53
54
  }
54
- if (isUnset('browserAutoReattachTimeout') && typeof browser.autoReattachTimeoutMs === 'number') {
55
+ if (isUnset("browserAutoReattachTimeout") && typeof browser.autoReattachTimeoutMs === "number") {
55
56
  options.browserAutoReattachTimeout = String(browser.autoReattachTimeoutMs);
56
57
  }
57
- if (isUnset('browserCookieWait') && typeof browser.cookieSyncWaitMs === 'number') {
58
+ if (isUnset("browserCookieWait") && typeof browser.cookieSyncWaitMs === "number") {
58
59
  options.browserCookieWait = String(browser.cookieSyncWaitMs);
59
60
  }
60
- if (isUnset('browserHeadless') && browser.headless !== undefined) {
61
+ if (isUnset("browserHeadless") && browser.headless !== undefined) {
61
62
  options.browserHeadless = browser.headless;
62
63
  }
63
- if (isUnset('browserHideWindow') && browser.hideWindow !== undefined) {
64
+ if (isUnset("browserHideWindow") && browser.hideWindow !== undefined) {
64
65
  options.browserHideWindow = browser.hideWindow;
65
66
  }
66
- if (isUnset('browserKeepBrowser') && browser.keepBrowser !== undefined) {
67
+ if (isUnset("browserKeepBrowser") && browser.keepBrowser !== undefined) {
67
68
  options.browserKeepBrowser = browser.keepBrowser;
68
69
  }
69
- if (isUnset('browserModelStrategy') && browser.modelStrategy !== undefined) {
70
+ if (isUnset("browserModelStrategy") && browser.modelStrategy !== undefined) {
70
71
  options.browserModelStrategy = browser.modelStrategy;
71
72
  }
72
- if (isUnset('browserThinkingTime') && browser.thinkingTime !== undefined) {
73
+ if (isUnset("browserThinkingTime") && browser.thinkingTime !== undefined) {
73
74
  options.browserThinkingTime = browser.thinkingTime;
74
75
  }
75
- if (isUnset('browserManualLogin') && browser.manualLogin !== undefined) {
76
+ if (isUnset("browserManualLogin") && browser.manualLogin !== undefined) {
76
77
  options.browserManualLogin = browser.manualLogin;
77
78
  }
78
- if (isUnset('browserManualLoginProfileDir') && browser.manualLoginProfileDir !== undefined) {
79
+ if (isUnset("browserManualLoginProfileDir") && browser.manualLoginProfileDir !== undefined) {
79
80
  options.browserManualLoginProfileDir = browser.manualLoginProfileDir;
80
81
  }
81
82
  }
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import chalk from "chalk";
2
2
  export function warnIfOversizeBundle(estimatedTokens, threshold = 196_000, log = console.log) {
3
3
  if (Number.isNaN(estimatedTokens) || estimatedTokens <= threshold) {
4
4
  return false;
@@ -1,8 +1,17 @@
1
- import clipboard from 'clipboardy';
1
+ async function loadClipboard() {
2
+ if (process.platform === "darwin" && process.arch === "x64") {
3
+ const paths = (process.env.PATH ?? "").split(":").filter(Boolean);
4
+ if (!paths.includes("/usr/sbin")) {
5
+ process.env.PATH = ["/usr/sbin", ...paths].join(":");
6
+ }
7
+ }
8
+ return (await import("clipboardy")).default;
9
+ }
2
10
  export async function copyToClipboard(text) {
3
11
  try {
12
+ const clipboard = await loadClipboard();
4
13
  await clipboard.write(text);
5
- return { success: true, command: 'clipboardy' };
14
+ return { success: true, command: "clipboardy" };
6
15
  }
7
16
  catch (error) {
8
17
  return { success: false, error };
@@ -1,4 +1,4 @@
1
- import { isProModel } from '../oracle/modelResolver.js';
1
+ import { isProModel } from "../oracle/modelResolver.js";
2
2
  export function shouldDetachSession({
3
3
  // Params kept for policy tweaks.
4
4
  engine, model, waitPreference, disableDetachEnv, }) {
@@ -8,7 +8,7 @@ engine, model, waitPreference, disableDetachEnv, }) {
8
8
  if (waitPreference)
9
9
  return false;
10
10
  // Only Pro-tier API runs should start detached by default; browser runs stay inline so failures surface.
11
- if (isProModel(model) && engine === 'api')
11
+ if (isProModel(model) && engine === "api")
12
12
  return true;
13
13
  return false;
14
14
  }