yappr 0.1.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/.env.example +115 -0
- package/config/context/personality.md +7 -0
- package/config/context/security.md +10 -0
- package/config/hooks/example.ts +47 -0
- package/config/hooks/holder.ts +154 -0
- package/config/hooks/user-memory.ts +102 -0
- package/config/skills/compute/handler.ts +6 -0
- package/config/skills/compute/skill.md +7 -0
- package/config/skills/cron/handler.ts +89 -0
- package/config/skills/cron/skill.md +36 -0
- package/config/skills/generate-image/handler.ts +133 -0
- package/config/skills/generate-image/skill.md +20 -0
- package/config/skills/generate-meme-prompt/handler.ts +40 -0
- package/config/skills/generate-meme-prompt/skill.md +23 -0
- package/config/skills/stats/handler.ts +76 -0
- package/config/skills/stats/skill.md +18 -0
- package/config/skills/wallet/handler.ts +56 -0
- package/config/skills/wallet/skill.md +17 -0
- package/config/skills/x/handler.ts +135 -0
- package/config/skills/x/skill.md +163 -0
- package/dist/config/hooks/example.d.ts +2 -0
- package/dist/config/hooks/example.js +37 -0
- package/dist/config/hooks/holder.d.ts +2 -0
- package/dist/config/hooks/holder.js +147 -0
- package/dist/config/hooks/user-memory.d.ts +2 -0
- package/dist/config/hooks/user-memory.js +79 -0
- package/dist/config/skills/compute/handler.d.ts +2 -0
- package/dist/config/skills/compute/handler.js +5 -0
- package/dist/config/skills/cron/handler.d.ts +2 -0
- package/dist/config/skills/cron/handler.js +84 -0
- package/dist/config/skills/generate-image/handler.d.ts +2 -0
- package/dist/config/skills/generate-image/handler.js +122 -0
- package/dist/config/skills/generate-meme/handler.d.ts +2 -0
- package/dist/config/skills/generate-meme/handler.js +121 -0
- package/dist/config/skills/generate-meme-prompt/handler.d.ts +2 -0
- package/dist/config/skills/generate-meme-prompt/handler.js +38 -0
- package/dist/config/skills/stats/handler.d.ts +2 -0
- package/dist/config/skills/stats/handler.js +71 -0
- package/dist/config/skills/wallet/handler.d.ts +2 -0
- package/dist/config/skills/wallet/handler.js +54 -0
- package/dist/config/skills/x/handler.d.ts +2 -0
- package/dist/config/skills/x/handler.js +115 -0
- package/dist/src/agent-prompt.d.ts +1 -0
- package/dist/src/agent-prompt.js +45 -0
- package/dist/src/bankr.d.ts +41 -0
- package/dist/src/bankr.js +76 -0
- package/dist/src/cli/backup.d.ts +7 -0
- package/dist/src/cli/backup.js +78 -0
- package/dist/src/cli/charts.d.ts +32 -0
- package/dist/src/cli/charts.js +222 -0
- package/dist/src/cli/config-sync.d.ts +7 -0
- package/dist/src/cli/config-sync.js +71 -0
- package/dist/src/cli/deploy.d.ts +2 -0
- package/dist/src/cli/deploy.js +1059 -0
- package/dist/src/cli/env.d.ts +4 -0
- package/dist/src/cli/env.js +50 -0
- package/dist/src/cli/host-key.d.ts +4 -0
- package/dist/src/cli/host-key.js +50 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/cli/index.js +71 -0
- package/dist/src/cli/init.d.ts +1 -0
- package/dist/src/cli/init.js +51 -0
- package/dist/src/cli/ssh.d.ts +2 -0
- package/dist/src/cli/ssh.js +141 -0
- package/dist/src/cli/status.d.ts +7 -0
- package/dist/src/cli/status.js +1184 -0
- package/dist/src/cli/tui.d.ts +18 -0
- package/dist/src/cli/tui.js +115 -0
- package/dist/src/cli/ui.d.ts +30 -0
- package/dist/src/cli/ui.js +164 -0
- package/dist/src/cli/update.d.ts +1 -0
- package/dist/src/cli/update.js +263 -0
- package/dist/src/cli/x-login.d.ts +6 -0
- package/dist/src/cli/x-login.js +70 -0
- package/dist/src/compute.d.ts +11 -0
- package/dist/src/compute.js +109 -0
- package/dist/src/config-loader.d.ts +19 -0
- package/dist/src/config-loader.js +82 -0
- package/dist/src/config.d.ts +29 -0
- package/dist/src/config.js +68 -0
- package/dist/src/cron/capability.d.ts +6 -0
- package/dist/src/cron/capability.js +66 -0
- package/dist/src/cron/runner.d.ts +2 -0
- package/dist/src/cron/runner.js +113 -0
- package/dist/src/cron/schedule.d.ts +19 -0
- package/dist/src/cron/schedule.js +154 -0
- package/dist/src/cron/store.d.ts +46 -0
- package/dist/src/cron/store.js +220 -0
- package/dist/src/db.d.ts +4 -0
- package/dist/src/db.js +53 -0
- package/dist/src/hooks/loader.d.ts +1 -0
- package/dist/src/hooks/loader.js +17 -0
- package/dist/src/hooks/registry.d.ts +17 -0
- package/dist/src/hooks/registry.js +78 -0
- package/dist/src/hooks/types.d.ts +45 -0
- package/dist/src/hooks/types.js +1 -0
- package/dist/src/index.d.ts +25 -0
- package/dist/src/index.js +35 -0
- package/dist/src/llm/index.d.ts +23 -0
- package/dist/src/llm/index.js +213 -0
- package/dist/src/llm/prompts.d.ts +6 -0
- package/dist/src/llm/prompts.js +99 -0
- package/dist/src/log.d.ts +2 -0
- package/dist/src/log.js +30 -0
- package/dist/src/reply/agent.d.ts +20 -0
- package/dist/src/reply/agent.js +215 -0
- package/dist/src/reply/context-blocks.d.ts +12 -0
- package/dist/src/reply/context-blocks.js +22 -0
- package/dist/src/reply/gating.d.ts +3 -0
- package/dist/src/reply/gating.js +35 -0
- package/dist/src/reply/pipeline.d.ts +3 -0
- package/dist/src/reply/pipeline.js +144 -0
- package/dist/src/reply/poller.d.ts +5 -0
- package/dist/src/reply/poller.js +79 -0
- package/dist/src/skills/holder-access.d.ts +7 -0
- package/dist/src/skills/holder-access.js +53 -0
- package/dist/src/skills/loader.d.ts +2 -0
- package/dist/src/skills/loader.js +64 -0
- package/dist/src/skills/registry.d.ts +4 -0
- package/dist/src/skills/registry.js +10 -0
- package/dist/src/skills/types.d.ts +16 -0
- package/dist/src/skills/types.js +1 -0
- package/dist/src/state.d.ts +5 -0
- package/dist/src/state.js +26 -0
- package/dist/src/stats-cli.d.ts +1 -0
- package/dist/src/stats-cli.js +82 -0
- package/dist/src/stats.d.ts +41 -0
- package/dist/src/stats.js +236 -0
- package/dist/src/storage.d.ts +16 -0
- package/dist/src/storage.js +107 -0
- package/dist/src/treasury/abi.d.ts +99 -0
- package/dist/src/treasury/abi.js +71 -0
- package/dist/src/treasury/cycle.d.ts +16 -0
- package/dist/src/treasury/cycle.js +154 -0
- package/dist/src/treasury/index.d.ts +28 -0
- package/dist/src/treasury/index.js +222 -0
- package/dist/src/util.d.ts +3 -0
- package/dist/src/util.js +18 -0
- package/dist/src/wallet.d.ts +5 -0
- package/dist/src/wallet.js +241 -0
- package/dist/src/x/client.d.ts +74 -0
- package/dist/src/x/client.js +323 -0
- package/dist/src/x/types.d.ts +61 -0
- package/dist/src/x/types.js +1 -0
- package/dist/src/x402.d.ts +6 -0
- package/dist/src/x402.js +11 -0
- package/dist/src/yappr.d.ts +1 -0
- package/dist/src/yappr.js +85 -0
- package/package.json +52 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { withSchema } from "./db.js";
|
|
2
|
+
// Trailing window used to estimate the current burn/earn rate for runway (24h).
|
|
3
|
+
const RATE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
4
|
+
// Resolution of the dashboard chart series (points across the 24h window).
|
|
5
|
+
const CHART_BUCKETS = 120;
|
|
6
|
+
// This feature's tables (created on first use via the shared connection).
|
|
7
|
+
const SCHEMA = `
|
|
8
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
9
|
+
id INTEGER PRIMARY KEY,
|
|
10
|
+
ts TEXT NOT NULL,
|
|
11
|
+
kind TEXT NOT NULL,
|
|
12
|
+
type TEXT,
|
|
13
|
+
usdc REAL,
|
|
14
|
+
weth REAL,
|
|
15
|
+
n INTEGER
|
|
16
|
+
);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_events_ts ON events(ts);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind);
|
|
19
|
+
CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value REAL);
|
|
20
|
+
`;
|
|
21
|
+
const conn = () => withSchema(SCHEMA);
|
|
22
|
+
const now = () => new Date().toISOString();
|
|
23
|
+
function insert(ev) {
|
|
24
|
+
const d = conn();
|
|
25
|
+
if (!d)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
d.prepare("INSERT INTO events (ts, kind, type, usdc, weth, n) VALUES (?, ?, ?, ?, ?, ?)")
|
|
29
|
+
.run(now(), ev.kind, ev.type ?? null, ev.usdc ?? null, ev.weth ?? null, ev.n ?? null);
|
|
30
|
+
}
|
|
31
|
+
catch { /* best-effort */ }
|
|
32
|
+
}
|
|
33
|
+
function getMeta(key) {
|
|
34
|
+
const d = conn();
|
|
35
|
+
if (!d)
|
|
36
|
+
return undefined;
|
|
37
|
+
try {
|
|
38
|
+
const row = d.prepare("SELECT value FROM meta WHERE key = ?").get(key);
|
|
39
|
+
return row?.value;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function setMeta(key, value) {
|
|
46
|
+
const d = conn();
|
|
47
|
+
if (!d)
|
|
48
|
+
return;
|
|
49
|
+
try {
|
|
50
|
+
d.prepare("INSERT INTO meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
51
|
+
}
|
|
52
|
+
catch { /* best-effort */ }
|
|
53
|
+
}
|
|
54
|
+
export function recordSpend(type, usdc) {
|
|
55
|
+
if (!Number.isFinite(usdc) || usdc <= 0)
|
|
56
|
+
return;
|
|
57
|
+
insert({ kind: "spend", type, usdc });
|
|
58
|
+
}
|
|
59
|
+
export function recordMention(n) {
|
|
60
|
+
if (!(n > 0))
|
|
61
|
+
return;
|
|
62
|
+
insert({ kind: "mention", n });
|
|
63
|
+
}
|
|
64
|
+
export function recordReply() { insert({ kind: "reply" }); }
|
|
65
|
+
export function recordLlm() { insert({ kind: "llm" }); }
|
|
66
|
+
export function recordWarn() { insert({ kind: "warn" }); }
|
|
67
|
+
export function recordError() { insert({ kind: "error" }); }
|
|
68
|
+
// Inference spend is recorded per-request from token usage × per-model pricing (see
|
|
69
|
+
// llm/index.ts: recordSpend("inference", …)), so there's no credit-balance polling
|
|
70
|
+
// here. The live credit balance is still shown on the dashboard as the remaining
|
|
71
|
+
// budget, but it's read directly from the gateway, not tracked as spend.
|
|
72
|
+
// All-time creator fees earned (WETH), reported by Bankr as a cumulative total.
|
|
73
|
+
// Polled on a short timer, so treat it as a gauge: always refresh the stored total,
|
|
74
|
+
// but only append an `earned` event (the increment) when it actually grows — keeping
|
|
75
|
+
// the events table a clean, sliceable record of real earnings, not poll noise.
|
|
76
|
+
export function recordEarned(weth) {
|
|
77
|
+
if (!Number.isFinite(weth) || weth < 0)
|
|
78
|
+
return;
|
|
79
|
+
const prev = getMeta("last_earned_weth");
|
|
80
|
+
// First observation ever (fresh DB, no restored backup): just set the baseline.
|
|
81
|
+
// Booking an event here would drop the token's entire pre-agent fee history into
|
|
82
|
+
// the trailing window as one giant "earned" spike, faking the windowed metrics
|
|
83
|
+
// ("Sustainable: yes") and charts for a day. All-time earned still shows in full
|
|
84
|
+
// — summary() reads it from this gauge, not from summing events.
|
|
85
|
+
if (prev === undefined) {
|
|
86
|
+
setMeta("last_earned_weth", weth);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (weth > prev + 1e-12)
|
|
90
|
+
insert({ kind: "earned", weth: weth - prev });
|
|
91
|
+
setMeta("last_earned_weth", weth);
|
|
92
|
+
}
|
|
93
|
+
// WETH paid out to the dev address this treasury cycle (the dev fee). Each payment is its
|
|
94
|
+
// own `dev` event (an increment), so summing them gives all-time dev revenue.
|
|
95
|
+
export function recordDevWeth(weth) {
|
|
96
|
+
if (!Number.isFinite(weth) || weth <= 0)
|
|
97
|
+
return;
|
|
98
|
+
insert({ kind: "dev", weth });
|
|
99
|
+
}
|
|
100
|
+
// Build a cumulative spend/earn chart series over [startMs, endMs] in CHART_BUCKETS evenly
|
|
101
|
+
// spaced points. Bucketing is done in SQL (one grouped row per bucket via unixepoch), so it
|
|
102
|
+
// stays cheap no matter how many events the range covers — then we running-sum in JS.
|
|
103
|
+
function buildSeries(d, startMs, endMs) {
|
|
104
|
+
const spendUsd = new Array(CHART_BUCKETS).fill(0);
|
|
105
|
+
const earnedWeth = new Array(CHART_BUCKETS).fill(0);
|
|
106
|
+
const span = endMs - startMs;
|
|
107
|
+
if (span > 0) {
|
|
108
|
+
const startSec = Math.floor(startMs / 1000);
|
|
109
|
+
const bucketSec = span / 1000 / CHART_BUCKETS;
|
|
110
|
+
const rows = d.prepare(`
|
|
111
|
+
SELECT CAST((unixepoch(ts) - ?) / ? AS INTEGER) AS b,
|
|
112
|
+
COALESCE(SUM(usdc) FILTER (WHERE kind = 'spend'), 0) AS spend,
|
|
113
|
+
COALESCE(SUM(weth) FILTER (WHERE kind = 'earned'), 0) AS earned
|
|
114
|
+
FROM events
|
|
115
|
+
WHERE ts >= ? AND kind IN ('spend','earned')
|
|
116
|
+
GROUP BY b
|
|
117
|
+
`).all(startSec, bucketSec, new Date(startMs).toISOString());
|
|
118
|
+
for (const r of rows) {
|
|
119
|
+
const i = Math.min(CHART_BUCKETS - 1, Math.max(0, r.b));
|
|
120
|
+
spendUsd[i] += r.spend || 0;
|
|
121
|
+
earnedWeth[i] += r.earned || 0;
|
|
122
|
+
}
|
|
123
|
+
for (let i = 1; i < CHART_BUCKETS; i++) {
|
|
124
|
+
spendUsd[i] += spendUsd[i - 1];
|
|
125
|
+
earnedWeth[i] += earnedWeth[i - 1];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { spendUsd, earnedWeth, startMs, endMs };
|
|
129
|
+
}
|
|
130
|
+
// Per-hour spend by type AND per-hour earnings over the 24 clock-hours ending with the
|
|
131
|
+
// current hour. Returns 24 buckets each (spend USD by type; earned WETH), not cumulative,
|
|
132
|
+
// plus the first bucket's (clock-aligned) start. For the hourly bar charts. Bucketed in SQL.
|
|
133
|
+
function buildHourlyByType(d) {
|
|
134
|
+
const N = 24, HOUR = 3_600_000;
|
|
135
|
+
const startMs = Math.floor(Date.now() / HOUR) * HOUR - (N - 1) * HOUR; // 24 hours ending this hour
|
|
136
|
+
const xapi = new Array(N).fill(0), inference = new Array(N).fill(0), compute = new Array(N).fill(0);
|
|
137
|
+
const x402 = new Array(N).fill(0);
|
|
138
|
+
const earned = new Array(N).fill(0);
|
|
139
|
+
const startSec = startMs / 1000;
|
|
140
|
+
const startIso = new Date(startMs).toISOString(), endIso = new Date(startMs + N * HOUR).toISOString();
|
|
141
|
+
const rows = d.prepare(`
|
|
142
|
+
SELECT CAST((unixepoch(ts) - ?) / 3600 AS INTEGER) AS b, type, COALESCE(SUM(usdc), 0) AS u
|
|
143
|
+
FROM events
|
|
144
|
+
WHERE kind = 'spend' AND ts >= ? AND ts < ?
|
|
145
|
+
GROUP BY b, type
|
|
146
|
+
`).all(startSec, startIso, endIso);
|
|
147
|
+
for (const r of rows) {
|
|
148
|
+
if (r.b < 0 || r.b >= N)
|
|
149
|
+
continue;
|
|
150
|
+
if (r.type === "x-api")
|
|
151
|
+
xapi[r.b] += r.u || 0;
|
|
152
|
+
else if (r.type === "inference")
|
|
153
|
+
inference[r.b] += r.u || 0;
|
|
154
|
+
else if (r.type === "compute")
|
|
155
|
+
compute[r.b] += r.u || 0;
|
|
156
|
+
else if (r.type === "x402")
|
|
157
|
+
x402[r.b] += r.u || 0;
|
|
158
|
+
}
|
|
159
|
+
const erows = d.prepare(`
|
|
160
|
+
SELECT CAST((unixepoch(ts) - ?) / 3600 AS INTEGER) AS b, COALESCE(SUM(weth), 0) AS w
|
|
161
|
+
FROM events
|
|
162
|
+
WHERE kind = 'earned' AND ts >= ? AND ts < ?
|
|
163
|
+
GROUP BY b
|
|
164
|
+
`).all(startSec, startIso, endIso);
|
|
165
|
+
for (const r of erows)
|
|
166
|
+
if (r.b >= 0 && r.b < N)
|
|
167
|
+
earned[r.b] += r.w || 0;
|
|
168
|
+
return { startMs, xapi, inference, compute, x402, earned };
|
|
169
|
+
}
|
|
170
|
+
// All-time rolled-up totals, computed straight from the events table. Cheap with the
|
|
171
|
+
// kind/ts indexes; the dashboard polls this via the CLI.
|
|
172
|
+
export function summary() {
|
|
173
|
+
const empty = {
|
|
174
|
+
mentions: 0, replies: 0, llm: 0, warns: 0, errors: 0,
|
|
175
|
+
spentUsd: 0, spentByType: { "x-api": 0, compute: 0, inference: 0, x402: 0 }, earnedWeth: 0, devWeth: 0,
|
|
176
|
+
spentUsdWindow: 0, inferenceUsdWindow: 0, earnedWethWindow: 0, rateWindowHours: 0,
|
|
177
|
+
chart: {
|
|
178
|
+
day: { spendUsd: [], earnedWeth: [], startMs: 0, endMs: 0 },
|
|
179
|
+
byType: { startMs: 0, xapi: [], inference: [], compute: [], x402: [], earned: [] },
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
const d = conn();
|
|
183
|
+
if (!d)
|
|
184
|
+
return empty;
|
|
185
|
+
try {
|
|
186
|
+
const agg = d.prepare(`
|
|
187
|
+
SELECT
|
|
188
|
+
COALESCE(SUM(n) FILTER (WHERE kind = 'mention'), 0) AS mentions,
|
|
189
|
+
COUNT(*) FILTER (WHERE kind = 'reply') AS replies,
|
|
190
|
+
COUNT(*) FILTER (WHERE kind = 'llm') AS llm,
|
|
191
|
+
COUNT(*) FILTER (WHERE kind = 'warn') AS warns,
|
|
192
|
+
COUNT(*) FILTER (WHERE kind = 'error') AS errors,
|
|
193
|
+
COALESCE(SUM(usdc) FILTER (WHERE kind = 'spend'), 0) AS spentUsd,
|
|
194
|
+
COALESCE(SUM(weth) FILTER (WHERE kind = 'dev'), 0) AS devWeth
|
|
195
|
+
FROM events
|
|
196
|
+
`).get();
|
|
197
|
+
const spentByType = { "x-api": 0, compute: 0, inference: 0, x402: 0 };
|
|
198
|
+
const rows = d.prepare("SELECT type, COALESCE(SUM(usdc), 0) AS u FROM events WHERE kind = 'spend' GROUP BY type").all();
|
|
199
|
+
for (const r of rows)
|
|
200
|
+
if (r.type && r.type in spentByType)
|
|
201
|
+
spentByType[r.type] = r.u;
|
|
202
|
+
// Burn/earn over the trailing window for the runway estimate. The divisor is the
|
|
203
|
+
// window length, but never longer than the agent's age (first event → now), so a
|
|
204
|
+
// young agent's rate isn't diluted by counting hours it wasn't running.
|
|
205
|
+
const since = new Date(Date.now() - RATE_WINDOW_MS).toISOString();
|
|
206
|
+
const win = d.prepare(`
|
|
207
|
+
SELECT
|
|
208
|
+
COALESCE(SUM(usdc) FILTER (WHERE kind = 'spend'), 0) AS spentUsdWindow,
|
|
209
|
+
COALESCE(SUM(usdc) FILTER (WHERE kind = 'spend' AND type = 'inference'), 0) AS inferenceUsdWindow,
|
|
210
|
+
COALESCE(SUM(weth) FILTER (WHERE kind = 'earned'), 0) AS earnedWethWindow
|
|
211
|
+
FROM events WHERE ts >= ?
|
|
212
|
+
`).get(since);
|
|
213
|
+
const firstTs = d.prepare("SELECT MIN(ts) AS t FROM events").get().t;
|
|
214
|
+
const startMs = firstTs ? Math.max(Date.now() - RATE_WINDOW_MS, Date.parse(firstTs)) : Date.now();
|
|
215
|
+
const rateWindowHours = Math.max(0, (Date.now() - startMs) / 3_600_000);
|
|
216
|
+
// Chart series for the dashboard: a 24h view (`day`, from startMs = max(now-24h,
|
|
217
|
+
// firstEvent)) through now.
|
|
218
|
+
const chartNow = Date.now();
|
|
219
|
+
const day = buildSeries(d, startMs, chartNow);
|
|
220
|
+
const byType = buildHourlyByType(d);
|
|
221
|
+
// `earnedWeth`/`earnedWethWindow` are GROSS creator fees (the dev cut is part of
|
|
222
|
+
// this revenue, not subtracted). `devWeth` is surfaced separately as a breakdown
|
|
223
|
+
// ("Dev rev") of how much of that gross was routed to the dev address.
|
|
224
|
+
return {
|
|
225
|
+
mentions: agg.mentions, replies: agg.replies, llm: agg.llm, warns: agg.warns, errors: agg.errors,
|
|
226
|
+
spentUsd: agg.spentUsd, spentByType,
|
|
227
|
+
earnedWeth: getMeta("last_earned_weth") ?? 0, devWeth: agg.devWeth,
|
|
228
|
+
spentUsdWindow: win.spentUsdWindow, inferenceUsdWindow: win.inferenceUsdWindow,
|
|
229
|
+
earnedWethWindow: win.earnedWethWindow, rateWindowHours,
|
|
230
|
+
chart: { day, byType },
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return empty;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type SkillStoreEntry = {
|
|
2
|
+
key: string;
|
|
3
|
+
value: string;
|
|
4
|
+
updatedAt: number;
|
|
5
|
+
};
|
|
6
|
+
export type SkillStore = {
|
|
7
|
+
get(key: string): string | null;
|
|
8
|
+
set(key: string, value: string): void;
|
|
9
|
+
/** JSON.parse'd get(); null if missing or not valid JSON (logged). */
|
|
10
|
+
getJSON<T = unknown>(key: string): T | null;
|
|
11
|
+
/** set() with JSON.stringify — for structured values. */
|
|
12
|
+
setJSON(key: string, value: unknown): void;
|
|
13
|
+
delete(key: string): boolean;
|
|
14
|
+
list(prefix?: string): SkillStoreEntry[];
|
|
15
|
+
};
|
|
16
|
+
export declare function skillStore(ns: string): SkillStore;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { withSchema } from "./db.js";
|
|
2
|
+
import { log } from "./log.js";
|
|
3
|
+
// Namespaced key/value storage for skills and hooks, in the shared SQLite DB
|
|
4
|
+
// (see db.ts). This is the convenience layer of the public storage API: a skill
|
|
5
|
+
// calls skillStore("<its-name>") and gets get/set/delete/list with zero SQL and
|
|
6
|
+
// zero schema decisions. All namespaces share one table (skill_kv) keyed on
|
|
7
|
+
// (ns, key), so a store can only see its own namespace's rows.
|
|
8
|
+
//
|
|
9
|
+
// Because rows live in yappr.db at DB_PATH, skill data survives restarts and
|
|
10
|
+
// redeploys and is included in the dashboard's rolling backups — which is why
|
|
11
|
+
// skills should store here rather than open their own files.
|
|
12
|
+
//
|
|
13
|
+
// Convention: namespace = the skill's folder name, unless two extensions are
|
|
14
|
+
// deliberately sharing data. Skills that need real columns instead of KV use
|
|
15
|
+
// withSchema() directly with their own `skill_<name>_*` table.
|
|
16
|
+
//
|
|
17
|
+
// Like everything on db.ts, operations are best-effort: if the DB can't be
|
|
18
|
+
// opened, reads return empty and writes no-op, degrading the skill instead of
|
|
19
|
+
// crashing the agent. Unlike the engine's own ledgers, failures here are
|
|
20
|
+
// logged (warn): skill params come from LLM JSON, so a non-string value
|
|
21
|
+
// sneaking past the types is a realistic bug the author needs to see.
|
|
22
|
+
const SCHEMA = `CREATE TABLE IF NOT EXISTS skill_kv (
|
|
23
|
+
ns TEXT NOT NULL,
|
|
24
|
+
key TEXT NOT NULL,
|
|
25
|
+
value TEXT NOT NULL,
|
|
26
|
+
updated_at INTEGER NOT NULL,
|
|
27
|
+
PRIMARY KEY (ns, key)
|
|
28
|
+
)`;
|
|
29
|
+
const conn = () => withSchema(SCHEMA);
|
|
30
|
+
// Escape LIKE wildcards so list("user:") treats the prefix literally.
|
|
31
|
+
const likePrefix = (prefix) => prefix.replace(/[\\%_]/g, (c) => "\\" + c) + "%";
|
|
32
|
+
export function skillStore(ns) {
|
|
33
|
+
const store = {
|
|
34
|
+
get(key) {
|
|
35
|
+
const d = conn();
|
|
36
|
+
if (!d)
|
|
37
|
+
return null;
|
|
38
|
+
try {
|
|
39
|
+
const row = d.prepare("SELECT value FROM skill_kv WHERE ns = ? AND key = ?").get(ns, key);
|
|
40
|
+
return row?.value ?? null;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
log.warn({ ns, key, err }, "skillStore.get failed");
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
set(key, value) {
|
|
48
|
+
const d = conn();
|
|
49
|
+
if (!d)
|
|
50
|
+
return;
|
|
51
|
+
try {
|
|
52
|
+
d.prepare("INSERT INTO skill_kv(ns, key, value, updated_at) VALUES(?, ?, ?, ?) " +
|
|
53
|
+
"ON CONFLICT(ns, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at").run(ns, key, value, Date.now());
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
// Most likely a non-string value from untyped LLM params — surface it,
|
|
57
|
+
// a skill that thinks it stored something must not fail silently.
|
|
58
|
+
log.warn({ ns, key, err }, "skillStore.set failed (value not stored)");
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
getJSON(key) {
|
|
62
|
+
const raw = store.get(key);
|
|
63
|
+
if (raw === null)
|
|
64
|
+
return null;
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(raw);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
log.warn({ ns, key, err }, "skillStore.getJSON: stored value is not valid JSON");
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
setJSON(key, value) {
|
|
74
|
+
// stringify(undefined) is undefined, which would fail to bind — store null.
|
|
75
|
+
store.set(key, JSON.stringify(value) ?? "null");
|
|
76
|
+
},
|
|
77
|
+
delete(key) {
|
|
78
|
+
const d = conn();
|
|
79
|
+
if (!d)
|
|
80
|
+
return false;
|
|
81
|
+
try {
|
|
82
|
+
return d.prepare("DELETE FROM skill_kv WHERE ns = ? AND key = ?").run(ns, key).changes > 0;
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
log.warn({ ns, key, err }, "skillStore.delete failed");
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
list(prefix) {
|
|
90
|
+
const d = conn();
|
|
91
|
+
if (!d)
|
|
92
|
+
return [];
|
|
93
|
+
try {
|
|
94
|
+
const rows = prefix !== undefined
|
|
95
|
+
? d.prepare("SELECT key, value, updated_at FROM skill_kv WHERE ns = ? AND key LIKE ? ESCAPE '\\' ORDER BY key").all(ns, likePrefix(prefix))
|
|
96
|
+
: d.prepare("SELECT key, value, updated_at FROM skill_kv WHERE ns = ? ORDER BY key").all(ns);
|
|
97
|
+
return rows
|
|
98
|
+
.map((r) => ({ key: r.key, value: r.value, updatedAt: r.updated_at }));
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
log.warn({ ns, prefix, err }, "skillStore.list failed");
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
return store;
|
|
107
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export declare const ERC20_ABI: readonly [{
|
|
2
|
+
readonly name: "transfer";
|
|
3
|
+
readonly type: "function";
|
|
4
|
+
readonly inputs: readonly [{
|
|
5
|
+
readonly name: "to";
|
|
6
|
+
readonly type: "address";
|
|
7
|
+
}, {
|
|
8
|
+
readonly name: "amount";
|
|
9
|
+
readonly type: "uint256";
|
|
10
|
+
}];
|
|
11
|
+
readonly outputs: readonly [{
|
|
12
|
+
readonly type: "bool";
|
|
13
|
+
}];
|
|
14
|
+
readonly stateMutability: "nonpayable";
|
|
15
|
+
}, {
|
|
16
|
+
readonly name: "balanceOf";
|
|
17
|
+
readonly type: "function";
|
|
18
|
+
readonly inputs: readonly [{
|
|
19
|
+
readonly name: "account";
|
|
20
|
+
readonly type: "address";
|
|
21
|
+
}];
|
|
22
|
+
readonly outputs: readonly [{
|
|
23
|
+
readonly type: "uint256";
|
|
24
|
+
}];
|
|
25
|
+
readonly stateMutability: "view";
|
|
26
|
+
}, {
|
|
27
|
+
readonly name: "approve";
|
|
28
|
+
readonly type: "function";
|
|
29
|
+
readonly inputs: readonly [{
|
|
30
|
+
readonly name: "spender";
|
|
31
|
+
readonly type: "address";
|
|
32
|
+
}, {
|
|
33
|
+
readonly name: "amount";
|
|
34
|
+
readonly type: "uint256";
|
|
35
|
+
}];
|
|
36
|
+
readonly outputs: readonly [{
|
|
37
|
+
readonly type: "bool";
|
|
38
|
+
}];
|
|
39
|
+
readonly stateMutability: "nonpayable";
|
|
40
|
+
}, {
|
|
41
|
+
readonly name: "allowance";
|
|
42
|
+
readonly type: "function";
|
|
43
|
+
readonly inputs: readonly [{
|
|
44
|
+
readonly name: "owner";
|
|
45
|
+
readonly type: "address";
|
|
46
|
+
}, {
|
|
47
|
+
readonly name: "spender";
|
|
48
|
+
readonly type: "address";
|
|
49
|
+
}];
|
|
50
|
+
readonly outputs: readonly [{
|
|
51
|
+
readonly type: "uint256";
|
|
52
|
+
}];
|
|
53
|
+
readonly stateMutability: "view";
|
|
54
|
+
}];
|
|
55
|
+
export declare const WETH_ABI: readonly [{
|
|
56
|
+
readonly name: "withdraw";
|
|
57
|
+
readonly type: "function";
|
|
58
|
+
readonly inputs: readonly [{
|
|
59
|
+
readonly name: "wad";
|
|
60
|
+
readonly type: "uint256";
|
|
61
|
+
}];
|
|
62
|
+
readonly outputs: readonly [];
|
|
63
|
+
readonly stateMutability: "nonpayable";
|
|
64
|
+
}];
|
|
65
|
+
export declare const UNISWAP_ROUTER_ABI: readonly [{
|
|
66
|
+
readonly name: "exactInputSingle";
|
|
67
|
+
readonly type: "function";
|
|
68
|
+
readonly inputs: readonly [{
|
|
69
|
+
readonly name: "params";
|
|
70
|
+
readonly type: "tuple";
|
|
71
|
+
readonly components: readonly [{
|
|
72
|
+
readonly name: "tokenIn";
|
|
73
|
+
readonly type: "address";
|
|
74
|
+
}, {
|
|
75
|
+
readonly name: "tokenOut";
|
|
76
|
+
readonly type: "address";
|
|
77
|
+
}, {
|
|
78
|
+
readonly name: "fee";
|
|
79
|
+
readonly type: "uint24";
|
|
80
|
+
}, {
|
|
81
|
+
readonly name: "recipient";
|
|
82
|
+
readonly type: "address";
|
|
83
|
+
}, {
|
|
84
|
+
readonly name: "amountIn";
|
|
85
|
+
readonly type: "uint256";
|
|
86
|
+
}, {
|
|
87
|
+
readonly name: "amountOutMinimum";
|
|
88
|
+
readonly type: "uint256";
|
|
89
|
+
}, {
|
|
90
|
+
readonly name: "sqrtPriceLimitX96";
|
|
91
|
+
readonly type: "uint160";
|
|
92
|
+
}];
|
|
93
|
+
}];
|
|
94
|
+
readonly outputs: readonly [{
|
|
95
|
+
readonly name: "amountOut";
|
|
96
|
+
readonly type: "uint256";
|
|
97
|
+
}];
|
|
98
|
+
readonly stateMutability: "payable";
|
|
99
|
+
}];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const ERC20_ABI = [
|
|
2
|
+
{
|
|
3
|
+
name: "transfer",
|
|
4
|
+
type: "function",
|
|
5
|
+
inputs: [
|
|
6
|
+
{ name: "to", type: "address" },
|
|
7
|
+
{ name: "amount", type: "uint256" },
|
|
8
|
+
],
|
|
9
|
+
outputs: [{ type: "bool" }],
|
|
10
|
+
stateMutability: "nonpayable",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: "balanceOf",
|
|
14
|
+
type: "function",
|
|
15
|
+
inputs: [{ name: "account", type: "address" }],
|
|
16
|
+
outputs: [{ type: "uint256" }],
|
|
17
|
+
stateMutability: "view",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "approve",
|
|
21
|
+
type: "function",
|
|
22
|
+
inputs: [
|
|
23
|
+
{ name: "spender", type: "address" },
|
|
24
|
+
{ name: "amount", type: "uint256" },
|
|
25
|
+
],
|
|
26
|
+
outputs: [{ type: "bool" }],
|
|
27
|
+
stateMutability: "nonpayable",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "allowance",
|
|
31
|
+
type: "function",
|
|
32
|
+
inputs: [
|
|
33
|
+
{ name: "owner", type: "address" },
|
|
34
|
+
{ name: "spender", type: "address" },
|
|
35
|
+
],
|
|
36
|
+
outputs: [{ type: "uint256" }],
|
|
37
|
+
stateMutability: "view",
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
export const WETH_ABI = [
|
|
41
|
+
{
|
|
42
|
+
name: "withdraw",
|
|
43
|
+
type: "function",
|
|
44
|
+
inputs: [{ name: "wad", type: "uint256" }],
|
|
45
|
+
outputs: [],
|
|
46
|
+
stateMutability: "nonpayable",
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
export const UNISWAP_ROUTER_ABI = [
|
|
50
|
+
{
|
|
51
|
+
name: "exactInputSingle",
|
|
52
|
+
type: "function",
|
|
53
|
+
inputs: [
|
|
54
|
+
{
|
|
55
|
+
name: "params",
|
|
56
|
+
type: "tuple",
|
|
57
|
+
components: [
|
|
58
|
+
{ name: "tokenIn", type: "address" },
|
|
59
|
+
{ name: "tokenOut", type: "address" },
|
|
60
|
+
{ name: "fee", type: "uint24" },
|
|
61
|
+
{ name: "recipient", type: "address" },
|
|
62
|
+
{ name: "amountIn", type: "uint256" },
|
|
63
|
+
{ name: "amountOutMinimum", type: "uint256" },
|
|
64
|
+
{ name: "sqrtPriceLimitX96", type: "uint160" },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
outputs: [{ name: "amountOut", type: "uint256" }],
|
|
69
|
+
stateMutability: "payable",
|
|
70
|
+
},
|
|
71
|
+
];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Logger } from "pino";
|
|
2
|
+
import { type Treasury } from "./index.js";
|
|
3
|
+
export type TreasuryCycleResult = {
|
|
4
|
+
tokenClaimed: bigint;
|
|
5
|
+
wethClaimed: bigint;
|
|
6
|
+
tokenBurned: bigint;
|
|
7
|
+
tokenToDev: bigint;
|
|
8
|
+
wethToDev: bigint;
|
|
9
|
+
wethUnwrapped: bigint;
|
|
10
|
+
wethSwapped: bigint;
|
|
11
|
+
computeExtended: boolean;
|
|
12
|
+
txHashes: string[];
|
|
13
|
+
cycledAt: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function runTreasuryCycle(treasury: Treasury, log: Logger): Promise<void>;
|
|
16
|
+
export declare function startTreasury(treasury: Treasury, log: Logger): () => void;
|