@steipete/oracle 1.0.6 → 1.0.8
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/README.md
CHANGED
|
@@ -200,6 +200,19 @@ function isWebSocketClosureError(error) {
|
|
|
200
200
|
message.includes('websocket error') ||
|
|
201
201
|
message.includes('target closed'));
|
|
202
202
|
}
|
|
203
|
+
export function formatThinkingLog(startedAt, now, message, locatorSuffix) {
|
|
204
|
+
const elapsedMs = now - startedAt;
|
|
205
|
+
const elapsedText = formatElapsed(elapsedMs);
|
|
206
|
+
const progress = Math.min(1, elapsedMs / 600_000); // soft target: 10 minutes
|
|
207
|
+
const barSegments = 10;
|
|
208
|
+
const filled = Math.round(progress * barSegments);
|
|
209
|
+
const bar = `${'█'.repeat(filled).padEnd(barSegments, '░')}`;
|
|
210
|
+
const pct = Math.round(progress * 100)
|
|
211
|
+
.toString()
|
|
212
|
+
.padStart(3, ' ');
|
|
213
|
+
const statusLabel = message ? ` — ${message}` : '';
|
|
214
|
+
return `[${elapsedText} / ~10m] ${bar} ${pct}%${statusLabel}${locatorSuffix}`;
|
|
215
|
+
}
|
|
203
216
|
function startThinkingStatusMonitor(Runtime, logger, includeDiagnostics = false) {
|
|
204
217
|
let stopped = false;
|
|
205
218
|
let pending = false;
|
|
@@ -215,7 +228,6 @@ function startThinkingStatusMonitor(Runtime, logger, includeDiagnostics = false)
|
|
|
215
228
|
const nextMessage = await readThinkingStatus(Runtime);
|
|
216
229
|
if (nextMessage && nextMessage !== lastMessage) {
|
|
217
230
|
lastMessage = nextMessage;
|
|
218
|
-
const elapsedText = formatElapsed(Date.now() - startedAt);
|
|
219
231
|
let locatorSuffix = '';
|
|
220
232
|
if (includeDiagnostics) {
|
|
221
233
|
try {
|
|
@@ -226,7 +238,7 @@ function startThinkingStatusMonitor(Runtime, logger, includeDiagnostics = false)
|
|
|
226
238
|
locatorSuffix = ' | assistant-turn=error';
|
|
227
239
|
}
|
|
228
240
|
}
|
|
229
|
-
logger(
|
|
241
|
+
logger(formatThinkingLog(startedAt, Date.now(), nextMessage, locatorSuffix));
|
|
230
242
|
}
|
|
231
243
|
}
|
|
232
244
|
catch {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
import { readFiles, createFileSections, DEFAULT_SYSTEM_PROMPT, MODEL_CONFIGS, TOKENIZER_OPTIONS } from '../oracle.js';
|
|
2
5
|
export async function assembleBrowserPrompt(runOptions, deps = {}) {
|
|
3
6
|
const cwd = deps.cwd ?? process.cwd();
|
|
@@ -39,6 +42,25 @@ export async function assembleBrowserPrompt(runOptions, deps = {}) {
|
|
|
39
42
|
displayPath: section.displayPath,
|
|
40
43
|
sizeBytes: Buffer.byteLength(section.content, 'utf8'),
|
|
41
44
|
}));
|
|
45
|
+
const MAX_BROWSER_ATTACHMENTS = 10;
|
|
46
|
+
if (!inlineFiles && attachments.length > MAX_BROWSER_ATTACHMENTS) {
|
|
47
|
+
const bundleDir = await fs.mkdtemp(path.join(os.tmpdir(), 'oracle-browser-bundle-'));
|
|
48
|
+
const bundlePath = path.join(bundleDir, 'attachments-bundle.txt');
|
|
49
|
+
const bundleLines = [];
|
|
50
|
+
sections.forEach((section) => {
|
|
51
|
+
bundleLines.push(`### File: ${section.displayPath}`);
|
|
52
|
+
bundleLines.push(section.content.trimEnd());
|
|
53
|
+
bundleLines.push('');
|
|
54
|
+
});
|
|
55
|
+
const bundleText = `${bundleLines.join('\n').trimEnd()}\n`;
|
|
56
|
+
await fs.writeFile(bundlePath, bundleText, 'utf8');
|
|
57
|
+
attachments.length = 0;
|
|
58
|
+
attachments.push({
|
|
59
|
+
path: bundlePath,
|
|
60
|
+
displayPath: 'attachments-bundle.txt',
|
|
61
|
+
sizeBytes: Buffer.byteLength(bundleText, 'utf8'),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
42
64
|
const inlineFileCount = inlineFiles ? sections.length : 0;
|
|
43
65
|
const tokenizer = MODEL_CONFIGS[runOptions.model].tokenizer;
|
|
44
66
|
const tokenizerUserContent = inlineFileCount > 0 && inlineBlock
|
|
@@ -52,5 +74,15 @@ export async function assembleBrowserPrompt(runOptions, deps = {}) {
|
|
|
52
74
|
? tokenizerMessages
|
|
53
75
|
: [{ role: 'user', content: '' }], TOKENIZER_OPTIONS);
|
|
54
76
|
const tokenEstimateIncludesInlineFiles = inlineFileCount > 0 && Boolean(inlineBlock);
|
|
55
|
-
return {
|
|
77
|
+
return {
|
|
78
|
+
markdown,
|
|
79
|
+
composerText,
|
|
80
|
+
estimatedInputTokens,
|
|
81
|
+
attachments,
|
|
82
|
+
inlineFileCount,
|
|
83
|
+
tokenEstimateIncludesInlineFiles,
|
|
84
|
+
bundled: !inlineFiles && attachments.length === 1 && sections.length > MAX_BROWSER_ATTACHMENTS && attachments[0]?.displayPath
|
|
85
|
+
? { originalCount: sections.length, bundlePath: attachments[0].displayPath }
|
|
86
|
+
: null,
|
|
87
|
+
};
|
|
56
88
|
}
|
|
@@ -15,6 +15,9 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
|
|
|
15
15
|
if (promptArtifacts.attachments.length > 0) {
|
|
16
16
|
const attachmentList = promptArtifacts.attachments.map((attachment) => attachment.displayPath).join(', ');
|
|
17
17
|
log(chalk.dim(`[verbose] Browser attachments: ${attachmentList}`));
|
|
18
|
+
if (promptArtifacts.bundled) {
|
|
19
|
+
log(chalk.yellow(`[browser] More than 10 files provided; bundled ${promptArtifacts.bundled.originalCount} files into ${promptArtifacts.bundled.bundlePath} to satisfy ChatGPT upload limits.`));
|
|
20
|
+
}
|
|
18
21
|
}
|
|
19
22
|
else if (runOptions.file && runOptions.file.length > 0 && runOptions.browserInlineFiles) {
|
|
20
23
|
log(chalk.dim('[verbose] Browser inline file fallback enabled (pasting file contents).'));
|
package/dist/src/cli/help.js
CHANGED
|
@@ -43,11 +43,13 @@ function renderHelpBanner(version, colors) {
|
|
|
43
43
|
}
|
|
44
44
|
function renderHelpFooter(program, colors) {
|
|
45
45
|
const tips = [
|
|
46
|
-
`${colors.bullet('•')} Attach source
|
|
47
|
-
`${colors.bullet('•')}
|
|
46
|
+
`${colors.bullet('•')} Attach lots of source (whole directories beat single files) and keep total input under ~196k tokens.`,
|
|
47
|
+
`${colors.bullet('•')} Oracle starts empty—open with a short project briefing (stack, services, build steps), spell out the question and prior attempts, and why it matters; the more explanation and context you provide, the better the response will be.`,
|
|
48
|
+
`${colors.bullet('•')} Oracle is one-shot: it does not remember prior runs, so start fresh each time with full context.`,
|
|
48
49
|
`${colors.bullet('•')} Run ${colors.accent('--files-report')} to inspect token spend before hitting the API.`,
|
|
49
50
|
`${colors.bullet('•')} Non-preview runs spawn detached sessions so they keep streaming even if your terminal closes — reattach anytime via ${colors.accent('pnpm oracle session <slug>')}.`,
|
|
50
51
|
`${colors.bullet('•')} Set a memorable 3–5 word slug via ${colors.accent('--slug "<words>"')} to keep session IDs tidy.`,
|
|
52
|
+
`${colors.bullet('•')} Finished sessions auto-hide preamble logs when reattached; raw timestamps remain in the saved log file.`,
|
|
51
53
|
`${colors.bullet('•')} Need hidden flags? Run ${colors.accent(`${program.name()} --help --verbose`)} to list search/token/browser overrides.`,
|
|
52
54
|
].join('\n');
|
|
53
55
|
const formatExample = (command, description) => `${colors.command(` ${command}`)}\n${colors.muted(` ${description}`)}`;
|
|
@@ -71,6 +71,13 @@ export async function attachSession(sessionId, options) {
|
|
|
71
71
|
console.log(dim(`User error: ${userErrorSummary}`));
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
const shouldTrimIntro = initialStatus === 'completed' || initialStatus === 'error';
|
|
75
|
+
if (shouldTrimIntro) {
|
|
76
|
+
const fullLog = await readSessionLog(sessionId);
|
|
77
|
+
const trimmed = trimBeforeFirstAnswer(fullLog);
|
|
78
|
+
process.stdout.write(trimmed);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
74
81
|
let lastLength = 0;
|
|
75
82
|
const printNew = async () => {
|
|
76
83
|
const text = await readSessionLog(sessionId);
|
|
@@ -170,6 +177,14 @@ export function buildReattachLine(metadata) {
|
|
|
170
177
|
}
|
|
171
178
|
return null;
|
|
172
179
|
}
|
|
180
|
+
export function trimBeforeFirstAnswer(logText) {
|
|
181
|
+
const marker = 'Answer:';
|
|
182
|
+
const index = logText.indexOf(marker);
|
|
183
|
+
if (index === -1) {
|
|
184
|
+
return logText;
|
|
185
|
+
}
|
|
186
|
+
return logText.slice(index);
|
|
187
|
+
}
|
|
173
188
|
function formatRelativeDuration(referenceIso) {
|
|
174
189
|
const timestamp = Date.parse(referenceIso);
|
|
175
190
|
if (Number.isNaN(timestamp)) {
|
package/package.json
CHANGED