@steipete/oracle 0.8.6 → 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.
- package/LICENSE +1 -1
- package/README.md +130 -45
- package/dist/bin/oracle-cli.js +613 -379
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/assistantResponse.js +149 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +314 -104
- package/dist/src/browser/actions/navigation.js +161 -136
- package/dist/src/browser/actions/promptComposer.js +100 -64
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/chromeLifecycle.js +62 -60
- package/dist/src/browser/config.js +34 -15
- package/dist/src/browser/constants.js +17 -12
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +62 -62
- package/dist/src/browser/domDebug.js +1 -1
- package/dist/src/browser/index.js +452 -303
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +44 -39
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +17 -0
- package/dist/src/browser/providers/chatgptDomProvider.js +49 -0
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +254 -0
- package/dist/src/browser/providers/index.js +2 -0
- package/dist/src/browser/reattach.js +67 -34
- package/dist/src/browser/reattachHelpers.js +31 -26
- package/dist/src/browser/sessionRunner.js +37 -25
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +16 -16
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +65 -45
- package/dist/src/cli/browserDefaults.js +27 -26
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +7 -4
- package/dist/src/cli/dryRun.js +29 -25
- package/dist/src/cli/duplicatePromptGuard.js +3 -3
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +11 -0
- package/dist/src/cli/format.js +2 -2
- package/dist/src/cli/help.js +28 -28
- package/dist/src/cli/hiddenAliases.js +3 -3
- package/dist/src/cli/markdownBundle.js +12 -8
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +145 -87
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +37 -25
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +182 -79
- package/dist/src/cli/sessionLineage.js +60 -0
- package/dist/src/cli/sessionRunner.js +118 -90
- package/dist/src/cli/sessionTable.js +28 -24
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +140 -127
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +80 -0
- package/dist/src/gemini-web/client.js +81 -64
- package/dist/src/gemini-web/executionMode.js +16 -0
- package/dist/src/gemini-web/executor.js +327 -169
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +81 -64
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +5 -5
- package/dist/src/mcp/utils.js +15 -7
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +84 -46
- package/dist/src/oracle/config.js +124 -58
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +69 -45
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -30
- package/dist/src/oracle/logging.js +7 -7
- package/dist/src/oracle/markdown.js +28 -28
- package/dist/src/oracle/modelResolver.js +16 -16
- package/dist/src/oracle/multiModelRunner.js +12 -12
- package/dist/src/oracle/oscProgress.js +8 -8
- package/dist/src/oracle/promptAssembly.js +6 -3
- package/dist/src/oracle/request.js +23 -15
- package/dist/src/oracle/run.js +172 -140
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +81 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +69 -65
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/vendor/oracle-notifier/README.md +2 -0
- package/dist/markdansi/types/index.js +0 -4
- package/dist/oracle/bin/oracle-cli.js +0 -472
- package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
- package/dist/oracle/src/browser/actions/attachments.js +0 -82
- package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
- package/dist/oracle/src/browser/actions/navigation.js +0 -75
- package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
- package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
- package/dist/oracle/src/browser/config.js +0 -33
- package/dist/oracle/src/browser/constants.js +0 -40
- package/dist/oracle/src/browser/cookies.js +0 -210
- package/dist/oracle/src/browser/domDebug.js +0 -36
- package/dist/oracle/src/browser/index.js +0 -331
- package/dist/oracle/src/browser/pageActions.js +0 -5
- package/dist/oracle/src/browser/prompt.js +0 -88
- package/dist/oracle/src/browser/promptSummary.js +0 -20
- package/dist/oracle/src/browser/sessionRunner.js +0 -80
- package/dist/oracle/src/browser/utils.js +0 -62
- package/dist/oracle/src/browserMode.js +0 -1
- package/dist/oracle/src/cli/browserConfig.js +0 -44
- package/dist/oracle/src/cli/dryRun.js +0 -59
- package/dist/oracle/src/cli/engine.js +0 -17
- package/dist/oracle/src/cli/errorUtils.js +0 -9
- package/dist/oracle/src/cli/help.js +0 -70
- package/dist/oracle/src/cli/markdownRenderer.js +0 -15
- package/dist/oracle/src/cli/options.js +0 -103
- package/dist/oracle/src/cli/promptRequirement.js +0 -14
- package/dist/oracle/src/cli/rootAlias.js +0 -30
- package/dist/oracle/src/cli/sessionCommand.js +0 -77
- package/dist/oracle/src/cli/sessionDisplay.js +0 -270
- package/dist/oracle/src/cli/sessionRunner.js +0 -94
- package/dist/oracle/src/heartbeat.js +0 -43
- package/dist/oracle/src/oracle/client.js +0 -48
- package/dist/oracle/src/oracle/config.js +0 -29
- package/dist/oracle/src/oracle/errors.js +0 -101
- package/dist/oracle/src/oracle/files.js +0 -220
- package/dist/oracle/src/oracle/format.js +0 -33
- package/dist/oracle/src/oracle/fsAdapter.js +0 -7
- package/dist/oracle/src/oracle/oscProgress.js +0 -60
- package/dist/oracle/src/oracle/request.js +0 -48
- package/dist/oracle/src/oracle/run.js +0 -444
- package/dist/oracle/src/oracle/tokenStats.js +0 -39
- package/dist/oracle/src/oracle/types.js +0 -1
- package/dist/oracle/src/oracle.js +0 -9
- package/dist/oracle/src/sessionManager.js +0 -205
- package/dist/oracle/src/version.js +0 -39
- package/dist/scripts/chrome/browser-tools.js +0 -295
- package/dist/src/browser/profileSync.js +0 -141
- /package/dist/{oracle/src/browser/types.js → src/gemini-web/executionClients.js} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import { formatTokenCount } from
|
|
3
|
-
import { formatFinishLine } from
|
|
4
|
-
import { runBrowserMode } from
|
|
5
|
-
import { assembleBrowserPrompt } from
|
|
6
|
-
import { BrowserAutomationError } from
|
|
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
|
|
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 &&
|
|
24
|
-
|
|
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 !==
|
|
36
|
+
if (typeof message !== "string")
|
|
33
37
|
return;
|
|
34
|
-
const shouldAlwaysPrint = message.startsWith(
|
|
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(
|
|
46
|
+
log(chalk.dim("This run can take up to an hour (usually ~10 minutes)."));
|
|
43
47
|
if (runOptions.verbose) {
|
|
44
|
-
log(chalk.dim(
|
|
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
|
-
? {
|
|
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({
|
|
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 :
|
|
69
|
-
throw new BrowserAutomationError(message, { stage:
|
|
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(
|
|
73
|
-
log(browserResult.answerMarkdown || browserResult.answerText || chalk.dim(
|
|
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: [
|
|
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
|
|
36
|
+
case "ms":
|
|
37
37
|
return value;
|
|
38
|
-
case
|
|
38
|
+
case "s":
|
|
39
39
|
return value * 1000;
|
|
40
|
-
case
|
|
40
|
+
case "m":
|
|
41
41
|
return value * 60_000;
|
|
42
|
-
case
|
|
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(
|
|
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
|
|
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(
|
|
117
|
-
return value ===
|
|
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;
|
package/dist/src/browserMode.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { runBrowserMode, CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, parseDuration, normalizeChatgptUrl, isTemporaryChatUrl, } from
|
|
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
|
|
2
|
-
import os from
|
|
3
|
-
import path from
|
|
4
|
-
import { loadUserConfig } from
|
|
5
|
-
import { resolveRemoteServiceConfig } from
|
|
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(),
|
|
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(),
|
|
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(
|
|
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[
|
|
31
|
+
env["ORACLE_ENGINE"] = "browser";
|
|
32
32
|
// biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
|
|
33
|
-
env[
|
|
33
|
+
env["ORACLE_HOME_DIR"] = oracleHomeDir;
|
|
34
34
|
// biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
|
|
35
|
-
env[
|
|
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[
|
|
38
|
+
env["ORACLE_REMOTE_HOST"] = remoteHost;
|
|
39
39
|
// biome-ignore lint/complexity/useLiteralKeys: env vars are uppercase and include underscores.
|
|
40
|
-
env[
|
|
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:
|
|
48
|
-
command:
|
|
47
|
+
type: "stdio",
|
|
48
|
+
command: "oracle-mcp",
|
|
49
49
|
args: [],
|
|
50
50
|
env,
|
|
51
51
|
},
|
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import chalk from
|
|
4
|
-
import { configPath as defaultConfigPath } from
|
|
5
|
-
import { parseBridgeConnectionString, readBridgeConnectionArtifact, looksLikePath, } from
|
|
6
|
-
import { readUserConfigFile, writeUserConfigFile } from
|
|
7
|
-
import { checkRemoteHealth } from
|
|
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(
|
|
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({
|
|
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 ??
|
|
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: { ...
|
|
26
|
-
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(
|
|
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(
|
|
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 {
|
|
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
|
|
2
|
-
import { loadUserConfig } from
|
|
3
|
-
import { resolveRemoteServiceConfig } from
|
|
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(
|
|
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 ??
|
|
25
|
-
const tokenValue = includeToken ? remoteToken ??
|
|
24
|
+
const hostValue = remoteHost ?? "127.0.0.1:9473";
|
|
25
|
+
const tokenValue = includeToken ? (remoteToken ?? "<YOUR_TOKEN>") : "<YOUR_TOKEN>";
|
|
26
26
|
return [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
"# ~/.codex/config.toml",
|
|
28
|
+
"",
|
|
29
|
+
"[mcp.servers.oracle]",
|
|
30
30
|
'command = "oracle-mcp"',
|
|
31
|
-
|
|
31
|
+
"args = []",
|
|
32
32
|
`env = { ORACLE_ENGINE = "browser", ORACLE_REMOTE_HOST = "${escapeTomlString(hostValue)}", ORACLE_REMOTE_TOKEN = "${escapeTomlString(tokenValue)}" }`,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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(
|
|
39
|
+
].join("\n");
|
|
40
40
|
}
|
|
41
41
|
function escapeTomlString(value) {
|
|
42
|
-
return value.replace(/\\/g,
|
|
42
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
43
43
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import os from
|
|
2
|
-
import chalk from
|
|
3
|
-
import { getCliVersion } from
|
|
4
|
-
import { loadUserConfig } from
|
|
5
|
-
import { resolveRemoteServiceConfig } from
|
|
6
|
-
import { checkTcpConnection, checkRemoteHealth } from
|
|
7
|
-
import { detectChromeBinary, detectChromeCookieDb } from
|
|
8
|
-
import { formatCodexMcpSnippet } from
|
|
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(
|
|
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 :
|
|
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(
|
|
32
|
+
lines.push("");
|
|
33
|
+
lines.push(chalk.bold("Browser mode"));
|
|
34
34
|
if (resolvedRemote.host) {
|
|
35
|
-
lines.push(`Remote service: ${chalk.green(
|
|
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 ?
|
|
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(
|
|
40
|
+
lines.push(chalk.dim(`TCP connect: ${chalk.green("ok")}`));
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
43
|
-
fail.push(`Cannot reach ${resolvedRemote.host} (${tcp.error ??
|
|
44
|
-
lines.push(chalk.dim(`TCP connect: ${chalk.red(`failed (${tcp.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(
|
|
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({
|
|
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}` :
|
|
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 ??
|
|
60
|
+
const detail = health.error ?? "unknown error";
|
|
57
61
|
fail.push(`Remote auth failed: ${detail}`);
|
|
58
|
-
const suffix = health.statusCode ? `HTTP ${health.statusCode}` :
|
|
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(
|
|
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(
|
|
71
|
-
lines.push(chalk.dim(`Chrome: ${chalk.red(
|
|
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 ===
|
|
74
|
-
warn.push(
|
|
75
|
-
lines.push(chalk.dim(
|
|
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:
|
|
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(
|
|
84
|
-
lines.push(chalk.dim(`Cookies DB: ${chalk.yellow(
|
|
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(
|
|
90
|
-
lines.push(formatCodexMcpSnippet({
|
|
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(
|
|
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(
|
|
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(
|
|
113
|
+
console.log(lines.join("\n"));
|
|
106
114
|
process.exitCode = fail.length ? 1 : 0;
|
|
107
115
|
}
|