@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.
- package/LICENSE +1 -1
- package/README.md +61 -48
- package/dist/bin/oracle-cli.js +455 -402
- 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 +275 -117
- package/dist/src/browser/actions/navigation.js +161 -137
- 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 +390 -295
- 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 +1 -1
- package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
- package/dist/src/browser/providers/index.js +2 -2
- 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 +62 -48
- 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 +2 -2
- 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 +3 -3
- 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 +7 -7
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +127 -106
- 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 +32 -28
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +95 -81
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +103 -93
- package/dist/src/cli/sessionTable.js +26 -23
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +139 -128
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +19 -15
- package/dist/src/gemini-web/client.js +76 -70
- package/dist/src/gemini-web/executionMode.js +6 -8
- package/dist/src/gemini-web/executor.js +98 -93
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +51 -47
- 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 +50 -41
- package/dist/src/oracle/config.js +96 -66
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +55 -46
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -33
- 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 +16 -13
- package/dist/src/oracle/run.js +156 -134
- 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 +77 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +66 -62
- 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/types.js +0 -1
- 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
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import inquirer from
|
|
3
|
-
import kleur from
|
|
4
|
-
import path from
|
|
5
|
-
import os from
|
|
6
|
-
import fs from
|
|
7
|
-
import { DEFAULT_MODEL, MODEL_CONFIGS } from
|
|
8
|
-
import { renderMarkdownAnsi } from
|
|
9
|
-
import { sessionStore, pruneOldSessions } from
|
|
10
|
-
import { performSessionRun } from
|
|
11
|
-
import { MAX_RENDER_BYTES, trimBeforeFirstAnswer } from
|
|
12
|
-
import { formatSessionTableHeader, formatSessionTableRow } from
|
|
13
|
-
import { buildBrowserConfig, resolveBrowserModelLabel } from
|
|
14
|
-
import { resolveNotificationSettings } from
|
|
15
|
-
import { loadUserConfig } from
|
|
16
|
-
import { resolveConfiguredMaxFileSizeBytes } from
|
|
17
|
-
import { formatTokenCount } from
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import inquirer from "inquirer";
|
|
3
|
+
import kleur from "kleur";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import { DEFAULT_MODEL, MODEL_CONFIGS, } from "../../oracle.js";
|
|
8
|
+
import { renderMarkdownAnsi } from "../markdownRenderer.js";
|
|
9
|
+
import { sessionStore, pruneOldSessions } from "../../sessionStore.js";
|
|
10
|
+
import { performSessionRun } from "../sessionRunner.js";
|
|
11
|
+
import { MAX_RENDER_BYTES, trimBeforeFirstAnswer } from "../sessionDisplay.js";
|
|
12
|
+
import { formatSessionTableHeader, formatSessionTableRow } from "../sessionTable.js";
|
|
13
|
+
import { buildBrowserConfig, resolveBrowserModelLabel } from "../browserConfig.js";
|
|
14
|
+
import { resolveNotificationSettings } from "../notifier.js";
|
|
15
|
+
import { loadUserConfig } from "../../config.js";
|
|
16
|
+
import { resolveConfiguredMaxFileSizeBytes } from "../fileSize.js";
|
|
17
|
+
import { formatTokenCount } from "../../oracle/runUtils.js";
|
|
18
18
|
const isTty = () => Boolean(process.stdout.isTTY && chalk.level > 0);
|
|
19
19
|
const dim = (text) => (isTty() ? kleur.dim(text) : text);
|
|
20
20
|
const RECENT_WINDOW_HOURS = 24;
|
|
@@ -26,20 +26,20 @@ export async function launchTui({ version, printIntro = true }) {
|
|
|
26
26
|
let exitMessageShown = false;
|
|
27
27
|
if (printIntro) {
|
|
28
28
|
if (rich) {
|
|
29
|
-
console.log(chalk.bold(
|
|
29
|
+
console.log(chalk.bold("🧿 oracle"), `${version}`, dim("— Whispering your tokens to the silicon sage"));
|
|
30
30
|
}
|
|
31
31
|
else {
|
|
32
32
|
console.log(`🧿 oracle ${version} — Whispering your tokens to the silicon sage`);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
console.log(
|
|
35
|
+
console.log("");
|
|
36
36
|
let showingOlder = false;
|
|
37
37
|
for (;;) {
|
|
38
38
|
const { recent, older, olderTotal } = await fetchSessionBuckets();
|
|
39
39
|
const choices = [];
|
|
40
40
|
const headerLabel = formatSessionTableHeader(isTty());
|
|
41
41
|
// Start with a selectable row so focus never lands on a separator
|
|
42
|
-
choices.push({ name: chalk.bold.green(
|
|
42
|
+
choices.push({ name: chalk.bold.green("ask oracle"), value: "__ask__" });
|
|
43
43
|
if (!showingOlder) {
|
|
44
44
|
if (recent.length > 0) {
|
|
45
45
|
choices.push(new inquirer.Separator(headerLabel));
|
|
@@ -55,22 +55,22 @@ export async function launchTui({ version, printIntro = true }) {
|
|
|
55
55
|
choices.push(new inquirer.Separator(headerLabel));
|
|
56
56
|
choices.push(...older.map(toSessionChoice));
|
|
57
57
|
}
|
|
58
|
-
choices.push(new inquirer.Separator(
|
|
59
|
-
choices.push(new inquirer.Separator(
|
|
60
|
-
choices.push({ name: chalk.bold.green(
|
|
58
|
+
choices.push(new inquirer.Separator(" "));
|
|
59
|
+
choices.push(new inquirer.Separator("Actions"));
|
|
60
|
+
choices.push({ name: chalk.bold.green("ask oracle"), value: "__ask__" });
|
|
61
61
|
if (!showingOlder && olderTotal > 0) {
|
|
62
|
-
choices.push({ name:
|
|
62
|
+
choices.push({ name: "Older page", value: "__older__" });
|
|
63
63
|
}
|
|
64
64
|
else {
|
|
65
|
-
choices.push({ name:
|
|
65
|
+
choices.push({ name: "Newer (recent)", value: "__reset__" });
|
|
66
66
|
}
|
|
67
|
-
choices.push({ name:
|
|
67
|
+
choices.push({ name: "Exit", value: "__exit__" });
|
|
68
68
|
const selection = await new Promise((resolve) => {
|
|
69
69
|
const prompt = inquirer.prompt([
|
|
70
70
|
{
|
|
71
|
-
name:
|
|
72
|
-
type:
|
|
73
|
-
message:
|
|
71
|
+
name: "selection",
|
|
72
|
+
type: "select",
|
|
73
|
+
message: "Select a session or action",
|
|
74
74
|
choices,
|
|
75
75
|
pageSize: 16,
|
|
76
76
|
loop: false,
|
|
@@ -81,40 +81,40 @@ export async function launchTui({ version, printIntro = true }) {
|
|
|
81
81
|
.catch((error) => {
|
|
82
82
|
pagingFailures += 1;
|
|
83
83
|
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
-
if (message.includes(
|
|
85
|
-
console.log(chalk.green(
|
|
84
|
+
if (message.includes("SIGINT") || message.includes("force closed the prompt")) {
|
|
85
|
+
console.log(chalk.green("🧿 Closing the book. See you next prompt."));
|
|
86
86
|
exitMessageShown = true;
|
|
87
|
-
resolve(
|
|
87
|
+
resolve("__exit__");
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
-
console.error(chalk.red(
|
|
91
|
-
if (message.includes(
|
|
92
|
-
console.error(chalk.red(
|
|
93
|
-
resolve(
|
|
90
|
+
console.error(chalk.red("Paging failed; returning to recent list."), message);
|
|
91
|
+
if (message.includes("setRawMode") || message.includes("EIO") || pagingFailures >= 3) {
|
|
92
|
+
console.error(chalk.red("Terminal input unavailable; exiting TUI."), dim("Try `stty sane` then rerun oracle, or use `oracle recent`."));
|
|
93
|
+
resolve("__exit__");
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
|
-
resolve(
|
|
96
|
+
resolve("__reset__");
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
|
-
if (process.env.ORACLE_DEBUG_TUI ===
|
|
99
|
+
if (process.env.ORACLE_DEBUG_TUI === "1") {
|
|
100
100
|
console.error(`[tui] selection=${JSON.stringify(selection)}`);
|
|
101
101
|
}
|
|
102
102
|
pagingFailures = 0;
|
|
103
|
-
if (selection ===
|
|
103
|
+
if (selection === "__exit__") {
|
|
104
104
|
if (!exitMessageShown) {
|
|
105
|
-
console.log(chalk.green(
|
|
105
|
+
console.log(chalk.green("🧿 Closing the book. See you next prompt."));
|
|
106
106
|
}
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
-
if (selection ===
|
|
109
|
+
if (selection === "__ask__") {
|
|
110
110
|
await askOracleFlow(version, userConfig);
|
|
111
111
|
continue;
|
|
112
112
|
}
|
|
113
|
-
if (selection ===
|
|
113
|
+
if (selection === "__older__") {
|
|
114
114
|
showingOlder = true;
|
|
115
115
|
continue;
|
|
116
116
|
}
|
|
117
|
-
if (selection ===
|
|
117
|
+
if (selection === "__reset__") {
|
|
118
118
|
showingOlder = false;
|
|
119
119
|
continue;
|
|
120
120
|
}
|
|
@@ -124,13 +124,20 @@ export async function launchTui({ version, printIntro = true }) {
|
|
|
124
124
|
async function fetchSessionBuckets() {
|
|
125
125
|
const all = await sessionStore.listSessions();
|
|
126
126
|
const cutoff = Date.now() - RECENT_WINDOW_HOURS * 60 * 60 * 1000;
|
|
127
|
-
const recent = all
|
|
127
|
+
const recent = all
|
|
128
|
+
.filter((meta) => new Date(meta.createdAt).getTime() >= cutoff)
|
|
129
|
+
.slice(0, PAGE_SIZE);
|
|
128
130
|
const olderAll = all.filter((meta) => new Date(meta.createdAt).getTime() < cutoff);
|
|
129
131
|
const older = olderAll.slice(0, PAGE_SIZE);
|
|
130
132
|
const hasMoreOlder = olderAll.length > PAGE_SIZE;
|
|
131
133
|
if (recent.length === 0 && older.length === 0 && olderAll.length > 0) {
|
|
132
134
|
// No recent entries; fall back to top 10 overall.
|
|
133
|
-
return {
|
|
135
|
+
return {
|
|
136
|
+
recent: olderAll.slice(0, PAGE_SIZE),
|
|
137
|
+
older: [],
|
|
138
|
+
hasMoreOlder: olderAll.length > PAGE_SIZE,
|
|
139
|
+
olderTotal: olderAll.length,
|
|
140
|
+
};
|
|
134
141
|
}
|
|
135
142
|
return { recent, older, hasMoreOlder, olderTotal: olderAll.length };
|
|
136
143
|
}
|
|
@@ -154,78 +161,80 @@ async function showSessionDetail(sessionId) {
|
|
|
154
161
|
}
|
|
155
162
|
const prompt = await readStoredPrompt(sessionId);
|
|
156
163
|
if (prompt) {
|
|
157
|
-
console.log(chalk.bold(
|
|
164
|
+
console.log(chalk.bold("Prompt:"));
|
|
158
165
|
console.log(renderMarkdownAnsi(prompt));
|
|
159
|
-
console.log(dim(
|
|
166
|
+
console.log(dim("---"));
|
|
160
167
|
}
|
|
161
168
|
const logPath = await getSessionLogPath(sessionId);
|
|
162
169
|
if (logPath) {
|
|
163
170
|
console.log(dim(`Log file: ${logPath}`));
|
|
164
171
|
}
|
|
165
|
-
console.log(
|
|
172
|
+
console.log("");
|
|
166
173
|
await renderSessionLog(sessionId);
|
|
167
|
-
const isRunning = meta.status ===
|
|
174
|
+
const isRunning = meta.status === "running";
|
|
168
175
|
const modelActions = meta.models?.map((run) => ({
|
|
169
176
|
name: `View ${run.model} log (${run.status})`,
|
|
170
177
|
value: `log:${run.model}`,
|
|
171
178
|
})) ?? [];
|
|
172
179
|
const actions = [
|
|
173
|
-
{ name:
|
|
180
|
+
{ name: "View combined log", value: "log:__all__" },
|
|
174
181
|
...modelActions,
|
|
175
|
-
...(isRunning ? [{ name:
|
|
176
|
-
{ name:
|
|
182
|
+
...(isRunning ? [{ name: "Refresh", value: "refresh" }] : []),
|
|
183
|
+
{ name: "Back", value: "back" },
|
|
177
184
|
];
|
|
178
185
|
let next;
|
|
179
186
|
try {
|
|
180
187
|
({ next } = await inquirer.prompt([
|
|
181
188
|
{
|
|
182
|
-
name:
|
|
183
|
-
type:
|
|
184
|
-
message:
|
|
189
|
+
name: "next",
|
|
190
|
+
type: "select",
|
|
191
|
+
message: "Actions",
|
|
185
192
|
choices: actions,
|
|
186
193
|
},
|
|
187
194
|
]));
|
|
188
195
|
}
|
|
189
196
|
catch (error) {
|
|
190
197
|
const message = error instanceof Error ? error.message : String(error);
|
|
191
|
-
if (message.includes(
|
|
192
|
-
console.log(chalk.green(
|
|
198
|
+
if (message.includes("SIGINT") || message.includes("force closed the prompt")) {
|
|
199
|
+
console.log(chalk.green("🧿 Closing the book. See you next prompt."));
|
|
193
200
|
return;
|
|
194
201
|
}
|
|
195
|
-
console.error(chalk.red(
|
|
202
|
+
console.error(chalk.red("Paging failed; returning to session list."), message);
|
|
196
203
|
return;
|
|
197
204
|
}
|
|
198
|
-
if (next ===
|
|
205
|
+
if (next === "back") {
|
|
199
206
|
return;
|
|
200
207
|
}
|
|
201
|
-
if (next ===
|
|
208
|
+
if (next === "refresh") {
|
|
202
209
|
continue;
|
|
203
210
|
}
|
|
204
|
-
if (next.startsWith(
|
|
205
|
-
const [, target] = next.split(
|
|
206
|
-
await renderSessionLog(sessionId, target ===
|
|
211
|
+
if (next.startsWith("log:")) {
|
|
212
|
+
const [, target] = next.split(":");
|
|
213
|
+
await renderSessionLog(sessionId, target === "__all__" ? undefined : target);
|
|
207
214
|
}
|
|
208
215
|
}
|
|
209
216
|
}
|
|
210
217
|
async function renderSessionLog(sessionId, model) {
|
|
211
|
-
const raw = model
|
|
212
|
-
|
|
218
|
+
const raw = model
|
|
219
|
+
? await sessionStore.readModelLog(sessionId, model)
|
|
220
|
+
: await sessionStore.readLog(sessionId);
|
|
221
|
+
const headerLabel = model ? `Log (${model})` : "Log";
|
|
213
222
|
console.log(chalk.bold(headerLabel));
|
|
214
223
|
const text = trimBeforeFirstAnswer(raw);
|
|
215
|
-
const size = Buffer.byteLength(text,
|
|
224
|
+
const size = Buffer.byteLength(text, "utf8");
|
|
216
225
|
if (size > MAX_RENDER_BYTES) {
|
|
217
226
|
console.log(chalk.yellow(`Log is large (${size.toLocaleString()} bytes). Rendering raw text; open the log file for full context.`));
|
|
218
227
|
process.stdout.write(text);
|
|
219
|
-
console.log(
|
|
228
|
+
console.log("");
|
|
220
229
|
return;
|
|
221
230
|
}
|
|
222
231
|
if (!text.trim()) {
|
|
223
|
-
console.log(dim(
|
|
224
|
-
console.log(
|
|
232
|
+
console.log(dim("(log is empty)"));
|
|
233
|
+
console.log("");
|
|
225
234
|
return;
|
|
226
235
|
}
|
|
227
236
|
process.stdout.write(renderMarkdownAnsi(text));
|
|
228
|
-
console.log(
|
|
237
|
+
console.log("");
|
|
229
238
|
}
|
|
230
239
|
async function getSessionLogPath(sessionId) {
|
|
231
240
|
try {
|
|
@@ -238,14 +247,14 @@ async function getSessionLogPath(sessionId) {
|
|
|
238
247
|
}
|
|
239
248
|
function printSessionHeader(meta) {
|
|
240
249
|
console.log(chalk.bold(`Session ${chalk.cyan(meta.id)}`));
|
|
241
|
-
console.log(`${chalk.white(
|
|
242
|
-
console.log(`${chalk.white(
|
|
250
|
+
console.log(`${chalk.white("Status:")} ${meta.status}`);
|
|
251
|
+
console.log(`${chalk.white("Created:")} ${meta.createdAt}`);
|
|
243
252
|
if (meta.model) {
|
|
244
|
-
console.log(`${chalk.white(
|
|
253
|
+
console.log(`${chalk.white("Model:")} ${meta.model}`);
|
|
245
254
|
}
|
|
246
255
|
const mode = meta.mode ?? meta.options?.mode;
|
|
247
256
|
if (mode) {
|
|
248
|
-
console.log(`${chalk.white(
|
|
257
|
+
console.log(`${chalk.white("Mode:")} ${mode}`);
|
|
249
258
|
}
|
|
250
259
|
if (meta.errorMessage) {
|
|
251
260
|
console.log(chalk.red(`Error: ${meta.errorMessage}`));
|
|
@@ -255,66 +264,66 @@ function printModelSummaries(models) {
|
|
|
255
264
|
if (models.length === 0) {
|
|
256
265
|
return;
|
|
257
266
|
}
|
|
258
|
-
console.log(chalk.bold(
|
|
267
|
+
console.log(chalk.bold("Models:"));
|
|
259
268
|
for (const run of models) {
|
|
260
269
|
const usage = run.usage
|
|
261
270
|
? ` tok=${formatTokenCount(run.usage.outputTokens ?? 0)}/${formatTokenCount(run.usage.totalTokens ?? 0)}`
|
|
262
|
-
:
|
|
271
|
+
: "";
|
|
263
272
|
console.log(` - ${chalk.cyan(run.model)} — ${run.status}${usage}`);
|
|
264
273
|
}
|
|
265
|
-
console.log(
|
|
274
|
+
console.log("");
|
|
266
275
|
}
|
|
267
276
|
async function askOracleFlow(version, userConfig) {
|
|
268
277
|
const modelChoices = Object.keys(MODEL_CONFIGS);
|
|
269
278
|
const hasApiKey = Boolean(process.env.OPENAI_API_KEY);
|
|
270
|
-
const initialMode = hasApiKey ?
|
|
279
|
+
const initialMode = hasApiKey ? "api" : "browser";
|
|
271
280
|
const preferredMode = userConfig.engine ?? initialMode;
|
|
272
281
|
const wizardQuestions = [
|
|
273
282
|
{
|
|
274
|
-
name:
|
|
275
|
-
type:
|
|
276
|
-
message:
|
|
283
|
+
name: "promptInput",
|
|
284
|
+
type: "input",
|
|
285
|
+
message: "Paste your prompt text or a path to a file (leave blank to cancel):",
|
|
277
286
|
},
|
|
278
287
|
...(hasApiKey
|
|
279
288
|
? [
|
|
280
289
|
{
|
|
281
|
-
name:
|
|
282
|
-
type:
|
|
283
|
-
message:
|
|
290
|
+
name: "mode",
|
|
291
|
+
type: "select",
|
|
292
|
+
message: "Engine",
|
|
284
293
|
default: preferredMode,
|
|
285
294
|
choices: [
|
|
286
|
-
{ name:
|
|
287
|
-
{ name:
|
|
295
|
+
{ name: "API", value: "api" },
|
|
296
|
+
{ name: "Browser", value: "browser" },
|
|
288
297
|
],
|
|
289
298
|
},
|
|
290
299
|
]
|
|
291
300
|
: [
|
|
292
301
|
{
|
|
293
|
-
name:
|
|
294
|
-
type:
|
|
295
|
-
message:
|
|
302
|
+
name: "mode",
|
|
303
|
+
type: "select",
|
|
304
|
+
message: "Engine",
|
|
296
305
|
default: preferredMode,
|
|
297
|
-
choices: [{ name:
|
|
306
|
+
choices: [{ name: "Browser", value: "browser" }],
|
|
298
307
|
},
|
|
299
308
|
]),
|
|
300
309
|
{
|
|
301
|
-
name:
|
|
302
|
-
type:
|
|
303
|
-
message:
|
|
310
|
+
name: "slug",
|
|
311
|
+
type: "input",
|
|
312
|
+
message: "Optional slug (3–5 words, leave blank for auto):",
|
|
304
313
|
},
|
|
305
314
|
{
|
|
306
|
-
name:
|
|
307
|
-
type:
|
|
308
|
-
message:
|
|
315
|
+
name: "model",
|
|
316
|
+
type: "select",
|
|
317
|
+
message: "Model",
|
|
309
318
|
default: DEFAULT_MODEL,
|
|
310
319
|
choices: modelChoices,
|
|
311
320
|
},
|
|
312
321
|
{
|
|
313
|
-
name:
|
|
314
|
-
type:
|
|
315
|
-
message:
|
|
322
|
+
name: "models",
|
|
323
|
+
type: "checkbox",
|
|
324
|
+
message: "Additional API models to fan out to (optional)",
|
|
316
325
|
choices: modelChoices,
|
|
317
|
-
when: (ans) => ans.mode ===
|
|
326
|
+
when: (ans) => ans.mode === "api",
|
|
318
327
|
filter: (values) => Array.isArray(values)
|
|
319
328
|
? values
|
|
320
329
|
.map((entry) => entry.trim())
|
|
@@ -322,50 +331,52 @@ async function askOracleFlow(version, userConfig) {
|
|
|
322
331
|
: [],
|
|
323
332
|
},
|
|
324
333
|
{
|
|
325
|
-
name:
|
|
326
|
-
type:
|
|
327
|
-
message:
|
|
334
|
+
name: "files",
|
|
335
|
+
type: "input",
|
|
336
|
+
message: "Files or globs to attach (comma-separated, optional):",
|
|
328
337
|
filter: (value) => value
|
|
329
|
-
.split(
|
|
338
|
+
.split(",")
|
|
330
339
|
.map((entry) => entry.trim())
|
|
331
340
|
.filter(Boolean),
|
|
332
341
|
},
|
|
333
342
|
{
|
|
334
|
-
name:
|
|
335
|
-
type:
|
|
336
|
-
message:
|
|
337
|
-
default:
|
|
338
|
-
when: (ans) => ans.mode ===
|
|
343
|
+
name: "chromeProfile",
|
|
344
|
+
type: "input",
|
|
345
|
+
message: "Chrome profile to reuse cookies from:",
|
|
346
|
+
default: "Default",
|
|
347
|
+
when: (ans) => ans.mode === "browser",
|
|
339
348
|
},
|
|
340
349
|
{
|
|
341
|
-
name:
|
|
342
|
-
type:
|
|
343
|
-
message:
|
|
344
|
-
when: (ans) => ans.mode ===
|
|
350
|
+
name: "chromeCookiePath",
|
|
351
|
+
type: "input",
|
|
352
|
+
message: "Cookie DB path (Chromium/Edge, optional):",
|
|
353
|
+
when: (ans) => ans.mode === "browser",
|
|
345
354
|
},
|
|
346
355
|
{
|
|
347
|
-
name:
|
|
348
|
-
type:
|
|
349
|
-
message:
|
|
356
|
+
name: "hideWindow",
|
|
357
|
+
type: "confirm",
|
|
358
|
+
message: "Hide Chrome window (macOS headful only)?",
|
|
350
359
|
default: false,
|
|
351
|
-
when: (ans) => ans.mode ===
|
|
360
|
+
when: (ans) => ans.mode === "browser",
|
|
352
361
|
},
|
|
353
362
|
{
|
|
354
|
-
name:
|
|
355
|
-
type:
|
|
356
|
-
message:
|
|
363
|
+
name: "keepBrowser",
|
|
364
|
+
type: "confirm",
|
|
365
|
+
message: "Keep browser open after completion?",
|
|
357
366
|
default: false,
|
|
358
|
-
when: (ans) => ans.mode ===
|
|
367
|
+
when: (ans) => ans.mode === "browser",
|
|
359
368
|
},
|
|
360
369
|
];
|
|
361
370
|
const answers = await inquirer.prompt(wizardQuestions);
|
|
362
371
|
const mode = (answers.mode ?? initialMode);
|
|
363
372
|
const prompt = await resolvePromptInput(answers.promptInput);
|
|
364
373
|
if (!prompt.trim()) {
|
|
365
|
-
console.log(chalk.yellow(
|
|
374
|
+
console.log(chalk.yellow("Cancelled."));
|
|
366
375
|
return;
|
|
367
376
|
}
|
|
368
|
-
const promptWithSuffix = userConfig.promptSuffix
|
|
377
|
+
const promptWithSuffix = userConfig.promptSuffix
|
|
378
|
+
? `${prompt.trim()}\n${userConfig.promptSuffix}`
|
|
379
|
+
: prompt;
|
|
369
380
|
await sessionStore.ensureStorage();
|
|
370
381
|
await pruneOldSessions(userConfig.sessionRetentionHours, (message) => console.log(chalk.dim(message)));
|
|
371
382
|
const normalizedMultiModels = Array.isArray(answers.models) && answers.models.length > 0
|
|
@@ -390,12 +401,12 @@ async function askOracleFlow(version, userConfig) {
|
|
|
390
401
|
sessionId: undefined,
|
|
391
402
|
verbose: false,
|
|
392
403
|
heartbeatIntervalMs: undefined,
|
|
393
|
-
browserAttachments:
|
|
404
|
+
browserAttachments: "auto",
|
|
394
405
|
browserInlineFiles: false,
|
|
395
406
|
browserBundleFiles: false,
|
|
396
407
|
background: undefined,
|
|
397
408
|
};
|
|
398
|
-
const browserConfig = mode ===
|
|
409
|
+
const browserConfig = mode === "browser"
|
|
399
410
|
? await buildBrowserConfig({
|
|
400
411
|
browserChromeProfile: answers.chromeProfile,
|
|
401
412
|
browserCookiePath: answers.chromeCookiePath,
|
|
@@ -430,7 +441,7 @@ async function askOracleFlow(version, userConfig) {
|
|
|
430
441
|
return true;
|
|
431
442
|
};
|
|
432
443
|
console.log(chalk.bold(`Session ${sessionMeta.id} starting...`));
|
|
433
|
-
console.log(dim(`Log path: ${path.join(os.homedir(),
|
|
444
|
+
console.log(dim(`Log path: ${path.join(os.homedir(), ".oracle", "sessions", sessionMeta.id, "output.log")}`));
|
|
434
445
|
try {
|
|
435
446
|
await performSessionRun({
|
|
436
447
|
sessionMeta,
|
|
@@ -463,7 +474,7 @@ async function resolvePromptInput(rawInput) {
|
|
|
463
474
|
try {
|
|
464
475
|
const stats = await fs.stat(asPath);
|
|
465
476
|
if (stats.isFile()) {
|
|
466
|
-
const contents = await fs.readFile(asPath,
|
|
477
|
+
const contents = await fs.readFile(asPath, "utf8");
|
|
467
478
|
return contents;
|
|
468
479
|
}
|
|
469
480
|
}
|
|
@@ -485,4 +496,4 @@ async function readStoredPrompt(sessionId) {
|
|
|
485
496
|
}
|
|
486
497
|
// Exported for testing
|
|
487
498
|
export { askOracleFlow, showSessionDetail };
|
|
488
|
-
export { resolveSessionCost as resolveCost } from
|
|
499
|
+
export { resolveSessionCost as resolveCost } from "../sessionTable.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import os from
|
|
2
|
-
import path from
|
|
3
|
-
import { sessionStore } from
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { sessionStore } from "../sessionStore.js";
|
|
4
4
|
export function resolveOutputPath(input, cwd) {
|
|
5
5
|
if (!input || input.trim().length === 0) {
|
|
6
6
|
return undefined;
|
|
7
7
|
}
|
|
8
|
-
const expanded = input.startsWith(
|
|
9
|
-
if (expanded ===
|
|
8
|
+
const expanded = input.startsWith("~/") ? path.join(os.homedir(), input.slice(2)) : input;
|
|
9
|
+
if (expanded === "-" || expanded === "/dev/stdout") {
|
|
10
10
|
return expanded;
|
|
11
11
|
}
|
|
12
12
|
const absolute = path.isAbsolute(expanded) ? expanded : path.resolve(cwd, expanded);
|
package/dist/src/config.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import JSON5 from
|
|
4
|
-
import { getOracleHomeDir } from
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import JSON5 from "json5";
|
|
4
|
+
import { getOracleHomeDir } from "./oracleHome.js";
|
|
5
5
|
function resolveConfigPath() {
|
|
6
|
-
return path.join(getOracleHomeDir(),
|
|
6
|
+
return path.join(getOracleHomeDir(), "config.json");
|
|
7
7
|
}
|
|
8
8
|
export async function loadUserConfig() {
|
|
9
9
|
const CONFIG_PATH = resolveConfigPath();
|
|
10
10
|
try {
|
|
11
|
-
const raw = await fs.readFile(CONFIG_PATH,
|
|
11
|
+
const raw = await fs.readFile(CONFIG_PATH, "utf8");
|
|
12
12
|
const parsed = JSON5.parse(raw);
|
|
13
13
|
return { config: parsed ?? {}, path: CONFIG_PATH, loaded: true };
|
|
14
14
|
}
|
|
15
15
|
catch (error) {
|
|
16
16
|
const code = error.code;
|
|
17
|
-
if (code ===
|
|
17
|
+
if (code === "ENOENT") {
|
|
18
18
|
return { config: {}, path: CONFIG_PATH, loaded: false };
|
|
19
19
|
}
|
|
20
20
|
console.warn(`Failed to read ${CONFIG_PATH}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import os from
|
|
3
|
-
import { mkdir } from
|
|
4
|
-
import { launchChrome, connectWithNewTab, closeTab } from
|
|
5
|
-
import { resolveBrowserConfig } from
|
|
6
|
-
import { readDevToolsPort, writeDevToolsActivePort, writeChromePid, cleanupStaleProfileState, verifyDevToolsReachable, } from
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { mkdir } from "node:fs/promises";
|
|
4
|
+
import { launchChrome, connectWithNewTab, closeTab } from "../browser/chromeLifecycle.js";
|
|
5
|
+
import { resolveBrowserConfig } from "../browser/config.js";
|
|
6
|
+
import { readDevToolsPort, writeDevToolsActivePort, writeChromePid, cleanupStaleProfileState, verifyDevToolsReachable, } from "../browser/profileState.js";
|
|
7
7
|
export async function openGeminiBrowserSession(input) {
|
|
8
8
|
const { browserConfig, keepBrowserDefault, purpose, log } = input;
|
|
9
|
-
const profileDir = browserConfig?.manualLoginProfileDir
|
|
10
|
-
?? path.join(os.homedir(), '.oracle', 'browser-profile');
|
|
11
|
-
await mkdir(profileDir, { recursive: true });
|
|
12
9
|
const resolvedConfig = resolveBrowserConfig({
|
|
13
10
|
...browserConfig,
|
|
14
11
|
manualLogin: true,
|
|
15
|
-
manualLoginProfileDir: profileDir,
|
|
16
12
|
keepBrowser: browserConfig?.keepBrowser ?? keepBrowserDefault,
|
|
17
13
|
});
|
|
14
|
+
const profileDir = resolvedConfig.manualLoginProfileDir ?? path.join(os.homedir(), ".oracle", "browser-profile");
|
|
15
|
+
await mkdir(profileDir, { recursive: true });
|
|
18
16
|
const keepBrowser = Boolean(resolvedConfig.keepBrowser);
|
|
19
17
|
let port = await readDevToolsPort(profileDir);
|
|
20
18
|
let launchedChrome = null;
|
|
@@ -23,7 +21,7 @@ export async function openGeminiBrowserSession(input) {
|
|
|
23
21
|
const probe = await verifyDevToolsReachable({ port });
|
|
24
22
|
if (!probe.ok) {
|
|
25
23
|
log?.(`[gemini-web] Stale DevTools port ${port}; launching fresh Chrome for ${purpose}.`);
|
|
26
|
-
await cleanupStaleProfileState(profileDir, log, { lockRemovalMode:
|
|
24
|
+
await cleanupStaleProfileState(profileDir, log, { lockRemovalMode: "if_oracle_pid_dead" });
|
|
27
25
|
port = null;
|
|
28
26
|
}
|
|
29
27
|
}
|
|
@@ -48,7 +46,9 @@ export async function openGeminiBrowserSession(input) {
|
|
|
48
46
|
try {
|
|
49
47
|
await client.close();
|
|
50
48
|
}
|
|
51
|
-
catch {
|
|
49
|
+
catch {
|
|
50
|
+
/* ignore */
|
|
51
|
+
}
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
if (targetId && port) {
|
|
@@ -57,13 +57,17 @@ export async function openGeminiBrowserSession(input) {
|
|
|
57
57
|
try {
|
|
58
58
|
await client.close();
|
|
59
59
|
}
|
|
60
|
-
catch {
|
|
60
|
+
catch {
|
|
61
|
+
/* ignore */
|
|
62
|
+
}
|
|
61
63
|
if (chromeWasLaunched && launchedChrome) {
|
|
62
64
|
try {
|
|
63
65
|
launchedChrome.kill();
|
|
64
66
|
}
|
|
65
|
-
catch {
|
|
66
|
-
|
|
67
|
+
catch {
|
|
68
|
+
/* ignore */
|
|
69
|
+
}
|
|
70
|
+
await cleanupStaleProfileState(profileDir, log, { lockRemovalMode: "never" }).catch(() => undefined);
|
|
67
71
|
}
|
|
68
72
|
};
|
|
69
73
|
return {
|