@steipete/oracle 0.7.1 → 0.7.3
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/dist/src/browser/actions/assistantResponse.js +53 -33
- package/dist/src/browser/actions/attachments.js +276 -133
- package/dist/src/browser/actions/modelSelection.js +33 -2
- package/dist/src/browser/actions/promptComposer.js +38 -45
- package/dist/src/browser/chromeLifecycle.js +2 -0
- package/dist/src/browser/config.js +7 -2
- package/dist/src/browser/index.js +12 -2
- package/dist/src/browser/pageActions.js +1 -1
- package/dist/src/browser/reattach.js +192 -17
- package/dist/src/browser/utils.js +10 -0
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/browserConfig.js +11 -6
- package/dist/src/cli/notifier.js +8 -2
- package/dist/src/cli/oscUtils.js +1 -19
- package/dist/src/cli/sessionDisplay.js +6 -3
- package/dist/src/cli/sessionTable.js +5 -1
- package/dist/src/oracle/files.js +8 -1
- package/dist/src/oracle/modelResolver.js +11 -4
- package/dist/src/oracle/multiModelRunner.js +3 -14
- package/dist/src/oracle/oscProgress.js +12 -61
- package/dist/src/oracle/run.js +62 -34
- package/dist/src/sessionManager.js +91 -2
- 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/build-notifier.sh +0 -0
- package/package.json +43 -26
- 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 +24 -0
- package/vendor/oracle-notifier/build-notifier.sh +0 -0
|
@@ -2,24 +2,13 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { runOracle, OracleResponseError, OracleTransportError, extractResponseMetadata, asOracleUserError, extractTextOutput, } from '../oracle.js';
|
|
4
4
|
import { sessionStore } from '../sessionStore.js';
|
|
5
|
-
|
|
6
|
-
const OSC_PROGRESS_END = '\u001b\\';
|
|
5
|
+
import { findOscProgressSequences, OSC_PROGRESS_PREFIX } from 'osc-progress';
|
|
7
6
|
function forwardOscProgress(chunk, shouldForward) {
|
|
8
7
|
if (!shouldForward || !chunk.includes(OSC_PROGRESS_PREFIX)) {
|
|
9
8
|
return;
|
|
10
9
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const start = chunk.indexOf(OSC_PROGRESS_PREFIX, searchFrom);
|
|
14
|
-
if (start === -1) {
|
|
15
|
-
break;
|
|
16
|
-
}
|
|
17
|
-
const end = chunk.indexOf(OSC_PROGRESS_END, start + OSC_PROGRESS_PREFIX.length);
|
|
18
|
-
if (end === -1) {
|
|
19
|
-
break;
|
|
20
|
-
}
|
|
21
|
-
process.stdout.write(chunk.slice(start, end + OSC_PROGRESS_END.length));
|
|
22
|
-
searchFrom = end + OSC_PROGRESS_END.length;
|
|
10
|
+
for (const seq of findOscProgressSequences(chunk)) {
|
|
11
|
+
process.stdout.write(seq.raw);
|
|
23
12
|
}
|
|
24
13
|
}
|
|
25
14
|
const defaultDeps = {
|
|
@@ -1,66 +1,17 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
|
-
|
|
3
|
-
const ST = '\u001b\\';
|
|
4
|
-
function sanitizeLabel(label) {
|
|
5
|
-
const withoutEscape = label.split('\u001b').join('');
|
|
6
|
-
const withoutBellAndSt = withoutEscape.replaceAll('\u0007', '').replaceAll('\u009c', '');
|
|
7
|
-
return withoutBellAndSt.replaceAll(']', '').trim();
|
|
8
|
-
}
|
|
2
|
+
import { startOscProgress as startOscProgressShared, supportsOscProgress as supportsOscProgressShared, } from 'osc-progress';
|
|
9
3
|
export function supportsOscProgress(env = process.env, isTty = process.stdout.isTTY) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
if (env.ORACLE_FORCE_OSC_PROGRESS === '1') {
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
const termProgram = (env.TERM_PROGRAM ?? '').toLowerCase();
|
|
20
|
-
if (termProgram.includes('ghostty')) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
if (termProgram.includes('wezterm')) {
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
if (env.WT_SESSION) {
|
|
27
|
-
return true; // Windows Terminal exposes this
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
4
|
+
return supportsOscProgressShared(env, isTty, {
|
|
5
|
+
disableEnvVar: 'ORACLE_NO_OSC_PROGRESS',
|
|
6
|
+
forceEnvVar: 'ORACLE_FORCE_OSC_PROGRESS',
|
|
7
|
+
});
|
|
30
8
|
}
|
|
31
9
|
export function startOscProgress(options = {}) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return () => {
|
|
40
|
-
write(`${OSC}0;0;${cleanLabel}${ST}`);
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
const target = Math.max(targetMs, 1_000);
|
|
44
|
-
const send = (state, percent) => {
|
|
45
|
-
const clamped = Math.max(0, Math.min(100, Math.round(percent)));
|
|
46
|
-
write(`${OSC}${state};${clamped};${cleanLabel}${ST}`);
|
|
47
|
-
};
|
|
48
|
-
const startedAt = Date.now();
|
|
49
|
-
send(1, 0); // activate progress bar
|
|
50
|
-
const timer = setInterval(() => {
|
|
51
|
-
const elapsed = Date.now() - startedAt;
|
|
52
|
-
const percent = Math.min(99, (elapsed / target) * 100);
|
|
53
|
-
send(1, percent);
|
|
54
|
-
}, 900);
|
|
55
|
-
timer.unref?.();
|
|
56
|
-
let stopped = false;
|
|
57
|
-
return () => {
|
|
58
|
-
// multiple callers may try to stop
|
|
59
|
-
if (stopped) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
stopped = true;
|
|
63
|
-
clearInterval(timer);
|
|
64
|
-
send(0, 0); // clear the progress bar
|
|
65
|
-
};
|
|
10
|
+
return startOscProgressShared({
|
|
11
|
+
...options,
|
|
12
|
+
// Preserve Oracle's previous default: progress emits to stdout.
|
|
13
|
+
write: options.write ?? ((text) => process.stdout.write(text)),
|
|
14
|
+
disableEnvVar: 'ORACLE_NO_OSC_PROGRESS',
|
|
15
|
+
forceEnvVar: 'ORACLE_FORCE_OSC_PROGRESS',
|
|
16
|
+
});
|
|
66
17
|
}
|
package/dist/src/oracle/run.js
CHANGED
|
@@ -19,8 +19,10 @@ import { createFsAdapter } from './fsAdapter.js';
|
|
|
19
19
|
import { resolveGeminiModelId } from './gemini.js';
|
|
20
20
|
import { resolveClaudeModelId } from './claude.js';
|
|
21
21
|
import { renderMarkdownAnsi } from '../cli/markdownRenderer.js';
|
|
22
|
+
import { createLiveRenderer } from 'markdansi';
|
|
22
23
|
import { executeBackgroundResponse } from './background.js';
|
|
23
24
|
import { formatTokenEstimate, formatTokenValue, resolvePreviewMode } from './runUtils.js';
|
|
25
|
+
import { estimateUsdCost } from 'tokentally';
|
|
24
26
|
import { defaultOpenRouterBaseUrl, isKnownModel, isOpenRouterBaseUrl, isProModel, resolveModelConfig, normalizeOpenRouterBaseUrl, } from './modelResolver.js';
|
|
25
27
|
const isStdoutTty = process.stdout.isTTY && chalk.level > 0;
|
|
26
28
|
const dim = (text) => (isStdoutTty ? kleur.dim(text) : text);
|
|
@@ -309,8 +311,8 @@ export async function runOracle(options, deps = {}) {
|
|
|
309
311
|
let response = null;
|
|
310
312
|
let elapsedMs = 0;
|
|
311
313
|
let sawTextDelta = false;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
+
let streamedText = '';
|
|
315
|
+
let lastLiveFrameAtMs = 0;
|
|
314
316
|
let answerHeaderPrinted = false;
|
|
315
317
|
const allowAnswerHeader = options.suppressAnswerHeader !== true;
|
|
316
318
|
const timeoutExceeded = () => now() - runStart >= timeoutMs;
|
|
@@ -378,31 +380,52 @@ export async function runOracle(options, deps = {}) {
|
|
|
378
380
|
},
|
|
379
381
|
});
|
|
380
382
|
}
|
|
383
|
+
let liveRenderer = null;
|
|
381
384
|
try {
|
|
385
|
+
liveRenderer =
|
|
386
|
+
isTty && !renderPlain
|
|
387
|
+
? createLiveRenderer({
|
|
388
|
+
write: stdoutWrite,
|
|
389
|
+
width: process.stdout.columns ?? 80,
|
|
390
|
+
renderFrame: renderMarkdownAnsi,
|
|
391
|
+
})
|
|
392
|
+
: null;
|
|
382
393
|
for await (const event of stream) {
|
|
383
394
|
throwIfTimedOut();
|
|
384
395
|
const isTextDelta = event.type === 'chunk' || event.type === 'response.output_text.delta';
|
|
385
|
-
if (isTextDelta)
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
396
|
+
if (!isTextDelta)
|
|
397
|
+
continue;
|
|
398
|
+
stopOscProgress();
|
|
399
|
+
stopHeartbeatNow();
|
|
400
|
+
sawTextDelta = true;
|
|
401
|
+
ensureAnswerHeader();
|
|
402
|
+
if (options.silent || typeof event.delta !== 'string')
|
|
403
|
+
continue;
|
|
404
|
+
// Always keep the log/bookkeeping sink up to date.
|
|
405
|
+
sinkWrite(event.delta);
|
|
406
|
+
if (renderPlain) {
|
|
407
|
+
// Plain mode: stream directly to stdout regardless of write sink.
|
|
408
|
+
stdoutWrite(event.delta);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (liveRenderer) {
|
|
412
|
+
streamedText += event.delta;
|
|
413
|
+
const currentMs = now();
|
|
414
|
+
const due = currentMs - lastLiveFrameAtMs >= 120;
|
|
415
|
+
const hasNewline = event.delta.includes('\n');
|
|
416
|
+
if (hasNewline || due) {
|
|
417
|
+
liveRenderer.render(streamedText);
|
|
418
|
+
lastLiveFrameAtMs = currentMs;
|
|
405
419
|
}
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
// Non-TTY streams should still surface output; fall back to raw stdout.
|
|
423
|
+
stdoutWrite(event.delta);
|
|
424
|
+
}
|
|
425
|
+
if (liveRenderer) {
|
|
426
|
+
streamedText = streamedText.trim();
|
|
427
|
+
if (streamedText.length > 0) {
|
|
428
|
+
liveRenderer.render(streamedText);
|
|
406
429
|
}
|
|
407
430
|
}
|
|
408
431
|
throwIfTimedOut();
|
|
@@ -414,6 +437,9 @@ export async function runOracle(options, deps = {}) {
|
|
|
414
437
|
log(chalk.yellow(describeTransportError(transportError, timeoutMs)));
|
|
415
438
|
throw transportError;
|
|
416
439
|
}
|
|
440
|
+
finally {
|
|
441
|
+
liveRenderer?.finish();
|
|
442
|
+
}
|
|
417
443
|
response = await stream.finalResponse();
|
|
418
444
|
throwIfTimedOut();
|
|
419
445
|
stopHeartbeatNow();
|
|
@@ -428,21 +454,17 @@ export async function runOracle(options, deps = {}) {
|
|
|
428
454
|
}
|
|
429
455
|
// We only add spacing when streamed text was printed.
|
|
430
456
|
if (sawTextDelta && !options.silent) {
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
if (shouldRenderAfterStream) {
|
|
434
|
-
const rendered = renderMarkdownAnsi(fullStreamedText);
|
|
435
|
-
stdoutWrite(rendered);
|
|
436
|
-
if (!rendered.endsWith('\n')) {
|
|
437
|
-
stdoutWrite('\n');
|
|
438
|
-
}
|
|
439
|
-
log('');
|
|
440
|
-
}
|
|
441
|
-
else if (renderPlain) {
|
|
457
|
+
const shouldRenderAfterStream = isTty && !renderPlain && streamedText.length > 0;
|
|
458
|
+
if (renderPlain) {
|
|
442
459
|
// Plain streaming already wrote chunks; ensure clean separation.
|
|
443
460
|
stdoutWrite('\n');
|
|
444
461
|
}
|
|
462
|
+
else if (!shouldRenderAfterStream) {
|
|
463
|
+
// Non-TTY streams should still surface output; ensure separation.
|
|
464
|
+
log('');
|
|
465
|
+
}
|
|
445
466
|
else {
|
|
467
|
+
// Live-rendered mode already drew the final frame; only separate from logs.
|
|
446
468
|
log('');
|
|
447
469
|
}
|
|
448
470
|
}
|
|
@@ -503,7 +525,10 @@ export async function runOracle(options, deps = {}) {
|
|
|
503
525
|
const totalTokens = usage.total_tokens ?? inputTokens + outputTokens + reasoningTokens;
|
|
504
526
|
const pricing = modelConfig.pricing ?? undefined;
|
|
505
527
|
const cost = pricing
|
|
506
|
-
?
|
|
528
|
+
? estimateUsdCost({
|
|
529
|
+
usage: { inputTokens, outputTokens, reasoningTokens, totalTokens },
|
|
530
|
+
pricing: { inputUsdPerToken: pricing.inputPerToken, outputUsdPerToken: pricing.outputPerToken },
|
|
531
|
+
})?.totalUsd
|
|
507
532
|
: undefined;
|
|
508
533
|
const elapsedDisplay = formatElapsed(elapsedMs);
|
|
509
534
|
const statsParts = [];
|
|
@@ -541,6 +566,9 @@ export async function runOracle(options, deps = {}) {
|
|
|
541
566
|
statsParts.push(`files=${files.length}`);
|
|
542
567
|
}
|
|
543
568
|
const sessionPrefix = options.sessionId ? `${options.sessionId} ` : '';
|
|
569
|
+
if (!options.silent) {
|
|
570
|
+
log('');
|
|
571
|
+
}
|
|
544
572
|
log(chalk.blue(`Finished ${sessionPrefix}in ${elapsedDisplay} (${statsParts.join(' | ')})`));
|
|
545
573
|
return {
|
|
546
574
|
mode: 'live',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import { createWriteStream } from 'node:fs';
|
|
4
|
+
import net from 'node:net';
|
|
4
5
|
import { DEFAULT_MODEL } from './oracle.js';
|
|
5
6
|
import { safeModelSlug } from './oracle/modelResolver.js';
|
|
6
7
|
import { getOracleHomeDir } from './oracleHome.js';
|
|
@@ -15,6 +16,7 @@ const MODEL_JSON_EXTENSION = '.json';
|
|
|
15
16
|
const MODEL_LOG_EXTENSION = '.log';
|
|
16
17
|
const MAX_STATUS_LIMIT = 1000;
|
|
17
18
|
const ZOMBIE_MAX_AGE_MS = 60 * 60 * 1000; // 60 minutes
|
|
19
|
+
const CHROME_RUNTIME_TIMEOUT_MS = 250;
|
|
18
20
|
const DEFAULT_SLUG = 'session';
|
|
19
21
|
const MAX_SLUG_WORDS = 5;
|
|
20
22
|
const MIN_CUSTOM_SLUG_WORDS = 3;
|
|
@@ -245,7 +247,8 @@ async function readModernSessionMetadata(sessionId) {
|
|
|
245
247
|
return null;
|
|
246
248
|
}
|
|
247
249
|
const enriched = await attachModelRuns(parsed, sessionId);
|
|
248
|
-
|
|
250
|
+
const runtimeChecked = await markDeadBrowser(enriched, { persist: false });
|
|
251
|
+
return await markZombie(runtimeChecked, { persist: false });
|
|
249
252
|
}
|
|
250
253
|
catch {
|
|
251
254
|
return null;
|
|
@@ -256,7 +259,8 @@ async function readLegacySessionMetadata(sessionId) {
|
|
|
256
259
|
const raw = await fs.readFile(legacySessionPath(sessionId), 'utf8');
|
|
257
260
|
const parsed = JSON.parse(raw);
|
|
258
261
|
const enriched = await attachModelRuns(parsed, sessionId);
|
|
259
|
-
|
|
262
|
+
const runtimeChecked = await markDeadBrowser(enriched, { persist: false });
|
|
263
|
+
return await markZombie(runtimeChecked, { persist: false });
|
|
260
264
|
}
|
|
261
265
|
catch {
|
|
262
266
|
return null;
|
|
@@ -294,6 +298,7 @@ export async function listSessionsMetadata() {
|
|
|
294
298
|
for (const entry of entries) {
|
|
295
299
|
let meta = await readSessionMetadata(entry);
|
|
296
300
|
if (meta) {
|
|
301
|
+
meta = await markDeadBrowser(meta, { persist: true });
|
|
297
302
|
meta = await markZombie(meta, { persist: true }); // keep stored metadata consistent with zombie detection
|
|
298
303
|
metas.push(meta);
|
|
299
304
|
}
|
|
@@ -451,6 +456,44 @@ async function markZombie(meta, { persist }) {
|
|
|
451
456
|
}
|
|
452
457
|
return updated;
|
|
453
458
|
}
|
|
459
|
+
async function markDeadBrowser(meta, { persist }) {
|
|
460
|
+
if (meta.status !== 'running' || meta.mode !== 'browser') {
|
|
461
|
+
return meta;
|
|
462
|
+
}
|
|
463
|
+
const runtime = meta.browser?.runtime;
|
|
464
|
+
if (!runtime) {
|
|
465
|
+
return meta;
|
|
466
|
+
}
|
|
467
|
+
const signals = [];
|
|
468
|
+
if (runtime.chromePid) {
|
|
469
|
+
signals.push(isProcessAlive(runtime.chromePid));
|
|
470
|
+
}
|
|
471
|
+
if (runtime.chromePort) {
|
|
472
|
+
const host = runtime.chromeHost ?? '127.0.0.1';
|
|
473
|
+
signals.push(await isPortOpen(host, runtime.chromePort));
|
|
474
|
+
}
|
|
475
|
+
if (signals.length === 0 || signals.some(Boolean)) {
|
|
476
|
+
return meta;
|
|
477
|
+
}
|
|
478
|
+
const response = meta.response
|
|
479
|
+
? {
|
|
480
|
+
...meta.response,
|
|
481
|
+
status: 'error',
|
|
482
|
+
incompleteReason: meta.response.incompleteReason ?? 'chrome-disconnected',
|
|
483
|
+
}
|
|
484
|
+
: { status: 'error', incompleteReason: 'chrome-disconnected' };
|
|
485
|
+
const updated = {
|
|
486
|
+
...meta,
|
|
487
|
+
status: 'error',
|
|
488
|
+
errorMessage: 'Browser session ended (Chrome is no longer reachable)',
|
|
489
|
+
completedAt: new Date().toISOString(),
|
|
490
|
+
response,
|
|
491
|
+
};
|
|
492
|
+
if (persist) {
|
|
493
|
+
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2), 'utf8');
|
|
494
|
+
}
|
|
495
|
+
return updated;
|
|
496
|
+
}
|
|
454
497
|
function isZombie(meta) {
|
|
455
498
|
if (meta.status !== 'running') {
|
|
456
499
|
return false;
|
|
@@ -465,3 +508,49 @@ function isZombie(meta) {
|
|
|
465
508
|
}
|
|
466
509
|
return Date.now() - startedMs > ZOMBIE_MAX_AGE_MS;
|
|
467
510
|
}
|
|
511
|
+
function isProcessAlive(pid) {
|
|
512
|
+
if (!pid)
|
|
513
|
+
return false;
|
|
514
|
+
try {
|
|
515
|
+
process.kill(pid, 0);
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
const code = error instanceof Error ? error.code : undefined;
|
|
520
|
+
if (code === 'ESRCH' || code === 'EINVAL') {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
if (code === 'EPERM') {
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async function isPortOpen(host, port) {
|
|
530
|
+
if (!port || port <= 0 || port > 65535) {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
return new Promise((resolve) => {
|
|
534
|
+
const socket = net.createConnection({ host, port });
|
|
535
|
+
let settled = false;
|
|
536
|
+
const cleanup = (result) => {
|
|
537
|
+
if (settled)
|
|
538
|
+
return;
|
|
539
|
+
settled = true;
|
|
540
|
+
socket.removeAllListeners();
|
|
541
|
+
socket.end();
|
|
542
|
+
socket.destroy();
|
|
543
|
+
socket.unref();
|
|
544
|
+
resolve(result);
|
|
545
|
+
};
|
|
546
|
+
const timer = setTimeout(() => cleanup(false), CHROME_RUNTIME_TIMEOUT_MS);
|
|
547
|
+
socket.once('connect', () => {
|
|
548
|
+
clearTimeout(timer);
|
|
549
|
+
cleanup(true);
|
|
550
|
+
});
|
|
551
|
+
socket.once('error', () => {
|
|
552
|
+
clearTimeout(timer);
|
|
553
|
+
cleanup(false);
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
}
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steipete/oracle",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "CLI wrapper around OpenAI Responses API with GPT-5.2 Pro (via gpt-5.1-pro alias), GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/bin/oracle-cli.js",
|
|
@@ -8,6 +8,26 @@
|
|
|
8
8
|
"oracle": "dist/bin/oracle-cli.js",
|
|
9
9
|
"oracle-mcp": "dist/bin/oracle-mcp.js"
|
|
10
10
|
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"docs:list": "tsx scripts/docs-list.ts",
|
|
13
|
+
"build": "tsc -p tsconfig.build.json && pnpm run build:vendor",
|
|
14
|
+
"build:vendor": "node -e \"const fs=require('fs'); const path=require('path'); const vendorRoot=path.join('dist','vendor'); fs.rmSync(vendorRoot,{recursive:true,force:true}); const vendors=[['oracle-notifier']]; vendors.forEach(([name])=>{const src=path.join('vendor',name); const dest=path.join(vendorRoot,name); fs.mkdirSync(dest,{recursive:true}); if(fs.existsSync(src)){fs.cpSync(src,dest,{recursive:true,force:true});}});\"",
|
|
15
|
+
"start": "pnpm run build && node ./dist/scripts/run-cli.js",
|
|
16
|
+
"oracle": "pnpm start",
|
|
17
|
+
"check": "pnpm run typecheck",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"lint": "pnpm run typecheck && biome lint .",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:mcp": "pnpm run build && pnpm run test:mcp:unit && pnpm run test:mcp:mcporter",
|
|
22
|
+
"test:mcp:unit": "vitest run tests/mcp*.test.ts tests/mcp/**/*.test.ts",
|
|
23
|
+
"test:mcp:mcporter": "npx -y mcporter list oracle-local --schema --config config/mcporter.json && npx -y mcporter call oracle-local.sessions limit:1 --config config/mcporter.json",
|
|
24
|
+
"test:browser": "pnpm run build && tsx scripts/test-browser.ts && ./scripts/browser-smoke.sh",
|
|
25
|
+
"test:live": "ORACLE_LIVE_TEST=1 vitest run tests/live --exclude tests/live/openai-live.test.ts",
|
|
26
|
+
"test:pro": "ORACLE_LIVE_TEST=1 vitest run tests/live/openai-live.test.ts",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"prepare": "pnpm run build",
|
|
29
|
+
"mcp": "pnpm run build && node ./dist/bin/oracle-mcp.js"
|
|
30
|
+
},
|
|
11
31
|
"files": [
|
|
12
32
|
"dist/**/*",
|
|
13
33
|
"assets-oracle-icon.png",
|
|
@@ -57,25 +77,27 @@
|
|
|
57
77
|
"json5": "^2.2.3",
|
|
58
78
|
"keytar": "^7.9.0",
|
|
59
79
|
"kleur": "^4.1.5",
|
|
60
|
-
"markdansi": "
|
|
61
|
-
"openai": "^6.
|
|
80
|
+
"markdansi": "0.1.7",
|
|
81
|
+
"openai": "^6.15.0",
|
|
82
|
+
"osc-progress": "^0.1.0",
|
|
62
83
|
"shiki": "^3.20.0",
|
|
63
84
|
"sqlite3": "^5.1.7",
|
|
64
85
|
"toasted-notifier": "^10.1.0",
|
|
86
|
+
"tokentally": "github:steipete/tokentally#v0.1.0",
|
|
65
87
|
"zod": "^4.2.1"
|
|
66
88
|
},
|
|
67
89
|
"devDependencies": {
|
|
68
90
|
"@anthropic-ai/tokenizer": "^0.0.4",
|
|
69
|
-
"@biomejs/biome": "^2.3.
|
|
91
|
+
"@biomejs/biome": "^2.3.10",
|
|
70
92
|
"@cdktf/node-pty-prebuilt-multiarch": "0.10.2",
|
|
71
93
|
"@types/chrome-remote-interface": "^0.33.0",
|
|
72
94
|
"@types/inquirer": "^9.0.9",
|
|
73
95
|
"@types/node": "^25.0.3",
|
|
74
96
|
"@vitest/coverage-v8": "4.0.16",
|
|
75
|
-
"devtools-protocol": "0.0.
|
|
97
|
+
"devtools-protocol": "0.0.1561482",
|
|
76
98
|
"es-toolkit": "^1.43.0",
|
|
77
99
|
"esbuild": "^0.27.2",
|
|
78
|
-
"puppeteer-core": "^24.
|
|
100
|
+
"puppeteer-core": "^24.34.0",
|
|
79
101
|
"tsx": "^4.21.0",
|
|
80
102
|
"typescript": "^5.9.3",
|
|
81
103
|
"vitest": "^4.0.16"
|
|
@@ -83,23 +105,18 @@
|
|
|
83
105
|
"optionalDependencies": {
|
|
84
106
|
"win-dpapi": "npm:@primno/dpapi@2.0.1"
|
|
85
107
|
},
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"test:pro": "ORACLE_LIVE_TEST=1 vitest run tests/live/openai-live.test.ts",
|
|
102
|
-
"test:coverage": "vitest run --coverage",
|
|
103
|
-
"mcp": "pnpm run build && node ./dist/bin/oracle-mcp.js"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
108
|
+
"pnpm": {
|
|
109
|
+
"overrides": {
|
|
110
|
+
"devtools-protocol": "0.0.1561482",
|
|
111
|
+
"win-dpapi": "npm:@primno/dpapi@2.0.1"
|
|
112
|
+
},
|
|
113
|
+
"onlyBuiltDependencies": [
|
|
114
|
+
"@cdktf/node-pty-prebuilt-multiarch",
|
|
115
|
+
"esbuild",
|
|
116
|
+
"keytar",
|
|
117
|
+
"sqlite3",
|
|
118
|
+
"win-dpapi"
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
"packageManager": "pnpm@10.23.0+sha512.21c4e5698002ade97e4efe8b8b4a89a8de3c85a37919f957e7a0f30f38fbc5bbdd05980ffe29179b2fb6e6e691242e098d945d1601772cad0fef5fb6411e2a4b"
|
|
122
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Oracle Notifier helper (macOS, arm64)
|
|
2
|
+
|
|
3
|
+
Builds a tiny signed helper app for macOS notifications with the Oracle icon.
|
|
4
|
+
|
|
5
|
+
## Build
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd vendor/oracle-notifier
|
|
9
|
+
# Optional: notarize by setting App Store Connect key credentials
|
|
10
|
+
export APP_STORE_CONNECT_API_KEY_P8="$(cat AuthKey_XXXXXX.p8)" # with literal newlines or \n escaped
|
|
11
|
+
export APP_STORE_CONNECT_KEY_ID=XXXXXX
|
|
12
|
+
export APP_STORE_CONNECT_ISSUER_ID=YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY
|
|
13
|
+
./build-notifier.sh
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Requires Xcode command line tools (swiftc) and a macOS Developer ID certificate. Without a valid cert, the build fails (no ad-hoc fallback).
|
|
17
|
+
- If `APP_STORE_CONNECT_*` vars are set, the script notarizes and staples the ticket.
|
|
18
|
+
- Output: `OracleNotifier.app` (arm64 only), bundled with `OracleIcon.icns`.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
The CLI prefers this helper on macOS; if it fails or is missing, it falls back to toasted-notifier/terminal-notifier.
|
|
22
|
+
|
|
23
|
+
## Permissions
|
|
24
|
+
After first run, allow notifications for “Oracle Notifier” in System Settings → Notifications.
|
|
File without changes
|