@steipete/oracle 0.9.0 → 0.11.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 +107 -49
- package/dist/bin/oracle-cli.js +551 -410
- 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/archiveConversation.js +224 -0
- package/dist/src/browser/actions/assistantResponse.js +175 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/deepResearch.js +662 -0
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +342 -119
- package/dist/src/browser/actions/navigation.js +183 -137
- package/dist/src/browser/actions/projectSources.js +491 -0
- package/dist/src/browser/actions/promptComposer.js +152 -91
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingStatus.js +391 -0
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/artifacts.js +150 -0
- package/dist/src/browser/attachRunning.js +31 -0
- package/dist/src/browser/chatgptImages.js +315 -0
- package/dist/src/browser/chromeLifecycle.js +276 -63
- package/dist/src/browser/config.js +59 -16
- package/dist/src/browser/constants.js +25 -12
- package/dist/src/browser/controlPlan.js +81 -0
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +250 -77
- package/dist/src/browser/domDebug.js +50 -1
- package/dist/src/browser/index.js +1559 -692
- package/dist/src/browser/liveTabs.js +434 -0
- 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 +127 -42
- package/dist/src/browser/projectSourcesRunner.js +366 -0
- 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 +178 -73
- package/dist/src/browser/reattachHelpers.js +32 -27
- package/dist/src/browser/sessionRunner.js +89 -25
- package/dist/src/browser/tabLeaseRegistry.js +182 -0
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +24 -20
- 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 +102 -48
- package/dist/src/cli/browserDefaults.js +51 -26
- package/dist/src/cli/browserTabs.js +228 -0
- 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 +62 -26
- package/dist/src/cli/duplicatePromptGuard.js +12 -4
- 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 +131 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/projectSources.js +116 -0
- 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 +82 -21
- package/dist/src/cli/sessionDisplay.js +213 -87
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +149 -95
- 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/consultPresets.js +19 -0
- package/dist/src/mcp/server.js +18 -12
- package/dist/src/mcp/tools/consult.js +246 -67
- package/dist/src/mcp/tools/projectSources.js +123 -0
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +12 -5
- package/dist/src/mcp/utils.js +21 -8
- 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 +160 -135
- 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/projectSources/plan.js +27 -0
- package/dist/src/projectSources/url.js +23 -0
- 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 +78 -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 +67 -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/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 → src/projectSources}/types.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 { ensureSessionArtifacts, 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,33 +54,41 @@ 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
|
},
|
|
61
61
|
};
|
|
62
|
-
const result = await runBrowserSessionExecution({
|
|
62
|
+
const result = await runBrowserSessionExecution({
|
|
63
|
+
runOptions: { ...runOptions, sessionId: runOptions.sessionId ?? sessionMeta.id },
|
|
64
|
+
browserConfig,
|
|
65
|
+
cwd,
|
|
66
|
+
log,
|
|
67
|
+
}, runnerDeps);
|
|
63
68
|
if (modelForStatus) {
|
|
64
69
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
65
|
-
status:
|
|
70
|
+
status: "completed",
|
|
66
71
|
completedAt: new Date().toISOString(),
|
|
67
72
|
usage: result.usage,
|
|
68
73
|
});
|
|
69
74
|
}
|
|
70
75
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
71
|
-
status:
|
|
76
|
+
status: "completed",
|
|
72
77
|
completedAt: new Date().toISOString(),
|
|
73
78
|
usage: result.usage,
|
|
74
79
|
elapsedMs: result.elapsedMs,
|
|
80
|
+
errorMessage: undefined,
|
|
75
81
|
browser: {
|
|
76
82
|
config: browserConfig,
|
|
77
83
|
runtime: result.runtime,
|
|
84
|
+
archive: result.archive,
|
|
78
85
|
},
|
|
86
|
+
artifacts: mergeArtifacts(sessionMeta.artifacts, result.artifacts),
|
|
79
87
|
response: undefined,
|
|
80
88
|
transport: undefined,
|
|
81
89
|
error: undefined,
|
|
82
90
|
});
|
|
83
|
-
await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ??
|
|
91
|
+
await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ?? "", log);
|
|
84
92
|
await sendSessionNotification({
|
|
85
93
|
sessionId: sessionMeta.id,
|
|
86
94
|
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
@@ -95,7 +103,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
95
103
|
if (multiModels.length > 1) {
|
|
96
104
|
const [primaryModel] = multiModels;
|
|
97
105
|
if (!primaryModel) {
|
|
98
|
-
throw new Error(
|
|
106
|
+
throw new Error("Missing model name for multi-model run.");
|
|
99
107
|
}
|
|
100
108
|
const modelConfig = await resolveModelConfig(primaryModel, {
|
|
101
109
|
baseUrl: runOptions.baseUrl,
|
|
@@ -116,30 +124,30 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
116
124
|
storeResponse: runOptions.background,
|
|
117
125
|
});
|
|
118
126
|
const estimatedTokens = estimateRequestTokens(requestBody, modelConfig);
|
|
119
|
-
const tokenLabel = formatTokenEstimate(estimatedTokens, (text) =>
|
|
120
|
-
const filesPhrase = files.length === 0 ?
|
|
121
|
-
const modelsLabel = multiModels.join(
|
|
127
|
+
const tokenLabel = formatTokenEstimate(estimatedTokens, (text) => isTty ? kleur.green(text) : text);
|
|
128
|
+
const filesPhrase = files.length === 0 ? "no files" : `${files.length} files`;
|
|
129
|
+
const modelsLabel = multiModels.join(", ");
|
|
122
130
|
log(`Calling ${isTty ? kleur.cyan(modelsLabel) : modelsLabel} — ${tokenLabel} tokens, ${filesPhrase}.`);
|
|
123
131
|
const multiRunTips = [];
|
|
124
132
|
if (files.length === 0) {
|
|
125
|
-
multiRunTips.push(
|
|
133
|
+
multiRunTips.push("Tip: no files attached — Oracle works best with project context. Add files via --file path/to/code or docs.");
|
|
126
134
|
}
|
|
127
135
|
const shortPrompt = (runOptions.prompt?.trim().length ?? 0) < 80;
|
|
128
136
|
if (shortPrompt) {
|
|
129
|
-
multiRunTips.push(
|
|
137
|
+
multiRunTips.push("Tip: brief prompts often yield generic answers — aim for 6–30 sentences and attach key files.");
|
|
130
138
|
}
|
|
131
139
|
for (const tip of multiRunTips) {
|
|
132
140
|
log(dim(tip));
|
|
133
141
|
}
|
|
134
142
|
// Surface long-running model expectations up front so users know why a response might lag.
|
|
135
|
-
const longRunningModels = multiModels.filter((model) => isKnownModel(model) && MODEL_CONFIGS[model]?.reasoning?.effort ===
|
|
143
|
+
const longRunningModels = multiModels.filter((model) => isKnownModel(model) && MODEL_CONFIGS[model]?.reasoning?.effort === "high");
|
|
136
144
|
if (longRunningModels.length > 0) {
|
|
137
145
|
for (const model of longRunningModels) {
|
|
138
|
-
log(
|
|
146
|
+
log("");
|
|
139
147
|
const headingLabel = `[${model}]`;
|
|
140
148
|
log(isTty ? kleur.bold(headingLabel) : headingLabel);
|
|
141
|
-
log(dim(
|
|
142
|
-
log(dim(
|
|
149
|
+
log(dim("This model can take up to 60 minutes (usually replies much faster)."));
|
|
150
|
+
log(dim("Press Ctrl+C to cancel."));
|
|
143
151
|
}
|
|
144
152
|
}
|
|
145
153
|
const shouldStreamInline = !muteStdout && process.stdout.isTTY;
|
|
@@ -152,7 +160,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
152
160
|
return;
|
|
153
161
|
printedModels.add(model);
|
|
154
162
|
const body = stripOscProgress(await sessionStore.readModelLog(sessionMeta.id, model));
|
|
155
|
-
log(
|
|
163
|
+
log("");
|
|
156
164
|
const fallback = answerFallbacks.get(model);
|
|
157
165
|
const hasBody = body.length > 0;
|
|
158
166
|
if (!hasBody && !fallback) {
|
|
@@ -162,11 +170,11 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
162
170
|
const headingLabel = `[${model}]`;
|
|
163
171
|
const heading = shouldStreamInline ? kleur.bold(headingLabel) : headingLabel;
|
|
164
172
|
log(heading);
|
|
165
|
-
const content = hasBody ? body : fallback ??
|
|
173
|
+
const content = hasBody ? body : (fallback ?? "");
|
|
166
174
|
const printable = shouldRenderMarkdown ? renderMarkdownAnsi(content) : content;
|
|
167
175
|
writeInline(printable);
|
|
168
|
-
if (!printable.endsWith(
|
|
169
|
-
log(
|
|
176
|
+
if (!printable.endsWith("\n")) {
|
|
177
|
+
log("");
|
|
170
178
|
}
|
|
171
179
|
};
|
|
172
180
|
const summary = await runMultiModelApiSession({
|
|
@@ -192,7 +200,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
192
200
|
// If we couldn't stream inline (e.g., non-TTY), print all logs after completion.
|
|
193
201
|
for (const [index, result] of summary.fulfilled.entries()) {
|
|
194
202
|
if (index > 0) {
|
|
195
|
-
log(
|
|
203
|
+
log("");
|
|
196
204
|
}
|
|
197
205
|
await printModelLog(result.model);
|
|
198
206
|
}
|
|
@@ -216,14 +224,18 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
216
224
|
reasoning_tokens: aggregateUsage.reasoningTokens,
|
|
217
225
|
total_tokens: aggregateUsage.totalTokens,
|
|
218
226
|
}, idx))
|
|
219
|
-
.join(
|
|
227
|
+
.join("/");
|
|
220
228
|
const tokensPart = (() => {
|
|
221
|
-
const parts = tokensDisplay.split(
|
|
229
|
+
const parts = tokensDisplay.split("/");
|
|
222
230
|
if (parts.length !== 4)
|
|
223
231
|
return tokensDisplay;
|
|
224
232
|
return `↑${parts[0]} ↓${parts[1]} ↻${parts[2]} Δ${parts[3]}`;
|
|
225
233
|
})();
|
|
226
|
-
const statusColor = summary.rejected.length === 0
|
|
234
|
+
const statusColor = summary.rejected.length === 0
|
|
235
|
+
? kleur.green
|
|
236
|
+
: summary.fulfilled.length > 0
|
|
237
|
+
? kleur.yellow
|
|
238
|
+
: kleur.red;
|
|
227
239
|
const overallText = `${summary.fulfilled.length}/${multiModels.length} models`;
|
|
228
240
|
const { line1 } = formatFinishLine({
|
|
229
241
|
elapsedMs: summary.elapsedMs,
|
|
@@ -234,10 +246,11 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
234
246
|
log(statusColor(line1));
|
|
235
247
|
const hasFailure = summary.rejected.length > 0;
|
|
236
248
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
237
|
-
status: hasFailure ?
|
|
249
|
+
status: hasFailure ? "error" : "completed",
|
|
238
250
|
completedAt: new Date().toISOString(),
|
|
239
251
|
usage: aggregateUsage,
|
|
240
252
|
elapsedMs: summary.elapsedMs,
|
|
253
|
+
errorMessage: undefined,
|
|
241
254
|
response: undefined,
|
|
242
255
|
transport: undefined,
|
|
243
256
|
error: undefined,
|
|
@@ -261,7 +274,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
261
274
|
}
|
|
262
275
|
}
|
|
263
276
|
if (savedOutputs.length > 0) {
|
|
264
|
-
log(dim(
|
|
277
|
+
log(dim("Saved outputs:"));
|
|
265
278
|
for (const item of savedOutputs) {
|
|
266
279
|
log(dim(`- ${item.model} -> ${item.path}`));
|
|
267
280
|
}
|
|
@@ -278,7 +291,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
278
291
|
: runOptions;
|
|
279
292
|
if (modelForStatus && singleModelOverride == null) {
|
|
280
293
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
281
|
-
status:
|
|
294
|
+
status: "running",
|
|
282
295
|
startedAt: new Date().toISOString(),
|
|
283
296
|
});
|
|
284
297
|
}
|
|
@@ -288,21 +301,22 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
288
301
|
write,
|
|
289
302
|
allowStdout: !muteStdout,
|
|
290
303
|
});
|
|
291
|
-
if (result.mode !==
|
|
292
|
-
throw new Error(
|
|
304
|
+
if (result.mode !== "live") {
|
|
305
|
+
throw new Error("Unexpected preview result while running a session.");
|
|
293
306
|
}
|
|
294
307
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
295
|
-
status:
|
|
308
|
+
status: "completed",
|
|
296
309
|
completedAt: new Date().toISOString(),
|
|
297
310
|
usage: result.usage,
|
|
298
311
|
elapsedMs: result.elapsedMs,
|
|
312
|
+
errorMessage: undefined,
|
|
299
313
|
response: extractResponseMetadata(result.response),
|
|
300
314
|
transport: undefined,
|
|
301
315
|
error: undefined,
|
|
302
316
|
});
|
|
303
317
|
if (modelForStatus && singleModelOverride == null) {
|
|
304
318
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
305
|
-
status:
|
|
319
|
+
status: "completed",
|
|
306
320
|
completedAt: new Date().toISOString(),
|
|
307
321
|
usage: result.usage,
|
|
308
322
|
});
|
|
@@ -323,49 +337,65 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
323
337
|
log(`ERROR: ${message}`);
|
|
324
338
|
markErrorLogged(error);
|
|
325
339
|
const userError = asOracleUserError(error);
|
|
326
|
-
const connectionLost = userError?.category ===
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
userError.details?.stage ===
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
340
|
+
const connectionLost = userError?.category === "browser-automation" &&
|
|
341
|
+
userError.details?.stage === "connection-lost";
|
|
342
|
+
const assistantTimeout = userError?.category === "browser-automation" &&
|
|
343
|
+
userError.details?.stage === "assistant-timeout";
|
|
344
|
+
const cloudflareChallenge = userError?.category === "browser-automation" &&
|
|
345
|
+
userError.details?.stage === "cloudflare-challenge";
|
|
346
|
+
if (connectionLost && mode === "browser") {
|
|
347
|
+
const runtime = userError.details
|
|
348
|
+
?.runtime;
|
|
349
|
+
log(dim("Chrome disconnected before completion; keeping session running for reattach."));
|
|
333
350
|
if (modelForStatus) {
|
|
334
351
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
335
|
-
status:
|
|
352
|
+
status: "running",
|
|
336
353
|
completedAt: undefined,
|
|
337
354
|
});
|
|
338
355
|
}
|
|
339
356
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
340
|
-
status:
|
|
357
|
+
status: "running",
|
|
341
358
|
errorMessage: message,
|
|
342
359
|
mode,
|
|
343
360
|
browser: {
|
|
344
361
|
config: browserConfig,
|
|
345
362
|
runtime: runtime ?? sessionMeta.browser?.runtime,
|
|
346
363
|
},
|
|
347
|
-
response: { status:
|
|
364
|
+
response: { status: "running", incompleteReason: "chrome-disconnected" },
|
|
348
365
|
});
|
|
349
366
|
return;
|
|
350
367
|
}
|
|
351
|
-
if (assistantTimeout && mode ===
|
|
352
|
-
const runtime = userError.details
|
|
353
|
-
|
|
368
|
+
if (assistantTimeout && mode === "browser") {
|
|
369
|
+
const runtime = userError.details
|
|
370
|
+
?.runtime;
|
|
371
|
+
log(dim("Assistant response timed out; marking capture incomplete for reattach."));
|
|
354
372
|
if (modelForStatus) {
|
|
355
373
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
356
|
-
status:
|
|
357
|
-
completedAt:
|
|
374
|
+
status: "error",
|
|
375
|
+
completedAt: new Date().toISOString(),
|
|
376
|
+
response: { status: "incomplete", incompleteReason: "incomplete-capture" },
|
|
377
|
+
error: {
|
|
378
|
+
category: userError.category,
|
|
379
|
+
message: userError.message,
|
|
380
|
+
details: userError.details,
|
|
381
|
+
},
|
|
358
382
|
});
|
|
359
383
|
}
|
|
360
384
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
361
|
-
status:
|
|
385
|
+
status: "error",
|
|
386
|
+
completedAt: new Date().toISOString(),
|
|
362
387
|
errorMessage: message,
|
|
363
388
|
mode,
|
|
364
389
|
browser: {
|
|
365
390
|
config: browserConfig,
|
|
366
391
|
runtime: runtime ?? sessionMeta.browser?.runtime,
|
|
367
392
|
},
|
|
368
|
-
response: { status:
|
|
393
|
+
response: { status: "incomplete", incompleteReason: "incomplete-capture" },
|
|
394
|
+
error: {
|
|
395
|
+
category: userError.category,
|
|
396
|
+
message: userError.message,
|
|
397
|
+
details: userError.details,
|
|
398
|
+
},
|
|
369
399
|
});
|
|
370
400
|
const autoReattachIntervalMs = browserConfig?.autoReattachIntervalMs ?? 0;
|
|
371
401
|
if (autoReattachIntervalMs > 0) {
|
|
@@ -386,9 +416,9 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
386
416
|
log(dim(`Reattach later with: oracle session ${sessionMeta.id}`));
|
|
387
417
|
return;
|
|
388
418
|
}
|
|
389
|
-
if (cloudflareChallenge && mode ===
|
|
419
|
+
if (cloudflareChallenge && mode === "browser") {
|
|
390
420
|
const details = userError.details;
|
|
391
|
-
log(dim(
|
|
421
|
+
log(dim("Cloudflare challenge detected; browser left running so you can complete the check."));
|
|
392
422
|
if (details?.reuseProfileHint) {
|
|
393
423
|
log(dim(`Reuse this browser profile with: ${details.reuseProfileHint}`));
|
|
394
424
|
}
|
|
@@ -406,9 +436,11 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
406
436
|
if (transportLine) {
|
|
407
437
|
log(dim(`Transport: ${transportLine}`));
|
|
408
438
|
}
|
|
409
|
-
const browserRuntime = mode ===
|
|
439
|
+
const browserRuntime = mode === "browser"
|
|
440
|
+
? userError?.details?.runtime
|
|
441
|
+
: undefined;
|
|
410
442
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
411
|
-
status:
|
|
443
|
+
status: "error",
|
|
412
444
|
completedAt: new Date().toISOString(),
|
|
413
445
|
errorMessage: message,
|
|
414
446
|
mode,
|
|
@@ -430,13 +462,24 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
430
462
|
});
|
|
431
463
|
if (modelForStatus) {
|
|
432
464
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
433
|
-
status:
|
|
465
|
+
status: "error",
|
|
434
466
|
completedAt: new Date().toISOString(),
|
|
435
467
|
});
|
|
436
468
|
}
|
|
437
469
|
throw error;
|
|
438
470
|
}
|
|
439
471
|
}
|
|
472
|
+
function mergeArtifacts(existing, additions) {
|
|
473
|
+
const merged = new Map();
|
|
474
|
+
for (const artifact of existing ?? []) {
|
|
475
|
+
merged.set(`${artifact.kind}:${artifact.path}`, artifact);
|
|
476
|
+
}
|
|
477
|
+
for (const artifact of additions ?? []) {
|
|
478
|
+
merged.set(`${artifact.kind}:${artifact.path}`, artifact);
|
|
479
|
+
}
|
|
480
|
+
const values = Array.from(merged.values());
|
|
481
|
+
return values.length > 0 ? values : undefined;
|
|
482
|
+
}
|
|
440
483
|
function formatError(error) {
|
|
441
484
|
return error instanceof Error ? error.message : String(error);
|
|
442
485
|
}
|
|
@@ -444,7 +487,7 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
444
487
|
if (!targetPath)
|
|
445
488
|
return;
|
|
446
489
|
if (!content || content.trim().length === 0) {
|
|
447
|
-
log(dim(
|
|
490
|
+
log(dim("write-output skipped: no assistant content to save."));
|
|
448
491
|
return;
|
|
449
492
|
}
|
|
450
493
|
const normalizedTarget = path.resolve(targetPath);
|
|
@@ -456,8 +499,8 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
456
499
|
}
|
|
457
500
|
try {
|
|
458
501
|
await fs.mkdir(path.dirname(normalizedTarget), { recursive: true });
|
|
459
|
-
const payload = content.endsWith(
|
|
460
|
-
await fs.writeFile(normalizedTarget, payload,
|
|
502
|
+
const payload = content.endsWith("\n") ? content : `${content}\n`;
|
|
503
|
+
await fs.writeFile(normalizedTarget, payload, "utf8");
|
|
461
504
|
log(dim(`Saved assistant output to ${normalizedTarget}`));
|
|
462
505
|
return normalizedTarget;
|
|
463
506
|
}
|
|
@@ -468,8 +511,8 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
468
511
|
if (fallbackPath) {
|
|
469
512
|
try {
|
|
470
513
|
await fs.mkdir(path.dirname(fallbackPath), { recursive: true });
|
|
471
|
-
const payload = content.endsWith(
|
|
472
|
-
await fs.writeFile(fallbackPath, payload,
|
|
514
|
+
const payload = content.endsWith("\n") ? content : `${content}\n`;
|
|
515
|
+
await fs.writeFile(fallbackPath, payload, "utf8");
|
|
473
516
|
log(dim(`write-output fallback to ${fallbackPath} (original failed: ${reason})`));
|
|
474
517
|
return fallbackPath;
|
|
475
518
|
}
|
|
@@ -485,7 +528,7 @@ async function writeAssistantOutput(targetPath, content, log) {
|
|
|
485
528
|
}
|
|
486
529
|
async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig, runOptions, modelForStatus, notificationSettings, log, }) {
|
|
487
530
|
if (!runtime || !browserConfig) {
|
|
488
|
-
log(dim(
|
|
531
|
+
log(dim("Auto-reattach disabled: missing runtime or browser config."));
|
|
489
532
|
return false;
|
|
490
533
|
}
|
|
491
534
|
const delayMs = Math.max(0, browserConfig.autoReattachDelayMs ?? 0);
|
|
@@ -526,16 +569,25 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
526
569
|
const result = await resumeBrowserSession(runtime, reattachConfig, logger, {
|
|
527
570
|
promptPreview: sessionMeta.promptPreview,
|
|
528
571
|
});
|
|
529
|
-
const answerText = result.answerMarkdown || result.answerText ||
|
|
572
|
+
const answerText = result.answerMarkdown || result.answerText || "";
|
|
530
573
|
const outputTokens = estimateTokenCount(answerText);
|
|
574
|
+
const artifacts = await ensureSessionArtifacts({
|
|
575
|
+
sessionId: sessionMeta.id,
|
|
576
|
+
prompt: runOptions.prompt,
|
|
577
|
+
answerMarkdown: answerText,
|
|
578
|
+
conversationUrl: runtime.tabUrl,
|
|
579
|
+
browserConfig,
|
|
580
|
+
existingArtifacts: sessionMeta.artifacts,
|
|
581
|
+
logger,
|
|
582
|
+
});
|
|
531
583
|
const logWriter = sessionStore.createLogWriter(sessionMeta.id);
|
|
532
584
|
logWriter.logLine(`[auto-reattach] captured assistant response on attempt ${attempt}`);
|
|
533
|
-
logWriter.logLine(
|
|
585
|
+
logWriter.logLine("Answer:");
|
|
534
586
|
logWriter.logLine(answerText);
|
|
535
587
|
logWriter.stream.end();
|
|
536
588
|
if (modelForStatus) {
|
|
537
589
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
538
|
-
status:
|
|
590
|
+
status: "completed",
|
|
539
591
|
completedAt: new Date().toISOString(),
|
|
540
592
|
usage: {
|
|
541
593
|
inputTokens: 0,
|
|
@@ -546,7 +598,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
546
598
|
});
|
|
547
599
|
}
|
|
548
600
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
549
|
-
status:
|
|
601
|
+
status: "completed",
|
|
550
602
|
completedAt: new Date().toISOString(),
|
|
551
603
|
usage: {
|
|
552
604
|
inputTokens: 0,
|
|
@@ -554,11 +606,13 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
554
606
|
reasoningTokens: 0,
|
|
555
607
|
totalTokens: outputTokens,
|
|
556
608
|
},
|
|
609
|
+
errorMessage: undefined,
|
|
557
610
|
browser: {
|
|
558
611
|
config: browserConfig,
|
|
559
612
|
runtime,
|
|
560
613
|
},
|
|
561
|
-
|
|
614
|
+
artifacts: mergeArtifacts(sessionMeta.artifacts, artifacts),
|
|
615
|
+
response: { status: "completed" },
|
|
562
616
|
error: undefined,
|
|
563
617
|
transport: undefined,
|
|
564
618
|
});
|
|
@@ -566,7 +620,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
566
620
|
await sendSessionNotification({
|
|
567
621
|
sessionId: sessionMeta.id,
|
|
568
622
|
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
569
|
-
mode: sessionMeta.mode ??
|
|
623
|
+
mode: sessionMeta.mode ?? "browser",
|
|
570
624
|
model: sessionMeta.model ?? runOptions.model,
|
|
571
625
|
usage: {
|
|
572
626
|
inputTokens: 0,
|
|
@@ -574,7 +628,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
574
628
|
},
|
|
575
629
|
characters: answerText.length,
|
|
576
630
|
}, notificationSettings, log, answerText.slice(0, 140));
|
|
577
|
-
log(kleur.green(
|
|
631
|
+
log(kleur.green("Auto-reattach succeeded; session marked completed."));
|
|
578
632
|
return true;
|
|
579
633
|
}
|
|
580
634
|
catch (error) {
|
|
@@ -602,7 +656,7 @@ function isPermissionError(error) {
|
|
|
602
656
|
if (!(error instanceof Error))
|
|
603
657
|
return false;
|
|
604
658
|
const code = error.code;
|
|
605
|
-
return code ===
|
|
659
|
+
return code === "EACCES" || code === "EPERM";
|
|
606
660
|
}
|
|
607
661
|
function buildFallbackPath(original) {
|
|
608
662
|
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,23 +11,23 @@ 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
32
|
const slugValue = options?.displaySlug ?? meta.id;
|
|
33
33
|
const slug = rich ? chalk.cyan(slugValue) : slugValue;
|
|
@@ -35,7 +35,7 @@ export function formatSessionTableRow(meta, options) {
|
|
|
35
35
|
}
|
|
36
36
|
export function resolveSessionCost(meta) {
|
|
37
37
|
const mode = meta.mode ?? meta.options?.mode;
|
|
38
|
-
if (mode ===
|
|
38
|
+
if (mode === "browser") {
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
if (meta.usage?.cost != null) {
|
|
@@ -52,25 +52,28 @@ export function resolveSessionCost(meta) {
|
|
|
52
52
|
const output = meta.usage.outputTokens ?? 0;
|
|
53
53
|
const cost = estimateUsdCost({
|
|
54
54
|
usage: { inputTokens: input, outputTokens: output },
|
|
55
|
-
pricing: {
|
|
55
|
+
pricing: {
|
|
56
|
+
inputUsdPerToken: pricing.inputPerToken,
|
|
57
|
+
outputUsdPerToken: pricing.outputPerToken,
|
|
58
|
+
},
|
|
56
59
|
})?.totalUsd ?? 0;
|
|
57
60
|
return cost > 0 ? cost : null;
|
|
58
61
|
}
|
|
59
62
|
export function formatTimestampAligned(iso) {
|
|
60
63
|
const date = new Date(iso);
|
|
61
|
-
const locale =
|
|
64
|
+
const locale = "en-US";
|
|
62
65
|
const opts = {
|
|
63
|
-
year:
|
|
64
|
-
month:
|
|
65
|
-
day:
|
|
66
|
-
hour:
|
|
67
|
-
minute:
|
|
66
|
+
year: "numeric",
|
|
67
|
+
month: "2-digit",
|
|
68
|
+
day: "2-digit",
|
|
69
|
+
hour: "numeric",
|
|
70
|
+
minute: "2-digit",
|
|
68
71
|
second: undefined,
|
|
69
72
|
hour12: true,
|
|
70
73
|
};
|
|
71
74
|
let formatted = date.toLocaleString(locale, opts);
|
|
72
|
-
formatted = formatted.replace(
|
|
73
|
-
return formatted.replace(/(\s)(\d:)/,
|
|
75
|
+
formatted = formatted.replace(", ", " ");
|
|
76
|
+
return formatted.replace(/(\s)(\d:)/, "$1 $2");
|
|
74
77
|
}
|
|
75
78
|
function formatCostTable(cost) {
|
|
76
79
|
return `$${cost.toFixed(3)}`.padStart(COST_PAD);
|
|
@@ -81,11 +84,11 @@ function colorStatus(status, rich) {
|
|
|
81
84
|
return padded;
|
|
82
85
|
}
|
|
83
86
|
switch (status) {
|
|
84
|
-
case
|
|
87
|
+
case "completed":
|
|
85
88
|
return chalk.green(padded);
|
|
86
|
-
case
|
|
89
|
+
case "error":
|
|
87
90
|
return chalk.red(padded);
|
|
88
|
-
case
|
|
91
|
+
case "running":
|
|
89
92
|
return chalk.yellow(padded);
|
|
90
93
|
default:
|
|
91
94
|
return padded;
|