@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,28 +1,28 @@
|
|
|
1
|
-
import kleur from
|
|
2
|
-
import fs from
|
|
3
|
-
import path from
|
|
4
|
-
import { runOracle, OracleResponseError, OracleTransportError, extractResponseMetadata, asOracleUserError, extractTextOutput, } from
|
|
5
|
-
import { runBrowserSessionExecution } from
|
|
6
|
-
import { renderMarkdownAnsi } from
|
|
7
|
-
import { formatResponseMetadata, formatTransportMetadata } from
|
|
8
|
-
import { markErrorLogged } from
|
|
9
|
-
import { sendSessionNotification, deriveNotificationSettingsFromMetadata, } from
|
|
10
|
-
import { sessionStore } from
|
|
11
|
-
import { wait } from
|
|
12
|
-
import { runMultiModelApiSession } from
|
|
13
|
-
import { MODEL_CONFIGS, DEFAULT_SYSTEM_PROMPT } from
|
|
14
|
-
import { isKnownModel } from
|
|
15
|
-
import { resolveModelConfig } from
|
|
16
|
-
import { buildPrompt, buildRequestBody } from
|
|
17
|
-
import { estimateRequestTokens } from
|
|
18
|
-
import { formatTokenEstimate, formatTokenValue } from
|
|
19
|
-
import { formatFinishLine } from
|
|
20
|
-
import { sanitizeOscProgress } from
|
|
21
|
-
import { readFiles } from
|
|
22
|
-
import { cwd as getCwd } from
|
|
23
|
-
import { resumeBrowserSession } from
|
|
24
|
-
import { estimateTokenCount } from
|
|
25
|
-
import { formatElapsed } from
|
|
1
|
+
import kleur from "kleur";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { runOracle, OracleResponseError, OracleTransportError, extractResponseMetadata, asOracleUserError, extractTextOutput, } from "../oracle.js";
|
|
5
|
+
import { runBrowserSessionExecution, } from "../browser/sessionRunner.js";
|
|
6
|
+
import { renderMarkdownAnsi } from "./markdownRenderer.js";
|
|
7
|
+
import { formatResponseMetadata, formatTransportMetadata } from "./sessionDisplay.js";
|
|
8
|
+
import { markErrorLogged } from "./errorUtils.js";
|
|
9
|
+
import { sendSessionNotification, deriveNotificationSettingsFromMetadata, } from "./notifier.js";
|
|
10
|
+
import { sessionStore } from "../sessionStore.js";
|
|
11
|
+
import { wait } from "../sessionManager.js";
|
|
12
|
+
import { runMultiModelApiSession } from "../oracle/multiModelRunner.js";
|
|
13
|
+
import { MODEL_CONFIGS, DEFAULT_SYSTEM_PROMPT } from "../oracle/config.js";
|
|
14
|
+
import { isKnownModel } from "../oracle/modelResolver.js";
|
|
15
|
+
import { resolveModelConfig } from "../oracle/modelResolver.js";
|
|
16
|
+
import { buildPrompt, buildRequestBody } from "../oracle/request.js";
|
|
17
|
+
import { estimateRequestTokens } from "../oracle/tokenEstimate.js";
|
|
18
|
+
import { formatTokenEstimate, formatTokenValue } from "../oracle/runUtils.js";
|
|
19
|
+
import { formatFinishLine } from "../oracle/finishLine.js";
|
|
20
|
+
import { sanitizeOscProgress } from "./oscUtils.js";
|
|
21
|
+
import { readFiles } from "../oracle/files.js";
|
|
22
|
+
import { cwd as getCwd } from "node:process";
|
|
23
|
+
import { resumeBrowserSession } from "../browser/reattach.js";
|
|
24
|
+
import { estimateTokenCount } from "../browser/utils.js";
|
|
25
|
+
import { formatElapsed } from "../oracle/format.js";
|
|
26
26
|
const isTty = process.stdout.isTTY;
|
|
27
27
|
const dim = (text) => (isTty ? kleur.dim(text) : text);
|
|
28
28
|
export async function performSessionRun({ sessionMeta, runOptions, mode, browserConfig, cwd, log, write, version, notifications, browserDeps, muteStdout = false, }) {
|
|
@@ -32,7 +32,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
32
32
|
return muteStdout ? true : process.stdout.write(chunk);
|
|
33
33
|
};
|
|
34
34
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
35
|
-
status:
|
|
35
|
+
status: "running",
|
|
36
36
|
startedAt: new Date().toISOString(),
|
|
37
37
|
mode,
|
|
38
38
|
...(browserConfig ? { browser: { config: browserConfig } } : {}),
|
|
@@ -40,13 +40,13 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
40
40
|
const notificationSettings = notifications ?? deriveNotificationSettingsFromMetadata(sessionMeta, process.env);
|
|
41
41
|
const modelForStatus = runOptions.model ?? sessionMeta.model;
|
|
42
42
|
try {
|
|
43
|
-
if (mode ===
|
|
43
|
+
if (mode === "browser") {
|
|
44
44
|
if (!browserConfig) {
|
|
45
|
-
throw new Error(
|
|
45
|
+
throw new Error("Missing browser configuration for session.");
|
|
46
46
|
}
|
|
47
47
|
if (modelForStatus) {
|
|
48
48
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
49
|
-
status:
|
|
49
|
+
status: "running",
|
|
50
50
|
startedAt: new Date().toISOString(),
|
|
51
51
|
});
|
|
52
52
|
}
|
|
@@ -54,7 +54,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
54
54
|
...browserDeps,
|
|
55
55
|
persistRuntimeHint: async (runtime) => {
|
|
56
56
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
57
|
-
status:
|
|
57
|
+
status: "running",
|
|
58
58
|
browser: { config: browserConfig, runtime },
|
|
59
59
|
});
|
|
60
60
|
},
|
|
@@ -62,13 +62,13 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
62
62
|
const result = await runBrowserSessionExecution({ runOptions, browserConfig, cwd, log }, runnerDeps);
|
|
63
63
|
if (modelForStatus) {
|
|
64
64
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
65
|
-
status:
|
|
65
|
+
status: "completed",
|
|
66
66
|
completedAt: new Date().toISOString(),
|
|
67
67
|
usage: result.usage,
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
71
|
-
status:
|
|
71
|
+
status: "completed",
|
|
72
72
|
completedAt: new Date().toISOString(),
|
|
73
73
|
usage: result.usage,
|
|
74
74
|
elapsedMs: result.elapsedMs,
|
|
@@ -80,7 +80,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
80
80
|
transport: undefined,
|
|
81
81
|
error: undefined,
|
|
82
82
|
});
|
|
83
|
-
await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ??
|
|
83
|
+
await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ?? "", log);
|
|
84
84
|
await sendSessionNotification({
|
|
85
85
|
sessionId: sessionMeta.id,
|
|
86
86
|
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
@@ -95,13 +95,16 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
95
95
|
if (multiModels.length > 1) {
|
|
96
96
|
const [primaryModel] = multiModels;
|
|
97
97
|
if (!primaryModel) {
|
|
98
|
-
throw new Error(
|
|
98
|
+
throw new Error("Missing model name for multi-model run.");
|
|
99
99
|
}
|
|
100
100
|
const modelConfig = await resolveModelConfig(primaryModel, {
|
|
101
101
|
baseUrl: runOptions.baseUrl,
|
|
102
102
|
openRouterApiKey: process.env.OPENROUTER_API_KEY,
|
|
103
103
|
});
|
|
104
|
-
const files = await readFiles(runOptions.file ?? [], {
|
|
104
|
+
const files = await readFiles(runOptions.file ?? [], {
|
|
105
|
+
cwd,
|
|
106
|
+
maxFileSizeBytes: runOptions.maxFileSizeBytes,
|
|
107
|
+
});
|
|
105
108
|
const promptWithFiles = buildPrompt(runOptions.prompt, files, cwd);
|
|
106
109
|
const requestBody = buildRequestBody({
|
|
107
110
|
modelConfig,
|
|
@@ -113,30 +116,30 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
113
116
|
storeResponse: runOptions.background,
|
|
114
117
|
});
|
|
115
118
|
const estimatedTokens = estimateRequestTokens(requestBody, modelConfig);
|
|
116
|
-
const tokenLabel = formatTokenEstimate(estimatedTokens, (text) =>
|
|
117
|
-
const filesPhrase = files.length === 0 ?
|
|
118
|
-
const modelsLabel = multiModels.join(
|
|
119
|
+
const tokenLabel = formatTokenEstimate(estimatedTokens, (text) => isTty ? kleur.green(text) : text);
|
|
120
|
+
const filesPhrase = files.length === 0 ? "no files" : `${files.length} files`;
|
|
121
|
+
const modelsLabel = multiModels.join(", ");
|
|
119
122
|
log(`Calling ${isTty ? kleur.cyan(modelsLabel) : modelsLabel} — ${tokenLabel} tokens, ${filesPhrase}.`);
|
|
120
123
|
const multiRunTips = [];
|
|
121
124
|
if (files.length === 0) {
|
|
122
|
-
multiRunTips.push(
|
|
125
|
+
multiRunTips.push("Tip: no files attached — Oracle works best with project context. Add files via --file path/to/code or docs.");
|
|
123
126
|
}
|
|
124
127
|
const shortPrompt = (runOptions.prompt?.trim().length ?? 0) < 80;
|
|
125
128
|
if (shortPrompt) {
|
|
126
|
-
multiRunTips.push(
|
|
129
|
+
multiRunTips.push("Tip: brief prompts often yield generic answers — aim for 6–30 sentences and attach key files.");
|
|
127
130
|
}
|
|
128
131
|
for (const tip of multiRunTips) {
|
|
129
132
|
log(dim(tip));
|
|
130
133
|
}
|
|
131
134
|
// Surface long-running model expectations up front so users know why a response might lag.
|
|
132
|
-
const longRunningModels = multiModels.filter((model) => isKnownModel(model) && MODEL_CONFIGS[model]?.reasoning?.effort ===
|
|
135
|
+
const longRunningModels = multiModels.filter((model) => isKnownModel(model) && MODEL_CONFIGS[model]?.reasoning?.effort === "high");
|
|
133
136
|
if (longRunningModels.length > 0) {
|
|
134
137
|
for (const model of longRunningModels) {
|
|
135
|
-
log(
|
|
138
|
+
log("");
|
|
136
139
|
const headingLabel = `[${model}]`;
|
|
137
140
|
log(isTty ? kleur.bold(headingLabel) : headingLabel);
|
|
138
|
-
log(dim(
|
|
139
|
-
log(dim(
|
|
141
|
+
log(dim("This model can take up to 60 minutes (usually replies much faster)."));
|
|
142
|
+
log(dim("Press Ctrl+C to cancel."));
|
|
140
143
|
}
|
|
141
144
|
}
|
|
142
145
|
const shouldStreamInline = !muteStdout && process.stdout.isTTY;
|
|
@@ -149,7 +152,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
149
152
|
return;
|
|
150
153
|
printedModels.add(model);
|
|
151
154
|
const body = stripOscProgress(await sessionStore.readModelLog(sessionMeta.id, model));
|
|
152
|
-
log(
|
|
155
|
+
log("");
|
|
153
156
|
const fallback = answerFallbacks.get(model);
|
|
154
157
|
const hasBody = body.length > 0;
|
|
155
158
|
if (!hasBody && !fallback) {
|
|
@@ -159,11 +162,11 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
159
162
|
const headingLabel = `[${model}]`;
|
|
160
163
|
const heading = shouldStreamInline ? kleur.bold(headingLabel) : headingLabel;
|
|
161
164
|
log(heading);
|
|
162
|
-
const content = hasBody ? body : fallback ??
|
|
165
|
+
const content = hasBody ? body : (fallback ?? "");
|
|
163
166
|
const printable = shouldRenderMarkdown ? renderMarkdownAnsi(content) : content;
|
|
164
167
|
writeInline(printable);
|
|
165
|
-
if (!printable.endsWith(
|
|
166
|
-
log(
|
|
168
|
+
if (!printable.endsWith("\n")) {
|
|
169
|
+
log("");
|
|
167
170
|
}
|
|
168
171
|
};
|
|
169
172
|
const summary = await runMultiModelApiSession({
|
|
@@ -189,7 +192,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
189
192
|
// If we couldn't stream inline (e.g., non-TTY), print all logs after completion.
|
|
190
193
|
for (const [index, result] of summary.fulfilled.entries()) {
|
|
191
194
|
if (index > 0) {
|
|
192
|
-
log(
|
|
195
|
+
log("");
|
|
193
196
|
}
|
|
194
197
|
await printModelLog(result.model);
|
|
195
198
|
}
|
|
@@ -213,14 +216,18 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
213
216
|
reasoning_tokens: aggregateUsage.reasoningTokens,
|
|
214
217
|
total_tokens: aggregateUsage.totalTokens,
|
|
215
218
|
}, idx))
|
|
216
|
-
.join(
|
|
219
|
+
.join("/");
|
|
217
220
|
const tokensPart = (() => {
|
|
218
|
-
const parts = tokensDisplay.split(
|
|
221
|
+
const parts = tokensDisplay.split("/");
|
|
219
222
|
if (parts.length !== 4)
|
|
220
223
|
return tokensDisplay;
|
|
221
224
|
return `↑${parts[0]} ↓${parts[1]} ↻${parts[2]} Δ${parts[3]}`;
|
|
222
225
|
})();
|
|
223
|
-
const statusColor = summary.rejected.length === 0
|
|
226
|
+
const statusColor = summary.rejected.length === 0
|
|
227
|
+
? kleur.green
|
|
228
|
+
: summary.fulfilled.length > 0
|
|
229
|
+
? kleur.yellow
|
|
230
|
+
: kleur.red;
|
|
224
231
|
const overallText = `${summary.fulfilled.length}/${multiModels.length} models`;
|
|
225
232
|
const { line1 } = formatFinishLine({
|
|
226
233
|
elapsedMs: summary.elapsedMs,
|
|
@@ -231,7 +238,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
231
238
|
log(statusColor(line1));
|
|
232
239
|
const hasFailure = summary.rejected.length > 0;
|
|
233
240
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
234
|
-
status: hasFailure ?
|
|
241
|
+
status: hasFailure ? "error" : "completed",
|
|
235
242
|
completedAt: new Date().toISOString(),
|
|
236
243
|
usage: aggregateUsage,
|
|
237
244
|
elapsedMs: summary.elapsedMs,
|
|
@@ -258,7 +265,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
258
265
|
}
|
|
259
266
|
}
|
|
260
267
|
if (savedOutputs.length > 0) {
|
|
261
|
-
log(dim(
|
|
268
|
+
log(dim("Saved outputs:"));
|
|
262
269
|
for (const item of savedOutputs) {
|
|
263
270
|
log(dim(`- ${item.model} -> ${item.path}`));
|
|
264
271
|
}
|
|
@@ -275,7 +282,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
275
282
|
: runOptions;
|
|
276
283
|
if (modelForStatus && singleModelOverride == null) {
|
|
277
284
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
278
|
-
status:
|
|
285
|
+
status: "running",
|
|
279
286
|
startedAt: new Date().toISOString(),
|
|
280
287
|
});
|
|
281
288
|
}
|
|
@@ -285,11 +292,11 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
285
292
|
write,
|
|
286
293
|
allowStdout: !muteStdout,
|
|
287
294
|
});
|
|
288
|
-
if (result.mode !==
|
|
289
|
-
throw new Error(
|
|
295
|
+
if (result.mode !== "live") {
|
|
296
|
+
throw new Error("Unexpected preview result while running a session.");
|
|
290
297
|
}
|
|
291
298
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
292
|
-
status:
|
|
299
|
+
status: "completed",
|
|
293
300
|
completedAt: new Date().toISOString(),
|
|
294
301
|
usage: result.usage,
|
|
295
302
|
elapsedMs: result.elapsedMs,
|
|
@@ -299,7 +306,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
299
306
|
});
|
|
300
307
|
if (modelForStatus && singleModelOverride == null) {
|
|
301
308
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
302
|
-
status:
|
|
309
|
+
status: "completed",
|
|
303
310
|
completedAt: new Date().toISOString(),
|
|
304
311
|
usage: result.usage,
|
|
305
312
|
});
|
|
@@ -320,47 +327,53 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
320
327
|
log(`ERROR: ${message}`);
|
|
321
328
|
markErrorLogged(error);
|
|
322
329
|
const userError = asOracleUserError(error);
|
|
323
|
-
const connectionLost = userError?.category ===
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
const connectionLost = userError?.category === "browser-automation" &&
|
|
331
|
+
userError.details?.stage === "connection-lost";
|
|
332
|
+
const assistantTimeout = userError?.category === "browser-automation" &&
|
|
333
|
+
userError.details?.stage === "assistant-timeout";
|
|
334
|
+
const cloudflareChallenge = userError?.category === "browser-automation" &&
|
|
335
|
+
userError.details?.stage === "cloudflare-challenge";
|
|
336
|
+
if (connectionLost && mode === "browser") {
|
|
337
|
+
const runtime = userError.details
|
|
338
|
+
?.runtime;
|
|
339
|
+
log(dim("Chrome disconnected before completion; keeping session running for reattach."));
|
|
328
340
|
if (modelForStatus) {
|
|
329
341
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
330
|
-
status:
|
|
342
|
+
status: "running",
|
|
331
343
|
completedAt: undefined,
|
|
332
344
|
});
|
|
333
345
|
}
|
|
334
346
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
335
|
-
status:
|
|
347
|
+
status: "running",
|
|
336
348
|
errorMessage: message,
|
|
337
349
|
mode,
|
|
338
350
|
browser: {
|
|
339
351
|
config: browserConfig,
|
|
340
352
|
runtime: runtime ?? sessionMeta.browser?.runtime,
|
|
341
353
|
},
|
|
342
|
-
response: { status:
|
|
354
|
+
response: { status: "running", incompleteReason: "chrome-disconnected" },
|
|
343
355
|
});
|
|
344
356
|
return;
|
|
345
357
|
}
|
|
346
|
-
if (assistantTimeout && mode ===
|
|
347
|
-
const runtime = userError.details
|
|
348
|
-
|
|
358
|
+
if (assistantTimeout && mode === "browser") {
|
|
359
|
+
const runtime = userError.details
|
|
360
|
+
?.runtime;
|
|
361
|
+
log(dim("Assistant response timed out; keeping session running for reattach."));
|
|
349
362
|
if (modelForStatus) {
|
|
350
363
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
351
|
-
status:
|
|
364
|
+
status: "running",
|
|
352
365
|
completedAt: undefined,
|
|
353
366
|
});
|
|
354
367
|
}
|
|
355
368
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
356
|
-
status:
|
|
369
|
+
status: "running",
|
|
357
370
|
errorMessage: message,
|
|
358
371
|
mode,
|
|
359
372
|
browser: {
|
|
360
373
|
config: browserConfig,
|
|
361
374
|
runtime: runtime ?? sessionMeta.browser?.runtime,
|
|
362
375
|
},
|
|
363
|
-
response: { status:
|
|
376
|
+
response: { status: "running", incompleteReason: "assistant-timeout" },
|
|
364
377
|
});
|
|
365
378
|
const autoReattachIntervalMs = browserConfig?.autoReattachIntervalMs ?? 0;
|
|
366
379
|
if (autoReattachIntervalMs > 0) {
|
|
@@ -381,6 +394,13 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
381
394
|
log(dim(`Reattach later with: oracle session ${sessionMeta.id}`));
|
|
382
395
|
return;
|
|
383
396
|
}
|
|
397
|
+
if (cloudflareChallenge && mode === "browser") {
|
|
398
|
+
const details = userError.details;
|
|
399
|
+
log(dim("Cloudflare challenge detected; browser left running so you can complete the check."));
|
|
400
|
+
if (details?.reuseProfileHint) {
|
|
401
|
+
log(dim(`Reuse this browser profile with: ${details.reuseProfileHint}`));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
384
404
|
if (userError) {
|
|
385
405
|
log(dim(`User error (${userError.category}): ${userError.message}`));
|
|
386
406
|
}
|
|
@@ -394,12 +414,20 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
394
414
|
if (transportLine) {
|
|
395
415
|
log(dim(`Transport: ${transportLine}`));
|
|
396
416
|
}
|
|
417
|
+
const browserRuntime = mode === "browser"
|
|
418
|
+
? userError?.details?.runtime
|
|
419
|
+
: undefined;
|
|
397
420
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
398
|
-
status:
|
|
421
|
+
status: "error",
|
|
399
422
|
completedAt: new Date().toISOString(),
|
|
400
423
|
errorMessage: message,
|
|
401
424
|
mode,
|
|
402
|
-
browser: browserConfig
|
|
425
|
+
browser: browserConfig
|
|
426
|
+
? {
|
|
427
|
+
config: browserConfig,
|
|
428
|
+
runtime: browserRuntime ?? undefined,
|
|
429
|
+
}
|
|
430
|
+
: undefined,
|
|
403
431
|
response: responseMetadata,
|
|
404
432
|
transport: transportMetadata,
|
|
405
433
|
error: userError
|
|
@@ -412,7 +440,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
412
440
|
});
|
|
413
441
|
if (modelForStatus) {
|
|
414
442
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
415
|
-
status:
|
|
443
|
+
status: "error",
|
|
416
444
|
completedAt: new Date().toISOString(),
|
|
417
445
|
});
|
|
418
446
|
}
|
|
@@ -426,7 +454,7 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
426
454
|
if (!targetPath)
|
|
427
455
|
return;
|
|
428
456
|
if (!content || content.trim().length === 0) {
|
|
429
|
-
log(dim(
|
|
457
|
+
log(dim("write-output skipped: no assistant content to save."));
|
|
430
458
|
return;
|
|
431
459
|
}
|
|
432
460
|
const normalizedTarget = path.resolve(targetPath);
|
|
@@ -438,8 +466,8 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
438
466
|
}
|
|
439
467
|
try {
|
|
440
468
|
await fs.mkdir(path.dirname(normalizedTarget), { recursive: true });
|
|
441
|
-
const payload = content.endsWith(
|
|
442
|
-
await fs.writeFile(normalizedTarget, payload,
|
|
469
|
+
const payload = content.endsWith("\n") ? content : `${content}\n`;
|
|
470
|
+
await fs.writeFile(normalizedTarget, payload, "utf8");
|
|
443
471
|
log(dim(`Saved assistant output to ${normalizedTarget}`));
|
|
444
472
|
return normalizedTarget;
|
|
445
473
|
}
|
|
@@ -450,8 +478,8 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
450
478
|
if (fallbackPath) {
|
|
451
479
|
try {
|
|
452
480
|
await fs.mkdir(path.dirname(fallbackPath), { recursive: true });
|
|
453
|
-
const payload = content.endsWith(
|
|
454
|
-
await fs.writeFile(fallbackPath, payload,
|
|
481
|
+
const payload = content.endsWith("\n") ? content : `${content}\n`;
|
|
482
|
+
await fs.writeFile(fallbackPath, payload, "utf8");
|
|
455
483
|
log(dim(`write-output fallback to ${fallbackPath} (original failed: ${reason})`));
|
|
456
484
|
return fallbackPath;
|
|
457
485
|
}
|
|
@@ -467,7 +495,7 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
467
495
|
}
|
|
468
496
|
async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig, runOptions, modelForStatus, notificationSettings, log, }) {
|
|
469
497
|
if (!runtime || !browserConfig) {
|
|
470
|
-
log(dim(
|
|
498
|
+
log(dim("Auto-reattach disabled: missing runtime or browser config."));
|
|
471
499
|
return false;
|
|
472
500
|
}
|
|
473
501
|
const delayMs = Math.max(0, browserConfig.autoReattachDelayMs ?? 0);
|
|
@@ -508,16 +536,16 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
508
536
|
const result = await resumeBrowserSession(runtime, reattachConfig, logger, {
|
|
509
537
|
promptPreview: sessionMeta.promptPreview,
|
|
510
538
|
});
|
|
511
|
-
const answerText = result.answerMarkdown || result.answerText ||
|
|
539
|
+
const answerText = result.answerMarkdown || result.answerText || "";
|
|
512
540
|
const outputTokens = estimateTokenCount(answerText);
|
|
513
541
|
const logWriter = sessionStore.createLogWriter(sessionMeta.id);
|
|
514
542
|
logWriter.logLine(`[auto-reattach] captured assistant response on attempt ${attempt}`);
|
|
515
|
-
logWriter.logLine(
|
|
543
|
+
logWriter.logLine("Answer:");
|
|
516
544
|
logWriter.logLine(answerText);
|
|
517
545
|
logWriter.stream.end();
|
|
518
546
|
if (modelForStatus) {
|
|
519
547
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
520
|
-
status:
|
|
548
|
+
status: "completed",
|
|
521
549
|
completedAt: new Date().toISOString(),
|
|
522
550
|
usage: {
|
|
523
551
|
inputTokens: 0,
|
|
@@ -528,7 +556,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
528
556
|
});
|
|
529
557
|
}
|
|
530
558
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
531
|
-
status:
|
|
559
|
+
status: "completed",
|
|
532
560
|
completedAt: new Date().toISOString(),
|
|
533
561
|
usage: {
|
|
534
562
|
inputTokens: 0,
|
|
@@ -540,7 +568,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
540
568
|
config: browserConfig,
|
|
541
569
|
runtime,
|
|
542
570
|
},
|
|
543
|
-
response: { status:
|
|
571
|
+
response: { status: "completed" },
|
|
544
572
|
error: undefined,
|
|
545
573
|
transport: undefined,
|
|
546
574
|
});
|
|
@@ -548,7 +576,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
548
576
|
await sendSessionNotification({
|
|
549
577
|
sessionId: sessionMeta.id,
|
|
550
578
|
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
551
|
-
mode: sessionMeta.mode ??
|
|
579
|
+
mode: sessionMeta.mode ?? "browser",
|
|
552
580
|
model: sessionMeta.model ?? runOptions.model,
|
|
553
581
|
usage: {
|
|
554
582
|
inputTokens: 0,
|
|
@@ -556,7 +584,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
556
584
|
},
|
|
557
585
|
characters: answerText.length,
|
|
558
586
|
}, notificationSettings, log, answerText.slice(0, 140));
|
|
559
|
-
log(kleur.green(
|
|
587
|
+
log(kleur.green("Auto-reattach succeeded; session marked completed."));
|
|
560
588
|
return true;
|
|
561
589
|
}
|
|
562
590
|
catch (error) {
|
|
@@ -584,7 +612,7 @@ function isPermissionError(error) {
|
|
|
584
612
|
if (!(error instanceof Error))
|
|
585
613
|
return false;
|
|
586
614
|
const code = error.code;
|
|
587
|
-
return code ===
|
|
615
|
+
return code === "EACCES" || code === "EPERM";
|
|
588
616
|
}
|
|
589
617
|
function buildFallbackPath(original) {
|
|
590
618
|
const ext = path.extname(original);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import kleur from
|
|
3
|
-
import { MODEL_CONFIGS } from
|
|
4
|
-
import { estimateUsdCost } from
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import kleur from "kleur";
|
|
3
|
+
import { MODEL_CONFIGS } from "../oracle.js";
|
|
4
|
+
import { estimateUsdCost } from "tokentally";
|
|
5
5
|
const isRich = (rich) => rich ?? Boolean(process.stdout.isTTY && chalk.level > 0);
|
|
6
6
|
const dim = (text, rich) => (rich ? kleur.dim(text) : text);
|
|
7
7
|
export const STATUS_PAD = 9;
|
|
@@ -11,30 +11,31 @@ export const TIMESTAMP_PAD = 19;
|
|
|
11
11
|
export const CHARS_PAD = 5;
|
|
12
12
|
export const COST_PAD = 7;
|
|
13
13
|
export function formatSessionTableHeader(rich) {
|
|
14
|
-
const header = `${
|
|
14
|
+
const header = `${"Status".padEnd(STATUS_PAD)} ${"Model".padEnd(MODEL_PAD)} ${"Mode".padEnd(MODE_PAD)} ${"Timestamp".padEnd(TIMESTAMP_PAD)} ${"Chars".padStart(CHARS_PAD)} ${"Cost".padStart(COST_PAD)} Slug`;
|
|
15
15
|
return dim(header, isRich(rich));
|
|
16
16
|
}
|
|
17
17
|
export function formatSessionTableRow(meta, options) {
|
|
18
18
|
const rich = isRich(options?.rich);
|
|
19
|
-
const status = colorStatus(meta.status ??
|
|
20
|
-
const modelLabel = (meta.model ??
|
|
19
|
+
const status = colorStatus(meta.status ?? "unknown", rich);
|
|
20
|
+
const modelLabel = (meta.model ?? "n/a").padEnd(MODEL_PAD);
|
|
21
21
|
const model = rich ? chalk.white(modelLabel) : modelLabel;
|
|
22
|
-
const modeLabel = (meta.mode ?? meta.options?.mode ??
|
|
22
|
+
const modeLabel = (meta.mode ?? meta.options?.mode ?? "api").padEnd(MODE_PAD);
|
|
23
23
|
const mode = rich ? chalk.gray(modeLabel) : modeLabel;
|
|
24
24
|
const timestampLabel = formatTimestampAligned(meta.createdAt).padEnd(TIMESTAMP_PAD);
|
|
25
25
|
const timestamp = rich ? chalk.gray(timestampLabel) : timestampLabel;
|
|
26
26
|
const charsValue = meta.options?.prompt?.length ?? meta.promptPreview?.length ?? 0;
|
|
27
|
-
const charsRaw = charsValue > 0 ? String(charsValue).padStart(CHARS_PAD) : `${
|
|
27
|
+
const charsRaw = charsValue > 0 ? String(charsValue).padStart(CHARS_PAD) : `${"".padStart(CHARS_PAD - 1)}-`;
|
|
28
28
|
const chars = rich ? chalk.gray(charsRaw) : charsRaw;
|
|
29
29
|
const costValue = resolveSessionCost(meta);
|
|
30
|
-
const costRaw = costValue != null ? formatCostTable(costValue) : `${
|
|
30
|
+
const costRaw = costValue != null ? formatCostTable(costValue) : `${"".padStart(COST_PAD - 1)}-`;
|
|
31
31
|
const cost = rich ? chalk.gray(costRaw) : costRaw;
|
|
32
|
-
const
|
|
32
|
+
const slugValue = options?.displaySlug ?? meta.id;
|
|
33
|
+
const slug = rich ? chalk.cyan(slugValue) : slugValue;
|
|
33
34
|
return `${status} ${model} ${mode} ${timestamp} ${chars} ${cost} ${slug}`;
|
|
34
35
|
}
|
|
35
36
|
export function resolveSessionCost(meta) {
|
|
36
37
|
const mode = meta.mode ?? meta.options?.mode;
|
|
37
|
-
if (mode ===
|
|
38
|
+
if (mode === "browser") {
|
|
38
39
|
return null;
|
|
39
40
|
}
|
|
40
41
|
if (meta.usage?.cost != null) {
|
|
@@ -51,25 +52,28 @@ export function resolveSessionCost(meta) {
|
|
|
51
52
|
const output = meta.usage.outputTokens ?? 0;
|
|
52
53
|
const cost = estimateUsdCost({
|
|
53
54
|
usage: { inputTokens: input, outputTokens: output },
|
|
54
|
-
pricing: {
|
|
55
|
+
pricing: {
|
|
56
|
+
inputUsdPerToken: pricing.inputPerToken,
|
|
57
|
+
outputUsdPerToken: pricing.outputPerToken,
|
|
58
|
+
},
|
|
55
59
|
})?.totalUsd ?? 0;
|
|
56
60
|
return cost > 0 ? cost : null;
|
|
57
61
|
}
|
|
58
62
|
export function formatTimestampAligned(iso) {
|
|
59
63
|
const date = new Date(iso);
|
|
60
|
-
const locale =
|
|
64
|
+
const locale = "en-US";
|
|
61
65
|
const opts = {
|
|
62
|
-
year:
|
|
63
|
-
month:
|
|
64
|
-
day:
|
|
65
|
-
hour:
|
|
66
|
-
minute:
|
|
66
|
+
year: "numeric",
|
|
67
|
+
month: "2-digit",
|
|
68
|
+
day: "2-digit",
|
|
69
|
+
hour: "numeric",
|
|
70
|
+
minute: "2-digit",
|
|
67
71
|
second: undefined,
|
|
68
72
|
hour12: true,
|
|
69
73
|
};
|
|
70
74
|
let formatted = date.toLocaleString(locale, opts);
|
|
71
|
-
formatted = formatted.replace(
|
|
72
|
-
return formatted.replace(/(\s)(\d:)/,
|
|
75
|
+
formatted = formatted.replace(", ", " ");
|
|
76
|
+
return formatted.replace(/(\s)(\d:)/, "$1 $2");
|
|
73
77
|
}
|
|
74
78
|
function formatCostTable(cost) {
|
|
75
79
|
return `$${cost.toFixed(3)}`.padStart(COST_PAD);
|
|
@@ -80,11 +84,11 @@ function colorStatus(status, rich) {
|
|
|
80
84
|
return padded;
|
|
81
85
|
}
|
|
82
86
|
switch (status) {
|
|
83
|
-
case
|
|
87
|
+
case "completed":
|
|
84
88
|
return chalk.green(padded);
|
|
85
|
-
case
|
|
89
|
+
case "error":
|
|
86
90
|
return chalk.red(padded);
|
|
87
|
-
case
|
|
91
|
+
case "running":
|
|
88
92
|
return chalk.yellow(padded);
|
|
89
93
|
default:
|
|
90
94
|
return padded;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export async function readStdin(stream = process.stdin) {
|
|
2
|
+
const chunks = [];
|
|
3
|
+
const maybeTextStream = stream;
|
|
4
|
+
maybeTextStream.setEncoding?.("utf8");
|
|
5
|
+
for await (const chunk of stream) {
|
|
6
|
+
chunks.push(typeof chunk === "string" ? chunk : String(chunk));
|
|
7
|
+
}
|
|
8
|
+
return chunks.join("");
|
|
9
|
+
}
|
|
10
|
+
export async function resolveDashPrompt(prompt, stream = process.stdin) {
|
|
11
|
+
if (prompt !== "-") {
|
|
12
|
+
return prompt;
|
|
13
|
+
}
|
|
14
|
+
if (stream.isTTY) {
|
|
15
|
+
throw new Error(`"-p -" requires piped input, for example: echo "prompt" | oracle -p -.`);
|
|
16
|
+
}
|
|
17
|
+
const stdinPrompt = (await readStdin(stream)).trim();
|
|
18
|
+
if (!stdinPrompt) {
|
|
19
|
+
throw new Error(`"-p -" received empty stdin.`);
|
|
20
|
+
}
|
|
21
|
+
return stdinPrompt;
|
|
22
|
+
}
|