@spfunctions/cli 1.7.39 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/101.index.js +1 -451
- package/dist/12.index.js +1 -536
- package/dist/160.index.js +1 -709
- package/dist/174.index.js +1 -44
- package/dist/278.index.js +2 -15129
- package/dist/582.index.js +1 -1066
- package/dist/641.index.js +67 -122146
- package/dist/669.index.js +1 -556
- package/dist/722.index.js +1 -843
- package/dist/788.index.js +1 -6677
- package/dist/816.index.js +4 -7496
- package/dist/830.index.js +1 -1199
- package/dist/921.index.js +1 -10683
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/package.json +0 -52
package/dist/12.index.js
CHANGED
|
@@ -1,536 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
exports.id = 12;
|
|
3
|
-
exports.ids = [12];
|
|
4
|
-
exports.modules = {
|
|
5
|
-
|
|
6
|
-
/***/ 6012:
|
|
7
|
-
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
8
|
-
|
|
9
|
-
var __webpack_unused_export__;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Telegram Bot — main entry point
|
|
13
|
-
*
|
|
14
|
-
* Handles slash commands (zero LLM) and free text (via pi-agent-core).
|
|
15
|
-
* Polls delta API for push notifications.
|
|
16
|
-
*/
|
|
17
|
-
__webpack_unused_export__ = ({ value: true });
|
|
18
|
-
exports.startBot = startBot;
|
|
19
|
-
const grammy_1 = __webpack_require__(53278);
|
|
20
|
-
const client_js_1 = __webpack_require__(19218);
|
|
21
|
-
const config_js_1 = __webpack_require__(11627);
|
|
22
|
-
const format_js_1 = __webpack_require__(76342);
|
|
23
|
-
const commands_js_1 = __webpack_require__(77499);
|
|
24
|
-
const poller_js_1 = __webpack_require__(55875);
|
|
25
|
-
const sessions = new Map();
|
|
26
|
-
function getSession(chatId) {
|
|
27
|
-
if (!sessions.has(chatId)) {
|
|
28
|
-
sessions.set(chatId, { thesisId: null, agentMessages: [] });
|
|
29
|
-
}
|
|
30
|
-
return sessions.get(chatId);
|
|
31
|
-
}
|
|
32
|
-
async function startBot(opts) {
|
|
33
|
-
const config = (0, config_js_1.loadConfig)();
|
|
34
|
-
const botToken = opts.token || config.telegramBotToken || process.env.TELEGRAM_BOT_TOKEN;
|
|
35
|
-
if (!botToken) {
|
|
36
|
-
console.error('No Telegram bot token. Use --token, set TELEGRAM_BOT_TOKEN, or add to ~/.sf/config.json');
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
const client = new client_js_1.SFClient(config.apiKey, config.apiUrl);
|
|
40
|
-
const bot = new grammy_1.Bot(botToken);
|
|
41
|
-
const allowedChat = opts.chatId || null;
|
|
42
|
-
const pollers = new Map();
|
|
43
|
-
// Auth guard
|
|
44
|
-
function isAllowed(chatId) {
|
|
45
|
-
return !allowedChat || chatId === allowedChat;
|
|
46
|
-
}
|
|
47
|
-
// ── /start ──
|
|
48
|
-
bot.command('start', async (ctx) => {
|
|
49
|
-
if (!isAllowed(ctx.chat.id))
|
|
50
|
-
return;
|
|
51
|
-
const session = getSession(ctx.chat.id);
|
|
52
|
-
// Auto-detect first active thesis
|
|
53
|
-
try {
|
|
54
|
-
const { theses } = await client.listTheses();
|
|
55
|
-
const active = (theses || []).filter((t) => t.status === 'active');
|
|
56
|
-
if (active.length > 0) {
|
|
57
|
-
session.thesisId = active[0].id;
|
|
58
|
-
await ctx.reply(`✅ Connected to SimpleFunctions\n\n` +
|
|
59
|
-
`Active thesis: <b>${(active[0].rawThesis || '').slice(0, 60)}</b>\n` +
|
|
60
|
-
`ID: <code>${active[0].id.slice(0, 8)}</code>\n\n` +
|
|
61
|
-
`Commands:\n` +
|
|
62
|
-
`/context — thesis snapshot\n` +
|
|
63
|
-
`/positions — Kalshi positions\n` +
|
|
64
|
-
`/edges — top edges\n` +
|
|
65
|
-
`/balance — account balance\n` +
|
|
66
|
-
`/orders — resting orders\n` +
|
|
67
|
-
`/eval — trigger evaluation\n` +
|
|
68
|
-
`/list — all theses\n` +
|
|
69
|
-
`/switch — switch thesis\n\n` +
|
|
70
|
-
`Or just type naturally.`, { parse_mode: 'HTML' });
|
|
71
|
-
// Start polling for this chat
|
|
72
|
-
if (pollers.has(ctx.chat.id))
|
|
73
|
-
clearInterval(pollers.get(ctx.chat.id));
|
|
74
|
-
pollers.set(ctx.chat.id, (0, poller_js_1.startPoller)(bot, ctx.chat.id, client, session.thesisId));
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
await ctx.reply('No active theses found. Create one with `sf create "your thesis"`');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch (err) {
|
|
81
|
-
await ctx.reply(`❌ Connection failed: ${err.message}\nCheck SF_API_KEY in ~/.sf/config.json`);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
// ── /context ──
|
|
85
|
-
bot.command('context', async (ctx) => {
|
|
86
|
-
if (!isAllowed(ctx.chat.id))
|
|
87
|
-
return;
|
|
88
|
-
const session = getSession(ctx.chat.id);
|
|
89
|
-
if (!session.thesisId) {
|
|
90
|
-
await ctx.reply('No thesis selected. Use /start or /switch <id>');
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const text = await (0, commands_js_1.handleContext)(client, session.thesisId);
|
|
95
|
-
for (const chunk of (0, format_js_1.splitMessage)(text))
|
|
96
|
-
await ctx.reply(chunk, { parse_mode: 'HTML' });
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
// ── /positions ──
|
|
103
|
-
bot.command('positions', async (ctx) => {
|
|
104
|
-
if (!isAllowed(ctx.chat.id))
|
|
105
|
-
return;
|
|
106
|
-
try {
|
|
107
|
-
const text = await (0, commands_js_1.handlePositions)();
|
|
108
|
-
for (const chunk of (0, format_js_1.splitMessage)(text))
|
|
109
|
-
await ctx.reply(chunk, { parse_mode: 'HTML' });
|
|
110
|
-
}
|
|
111
|
-
catch (err) {
|
|
112
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
// ── /edges ──
|
|
116
|
-
bot.command('edges', async (ctx) => {
|
|
117
|
-
if (!isAllowed(ctx.chat.id))
|
|
118
|
-
return;
|
|
119
|
-
try {
|
|
120
|
-
const text = await (0, commands_js_1.handleEdges)(client);
|
|
121
|
-
for (const chunk of (0, format_js_1.splitMessage)(text))
|
|
122
|
-
await ctx.reply(chunk, { parse_mode: 'HTML' });
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
// ── /balance ──
|
|
129
|
-
bot.command('balance', async (ctx) => {
|
|
130
|
-
if (!isAllowed(ctx.chat.id))
|
|
131
|
-
return;
|
|
132
|
-
try {
|
|
133
|
-
const text = await (0, commands_js_1.handleBalance)();
|
|
134
|
-
await ctx.reply(text, { parse_mode: 'HTML' });
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
// ── /orders ──
|
|
141
|
-
bot.command('orders', async (ctx) => {
|
|
142
|
-
if (!isAllowed(ctx.chat.id))
|
|
143
|
-
return;
|
|
144
|
-
try {
|
|
145
|
-
const text = await (0, commands_js_1.handleOrders)();
|
|
146
|
-
for (const chunk of (0, format_js_1.splitMessage)(text))
|
|
147
|
-
await ctx.reply(chunk, { parse_mode: 'HTML' });
|
|
148
|
-
}
|
|
149
|
-
catch (err) {
|
|
150
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
// ── /eval ──
|
|
154
|
-
bot.command('eval', async (ctx) => {
|
|
155
|
-
if (!isAllowed(ctx.chat.id))
|
|
156
|
-
return;
|
|
157
|
-
const session = getSession(ctx.chat.id);
|
|
158
|
-
if (!session.thesisId) {
|
|
159
|
-
await ctx.reply('No thesis selected.');
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
try {
|
|
163
|
-
const text = await (0, commands_js_1.handleEval)(client, session.thesisId);
|
|
164
|
-
await ctx.reply(text);
|
|
165
|
-
}
|
|
166
|
-
catch (err) {
|
|
167
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
// ── /list ──
|
|
171
|
-
bot.command('list', async (ctx) => {
|
|
172
|
-
if (!isAllowed(ctx.chat.id))
|
|
173
|
-
return;
|
|
174
|
-
try {
|
|
175
|
-
const text = await (0, commands_js_1.handleList)(client);
|
|
176
|
-
for (const chunk of (0, format_js_1.splitMessage)(text))
|
|
177
|
-
await ctx.reply(chunk, { parse_mode: 'HTML' });
|
|
178
|
-
}
|
|
179
|
-
catch (err) {
|
|
180
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
// ── /switch <id> ──
|
|
184
|
-
bot.command('switch', async (ctx) => {
|
|
185
|
-
if (!isAllowed(ctx.chat.id))
|
|
186
|
-
return;
|
|
187
|
-
const session = getSession(ctx.chat.id);
|
|
188
|
-
const newId = ctx.match?.trim();
|
|
189
|
-
if (!newId) {
|
|
190
|
-
await ctx.reply('Usage: /switch <thesis_id>');
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
// Find matching thesis (prefix match)
|
|
194
|
-
try {
|
|
195
|
-
const { theses } = await client.listTheses();
|
|
196
|
-
const match = (theses || []).find((t) => t.id.startsWith(newId));
|
|
197
|
-
if (match) {
|
|
198
|
-
session.thesisId = match.id;
|
|
199
|
-
session.agentMessages = []; // reset conversation
|
|
200
|
-
// Restart poller
|
|
201
|
-
if (pollers.has(ctx.chat.id))
|
|
202
|
-
clearInterval(pollers.get(ctx.chat.id));
|
|
203
|
-
pollers.set(ctx.chat.id, (0, poller_js_1.startPoller)(bot, ctx.chat.id, client, session.thesisId));
|
|
204
|
-
await ctx.reply(`Switched to: <code>${match.id.slice(0, 8)}</code> — ${(match.rawThesis || '').slice(0, 60)}`, { parse_mode: 'HTML' });
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
await ctx.reply(`No thesis found matching "${newId}". Use /list to see all.`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
catch (err) {
|
|
211
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
// ── Free text → LLM agent ──
|
|
215
|
-
bot.on('message:text', async (ctx) => {
|
|
216
|
-
if (!isAllowed(ctx.chat.id))
|
|
217
|
-
return;
|
|
218
|
-
const session = getSession(ctx.chat.id);
|
|
219
|
-
const userMessage = ctx.message.text;
|
|
220
|
-
if (userMessage.startsWith('/'))
|
|
221
|
-
return; // skip unhandled commands
|
|
222
|
-
// Auto-detect thesis if not set
|
|
223
|
-
if (!session.thesisId) {
|
|
224
|
-
try {
|
|
225
|
-
const { theses } = await client.listTheses();
|
|
226
|
-
const active = (theses || []).filter((t) => t.status === 'active');
|
|
227
|
-
if (active.length > 0) {
|
|
228
|
-
session.thesisId = active[0].id;
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
await ctx.reply('No active theses. Create one with `sf create "your thesis"` then /start');
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
await ctx.reply('Could not connect. Use /start first.');
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
try {
|
|
241
|
-
// Show "typing" indicator
|
|
242
|
-
await ctx.replyWithChatAction('typing');
|
|
243
|
-
// Lazy-load agent module (heavy imports)
|
|
244
|
-
const { runAgentMessage } = await __webpack_require__.e(/* import() */ 160).then(__webpack_require__.bind(__webpack_require__, 28160));
|
|
245
|
-
// Timeout after 30 seconds
|
|
246
|
-
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Response timeout (30s)')), 30_000));
|
|
247
|
-
const response = await Promise.race([
|
|
248
|
-
runAgentMessage(client, session, userMessage),
|
|
249
|
-
timeout,
|
|
250
|
-
]);
|
|
251
|
-
if (!response || response.trim().length === 0) {
|
|
252
|
-
await ctx.reply('No response generated. Try rephrasing or use a slash command.');
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
for (const chunk of (0, format_js_1.splitMessage)(response)) {
|
|
256
|
-
// Use plain text if HTML parsing might fail
|
|
257
|
-
try {
|
|
258
|
-
await ctx.reply(chunk, { parse_mode: 'HTML' });
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
await ctx.reply(chunk); // fallback to plain text
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
catch (err) {
|
|
267
|
-
console.error('[Telegram] Agent error:', err.message);
|
|
268
|
-
await ctx.reply(`❌ ${err.message}`);
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
// ── Callback queries (for inline keyboard confirmations) ──
|
|
272
|
-
bot.on('callback_query:data', async (ctx) => {
|
|
273
|
-
const data = ctx.callbackQuery.data;
|
|
274
|
-
if (data.startsWith('order_confirm:')) {
|
|
275
|
-
// TODO: execute pending order
|
|
276
|
-
await ctx.answerCallbackQuery({ text: 'Order execution coming soon' });
|
|
277
|
-
}
|
|
278
|
-
else if (data === 'order_cancel') {
|
|
279
|
-
await ctx.answerCallbackQuery({ text: 'Order cancelled' });
|
|
280
|
-
await ctx.editMessageText('❌ Order cancelled.');
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
// ── Start bot ──
|
|
284
|
-
console.log('🤖 SimpleFunctions Telegram bot starting...');
|
|
285
|
-
console.log(` SF API: ${config.apiKey ? '✓' : '✗'}`);
|
|
286
|
-
console.log(` Kalshi: ${process.env.KALSHI_API_KEY_ID ? '✓' : '✗'}`);
|
|
287
|
-
console.log(` OpenRouter: ${config.openrouterKey ? '✓' : '✗'}`);
|
|
288
|
-
if (allowedChat)
|
|
289
|
-
console.log(` Restricted to chat: ${allowedChat}`);
|
|
290
|
-
console.log(' Press Ctrl+C to stop.\n');
|
|
291
|
-
// Prevent crashes from killing the daemon
|
|
292
|
-
process.on('uncaughtException', (err) => {
|
|
293
|
-
console.error('[Telegram] Uncaught exception:', err.message);
|
|
294
|
-
});
|
|
295
|
-
process.on('unhandledRejection', (err) => {
|
|
296
|
-
console.error('[Telegram] Unhandled rejection:', err?.message || err);
|
|
297
|
-
});
|
|
298
|
-
// Cleanup on exit
|
|
299
|
-
process.on('SIGINT', () => {
|
|
300
|
-
for (const timer of pollers.values())
|
|
301
|
-
clearInterval(timer);
|
|
302
|
-
bot.stop();
|
|
303
|
-
process.exit(0);
|
|
304
|
-
});
|
|
305
|
-
await bot.start();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
/***/ }),
|
|
310
|
-
|
|
311
|
-
/***/ 77499:
|
|
312
|
-
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Telegram slash command handlers — direct API calls, zero LLM cost
|
|
317
|
-
*/
|
|
318
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
319
|
-
exports.handleContext = handleContext;
|
|
320
|
-
exports.handlePositions = handlePositions;
|
|
321
|
-
exports.handleEdges = handleEdges;
|
|
322
|
-
exports.handleBalance = handleBalance;
|
|
323
|
-
exports.handleOrders = handleOrders;
|
|
324
|
-
exports.handleEval = handleEval;
|
|
325
|
-
exports.handleList = handleList;
|
|
326
|
-
const kalshi_js_1 = __webpack_require__(96139);
|
|
327
|
-
const format_js_1 = __webpack_require__(76342);
|
|
328
|
-
async function handleContext(client, thesisId) {
|
|
329
|
-
const ctx = await client.getContext(thesisId);
|
|
330
|
-
const conf = typeof ctx.confidence === 'number' ? Math.round(ctx.confidence * 100) : '?';
|
|
331
|
-
const thesis = ctx.thesis || ctx.rawThesis || 'N/A';
|
|
332
|
-
let text = `📋 <b>${(0, format_js_1.escapeHtml)(thesis.slice(0, 80))}</b>\n`;
|
|
333
|
-
text += `Confidence: <b>${conf}%</b> | Status: ${ctx.status}\n\n`;
|
|
334
|
-
const edges = (ctx.edges || []).slice(0, 8);
|
|
335
|
-
if (edges.length > 0) {
|
|
336
|
-
text += `<b>Top Edges:</b>\n<pre>`;
|
|
337
|
-
for (const e of edges) {
|
|
338
|
-
const name = (e.market || e.marketTitle || '???').slice(0, 35);
|
|
339
|
-
text += `${name.padEnd(36)} ${String(e.edge || 0).padStart(3)}¢ ${e.direction || 'yes'}\n`;
|
|
340
|
-
}
|
|
341
|
-
text += `</pre>`;
|
|
342
|
-
}
|
|
343
|
-
return text;
|
|
344
|
-
}
|
|
345
|
-
async function handlePositions() {
|
|
346
|
-
if (!(0, kalshi_js_1.isKalshiConfigured)())
|
|
347
|
-
return '⚠️ Kalshi not configured. Run <code>sf setup --kalshi</code>';
|
|
348
|
-
const positions = await (0, kalshi_js_1.getPositions)();
|
|
349
|
-
if (!positions || positions.length === 0)
|
|
350
|
-
return 'No open positions.';
|
|
351
|
-
// Enrich with live prices
|
|
352
|
-
let totalPnlCents = 0;
|
|
353
|
-
const lines = [];
|
|
354
|
-
for (const pos of positions) {
|
|
355
|
-
const livePrice = await (0, kalshi_js_1.getMarketPrice)(pos.ticker);
|
|
356
|
-
const current = livePrice ?? pos.average_price_paid;
|
|
357
|
-
const pnlCents = (current - pos.average_price_paid) * pos.quantity;
|
|
358
|
-
totalPnlCents += pnlCents;
|
|
359
|
-
const pnl = (0, format_js_1.fmtDollar)(pnlCents);
|
|
360
|
-
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}`);
|
|
361
|
-
}
|
|
362
|
-
let text = `📊 <b>Positions</b>\n<pre>`;
|
|
363
|
-
text += lines.join('\n');
|
|
364
|
-
text += `\n${'─'.repeat(50)}\nTotal P&L: ${(0, format_js_1.fmtDollar)(totalPnlCents)}`;
|
|
365
|
-
text += `</pre>`;
|
|
366
|
-
return text;
|
|
367
|
-
}
|
|
368
|
-
async function handleEdges(client) {
|
|
369
|
-
const { theses } = await client.listTheses();
|
|
370
|
-
const active = (theses || []).filter((t) => t.status === 'active');
|
|
371
|
-
const results = await Promise.allSettled(active.map(async (t) => {
|
|
372
|
-
const ctx = await client.getContext(t.id);
|
|
373
|
-
return (ctx.edges || []).map((e) => ({ ...e, thesisId: t.id }));
|
|
374
|
-
}));
|
|
375
|
-
const allEdges = [];
|
|
376
|
-
for (const r of results) {
|
|
377
|
-
if (r.status === 'fulfilled')
|
|
378
|
-
allEdges.push(...r.value);
|
|
379
|
-
}
|
|
380
|
-
allEdges.sort((a, b) => Math.abs(b.edge || 0) - Math.abs(a.edge || 0));
|
|
381
|
-
const top = allEdges.slice(0, 10);
|
|
382
|
-
if (top.length === 0)
|
|
383
|
-
return 'No edges found.';
|
|
384
|
-
let text = `📈 <b>Top Edges</b>\n<pre>`;
|
|
385
|
-
for (const e of top) {
|
|
386
|
-
const name = (e.market || '???').slice(0, 35);
|
|
387
|
-
const liq = e.orderbook?.liquidityScore || '?';
|
|
388
|
-
text += `${name.padEnd(36)} +${String(e.edge || 0).padStart(2)}¢ ${liq}\n`;
|
|
389
|
-
}
|
|
390
|
-
text += `</pre>`;
|
|
391
|
-
return text;
|
|
392
|
-
}
|
|
393
|
-
async function handleBalance() {
|
|
394
|
-
if (!(0, kalshi_js_1.isKalshiConfigured)())
|
|
395
|
-
return '⚠️ Kalshi not configured.';
|
|
396
|
-
const bal = await (0, kalshi_js_1.getBalance)();
|
|
397
|
-
if (!bal)
|
|
398
|
-
return '⚠️ Failed to fetch balance.';
|
|
399
|
-
return `💰 Balance: <b>$${bal.balance.toFixed(2)}</b> | Portfolio: <b>$${bal.portfolioValue.toFixed(2)}</b>`;
|
|
400
|
-
}
|
|
401
|
-
async function handleOrders() {
|
|
402
|
-
if (!(0, kalshi_js_1.isKalshiConfigured)())
|
|
403
|
-
return '⚠️ Kalshi not configured.';
|
|
404
|
-
const result = await (0, kalshi_js_1.getOrders)({ status: 'resting', limit: 20 });
|
|
405
|
-
if (!result || !result.orders || result.orders.length === 0)
|
|
406
|
-
return 'No resting orders.';
|
|
407
|
-
let text = `📋 <b>Resting Orders</b>\n<pre>`;
|
|
408
|
-
for (const o of result.orders) {
|
|
409
|
-
const qty = Math.round(parseFloat(o.remaining_count_fp || '0'));
|
|
410
|
-
const price = Math.round(parseFloat(o.yes_price_dollars || '0') * 100);
|
|
411
|
-
const ticker = (o.ticker || '???').slice(0, 25);
|
|
412
|
-
text += `${o.action} ${qty}x ${ticker} @ ${price}¢\n`;
|
|
413
|
-
}
|
|
414
|
-
text += `</pre>`;
|
|
415
|
-
return text;
|
|
416
|
-
}
|
|
417
|
-
async function handleEval(client, thesisId) {
|
|
418
|
-
await client.evaluate(thesisId);
|
|
419
|
-
return '⚡ Evaluation triggered. Results in ~2 minutes.';
|
|
420
|
-
}
|
|
421
|
-
async function handleList(client) {
|
|
422
|
-
const { theses } = await client.listTheses();
|
|
423
|
-
if (!theses || theses.length === 0)
|
|
424
|
-
return 'No theses.';
|
|
425
|
-
let text = `📝 <b>Theses</b>\n<pre>`;
|
|
426
|
-
for (const t of theses) {
|
|
427
|
-
const conf = typeof t.confidence === 'number' ? Math.round(t.confidence * 100) : 0;
|
|
428
|
-
const title = (t.rawThesis || t.thesis || '').slice(0, 50);
|
|
429
|
-
text += `${t.id.slice(0, 8)} ${String(conf).padStart(3)}% ${t.status.padEnd(8)} ${title}\n`;
|
|
430
|
-
}
|
|
431
|
-
text += `</pre>`;
|
|
432
|
-
return text;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
/***/ }),
|
|
437
|
-
|
|
438
|
-
/***/ 76342:
|
|
439
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Telegram message formatting utilities
|
|
444
|
-
*/
|
|
445
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
446
|
-
exports.splitMessage = splitMessage;
|
|
447
|
-
exports.fmtDollar = fmtDollar;
|
|
448
|
-
exports.sparkline = sparkline;
|
|
449
|
-
exports.escapeHtml = escapeHtml;
|
|
450
|
-
/** Split long text into chunks under Telegram's 4096 char limit */
|
|
451
|
-
function splitMessage(text, maxLen = 4000) {
|
|
452
|
-
if (text.length <= maxLen)
|
|
453
|
-
return [text];
|
|
454
|
-
const chunks = [];
|
|
455
|
-
let remaining = text;
|
|
456
|
-
while (remaining.length > 0) {
|
|
457
|
-
if (remaining.length <= maxLen) {
|
|
458
|
-
chunks.push(remaining);
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
// Find a good split point (newline near maxLen)
|
|
462
|
-
let splitAt = remaining.lastIndexOf('\n', maxLen);
|
|
463
|
-
if (splitAt < maxLen * 0.5)
|
|
464
|
-
splitAt = maxLen;
|
|
465
|
-
chunks.push(remaining.slice(0, splitAt));
|
|
466
|
-
remaining = remaining.slice(splitAt);
|
|
467
|
-
}
|
|
468
|
-
return chunks;
|
|
469
|
-
}
|
|
470
|
-
/** Format a number as dollars */
|
|
471
|
-
function fmtDollar(cents) {
|
|
472
|
-
const d = cents / 100;
|
|
473
|
-
return d >= 0 ? `+$${d.toFixed(2)}` : `-$${Math.abs(d).toFixed(2)}`;
|
|
474
|
-
}
|
|
475
|
-
/** Simple sparkline */
|
|
476
|
-
function sparkline(values) {
|
|
477
|
-
if (values.length === 0)
|
|
478
|
-
return '';
|
|
479
|
-
const min = Math.min(...values);
|
|
480
|
-
const max = Math.max(...values);
|
|
481
|
-
const range = max - min || 1;
|
|
482
|
-
const blocks = '▁▂▃▄▅▆▇█';
|
|
483
|
-
return values.map(v => {
|
|
484
|
-
const idx = Math.round(((v - min) / range) * 7);
|
|
485
|
-
return blocks[idx];
|
|
486
|
-
}).join('');
|
|
487
|
-
}
|
|
488
|
-
/** Escape HTML special characters for Telegram HTML parse mode */
|
|
489
|
-
function escapeHtml(s) {
|
|
490
|
-
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
/***/ }),
|
|
495
|
-
|
|
496
|
-
/***/ 55875:
|
|
497
|
-
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* Delta API poller — push confidence changes to Telegram
|
|
502
|
-
*/
|
|
503
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
504
|
-
exports.startPoller = startPoller;
|
|
505
|
-
const format_js_1 = __webpack_require__(76342);
|
|
506
|
-
function startPoller(bot, chatId, client, thesisId) {
|
|
507
|
-
let lastCheck = new Date().toISOString();
|
|
508
|
-
return setInterval(async () => {
|
|
509
|
-
try {
|
|
510
|
-
const changes = await client.getChanges(thesisId, lastCheck);
|
|
511
|
-
lastCheck = new Date().toISOString();
|
|
512
|
-
if (!changes || !changes.changed)
|
|
513
|
-
return;
|
|
514
|
-
const delta = changes.confidenceDelta ?? changes.delta ?? 0;
|
|
515
|
-
if (Math.abs(delta) < 0.02)
|
|
516
|
-
return;
|
|
517
|
-
const conf = typeof changes.confidence === 'number' ? Math.round(changes.confidence * 100) : '?';
|
|
518
|
-
const arrow = delta > 0 ? '📈' : '📉';
|
|
519
|
-
const sign = delta > 0 ? '+' : '';
|
|
520
|
-
const summary = changes.summary || changes.latestSummary || '';
|
|
521
|
-
let text = `${arrow} <b>Confidence ${sign}${Math.round(delta * 100)}% → ${conf}%</b>\n`;
|
|
522
|
-
if (summary)
|
|
523
|
-
text += `${(0, format_js_1.escapeHtml)(summary.slice(0, 200))}`;
|
|
524
|
-
await bot.api.sendMessage(chatId, text, { parse_mode: 'HTML' });
|
|
525
|
-
}
|
|
526
|
-
catch {
|
|
527
|
-
// Silent — polling errors should not crash the bot
|
|
528
|
-
}
|
|
529
|
-
}, 60_000); // every 60 seconds
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
/***/ })
|
|
534
|
-
|
|
535
|
-
};
|
|
536
|
-
;
|
|
1
|
+
"use strict";exports.id=12,exports.ids=[12],exports.modules={6012:function(e,t,s){var a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.startBot=async function(e){const t=(0,l.loadConfig)(),a=e.token||t.telegramBotToken||process.env.TELEGRAM_BOT_TOKEN;a||(console.error("No Telegram bot token. Use --token, set TELEGRAM_BOT_TOKEN, or add to ~/.sf/config.json"),process.exit(1));const o=new c.SFClient(t.apiKey,t.apiUrl),f=new n.Bot(a),m=e.chatId||null,y=new Map;function w(e){return!m||e===m}f.command("start",async e=>{if(!w(e.chat.id))return;const t=g(e.chat.id);try{const{theses:s}=await o.listTheses(),a=(s||[]).filter(e=>"active"===e.status);a.length>0?(t.thesisId=a[0].id,await e.reply(`✅ Connected to SimpleFunctions\n\nActive thesis: <b>${(a[0].rawThesis||"").slice(0,60)}</b>\nID: <code>${a[0].id.slice(0,8)}</code>\n\nCommands:\n/context — thesis snapshot\n/positions — Kalshi positions\n/edges — top edges\n/balance — account balance\n/orders — resting orders\n/eval — trigger evaluation\n/list — all theses\n/switch — switch thesis\n\nOr just type naturally.`,{parse_mode:"HTML"}),y.has(e.chat.id)&&clearInterval(y.get(e.chat.id)),y.set(e.chat.id,(0,p.startPoller)(f,e.chat.id,o,t.thesisId))):await e.reply('No active theses found. Create one with `sf create "your thesis"`')}catch(t){await e.reply(`❌ Connection failed: ${t.message}\nCheck SF_API_KEY in ~/.sf/config.json`)}}),f.command("context",async e=>{if(!w(e.chat.id))return;const t=g(e.chat.id);if(t.thesisId)try{const s=await(0,h.handleContext)(o,t.thesisId);for(const t of(0,d.splitMessage)(s))await e.reply(t,{parse_mode:"HTML"})}catch(t){await e.reply(`❌ ${t.message}`)}else await e.reply("No thesis selected. Use /start or /switch <id>")}),f.command("positions",async e=>{if(w(e.chat.id))try{const t=await(0,h.handlePositions)();for(const s of(0,d.splitMessage)(t))await e.reply(s,{parse_mode:"HTML"})}catch(t){await e.reply(`❌ ${t.message}`)}}),f.command("edges",async e=>{if(w(e.chat.id))try{const t=await(0,h.handleEdges)(o);for(const s of(0,d.splitMessage)(t))await e.reply(s,{parse_mode:"HTML"})}catch(t){await e.reply(`❌ ${t.message}`)}}),f.command("balance",async e=>{if(w(e.chat.id))try{const t=await(0,h.handleBalance)();await e.reply(t,{parse_mode:"HTML"})}catch(t){await e.reply(`❌ ${t.message}`)}}),f.command("orders",async e=>{if(w(e.chat.id))try{const t=await(0,h.handleOrders)();for(const s of(0,d.splitMessage)(t))await e.reply(s,{parse_mode:"HTML"})}catch(t){await e.reply(`❌ ${t.message}`)}}),f.command("eval",async e=>{if(!w(e.chat.id))return;const t=g(e.chat.id);if(t.thesisId)try{const s=await(0,h.handleEval)(o,t.thesisId);await e.reply(s)}catch(t){await e.reply(`❌ ${t.message}`)}else await e.reply("No thesis selected.")}),f.command("list",async e=>{if(w(e.chat.id))try{const t=await(0,h.handleList)(o);for(const s of(0,d.splitMessage)(t))await e.reply(s,{parse_mode:"HTML"})}catch(t){await e.reply(`❌ ${t.message}`)}}),f.command("switch",async e=>{if(!w(e.chat.id))return;const t=g(e.chat.id),s=e.match?.trim();if(s)try{const{theses:a}=await o.listTheses(),n=(a||[]).find(e=>e.id.startsWith(s));n?(t.thesisId=n.id,t.agentMessages=[],y.has(e.chat.id)&&clearInterval(y.get(e.chat.id)),y.set(e.chat.id,(0,p.startPoller)(f,e.chat.id,o,t.thesisId)),await e.reply(`Switched to: <code>${n.id.slice(0,8)}</code> — ${(n.rawThesis||"").slice(0,60)}`,{parse_mode:"HTML"})):await e.reply(`No thesis found matching "${s}". Use /list to see all.`)}catch(t){await e.reply(`❌ ${t.message}`)}else await e.reply("Usage: /switch <thesis_id>")}),f.on("message:text",async e=>{if(!w(e.chat.id))return;const t=g(e.chat.id),a=e.message.text;if(!a.startsWith("/")){if(!t.thesisId)try{const{theses:s}=await o.listTheses(),a=(s||[]).filter(e=>"active"===e.status);if(!(a.length>0))return void await e.reply('No active theses. Create one with `sf create "your thesis"` then /start');t.thesisId=a[0].id}catch{return void await e.reply("Could not connect. Use /start first.")}try{await e.replyWithChatAction("typing");const{runAgentMessage:n}=await s.e(160).then(s.bind(s,28160)),i=new Promise((e,t)=>setTimeout(()=>t(new Error("Response timeout (30s)")),3e4)),r=await Promise.race([n(o,t,a),i]);if(r&&0!==r.trim().length)for(const t of(0,d.splitMessage)(r))try{await e.reply(t,{parse_mode:"HTML"})}catch{await e.reply(t)}else await e.reply("No response generated. Try rephrasing or use a slash command.")}catch(t){console.error("[Telegram] Agent error:",t.message),await e.reply(`❌ ${t.message}`)}}}),f.on("callback_query:data",async e=>{const t=e.callbackQuery.data;t.startsWith("order_confirm:")?await e.answerCallbackQuery({text:"Order execution coming soon"}):"order_cancel"===t&&(await e.answerCallbackQuery({text:"Order cancelled"}),await e.editMessageText("❌ Order cancelled."))}),console.log("🤖 SimpleFunctions Telegram bot starting..."),console.log(" SF API: "+(t.apiKey?"✓":"✗")),console.log(" Kalshi: "+(process.env.KALSHI_API_KEY_ID?"✓":"✗")),console.log(" OpenRouter: "+(t.openrouterKey?"✓":"✗")),m&&console.log(` Restricted to chat: ${m}`);function $(){for(const e of y.values())clearInterval(e);f.stop();try{i.default.unlinkSync(u)}catch{}}console.log(" Press Ctrl+C to stop.\n"),i.default.mkdirSync(r.default.dirname(u),{recursive:!0}),i.default.writeFileSync(u,String(process.pid)),process.on("uncaughtException",e=>{console.error("[Telegram] Uncaught exception:",e.message)}),process.on("unhandledRejection",e=>{console.error("[Telegram] Unhandled rejection:",e?.message||e)}),process.on("SIGINT",()=>{$(),process.exit(0)}),process.on("SIGTERM",()=>{$(),process.exit(0)}),process.on("exit",()=>{try{i.default.unlinkSync(u)}catch{}}),await f.start()};const n=s(53278),i=a(s(79896)),r=a(s(16928)),o=a(s(70857)),c=s(19218),l=s(11627),d=s(76342),h=s(77499),p=s(55875),u=r.default.join(o.default.homedir(),".sf","telegram.pid"),f=new Map;function g(e){return f.has(e)||f.set(e,{thesisId:null,agentMessages:[]}),f.get(e)}},77499:(e,t,s)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.handleContext=async function(e,t){const s=await e.getContext(t),a="number"==typeof s.confidence?Math.round(100*s.confidence):"?",i=s.thesis||s.rawThesis||"N/A";let r=`📋 <b>${(0,n.escapeHtml)(i.slice(0,80))}</b>\n`;r+=`Confidence: <b>${a}%</b> | Status: ${s.status}\n\n`;const o=(s.edges||[]).slice(0,8);if(o.length>0){r+="<b>Top Edges:</b>\n<pre>";for(const e of o){r+=`${(e.market||e.marketTitle||"???").slice(0,35).padEnd(36)} ${String(e.edge||0).padStart(3)}¢ ${e.direction||"yes"}\n`}r+="</pre>"}return r},t.handlePositions=async function(){if(!(0,a.isKalshiConfigured)())return"⚠️ Kalshi not configured. Run <code>sf setup --kalshi</code>";const e=await(0,a.getPositions)();if(!e||0===e.length)return"No open positions.";let t=0;const s=[];for(const i of e){const e=await(0,a.getMarketPrice)(i.ticker)??i.average_price_paid,r=(e-i.average_price_paid)*i.quantity;t+=r;const o=(0,n.fmtDollar)(r);s.push(`${i.ticker.slice(0,28).padEnd(29)} ${String(i.quantity).padStart(5)} ${String(i.average_price_paid).padStart(3)}¢→${String(e).padStart(3)}¢ ${o}`)}let i="📊 <b>Positions</b>\n<pre>";return i+=s.join("\n"),i+=`\n${"─".repeat(50)}\nTotal P&L: ${(0,n.fmtDollar)(t)}`,i+="</pre>",i},t.handleEdges=async function(e){const{theses:t}=await e.listTheses(),s=(t||[]).filter(e=>"active"===e.status),a=await Promise.allSettled(s.map(async t=>((await e.getContext(t.id)).edges||[]).map(e=>({...e,thesisId:t.id})))),n=[];for(const e of a)"fulfilled"===e.status&&n.push(...e.value);n.sort((e,t)=>Math.abs(t.edge||0)-Math.abs(e.edge||0));const i=n.slice(0,10);if(0===i.length)return"No edges found.";let r="📈 <b>Top Edges</b>\n<pre>";for(const e of i){const t=(e.market||"???").slice(0,35),s=e.orderbook?.liquidityScore||"?";r+=`${t.padEnd(36)} +${String(e.edge||0).padStart(2)}¢ ${s}\n`}return r+="</pre>",r},t.handleBalance=async function(){if(!(0,a.isKalshiConfigured)())return"⚠️ Kalshi not configured.";const e=await(0,a.getBalance)();return e?`💰 Balance: <b>$${e.balance.toFixed(2)}</b> | Portfolio: <b>$${e.portfolioValue.toFixed(2)}</b>`:"⚠️ Failed to fetch balance."},t.handleOrders=async function(){if(!(0,a.isKalshiConfigured)())return"⚠️ Kalshi not configured.";const e=await(0,a.getOrders)({status:"resting",limit:20});if(!e||!e.orders||0===e.orders.length)return"No resting orders.";let t="📋 <b>Resting Orders</b>\n<pre>";for(const s of e.orders){const e=Math.round(parseFloat(s.remaining_count_fp||"0")),a=Math.round(100*parseFloat(s.yes_price_dollars||"0")),n=(s.ticker||"???").slice(0,25);t+=`${s.action} ${e}x ${n} @ ${a}¢\n`}return t+="</pre>",t},t.handleEval=async function(e,t){return await e.evaluate(t),"⚡ Evaluation triggered. Results in ~2 minutes."},t.handleList=async function(e){const{theses:t}=await e.listTheses();if(!t||0===t.length)return"No theses.";let s="📝 <b>Theses</b>\n<pre>";for(const e of t){const t="number"==typeof e.confidence?Math.round(100*e.confidence):0,a=(e.rawThesis||e.thesis||"").slice(0,50);s+=`${e.id.slice(0,8)} ${String(t).padStart(3)}% ${e.status.padEnd(8)} ${a}\n`}return s+="</pre>",s};const a=s(96139),n=s(76342)},76342:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.splitMessage=function(e,t=4e3){if(e.length<=t)return[e];const s=[];let a=e;for(;a.length>0;){if(a.length<=t){s.push(a);break}let e=a.lastIndexOf("\n",t);e<.5*t&&(e=t),s.push(a.slice(0,e)),a=a.slice(e)}return s},t.fmtDollar=function(e){const t=e/100;return t>=0?`+$${t.toFixed(2)}`:`-$${Math.abs(t).toFixed(2)}`},t.sparkline=function(e){if(0===e.length)return"";const t=Math.min(...e),s=Math.max(...e)-t||1;return e.map(e=>"▁▂▃▄▅▆▇█"[Math.round((e-t)/s*7)]).join("")},t.escapeHtml=function(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}},55875:(e,t,s)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.startPoller=function(e,t,s,n){let i=(new Date).toISOString();return setInterval(async()=>{try{const r=await s.getChanges(n,i);if(i=(new Date).toISOString(),!r||!r.changed)return;const o=r.confidenceDelta??r.delta??0;if(Math.abs(o)<.02)return;const c="number"==typeof r.confidence?Math.round(100*r.confidence):"?",l=o>0?"📈":"📉",d=o>0?"+":"",h=r.summary||r.latestSummary||"";let p=`${l} <b>Confidence ${d}${Math.round(100*o)}% → ${c}%</b>\n`;h&&(p+=`${(0,a.escapeHtml)(h.slice(0,200))}`),await e.api.sendMessage(t,p,{parse_mode:"HTML"})}catch{}},6e4)};const a=s(76342)}};
|