@blockrun/franklin 3.8.8 → 3.8.10
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/agent/error-classifier.js +1 -0
- package/dist/agent/llm.d.ts +7 -0
- package/dist/agent/llm.js +48 -7
- package/dist/agent/loop.js +66 -3
- package/dist/agent/permissions.js +2 -2
- package/dist/agent/types.d.ts +7 -0
- package/dist/banner.js +15 -0
- package/dist/commands/start.d.ts +4 -0
- package/dist/commands/start.js +72 -2
- package/dist/index.js +11 -3
- package/dist/panel/html.js +111 -21
- package/dist/panel/server.js +15 -4
- package/dist/tools/activate.d.ts +29 -0
- package/dist/tools/activate.js +96 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/tool-categories.d.ts +22 -0
- package/dist/tools/tool-categories.js +44 -0
- package/dist/tools/trading-execute.d.ts +11 -21
- package/dist/tools/trading-execute.js +43 -130
- package/dist/tools/trading-views.d.ts +64 -0
- package/dist/tools/trading-views.js +115 -0
- package/dist/tools/trading.js +86 -7
- package/dist/tools/webhook.d.ts +18 -0
- package/dist/tools/webhook.js +185 -0
- package/dist/trading/data.d.ts +24 -1
- package/dist/trading/data.js +67 -102
- package/dist/trading/providers/blockrun/client.d.ts +48 -0
- package/dist/trading/providers/blockrun/client.js +253 -0
- package/dist/trading/providers/blockrun/price.d.ts +24 -0
- package/dist/trading/providers/blockrun/price.js +110 -0
- package/dist/trading/providers/coingecko/client.d.ts +20 -0
- package/dist/trading/providers/coingecko/client.js +87 -0
- package/dist/trading/providers/coingecko/markets.d.ts +3 -0
- package/dist/trading/providers/coingecko/markets.js +25 -0
- package/dist/trading/providers/coingecko/ohlcv.d.ts +3 -0
- package/dist/trading/providers/coingecko/ohlcv.js +29 -0
- package/dist/trading/providers/coingecko/price.d.ts +11 -0
- package/dist/trading/providers/coingecko/price.js +41 -0
- package/dist/trading/providers/coingecko/trending.d.ts +3 -0
- package/dist/trading/providers/coingecko/trending.js +22 -0
- package/dist/trading/providers/fetcher.d.ts +43 -0
- package/dist/trading/providers/fetcher.js +45 -0
- package/dist/trading/providers/registry.d.ts +45 -0
- package/dist/trading/providers/registry.js +82 -0
- package/dist/trading/providers/standard-models.d.ts +94 -0
- package/dist/trading/providers/standard-models.js +21 -0
- package/dist/trading/providers/telemetry.d.ts +51 -0
- package/dist/trading/providers/telemetry.js +115 -0
- package/dist/ui/app.js +28 -2
- package/package.json +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-layer telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Records every fetcher call so the Panel Markets page can show live health
|
|
5
|
+
* ("• CoinGecko OK", "• BlockRun OK"), today's call count, today's spend,
|
|
6
|
+
* and a p50 latency estimate. Entirely in-memory — dies with the process
|
|
7
|
+
* and re-hydrates from the on-disk wallet/stats files if we ever care to
|
|
8
|
+
* persist (we don't yet).
|
|
9
|
+
*
|
|
10
|
+
* Tiny by design: zero deps, no background timers, no DB. Callers push a
|
|
11
|
+
* single record per fetch; the Panel pulls a snapshot on demand.
|
|
12
|
+
*/
|
|
13
|
+
const LATENCY_RING = 64;
|
|
14
|
+
const PAID_ROW_RING = 32;
|
|
15
|
+
function newRoll() {
|
|
16
|
+
return {
|
|
17
|
+
calls: 0,
|
|
18
|
+
ok: 0,
|
|
19
|
+
failures: 0,
|
|
20
|
+
lastOkAt: null,
|
|
21
|
+
lastErrorAt: null,
|
|
22
|
+
spendUsdToday: 0,
|
|
23
|
+
spendResetAt: startOfUtcDay(Date.now()),
|
|
24
|
+
latencies: [],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function startOfUtcDay(ts) {
|
|
28
|
+
const d = new Date(ts);
|
|
29
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
30
|
+
}
|
|
31
|
+
const rolls = {
|
|
32
|
+
coingecko: newRoll(),
|
|
33
|
+
blockrun: newRoll(),
|
|
34
|
+
};
|
|
35
|
+
const paidRecent = [];
|
|
36
|
+
export function recordFetch(evt) {
|
|
37
|
+
const roll = rolls[evt.provider];
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
// Daily rollover for spend.
|
|
40
|
+
const dayNow = startOfUtcDay(now);
|
|
41
|
+
if (dayNow !== roll.spendResetAt) {
|
|
42
|
+
roll.spendUsdToday = 0;
|
|
43
|
+
roll.spendResetAt = dayNow;
|
|
44
|
+
}
|
|
45
|
+
roll.calls++;
|
|
46
|
+
if (evt.ok) {
|
|
47
|
+
roll.ok++;
|
|
48
|
+
roll.lastOkAt = now;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
roll.failures++;
|
|
52
|
+
roll.lastErrorAt = now;
|
|
53
|
+
}
|
|
54
|
+
if (evt.latencyMs >= 0) {
|
|
55
|
+
roll.latencies.push(evt.latencyMs);
|
|
56
|
+
if (roll.latencies.length > LATENCY_RING)
|
|
57
|
+
roll.latencies.shift();
|
|
58
|
+
}
|
|
59
|
+
if (evt.costUsd && evt.costUsd > 0) {
|
|
60
|
+
roll.spendUsdToday += evt.costUsd;
|
|
61
|
+
paidRecent.push({ endpoint: evt.endpoint, costUsd: evt.costUsd, ts: now });
|
|
62
|
+
if (paidRecent.length > PAID_ROW_RING)
|
|
63
|
+
paidRecent.shift();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function p50(values) {
|
|
67
|
+
if (values.length === 0)
|
|
68
|
+
return null;
|
|
69
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
70
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
71
|
+
}
|
|
72
|
+
function snapshotProvider(name) {
|
|
73
|
+
const r = rolls[name];
|
|
74
|
+
const p = p50(r.latencies);
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
let status = 'cold';
|
|
77
|
+
if (r.calls > 0) {
|
|
78
|
+
const freshError = r.lastErrorAt && now - r.lastErrorAt < 60_000;
|
|
79
|
+
const freshOk = r.lastOkAt && now - r.lastOkAt < 5 * 60_000;
|
|
80
|
+
if (freshError && !freshOk)
|
|
81
|
+
status = 'degraded';
|
|
82
|
+
else
|
|
83
|
+
status = 'ok';
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
name,
|
|
87
|
+
calls: r.calls,
|
|
88
|
+
ok: r.ok,
|
|
89
|
+
failures: r.failures,
|
|
90
|
+
p50LatencyMs: p,
|
|
91
|
+
lastOkAt: r.lastOkAt,
|
|
92
|
+
lastErrorAt: r.lastErrorAt,
|
|
93
|
+
spendUsdToday: r.spendUsdToday,
|
|
94
|
+
status,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export function snapshot() {
|
|
98
|
+
const providers = [snapshotProvider('coingecko'), snapshotProvider('blockrun')];
|
|
99
|
+
const allLatencies = [...rolls.coingecko.latencies, ...rolls.blockrun.latencies];
|
|
100
|
+
return {
|
|
101
|
+
providers,
|
|
102
|
+
totals: {
|
|
103
|
+
callsToday: providers.reduce((s, p) => s + p.calls, 0),
|
|
104
|
+
spendUsdToday: providers.reduce((s, p) => s + p.spendUsdToday, 0),
|
|
105
|
+
p50LatencyMs: p50(allLatencies),
|
|
106
|
+
},
|
|
107
|
+
recentPaidCalls: [...paidRecent].reverse(),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Test helper: reset all counters. Do not call in production code paths. */
|
|
111
|
+
export function resetTelemetry() {
|
|
112
|
+
rolls.coingecko = newRoll();
|
|
113
|
+
rolls.blockrun = newRoll();
|
|
114
|
+
paidRecent.length = 0;
|
|
115
|
+
}
|
package/dist/ui/app.js
CHANGED
|
@@ -39,12 +39,16 @@ function useTerminalSize() {
|
|
|
39
39
|
}
|
|
40
40
|
function InputBox({ input, setInput, onSubmit, model, balance, sessionCost, queued, queuedCount, focused, busy, contextPct, vimMode, onVimModeChange }) {
|
|
41
41
|
const { cols } = useTerminalSize();
|
|
42
|
+
// Avoid drawing right up to the terminal edge. Several terminals auto-wrap
|
|
43
|
+
// a full-width border glyph onto the next row, which leaves "ghost" top
|
|
44
|
+
// borders behind on re-render after errors / status changes.
|
|
45
|
+
const boxWidth = Math.max(20, cols - 2);
|
|
42
46
|
const placeholder = busy
|
|
43
47
|
? (queued
|
|
44
48
|
? `⏎ ${queuedCount ?? 1} queued: ${queued.slice(0, 40)}`
|
|
45
49
|
: 'Working...')
|
|
46
50
|
: 'Type a message...';
|
|
47
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { borderStyle: "round", borderDimColor: true, paddingX: 1, width:
|
|
51
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { borderStyle: "round", borderDimColor: true, paddingX: 1, width: boxWidth, children: [busy && !input ? _jsxs(Text, { color: "yellow", children: [_jsx(Spinner, { type: "dots" }), " "] }) : null, _jsx(Box, { flexGrow: 1, children: vimMode ? (_jsx(VimInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: placeholder, focus: focused !== false, showMode: true, onModeChange: onVimModeChange })) : (_jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: placeholder, focus: focused !== false })) })] }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { dimColor: true, children: [busy ? _jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }) : null, busy ? ' ' : '', shortModelName(model), " \u00B7 ", balance, sessionCost > 0.00001 ? _jsxs(Text, { color: "yellow", children: [" -$", sessionCost.toFixed(4)] }) : '', contextPct !== undefined && contextPct > 0 ? (() => {
|
|
48
52
|
// Visual context bar: ▓▓▓▓▓▓░░░░ 75%
|
|
49
53
|
const filled = Math.round(contextPct / 10);
|
|
50
54
|
const empty = 10 - filled;
|
|
@@ -52,6 +56,28 @@ function InputBox({ input, setInput, onSubmit, model, balance, sessionCost, queu
|
|
|
52
56
|
return (_jsxs(Text, { children: [' ', _jsx(Text, { color: barColor, children: '▓'.repeat(filled) }), _jsx(Text, { dimColor: true, children: '░'.repeat(empty) }), _jsxs(Text, { color: barColor, children: [' ', contextPct, "%"] })] }));
|
|
53
57
|
})() : null, (queuedCount ?? 0) > 0 ? _jsxs(Text, { color: "cyan", children: [" \u00B7 ", queuedCount, " queued"] }) : null, ' · esc'] }) })] }));
|
|
54
58
|
}
|
|
59
|
+
function formatAgentErrorForDisplay(error) {
|
|
60
|
+
const lines = error.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
61
|
+
const tipIndex = lines.findIndex((line) => /^tip:/i.test(line));
|
|
62
|
+
const mainLines = tipIndex >= 0 ? lines.slice(0, tipIndex) : lines;
|
|
63
|
+
const tipLines = tipIndex >= 0 ? lines.slice(tipIndex) : [];
|
|
64
|
+
let main = mainLines.join(' ').replace(/\s+/g, ' ').trim();
|
|
65
|
+
let tip = tipLines.join(' ').replace(/\s+/g, ' ').trim();
|
|
66
|
+
const labelMatch = /^\[([^\]]+)\]\s*/.exec(main);
|
|
67
|
+
const label = labelMatch?.[1];
|
|
68
|
+
if (labelMatch)
|
|
69
|
+
main = main.slice(labelMatch[0].length).trim();
|
|
70
|
+
if (tip)
|
|
71
|
+
tip = tip.replace(/^tip:\s*/i, '');
|
|
72
|
+
const out = ['**Request failed**'];
|
|
73
|
+
if (label)
|
|
74
|
+
out.push(`- Type: ${label}`);
|
|
75
|
+
if (main)
|
|
76
|
+
out.push(`- Message: ${main}`);
|
|
77
|
+
if (tip)
|
|
78
|
+
out.push(`- Tip: ${tip}`);
|
|
79
|
+
return out.join('\n');
|
|
80
|
+
}
|
|
55
81
|
function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain, startWithPicker, onSubmit, onModelChange, onAbort, onExit, }) {
|
|
56
82
|
const { exit } = useApp();
|
|
57
83
|
const [input, setInput] = useState('');
|
|
@@ -635,7 +661,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
635
661
|
setStreamText('');
|
|
636
662
|
}
|
|
637
663
|
if (event.reason === 'error' && event.error) {
|
|
638
|
-
commitResponse(
|
|
664
|
+
commitResponse(formatAgentErrorForDisplay(event.error), turnTokensRef.current, turnCostRef.current);
|
|
639
665
|
showStatus('Turn failed', 'error', 5000);
|
|
640
666
|
}
|
|
641
667
|
else if (event.reason === 'aborted') {
|
package/package.json
CHANGED