@spfunctions/cli 1.7.19 → 1.7.20

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 (180) hide show
  1. package/dist/101.index.js +1 -0
  2. package/dist/12.index.js +1 -0
  3. package/dist/160.index.js +1 -0
  4. package/dist/174.index.js +1 -0
  5. package/dist/278.index.js +6 -0
  6. package/dist/582.index.js +1 -0
  7. package/dist/641.index.js +324 -0
  8. package/dist/669.index.js +1 -0
  9. package/dist/722.index.js +1 -0
  10. package/dist/788.index.js +1 -0
  11. package/dist/816.index.js +12 -0
  12. package/dist/830.index.js +1 -0
  13. package/dist/921.index.js +1 -0
  14. package/dist/index.js +1 -833
  15. package/package.json +5 -2
  16. package/dist/cache.d.ts +0 -6
  17. package/dist/cache.js +0 -31
  18. package/dist/cache.test.d.ts +0 -1
  19. package/dist/cache.test.js +0 -73
  20. package/dist/client.d.ts +0 -56
  21. package/dist/client.js +0 -205
  22. package/dist/client.test.d.ts +0 -1
  23. package/dist/client.test.js +0 -89
  24. package/dist/commands/agent.d.ts +0 -20
  25. package/dist/commands/agent.js +0 -4119
  26. package/dist/commands/announcements.d.ts +0 -3
  27. package/dist/commands/announcements.js +0 -28
  28. package/dist/commands/augment.d.ts +0 -12
  29. package/dist/commands/augment.js +0 -56
  30. package/dist/commands/balance.d.ts +0 -3
  31. package/dist/commands/balance.js +0 -17
  32. package/dist/commands/book.d.ts +0 -17
  33. package/dist/commands/book.js +0 -220
  34. package/dist/commands/cancel.d.ts +0 -5
  35. package/dist/commands/cancel.js +0 -41
  36. package/dist/commands/context.d.ts +0 -6
  37. package/dist/commands/context.js +0 -208
  38. package/dist/commands/create.d.ts +0 -7
  39. package/dist/commands/create.js +0 -42
  40. package/dist/commands/dashboard.d.ts +0 -14
  41. package/dist/commands/dashboard.js +0 -215
  42. package/dist/commands/delta.d.ts +0 -16
  43. package/dist/commands/delta.js +0 -115
  44. package/dist/commands/edges.d.ts +0 -26
  45. package/dist/commands/edges.js +0 -246
  46. package/dist/commands/evaluate.d.ts +0 -4
  47. package/dist/commands/evaluate.js +0 -30
  48. package/dist/commands/explore.d.ts +0 -14
  49. package/dist/commands/explore.js +0 -116
  50. package/dist/commands/feed.d.ts +0 -13
  51. package/dist/commands/feed.js +0 -73
  52. package/dist/commands/fills.d.ts +0 -4
  53. package/dist/commands/fills.js +0 -29
  54. package/dist/commands/forecast.d.ts +0 -4
  55. package/dist/commands/forecast.js +0 -53
  56. package/dist/commands/get.d.ts +0 -5
  57. package/dist/commands/get.js +0 -98
  58. package/dist/commands/heartbeat.d.ts +0 -20
  59. package/dist/commands/heartbeat.js +0 -73
  60. package/dist/commands/history.d.ts +0 -3
  61. package/dist/commands/history.js +0 -38
  62. package/dist/commands/liquidity.d.ts +0 -14
  63. package/dist/commands/liquidity.js +0 -378
  64. package/dist/commands/list.d.ts +0 -5
  65. package/dist/commands/list.js +0 -38
  66. package/dist/commands/login.d.ts +0 -10
  67. package/dist/commands/login.js +0 -98
  68. package/dist/commands/markets.d.ts +0 -10
  69. package/dist/commands/markets.js +0 -39
  70. package/dist/commands/milestones.d.ts +0 -8
  71. package/dist/commands/milestones.js +0 -56
  72. package/dist/commands/orders.d.ts +0 -4
  73. package/dist/commands/orders.js +0 -28
  74. package/dist/commands/performance.d.ts +0 -11
  75. package/dist/commands/performance.js +0 -250
  76. package/dist/commands/positions.d.ts +0 -19
  77. package/dist/commands/positions.js +0 -294
  78. package/dist/commands/prompt.d.ts +0 -13
  79. package/dist/commands/prompt.js +0 -35
  80. package/dist/commands/publish.d.ts +0 -15
  81. package/dist/commands/publish.js +0 -39
  82. package/dist/commands/query.d.ts +0 -15
  83. package/dist/commands/query.js +0 -132
  84. package/dist/commands/rfq.d.ts +0 -5
  85. package/dist/commands/rfq.js +0 -35
  86. package/dist/commands/scan.d.ts +0 -11
  87. package/dist/commands/scan.js +0 -230
  88. package/dist/commands/schedule.d.ts +0 -3
  89. package/dist/commands/schedule.js +0 -38
  90. package/dist/commands/settlements.d.ts +0 -6
  91. package/dist/commands/settlements.js +0 -50
  92. package/dist/commands/setup.d.ts +0 -24
  93. package/dist/commands/setup.js +0 -700
  94. package/dist/commands/signal.d.ts +0 -6
  95. package/dist/commands/signal.js +0 -32
  96. package/dist/commands/strategies.d.ts +0 -11
  97. package/dist/commands/strategies.js +0 -130
  98. package/dist/commands/telegram.d.ts +0 -15
  99. package/dist/commands/telegram.js +0 -125
  100. package/dist/commands/trade.d.ts +0 -12
  101. package/dist/commands/trade.js +0 -112
  102. package/dist/commands/watch.d.ts +0 -19
  103. package/dist/commands/watch.js +0 -157
  104. package/dist/commands/whatif.d.ts +0 -17
  105. package/dist/commands/whatif.js +0 -209
  106. package/dist/commands/x.d.ts +0 -28
  107. package/dist/commands/x.js +0 -167
  108. package/dist/config.d.ts +0 -55
  109. package/dist/config.js +0 -139
  110. package/dist/config.test.d.ts +0 -1
  111. package/dist/config.test.js +0 -138
  112. package/dist/index.d.ts +0 -20
  113. package/dist/kalshi.d.ts +0 -144
  114. package/dist/kalshi.js +0 -498
  115. package/dist/polymarket.d.ts +0 -237
  116. package/dist/polymarket.js +0 -353
  117. package/dist/polymarket.test.d.ts +0 -1
  118. package/dist/polymarket.test.js +0 -424
  119. package/dist/share.d.ts +0 -4
  120. package/dist/share.js +0 -27
  121. package/dist/skills/loader.d.ts +0 -19
  122. package/dist/skills/loader.js +0 -86
  123. package/dist/telegram/agent-bridge.d.ts +0 -15
  124. package/dist/telegram/agent-bridge.js +0 -573
  125. package/dist/telegram/bot.d.ts +0 -10
  126. package/dist/telegram/bot.js +0 -297
  127. package/dist/telegram/commands.d.ts +0 -11
  128. package/dist/telegram/commands.js +0 -120
  129. package/dist/telegram/format.d.ts +0 -11
  130. package/dist/telegram/format.js +0 -51
  131. package/dist/telegram/format.test.d.ts +0 -1
  132. package/dist/telegram/format.test.js +0 -73
  133. package/dist/telegram/poller.d.ts +0 -6
  134. package/dist/telegram/poller.js +0 -32
  135. package/dist/topics.d.ts +0 -17
  136. package/dist/topics.js +0 -102
  137. package/dist/topics.test.d.ts +0 -1
  138. package/dist/topics.test.js +0 -131
  139. package/dist/tui/border.d.ts +0 -33
  140. package/dist/tui/border.js +0 -87
  141. package/dist/tui/chart.d.ts +0 -19
  142. package/dist/tui/chart.js +0 -117
  143. package/dist/tui/dashboard.d.ts +0 -9
  144. package/dist/tui/dashboard.js +0 -814
  145. package/dist/tui/layout.d.ts +0 -16
  146. package/dist/tui/layout.js +0 -41
  147. package/dist/tui/screen.d.ts +0 -33
  148. package/dist/tui/screen.js +0 -102
  149. package/dist/tui/state.d.ts +0 -40
  150. package/dist/tui/state.js +0 -36
  151. package/dist/tui/widgets/commandbar.d.ts +0 -8
  152. package/dist/tui/widgets/commandbar.js +0 -82
  153. package/dist/tui/widgets/detail.d.ts +0 -9
  154. package/dist/tui/widgets/detail.js +0 -151
  155. package/dist/tui/widgets/edges.d.ts +0 -4
  156. package/dist/tui/widgets/edges.js +0 -34
  157. package/dist/tui/widgets/liquidity.d.ts +0 -9
  158. package/dist/tui/widgets/liquidity.js +0 -142
  159. package/dist/tui/widgets/orders.d.ts +0 -4
  160. package/dist/tui/widgets/orders.js +0 -37
  161. package/dist/tui/widgets/portfolio.d.ts +0 -4
  162. package/dist/tui/widgets/portfolio.js +0 -59
  163. package/dist/tui/widgets/signals.d.ts +0 -4
  164. package/dist/tui/widgets/signals.js +0 -31
  165. package/dist/tui/widgets/statusbar.d.ts +0 -8
  166. package/dist/tui/widgets/statusbar.js +0 -72
  167. package/dist/tui/widgets/thesis.d.ts +0 -4
  168. package/dist/tui/widgets/thesis.js +0 -66
  169. package/dist/tui/widgets/trade.d.ts +0 -9
  170. package/dist/tui/widgets/trade.js +0 -117
  171. package/dist/tui/widgets/upcoming.d.ts +0 -4
  172. package/dist/tui/widgets/upcoming.js +0 -41
  173. package/dist/tui/widgets/whatif.d.ts +0 -7
  174. package/dist/tui/widgets/whatif.js +0 -113
  175. package/dist/types/output.d.ts +0 -412
  176. package/dist/types/output.js +0 -9
  177. package/dist/utils.d.ts +0 -52
  178. package/dist/utils.js +0 -146
  179. package/dist/utils.test.d.ts +0 -1
  180. package/dist/utils.test.js +0 -111
@@ -1,297 +0,0 @@
1
- "use strict";
2
- /**
3
- * Telegram Bot — main entry point
4
- *
5
- * Handles slash commands (zero LLM) and free text (via pi-agent-core).
6
- * Polls delta API for push notifications.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.startBot = startBot;
10
- const grammy_1 = require("grammy");
11
- const client_js_1 = require("../client.js");
12
- const config_js_1 = require("../config.js");
13
- const format_js_1 = require("./format.js");
14
- const commands_js_1 = require("./commands.js");
15
- const poller_js_1 = require("./poller.js");
16
- const sessions = new Map();
17
- function getSession(chatId) {
18
- if (!sessions.has(chatId)) {
19
- sessions.set(chatId, { thesisId: null, agentMessages: [] });
20
- }
21
- return sessions.get(chatId);
22
- }
23
- async function startBot(opts) {
24
- const config = (0, config_js_1.loadConfig)();
25
- const botToken = opts.token || config.telegramBotToken || process.env.TELEGRAM_BOT_TOKEN;
26
- if (!botToken) {
27
- console.error('No Telegram bot token. Use --token, set TELEGRAM_BOT_TOKEN, or add to ~/.sf/config.json');
28
- process.exit(1);
29
- }
30
- const client = new client_js_1.SFClient(config.apiKey, config.apiUrl);
31
- const bot = new grammy_1.Bot(botToken);
32
- const allowedChat = opts.chatId || null;
33
- const pollers = new Map();
34
- // Auth guard
35
- function isAllowed(chatId) {
36
- return !allowedChat || chatId === allowedChat;
37
- }
38
- // ── /start ──
39
- bot.command('start', async (ctx) => {
40
- if (!isAllowed(ctx.chat.id))
41
- return;
42
- const session = getSession(ctx.chat.id);
43
- // Auto-detect first active thesis
44
- try {
45
- const { theses } = await client.listTheses();
46
- const active = (theses || []).filter((t) => t.status === 'active');
47
- if (active.length > 0) {
48
- session.thesisId = active[0].id;
49
- await ctx.reply(`✅ Connected to SimpleFunctions\n\n` +
50
- `Active thesis: <b>${(active[0].rawThesis || '').slice(0, 60)}</b>\n` +
51
- `ID: <code>${active[0].id.slice(0, 8)}</code>\n\n` +
52
- `Commands:\n` +
53
- `/context — thesis snapshot\n` +
54
- `/positions — Kalshi positions\n` +
55
- `/edges — top edges\n` +
56
- `/balance — account balance\n` +
57
- `/orders — resting orders\n` +
58
- `/eval — trigger evaluation\n` +
59
- `/list — all theses\n` +
60
- `/switch — switch thesis\n\n` +
61
- `Or just type naturally.`, { parse_mode: 'HTML' });
62
- // Start polling for this chat
63
- if (pollers.has(ctx.chat.id))
64
- clearInterval(pollers.get(ctx.chat.id));
65
- pollers.set(ctx.chat.id, (0, poller_js_1.startPoller)(bot, ctx.chat.id, client, session.thesisId));
66
- }
67
- else {
68
- await ctx.reply('No active theses found. Create one with `sf create "your thesis"`');
69
- }
70
- }
71
- catch (err) {
72
- await ctx.reply(`❌ Connection failed: ${err.message}\nCheck SF_API_KEY in ~/.sf/config.json`);
73
- }
74
- });
75
- // ── /context ──
76
- bot.command('context', async (ctx) => {
77
- if (!isAllowed(ctx.chat.id))
78
- return;
79
- const session = getSession(ctx.chat.id);
80
- if (!session.thesisId) {
81
- await ctx.reply('No thesis selected. Use /start or /switch <id>');
82
- return;
83
- }
84
- try {
85
- const text = await (0, commands_js_1.handleContext)(client, session.thesisId);
86
- for (const chunk of (0, format_js_1.splitMessage)(text))
87
- await ctx.reply(chunk, { parse_mode: 'HTML' });
88
- }
89
- catch (err) {
90
- await ctx.reply(`❌ ${err.message}`);
91
- }
92
- });
93
- // ── /positions ──
94
- bot.command('positions', async (ctx) => {
95
- if (!isAllowed(ctx.chat.id))
96
- return;
97
- try {
98
- const text = await (0, commands_js_1.handlePositions)();
99
- for (const chunk of (0, format_js_1.splitMessage)(text))
100
- await ctx.reply(chunk, { parse_mode: 'HTML' });
101
- }
102
- catch (err) {
103
- await ctx.reply(`❌ ${err.message}`);
104
- }
105
- });
106
- // ── /edges ──
107
- bot.command('edges', async (ctx) => {
108
- if (!isAllowed(ctx.chat.id))
109
- return;
110
- try {
111
- const text = await (0, commands_js_1.handleEdges)(client);
112
- for (const chunk of (0, format_js_1.splitMessage)(text))
113
- await ctx.reply(chunk, { parse_mode: 'HTML' });
114
- }
115
- catch (err) {
116
- await ctx.reply(`❌ ${err.message}`);
117
- }
118
- });
119
- // ── /balance ──
120
- bot.command('balance', async (ctx) => {
121
- if (!isAllowed(ctx.chat.id))
122
- return;
123
- try {
124
- const text = await (0, commands_js_1.handleBalance)();
125
- await ctx.reply(text, { parse_mode: 'HTML' });
126
- }
127
- catch (err) {
128
- await ctx.reply(`❌ ${err.message}`);
129
- }
130
- });
131
- // ── /orders ──
132
- bot.command('orders', async (ctx) => {
133
- if (!isAllowed(ctx.chat.id))
134
- return;
135
- try {
136
- const text = await (0, commands_js_1.handleOrders)();
137
- for (const chunk of (0, format_js_1.splitMessage)(text))
138
- await ctx.reply(chunk, { parse_mode: 'HTML' });
139
- }
140
- catch (err) {
141
- await ctx.reply(`❌ ${err.message}`);
142
- }
143
- });
144
- // ── /eval ──
145
- bot.command('eval', async (ctx) => {
146
- if (!isAllowed(ctx.chat.id))
147
- return;
148
- const session = getSession(ctx.chat.id);
149
- if (!session.thesisId) {
150
- await ctx.reply('No thesis selected.');
151
- return;
152
- }
153
- try {
154
- const text = await (0, commands_js_1.handleEval)(client, session.thesisId);
155
- await ctx.reply(text);
156
- }
157
- catch (err) {
158
- await ctx.reply(`❌ ${err.message}`);
159
- }
160
- });
161
- // ── /list ──
162
- bot.command('list', async (ctx) => {
163
- if (!isAllowed(ctx.chat.id))
164
- return;
165
- try {
166
- const text = await (0, commands_js_1.handleList)(client);
167
- for (const chunk of (0, format_js_1.splitMessage)(text))
168
- await ctx.reply(chunk, { parse_mode: 'HTML' });
169
- }
170
- catch (err) {
171
- await ctx.reply(`❌ ${err.message}`);
172
- }
173
- });
174
- // ── /switch <id> ──
175
- bot.command('switch', async (ctx) => {
176
- if (!isAllowed(ctx.chat.id))
177
- return;
178
- const session = getSession(ctx.chat.id);
179
- const newId = ctx.match?.trim();
180
- if (!newId) {
181
- await ctx.reply('Usage: /switch <thesis_id>');
182
- return;
183
- }
184
- // Find matching thesis (prefix match)
185
- try {
186
- const { theses } = await client.listTheses();
187
- const match = (theses || []).find((t) => t.id.startsWith(newId));
188
- if (match) {
189
- session.thesisId = match.id;
190
- session.agentMessages = []; // reset conversation
191
- // Restart poller
192
- if (pollers.has(ctx.chat.id))
193
- clearInterval(pollers.get(ctx.chat.id));
194
- pollers.set(ctx.chat.id, (0, poller_js_1.startPoller)(bot, ctx.chat.id, client, session.thesisId));
195
- await ctx.reply(`Switched to: <code>${match.id.slice(0, 8)}</code> — ${(match.rawThesis || '').slice(0, 60)}`, { parse_mode: 'HTML' });
196
- }
197
- else {
198
- await ctx.reply(`No thesis found matching "${newId}". Use /list to see all.`);
199
- }
200
- }
201
- catch (err) {
202
- await ctx.reply(`❌ ${err.message}`);
203
- }
204
- });
205
- // ── Free text → LLM agent ──
206
- bot.on('message:text', async (ctx) => {
207
- if (!isAllowed(ctx.chat.id))
208
- return;
209
- const session = getSession(ctx.chat.id);
210
- const userMessage = ctx.message.text;
211
- if (userMessage.startsWith('/'))
212
- return; // skip unhandled commands
213
- // Auto-detect thesis if not set
214
- if (!session.thesisId) {
215
- try {
216
- const { theses } = await client.listTheses();
217
- const active = (theses || []).filter((t) => t.status === 'active');
218
- if (active.length > 0) {
219
- session.thesisId = active[0].id;
220
- }
221
- else {
222
- await ctx.reply('No active theses. Create one with `sf create "your thesis"` then /start');
223
- return;
224
- }
225
- }
226
- catch {
227
- await ctx.reply('Could not connect. Use /start first.');
228
- return;
229
- }
230
- }
231
- try {
232
- // Show "typing" indicator
233
- await ctx.replyWithChatAction('typing');
234
- // Lazy-load agent module (heavy imports)
235
- const { runAgentMessage } = await import('./agent-bridge.js');
236
- // Timeout after 30 seconds
237
- const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Response timeout (30s)')), 30_000));
238
- const response = await Promise.race([
239
- runAgentMessage(client, session, userMessage),
240
- timeout,
241
- ]);
242
- if (!response || response.trim().length === 0) {
243
- await ctx.reply('No response generated. Try rephrasing or use a slash command.');
244
- }
245
- else {
246
- for (const chunk of (0, format_js_1.splitMessage)(response)) {
247
- // Use plain text if HTML parsing might fail
248
- try {
249
- await ctx.reply(chunk, { parse_mode: 'HTML' });
250
- }
251
- catch {
252
- await ctx.reply(chunk); // fallback to plain text
253
- }
254
- }
255
- }
256
- }
257
- catch (err) {
258
- console.error('[Telegram] Agent error:', err.message);
259
- await ctx.reply(`❌ ${err.message}`);
260
- }
261
- });
262
- // ── Callback queries (for inline keyboard confirmations) ──
263
- bot.on('callback_query:data', async (ctx) => {
264
- const data = ctx.callbackQuery.data;
265
- if (data.startsWith('order_confirm:')) {
266
- // TODO: execute pending order
267
- await ctx.answerCallbackQuery({ text: 'Order execution coming soon' });
268
- }
269
- else if (data === 'order_cancel') {
270
- await ctx.answerCallbackQuery({ text: 'Order cancelled' });
271
- await ctx.editMessageText('❌ Order cancelled.');
272
- }
273
- });
274
- // ── Start bot ──
275
- console.log('🤖 SimpleFunctions Telegram bot starting...');
276
- console.log(` SF API: ${config.apiKey ? '✓' : '✗'}`);
277
- console.log(` Kalshi: ${process.env.KALSHI_API_KEY_ID ? '✓' : '✗'}`);
278
- console.log(` OpenRouter: ${config.openrouterKey ? '✓' : '✗'}`);
279
- if (allowedChat)
280
- console.log(` Restricted to chat: ${allowedChat}`);
281
- console.log(' Press Ctrl+C to stop.\n');
282
- // Prevent crashes from killing the daemon
283
- process.on('uncaughtException', (err) => {
284
- console.error('[Telegram] Uncaught exception:', err.message);
285
- });
286
- process.on('unhandledRejection', (err) => {
287
- console.error('[Telegram] Unhandled rejection:', err?.message || err);
288
- });
289
- // Cleanup on exit
290
- process.on('SIGINT', () => {
291
- for (const timer of pollers.values())
292
- clearInterval(timer);
293
- bot.stop();
294
- process.exit(0);
295
- });
296
- await bot.start();
297
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * Telegram slash command handlers — direct API calls, zero LLM cost
3
- */
4
- import { SFClient } from '../client.js';
5
- export declare function handleContext(client: SFClient, thesisId: string): Promise<string>;
6
- export declare function handlePositions(): Promise<string>;
7
- export declare function handleEdges(client: SFClient): Promise<string>;
8
- export declare function handleBalance(): Promise<string>;
9
- export declare function handleOrders(): Promise<string>;
10
- export declare function handleEval(client: SFClient, thesisId: string): Promise<string>;
11
- export declare function handleList(client: SFClient): Promise<string>;
@@ -1,120 +0,0 @@
1
- "use strict";
2
- /**
3
- * Telegram slash command handlers — direct API calls, zero LLM cost
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handleContext = handleContext;
7
- exports.handlePositions = handlePositions;
8
- exports.handleEdges = handleEdges;
9
- exports.handleBalance = handleBalance;
10
- exports.handleOrders = handleOrders;
11
- exports.handleEval = handleEval;
12
- exports.handleList = handleList;
13
- const kalshi_js_1 = require("../kalshi.js");
14
- const format_js_1 = require("./format.js");
15
- async function handleContext(client, thesisId) {
16
- const ctx = await client.getContext(thesisId);
17
- const conf = typeof ctx.confidence === 'number' ? Math.round(ctx.confidence * 100) : '?';
18
- const thesis = ctx.thesis || ctx.rawThesis || 'N/A';
19
- let text = `📋 <b>${(0, format_js_1.escapeHtml)(thesis.slice(0, 80))}</b>\n`;
20
- text += `Confidence: <b>${conf}%</b> | Status: ${ctx.status}\n\n`;
21
- const edges = (ctx.edges || []).slice(0, 8);
22
- if (edges.length > 0) {
23
- text += `<b>Top Edges:</b>\n<pre>`;
24
- for (const e of edges) {
25
- const name = (e.market || e.marketTitle || '???').slice(0, 35);
26
- text += `${name.padEnd(36)} ${String(e.edge || 0).padStart(3)}¢ ${e.direction || 'yes'}\n`;
27
- }
28
- text += `</pre>`;
29
- }
30
- return text;
31
- }
32
- async function handlePositions() {
33
- if (!(0, kalshi_js_1.isKalshiConfigured)())
34
- return '⚠️ Kalshi not configured. Run <code>sf setup --kalshi</code>';
35
- const positions = await (0, kalshi_js_1.getPositions)();
36
- if (!positions || positions.length === 0)
37
- return 'No open positions.';
38
- // Enrich with live prices
39
- let totalPnlCents = 0;
40
- const lines = [];
41
- for (const pos of positions) {
42
- const livePrice = await (0, kalshi_js_1.getMarketPrice)(pos.ticker);
43
- const current = livePrice ?? pos.average_price_paid;
44
- const pnlCents = (current - pos.average_price_paid) * pos.quantity;
45
- totalPnlCents += pnlCents;
46
- const pnl = (0, format_js_1.fmtDollar)(pnlCents);
47
- lines.push(`${pos.ticker.slice(0, 28).padEnd(29)} ${String(pos.quantity).padStart(5)} ${String(pos.average_price_paid).padStart(3)}¢→${String(current).padStart(3)}¢ ${pnl}`);
48
- }
49
- let text = `📊 <b>Positions</b>\n<pre>`;
50
- text += lines.join('\n');
51
- text += `\n${'─'.repeat(50)}\nTotal P&L: ${(0, format_js_1.fmtDollar)(totalPnlCents)}`;
52
- text += `</pre>`;
53
- return text;
54
- }
55
- async function handleEdges(client) {
56
- const { theses } = await client.listTheses();
57
- const active = (theses || []).filter((t) => t.status === 'active');
58
- const results = await Promise.allSettled(active.map(async (t) => {
59
- const ctx = await client.getContext(t.id);
60
- return (ctx.edges || []).map((e) => ({ ...e, thesisId: t.id }));
61
- }));
62
- const allEdges = [];
63
- for (const r of results) {
64
- if (r.status === 'fulfilled')
65
- allEdges.push(...r.value);
66
- }
67
- allEdges.sort((a, b) => Math.abs(b.edge || 0) - Math.abs(a.edge || 0));
68
- const top = allEdges.slice(0, 10);
69
- if (top.length === 0)
70
- return 'No edges found.';
71
- let text = `📈 <b>Top Edges</b>\n<pre>`;
72
- for (const e of top) {
73
- const name = (e.market || '???').slice(0, 35);
74
- const liq = e.orderbook?.liquidityScore || '?';
75
- text += `${name.padEnd(36)} +${String(e.edge || 0).padStart(2)}¢ ${liq}\n`;
76
- }
77
- text += `</pre>`;
78
- return text;
79
- }
80
- async function handleBalance() {
81
- if (!(0, kalshi_js_1.isKalshiConfigured)())
82
- return '⚠️ Kalshi not configured.';
83
- const bal = await (0, kalshi_js_1.getBalance)();
84
- if (!bal)
85
- return '⚠️ Failed to fetch balance.';
86
- return `💰 Balance: <b>$${bal.balance.toFixed(2)}</b> | Portfolio: <b>$${bal.portfolioValue.toFixed(2)}</b>`;
87
- }
88
- async function handleOrders() {
89
- if (!(0, kalshi_js_1.isKalshiConfigured)())
90
- return '⚠️ Kalshi not configured.';
91
- const result = await (0, kalshi_js_1.getOrders)({ status: 'resting', limit: 20 });
92
- if (!result || !result.orders || result.orders.length === 0)
93
- return 'No resting orders.';
94
- let text = `📋 <b>Resting Orders</b>\n<pre>`;
95
- for (const o of result.orders) {
96
- const qty = Math.round(parseFloat(o.remaining_count_fp || '0'));
97
- const price = Math.round(parseFloat(o.yes_price_dollars || '0') * 100);
98
- const ticker = (o.ticker || '???').slice(0, 25);
99
- text += `${o.action} ${qty}x ${ticker} @ ${price}¢\n`;
100
- }
101
- text += `</pre>`;
102
- return text;
103
- }
104
- async function handleEval(client, thesisId) {
105
- await client.evaluate(thesisId);
106
- return '⚡ Evaluation triggered. Results in ~2 minutes.';
107
- }
108
- async function handleList(client) {
109
- const { theses } = await client.listTheses();
110
- if (!theses || theses.length === 0)
111
- return 'No theses.';
112
- let text = `📝 <b>Theses</b>\n<pre>`;
113
- for (const t of theses) {
114
- const conf = typeof t.confidence === 'number' ? Math.round(t.confidence * 100) : 0;
115
- const title = (t.rawThesis || t.thesis || '').slice(0, 50);
116
- text += `${t.id.slice(0, 8)} ${String(conf).padStart(3)}% ${t.status.padEnd(8)} ${title}\n`;
117
- }
118
- text += `</pre>`;
119
- return text;
120
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * Telegram message formatting utilities
3
- */
4
- /** Split long text into chunks under Telegram's 4096 char limit */
5
- export declare function splitMessage(text: string, maxLen?: number): string[];
6
- /** Format a number as dollars */
7
- export declare function fmtDollar(cents: number): string;
8
- /** Simple sparkline */
9
- export declare function sparkline(values: number[]): string;
10
- /** Escape HTML special characters for Telegram HTML parse mode */
11
- export declare function escapeHtml(s: string): string;
@@ -1,51 +0,0 @@
1
- "use strict";
2
- /**
3
- * Telegram message formatting utilities
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.splitMessage = splitMessage;
7
- exports.fmtDollar = fmtDollar;
8
- exports.sparkline = sparkline;
9
- exports.escapeHtml = escapeHtml;
10
- /** Split long text into chunks under Telegram's 4096 char limit */
11
- function splitMessage(text, maxLen = 4000) {
12
- if (text.length <= maxLen)
13
- return [text];
14
- const chunks = [];
15
- let remaining = text;
16
- while (remaining.length > 0) {
17
- if (remaining.length <= maxLen) {
18
- chunks.push(remaining);
19
- break;
20
- }
21
- // Find a good split point (newline near maxLen)
22
- let splitAt = remaining.lastIndexOf('\n', maxLen);
23
- if (splitAt < maxLen * 0.5)
24
- splitAt = maxLen;
25
- chunks.push(remaining.slice(0, splitAt));
26
- remaining = remaining.slice(splitAt);
27
- }
28
- return chunks;
29
- }
30
- /** Format a number as dollars */
31
- function fmtDollar(cents) {
32
- const d = cents / 100;
33
- return d >= 0 ? `+$${d.toFixed(2)}` : `-$${Math.abs(d).toFixed(2)}`;
34
- }
35
- /** Simple sparkline */
36
- function sparkline(values) {
37
- if (values.length === 0)
38
- return '';
39
- const min = Math.min(...values);
40
- const max = Math.max(...values);
41
- const range = max - min || 1;
42
- const blocks = '▁▂▃▄▅▆▇█';
43
- return values.map(v => {
44
- const idx = Math.round(((v - min) / range) * 7);
45
- return blocks[idx];
46
- }).join('');
47
- }
48
- /** Escape HTML special characters for Telegram HTML parse mode */
49
- function escapeHtml(s) {
50
- return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
51
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,73 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const format_js_1 = require("./format.js");
5
- (0, vitest_1.describe)('splitMessage', () => {
6
- (0, vitest_1.it)('returns single chunk for short text', () => {
7
- (0, vitest_1.expect)((0, format_js_1.splitMessage)('short text')).toEqual(['short text']);
8
- });
9
- (0, vitest_1.it)('splits long text at newlines', () => {
10
- const lines = Array.from({ length: 50 }, (_, i) => `Line ${i}`).join('\n');
11
- const chunks = (0, format_js_1.splitMessage)(lines, 100);
12
- for (const chunk of chunks) {
13
- (0, vitest_1.expect)(chunk.length).toBeLessThanOrEqual(100);
14
- }
15
- (0, vitest_1.expect)(chunks.join('')).toBe(lines);
16
- });
17
- (0, vitest_1.it)('splits at maxLen when no good newline found', () => {
18
- const long = 'x'.repeat(200);
19
- const chunks = (0, format_js_1.splitMessage)(long, 100);
20
- (0, vitest_1.expect)(chunks.length).toBe(2);
21
- (0, vitest_1.expect)(chunks[0].length).toBe(100);
22
- });
23
- (0, vitest_1.it)('handles empty string', () => {
24
- (0, vitest_1.expect)((0, format_js_1.splitMessage)('')).toEqual(['']);
25
- });
26
- });
27
- (0, vitest_1.describe)('fmtDollar', () => {
28
- (0, vitest_1.it)('formats positive cents', () => {
29
- (0, vitest_1.expect)((0, format_js_1.fmtDollar)(550)).toBe('+$5.50');
30
- });
31
- (0, vitest_1.it)('formats negative cents', () => {
32
- (0, vitest_1.expect)((0, format_js_1.fmtDollar)(-200)).toBe('-$2.00');
33
- });
34
- (0, vitest_1.it)('formats zero', () => {
35
- (0, vitest_1.expect)((0, format_js_1.fmtDollar)(0)).toBe('+$0.00');
36
- });
37
- });
38
- (0, vitest_1.describe)('sparkline', () => {
39
- (0, vitest_1.it)('generates sparkline of correct length', () => {
40
- const result = (0, format_js_1.sparkline)([1, 5, 3, 8, 2]);
41
- (0, vitest_1.expect)(result.length).toBe(5);
42
- });
43
- (0, vitest_1.it)('returns empty for empty array', () => {
44
- (0, vitest_1.expect)((0, format_js_1.sparkline)([])).toBe('');
45
- });
46
- (0, vitest_1.it)('handles single value', () => {
47
- const result = (0, format_js_1.sparkline)([5]);
48
- (0, vitest_1.expect)(result.length).toBe(1);
49
- });
50
- (0, vitest_1.it)('handles equal values', () => {
51
- const result = (0, format_js_1.sparkline)([3, 3, 3]);
52
- (0, vitest_1.expect)(result.length).toBe(3);
53
- });
54
- (0, vitest_1.it)('uses correct block chars for min/max', () => {
55
- const result = (0, format_js_1.sparkline)([0, 100]);
56
- (0, vitest_1.expect)(result[0]).toBe('▁');
57
- (0, vitest_1.expect)(result[1]).toBe('█');
58
- });
59
- });
60
- (0, vitest_1.describe)('escapeHtml', () => {
61
- (0, vitest_1.it)('escapes ampersands', () => {
62
- (0, vitest_1.expect)((0, format_js_1.escapeHtml)('a&b')).toBe('a&amp;b');
63
- });
64
- (0, vitest_1.it)('escapes angle brackets', () => {
65
- (0, vitest_1.expect)((0, format_js_1.escapeHtml)('<div>')).toBe('&lt;div&gt;');
66
- });
67
- (0, vitest_1.it)('escapes multiple characters', () => {
68
- (0, vitest_1.expect)((0, format_js_1.escapeHtml)('<script>alert("xss")&</script>')).toBe('&lt;script&gt;alert("xss")&amp;&lt;/script&gt;');
69
- });
70
- (0, vitest_1.it)('leaves safe strings unchanged', () => {
71
- (0, vitest_1.expect)((0, format_js_1.escapeHtml)('hello world')).toBe('hello world');
72
- });
73
- });
@@ -1,6 +0,0 @@
1
- /**
2
- * Delta API poller — push confidence changes to Telegram
3
- */
4
- import type { Bot } from 'grammy';
5
- import { SFClient } from '../client.js';
6
- export declare function startPoller(bot: Bot, chatId: number, client: SFClient, thesisId: string): NodeJS.Timeout;
@@ -1,32 +0,0 @@
1
- "use strict";
2
- /**
3
- * Delta API poller — push confidence changes to Telegram
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.startPoller = startPoller;
7
- const format_js_1 = require("./format.js");
8
- function startPoller(bot, chatId, client, thesisId) {
9
- let lastCheck = new Date().toISOString();
10
- return setInterval(async () => {
11
- try {
12
- const changes = await client.getChanges(thesisId, lastCheck);
13
- lastCheck = new Date().toISOString();
14
- if (!changes || !changes.changed)
15
- return;
16
- const delta = changes.confidenceDelta ?? changes.delta ?? 0;
17
- if (Math.abs(delta) < 0.02)
18
- return;
19
- const conf = typeof changes.confidence === 'number' ? Math.round(changes.confidence * 100) : '?';
20
- const arrow = delta > 0 ? '📈' : '📉';
21
- const sign = delta > 0 ? '+' : '';
22
- const summary = changes.summary || changes.latestSummary || '';
23
- let text = `${arrow} <b>Confidence ${sign}${Math.round(delta * 100)}% → ${conf}%</b>\n`;
24
- if (summary)
25
- text += `${(0, format_js_1.escapeHtml)(summary.slice(0, 200))}`;
26
- await bot.api.sendMessage(chatId, text, { parse_mode: 'HTML' });
27
- }
28
- catch {
29
- // Silent — polling errors should not crash the bot
30
- }
31
- }, 60_000); // every 60 seconds
32
- }
package/dist/topics.d.ts DELETED
@@ -1,17 +0,0 @@
1
- /**
2
- * Topic → Kalshi series mapping
3
- *
4
- * Shared between dashboard, liquidity scanner, and other commands
5
- * that need to categorize markets by topic.
6
- *
7
- * Sourced from Kalshi's top series by volume (non-sports).
8
- * Run `sf scan` to discover new series.
9
- */
10
- export declare const TOPIC_SERIES: Record<string, string[]>;
11
- /** Map a series prefix to a human-readable category name (for dashboard display) */
12
- export declare const RISK_CATEGORIES: Record<string, string>;
13
- /**
14
- * Given a ticker string, return the topic name (uppercased).
15
- * Matches longest prefix first to avoid ambiguity (e.g. KXWTIMAX before KXWTI).
16
- */
17
- export declare function tickerToTopic(ticker: string): string;