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.
Files changed (149) hide show
  1. package/.env.example +115 -0
  2. package/config/context/personality.md +7 -0
  3. package/config/context/security.md +10 -0
  4. package/config/hooks/example.ts +47 -0
  5. package/config/hooks/holder.ts +154 -0
  6. package/config/hooks/user-memory.ts +102 -0
  7. package/config/skills/compute/handler.ts +6 -0
  8. package/config/skills/compute/skill.md +7 -0
  9. package/config/skills/cron/handler.ts +89 -0
  10. package/config/skills/cron/skill.md +36 -0
  11. package/config/skills/generate-image/handler.ts +133 -0
  12. package/config/skills/generate-image/skill.md +20 -0
  13. package/config/skills/generate-meme-prompt/handler.ts +40 -0
  14. package/config/skills/generate-meme-prompt/skill.md +23 -0
  15. package/config/skills/stats/handler.ts +76 -0
  16. package/config/skills/stats/skill.md +18 -0
  17. package/config/skills/wallet/handler.ts +56 -0
  18. package/config/skills/wallet/skill.md +17 -0
  19. package/config/skills/x/handler.ts +135 -0
  20. package/config/skills/x/skill.md +163 -0
  21. package/dist/config/hooks/example.d.ts +2 -0
  22. package/dist/config/hooks/example.js +37 -0
  23. package/dist/config/hooks/holder.d.ts +2 -0
  24. package/dist/config/hooks/holder.js +147 -0
  25. package/dist/config/hooks/user-memory.d.ts +2 -0
  26. package/dist/config/hooks/user-memory.js +79 -0
  27. package/dist/config/skills/compute/handler.d.ts +2 -0
  28. package/dist/config/skills/compute/handler.js +5 -0
  29. package/dist/config/skills/cron/handler.d.ts +2 -0
  30. package/dist/config/skills/cron/handler.js +84 -0
  31. package/dist/config/skills/generate-image/handler.d.ts +2 -0
  32. package/dist/config/skills/generate-image/handler.js +122 -0
  33. package/dist/config/skills/generate-meme/handler.d.ts +2 -0
  34. package/dist/config/skills/generate-meme/handler.js +121 -0
  35. package/dist/config/skills/generate-meme-prompt/handler.d.ts +2 -0
  36. package/dist/config/skills/generate-meme-prompt/handler.js +38 -0
  37. package/dist/config/skills/stats/handler.d.ts +2 -0
  38. package/dist/config/skills/stats/handler.js +71 -0
  39. package/dist/config/skills/wallet/handler.d.ts +2 -0
  40. package/dist/config/skills/wallet/handler.js +54 -0
  41. package/dist/config/skills/x/handler.d.ts +2 -0
  42. package/dist/config/skills/x/handler.js +115 -0
  43. package/dist/src/agent-prompt.d.ts +1 -0
  44. package/dist/src/agent-prompt.js +45 -0
  45. package/dist/src/bankr.d.ts +41 -0
  46. package/dist/src/bankr.js +76 -0
  47. package/dist/src/cli/backup.d.ts +7 -0
  48. package/dist/src/cli/backup.js +78 -0
  49. package/dist/src/cli/charts.d.ts +32 -0
  50. package/dist/src/cli/charts.js +222 -0
  51. package/dist/src/cli/config-sync.d.ts +7 -0
  52. package/dist/src/cli/config-sync.js +71 -0
  53. package/dist/src/cli/deploy.d.ts +2 -0
  54. package/dist/src/cli/deploy.js +1059 -0
  55. package/dist/src/cli/env.d.ts +4 -0
  56. package/dist/src/cli/env.js +50 -0
  57. package/dist/src/cli/host-key.d.ts +4 -0
  58. package/dist/src/cli/host-key.js +50 -0
  59. package/dist/src/cli/index.d.ts +2 -0
  60. package/dist/src/cli/index.js +71 -0
  61. package/dist/src/cli/init.d.ts +1 -0
  62. package/dist/src/cli/init.js +51 -0
  63. package/dist/src/cli/ssh.d.ts +2 -0
  64. package/dist/src/cli/ssh.js +141 -0
  65. package/dist/src/cli/status.d.ts +7 -0
  66. package/dist/src/cli/status.js +1184 -0
  67. package/dist/src/cli/tui.d.ts +18 -0
  68. package/dist/src/cli/tui.js +115 -0
  69. package/dist/src/cli/ui.d.ts +30 -0
  70. package/dist/src/cli/ui.js +164 -0
  71. package/dist/src/cli/update.d.ts +1 -0
  72. package/dist/src/cli/update.js +263 -0
  73. package/dist/src/cli/x-login.d.ts +6 -0
  74. package/dist/src/cli/x-login.js +70 -0
  75. package/dist/src/compute.d.ts +11 -0
  76. package/dist/src/compute.js +109 -0
  77. package/dist/src/config-loader.d.ts +19 -0
  78. package/dist/src/config-loader.js +82 -0
  79. package/dist/src/config.d.ts +29 -0
  80. package/dist/src/config.js +68 -0
  81. package/dist/src/cron/capability.d.ts +6 -0
  82. package/dist/src/cron/capability.js +66 -0
  83. package/dist/src/cron/runner.d.ts +2 -0
  84. package/dist/src/cron/runner.js +113 -0
  85. package/dist/src/cron/schedule.d.ts +19 -0
  86. package/dist/src/cron/schedule.js +154 -0
  87. package/dist/src/cron/store.d.ts +46 -0
  88. package/dist/src/cron/store.js +220 -0
  89. package/dist/src/db.d.ts +4 -0
  90. package/dist/src/db.js +53 -0
  91. package/dist/src/hooks/loader.d.ts +1 -0
  92. package/dist/src/hooks/loader.js +17 -0
  93. package/dist/src/hooks/registry.d.ts +17 -0
  94. package/dist/src/hooks/registry.js +78 -0
  95. package/dist/src/hooks/types.d.ts +45 -0
  96. package/dist/src/hooks/types.js +1 -0
  97. package/dist/src/index.d.ts +25 -0
  98. package/dist/src/index.js +35 -0
  99. package/dist/src/llm/index.d.ts +23 -0
  100. package/dist/src/llm/index.js +213 -0
  101. package/dist/src/llm/prompts.d.ts +6 -0
  102. package/dist/src/llm/prompts.js +99 -0
  103. package/dist/src/log.d.ts +2 -0
  104. package/dist/src/log.js +30 -0
  105. package/dist/src/reply/agent.d.ts +20 -0
  106. package/dist/src/reply/agent.js +215 -0
  107. package/dist/src/reply/context-blocks.d.ts +12 -0
  108. package/dist/src/reply/context-blocks.js +22 -0
  109. package/dist/src/reply/gating.d.ts +3 -0
  110. package/dist/src/reply/gating.js +35 -0
  111. package/dist/src/reply/pipeline.d.ts +3 -0
  112. package/dist/src/reply/pipeline.js +144 -0
  113. package/dist/src/reply/poller.d.ts +5 -0
  114. package/dist/src/reply/poller.js +79 -0
  115. package/dist/src/skills/holder-access.d.ts +7 -0
  116. package/dist/src/skills/holder-access.js +53 -0
  117. package/dist/src/skills/loader.d.ts +2 -0
  118. package/dist/src/skills/loader.js +64 -0
  119. package/dist/src/skills/registry.d.ts +4 -0
  120. package/dist/src/skills/registry.js +10 -0
  121. package/dist/src/skills/types.d.ts +16 -0
  122. package/dist/src/skills/types.js +1 -0
  123. package/dist/src/state.d.ts +5 -0
  124. package/dist/src/state.js +26 -0
  125. package/dist/src/stats-cli.d.ts +1 -0
  126. package/dist/src/stats-cli.js +82 -0
  127. package/dist/src/stats.d.ts +41 -0
  128. package/dist/src/stats.js +236 -0
  129. package/dist/src/storage.d.ts +16 -0
  130. package/dist/src/storage.js +107 -0
  131. package/dist/src/treasury/abi.d.ts +99 -0
  132. package/dist/src/treasury/abi.js +71 -0
  133. package/dist/src/treasury/cycle.d.ts +16 -0
  134. package/dist/src/treasury/cycle.js +154 -0
  135. package/dist/src/treasury/index.d.ts +28 -0
  136. package/dist/src/treasury/index.js +222 -0
  137. package/dist/src/util.d.ts +3 -0
  138. package/dist/src/util.js +18 -0
  139. package/dist/src/wallet.d.ts +5 -0
  140. package/dist/src/wallet.js +241 -0
  141. package/dist/src/x/client.d.ts +74 -0
  142. package/dist/src/x/client.js +323 -0
  143. package/dist/src/x/types.d.ts +61 -0
  144. package/dist/src/x/types.js +1 -0
  145. package/dist/src/x402.d.ts +6 -0
  146. package/dist/src/x402.js +11 -0
  147. package/dist/src/yappr.d.ts +1 -0
  148. package/dist/src/yappr.js +85 -0
  149. 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;