@spfunctions/cli 1.4.4 → 1.5.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 (86) hide show
  1. package/README.md +205 -48
  2. package/dist/cache.d.ts +6 -0
  3. package/dist/cache.js +31 -0
  4. package/dist/cache.test.d.ts +1 -0
  5. package/dist/cache.test.js +73 -0
  6. package/dist/client.test.d.ts +1 -0
  7. package/dist/client.test.js +89 -0
  8. package/dist/commands/agent.js +594 -106
  9. package/dist/commands/book.d.ts +17 -0
  10. package/dist/commands/book.js +220 -0
  11. package/dist/commands/dashboard.d.ts +6 -3
  12. package/dist/commands/dashboard.js +53 -22
  13. package/dist/commands/liquidity.d.ts +2 -0
  14. package/dist/commands/liquidity.js +128 -43
  15. package/dist/commands/performance.js +9 -2
  16. package/dist/commands/positions.js +50 -0
  17. package/dist/commands/scan.d.ts +1 -0
  18. package/dist/commands/scan.js +66 -15
  19. package/dist/commands/setup.d.ts +1 -0
  20. package/dist/commands/setup.js +71 -6
  21. package/dist/commands/telegram.d.ts +15 -0
  22. package/dist/commands/telegram.js +125 -0
  23. package/dist/config.d.ts +3 -0
  24. package/dist/config.js +9 -0
  25. package/dist/config.test.d.ts +1 -0
  26. package/dist/config.test.js +138 -0
  27. package/dist/index.js +107 -9
  28. package/dist/polymarket.d.ts +237 -0
  29. package/dist/polymarket.js +353 -0
  30. package/dist/polymarket.test.d.ts +1 -0
  31. package/dist/polymarket.test.js +424 -0
  32. package/dist/telegram/agent-bridge.d.ts +15 -0
  33. package/dist/telegram/agent-bridge.js +368 -0
  34. package/dist/telegram/bot.d.ts +10 -0
  35. package/dist/telegram/bot.js +297 -0
  36. package/dist/telegram/commands.d.ts +11 -0
  37. package/dist/telegram/commands.js +120 -0
  38. package/dist/telegram/format.d.ts +11 -0
  39. package/dist/telegram/format.js +51 -0
  40. package/dist/telegram/format.test.d.ts +1 -0
  41. package/dist/telegram/format.test.js +73 -0
  42. package/dist/telegram/poller.d.ts +6 -0
  43. package/dist/telegram/poller.js +32 -0
  44. package/dist/topics.d.ts +3 -0
  45. package/dist/topics.js +65 -7
  46. package/dist/topics.test.d.ts +1 -0
  47. package/dist/topics.test.js +131 -0
  48. package/dist/tui/border.d.ts +33 -0
  49. package/dist/tui/border.js +87 -0
  50. package/dist/tui/chart.d.ts +19 -0
  51. package/dist/tui/chart.js +117 -0
  52. package/dist/tui/dashboard.d.ts +9 -0
  53. package/dist/tui/dashboard.js +814 -0
  54. package/dist/tui/layout.d.ts +16 -0
  55. package/dist/tui/layout.js +41 -0
  56. package/dist/tui/screen.d.ts +33 -0
  57. package/dist/tui/screen.js +102 -0
  58. package/dist/tui/state.d.ts +40 -0
  59. package/dist/tui/state.js +36 -0
  60. package/dist/tui/widgets/commandbar.d.ts +8 -0
  61. package/dist/tui/widgets/commandbar.js +82 -0
  62. package/dist/tui/widgets/detail.d.ts +9 -0
  63. package/dist/tui/widgets/detail.js +151 -0
  64. package/dist/tui/widgets/edges.d.ts +4 -0
  65. package/dist/tui/widgets/edges.js +34 -0
  66. package/dist/tui/widgets/liquidity.d.ts +9 -0
  67. package/dist/tui/widgets/liquidity.js +142 -0
  68. package/dist/tui/widgets/orders.d.ts +4 -0
  69. package/dist/tui/widgets/orders.js +37 -0
  70. package/dist/tui/widgets/portfolio.d.ts +4 -0
  71. package/dist/tui/widgets/portfolio.js +59 -0
  72. package/dist/tui/widgets/signals.d.ts +4 -0
  73. package/dist/tui/widgets/signals.js +31 -0
  74. package/dist/tui/widgets/statusbar.d.ts +8 -0
  75. package/dist/tui/widgets/statusbar.js +72 -0
  76. package/dist/tui/widgets/thesis.d.ts +4 -0
  77. package/dist/tui/widgets/thesis.js +66 -0
  78. package/dist/tui/widgets/trade.d.ts +9 -0
  79. package/dist/tui/widgets/trade.js +117 -0
  80. package/dist/tui/widgets/upcoming.d.ts +4 -0
  81. package/dist/tui/widgets/upcoming.js +41 -0
  82. package/dist/tui/widgets/whatif.d.ts +7 -0
  83. package/dist/tui/widgets/whatif.js +113 -0
  84. package/dist/utils.test.d.ts +1 -0
  85. package/dist/utils.test.js +111 -0
  86. package/package.json +6 -2
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const polymarket_js_1 = require("./polymarket.js");
5
+ const mockFetch = vitest_1.vi.fn();
6
+ vitest_1.vi.stubGlobal('fetch', mockFetch);
7
+ (0, vitest_1.beforeEach)(() => {
8
+ mockFetch.mockReset();
9
+ });
10
+ // ============================================================================
11
+ // PURE FUNCTIONS (no network)
12
+ // ============================================================================
13
+ (0, vitest_1.describe)('computeOrderbookDepth', () => {
14
+ (0, vitest_1.it)('computes best bid/ask and spread in cents', () => {
15
+ const raw = {
16
+ market: 'cond1', asset_id: 'tok1', timestamp: '123', hash: 'h',
17
+ bids: [
18
+ { price: '0.45', size: '200' },
19
+ { price: '0.44', size: '150' },
20
+ { price: '0.43', size: '100' },
21
+ ],
22
+ asks: [
23
+ { price: '0.48', size: '300' },
24
+ { price: '0.49', size: '250' },
25
+ { price: '0.50', size: '100' },
26
+ ],
27
+ };
28
+ const depth = (0, polymarket_js_1.computeOrderbookDepth)(raw);
29
+ (0, vitest_1.expect)(depth.bestBid).toBe(45);
30
+ (0, vitest_1.expect)(depth.bestAsk).toBe(48);
31
+ (0, vitest_1.expect)(depth.spread).toBe(3);
32
+ });
33
+ (0, vitest_1.it)('computes top-3 depth', () => {
34
+ const raw = {
35
+ market: 'c', asset_id: 't', timestamp: '0', hash: '',
36
+ bids: [
37
+ { price: '0.50', size: '100' },
38
+ { price: '0.49', size: '200' },
39
+ { price: '0.48', size: '300' },
40
+ { price: '0.47', size: '400' }, // 4th level, not counted
41
+ ],
42
+ asks: [
43
+ { price: '0.52', size: '50' },
44
+ { price: '0.53', size: '60' },
45
+ { price: '0.54', size: '70' },
46
+ ],
47
+ };
48
+ const depth = (0, polymarket_js_1.computeOrderbookDepth)(raw);
49
+ (0, vitest_1.expect)(depth.bidDepthTop3).toBe(600); // 100+200+300
50
+ (0, vitest_1.expect)(depth.askDepthTop3).toBe(180); // 50+60+70
51
+ (0, vitest_1.expect)(depth.totalBidDepth).toBe(1000); // includes 4th level
52
+ (0, vitest_1.expect)(depth.totalAskDepth).toBe(180);
53
+ });
54
+ (0, vitest_1.it)('sorts bids descending and asks ascending', () => {
55
+ const raw = {
56
+ market: 'c', asset_id: 't', timestamp: '0', hash: '',
57
+ bids: [
58
+ { price: '0.40', size: '100' },
59
+ { price: '0.50', size: '200' }, // best bid (highest)
60
+ ],
61
+ asks: [
62
+ { price: '0.60', size: '100' },
63
+ { price: '0.55', size: '200' }, // best ask (lowest)
64
+ ],
65
+ };
66
+ const depth = (0, polymarket_js_1.computeOrderbookDepth)(raw);
67
+ (0, vitest_1.expect)(depth.bestBid).toBe(50);
68
+ (0, vitest_1.expect)(depth.bestAsk).toBe(55);
69
+ (0, vitest_1.expect)(depth.spread).toBe(5);
70
+ });
71
+ (0, vitest_1.it)('handles empty orderbook', () => {
72
+ const raw = {
73
+ market: 'c', asset_id: 't', timestamp: '0', hash: '',
74
+ bids: [], asks: [],
75
+ };
76
+ const depth = (0, polymarket_js_1.computeOrderbookDepth)(raw);
77
+ (0, vitest_1.expect)(depth.bestBid).toBe(0);
78
+ (0, vitest_1.expect)(depth.bestAsk).toBe(100);
79
+ (0, vitest_1.expect)(depth.spread).toBe(100);
80
+ (0, vitest_1.expect)(depth.bidDepthTop3).toBe(0);
81
+ (0, vitest_1.expect)(depth.askDepthTop3).toBe(0);
82
+ (0, vitest_1.expect)(depth.liquidityScore).toBe('low');
83
+ });
84
+ (0, vitest_1.it)('assigns correct liquidity scores', () => {
85
+ // high: spread ≤ 2¢, depth ≥ 500
86
+ const high = {
87
+ market: 'c', asset_id: 't', timestamp: '0', hash: '',
88
+ bids: [{ price: '0.49', size: '300' }],
89
+ asks: [{ price: '0.51', size: '300' }],
90
+ };
91
+ (0, vitest_1.expect)((0, polymarket_js_1.computeOrderbookDepth)(high).liquidityScore).toBe('high');
92
+ // medium: spread ≤ 5¢, depth ≥ 100
93
+ const medium = {
94
+ market: 'c', asset_id: 't', timestamp: '0', hash: '',
95
+ bids: [{ price: '0.47', size: '60' }],
96
+ asks: [{ price: '0.52', size: '60' }],
97
+ };
98
+ (0, vitest_1.expect)((0, polymarket_js_1.computeOrderbookDepth)(medium).liquidityScore).toBe('medium');
99
+ // low: wide spread
100
+ const low = {
101
+ market: 'c', asset_id: 't', timestamp: '0', hash: '',
102
+ bids: [{ price: '0.30', size: '10' }],
103
+ asks: [{ price: '0.70', size: '10' }],
104
+ };
105
+ (0, vitest_1.expect)((0, polymarket_js_1.computeOrderbookDepth)(low).liquidityScore).toBe('low');
106
+ });
107
+ });
108
+ (0, vitest_1.describe)('scoreLiquidity', () => {
109
+ (0, vitest_1.it)('returns high for tight spread + deep book', () => {
110
+ (0, vitest_1.expect)((0, polymarket_js_1.scoreLiquidity)(1, 600)).toBe('high');
111
+ (0, vitest_1.expect)((0, polymarket_js_1.scoreLiquidity)(2, 500)).toBe('high');
112
+ });
113
+ (0, vitest_1.it)('returns medium for moderate spread + depth', () => {
114
+ (0, vitest_1.expect)((0, polymarket_js_1.scoreLiquidity)(3, 200)).toBe('medium');
115
+ (0, vitest_1.expect)((0, polymarket_js_1.scoreLiquidity)(5, 100)).toBe('medium');
116
+ });
117
+ (0, vitest_1.it)('returns low for wide spread or thin book', () => {
118
+ (0, vitest_1.expect)((0, polymarket_js_1.scoreLiquidity)(6, 1000)).toBe('low');
119
+ (0, vitest_1.expect)((0, polymarket_js_1.scoreLiquidity)(1, 50)).toBe('low');
120
+ (0, vitest_1.expect)((0, polymarket_js_1.scoreLiquidity)(10, 10)).toBe('low');
121
+ });
122
+ });
123
+ (0, vitest_1.describe)('parseClobTokenIds', () => {
124
+ (0, vitest_1.it)('parses valid JSON array', () => {
125
+ const result = (0, polymarket_js_1.parseClobTokenIds)('["tok_yes_123","tok_no_456"]');
126
+ (0, vitest_1.expect)(result).toEqual(['tok_yes_123', 'tok_no_456']);
127
+ });
128
+ (0, vitest_1.it)('returns null for invalid JSON', () => {
129
+ (0, vitest_1.expect)((0, polymarket_js_1.parseClobTokenIds)('not json')).toBeNull();
130
+ });
131
+ (0, vitest_1.it)('returns null for array with < 2 elements', () => {
132
+ (0, vitest_1.expect)((0, polymarket_js_1.parseClobTokenIds)('["only_one"]')).toBeNull();
133
+ });
134
+ (0, vitest_1.it)('returns null for empty string', () => {
135
+ (0, vitest_1.expect)((0, polymarket_js_1.parseClobTokenIds)('')).toBeNull();
136
+ });
137
+ });
138
+ (0, vitest_1.describe)('parseOutcomes', () => {
139
+ (0, vitest_1.it)('parses yes/no outcomes', () => {
140
+ (0, vitest_1.expect)((0, polymarket_js_1.parseOutcomes)('["Yes","No"]')).toEqual(['Yes', 'No']);
141
+ });
142
+ (0, vitest_1.it)('parses multi-outcome', () => {
143
+ (0, vitest_1.expect)((0, polymarket_js_1.parseOutcomes)('["Trump","Biden","Other"]')).toEqual(['Trump', 'Biden', 'Other']);
144
+ });
145
+ (0, vitest_1.it)('returns empty for invalid', () => {
146
+ (0, vitest_1.expect)((0, polymarket_js_1.parseOutcomes)('bad')).toEqual([]);
147
+ });
148
+ });
149
+ (0, vitest_1.describe)('parseOutcomePrices', () => {
150
+ (0, vitest_1.it)('parses price array', () => {
151
+ (0, vitest_1.expect)((0, polymarket_js_1.parseOutcomePrices)('[0.65, 0.35]')).toEqual([0.65, 0.35]);
152
+ });
153
+ (0, vitest_1.it)('returns empty for invalid', () => {
154
+ (0, vitest_1.expect)((0, polymarket_js_1.parseOutcomePrices)('')).toEqual([]);
155
+ });
156
+ });
157
+ (0, vitest_1.describe)('toCents', () => {
158
+ (0, vitest_1.it)('converts dollars to cents', () => {
159
+ (0, vitest_1.expect)((0, polymarket_js_1.toCents)(0.55)).toBe(55);
160
+ (0, vitest_1.expect)((0, polymarket_js_1.toCents)(1.0)).toBe(100);
161
+ (0, vitest_1.expect)((0, polymarket_js_1.toCents)(0)).toBe(0);
162
+ (0, vitest_1.expect)((0, polymarket_js_1.toCents)(0.123)).toBe(12); // rounds
163
+ });
164
+ });
165
+ (0, vitest_1.describe)('polymarketUrl', () => {
166
+ (0, vitest_1.it)('builds correct URL', () => {
167
+ (0, vitest_1.expect)((0, polymarket_js_1.polymarketUrl)('fed-decision')).toBe('https://polymarket.com/event/fed-decision');
168
+ });
169
+ });
170
+ // ============================================================================
171
+ // API FUNCTIONS (mocked fetch)
172
+ // ============================================================================
173
+ (0, vitest_1.describe)('polymarketSearch', () => {
174
+ (0, vitest_1.it)('calls Gamma API with query params', async () => {
175
+ mockFetch.mockResolvedValue({
176
+ ok: true,
177
+ json: () => Promise.resolve({ events: [{ id: '1', title: 'Test' }] }),
178
+ });
179
+ const events = await (0, polymarket_js_1.polymarketSearch)('recession', 5);
180
+ (0, vitest_1.expect)(events).toHaveLength(1);
181
+ (0, vitest_1.expect)(events[0].title).toBe('Test');
182
+ const url = mockFetch.mock.calls[0][0];
183
+ (0, vitest_1.expect)(url).toContain('gamma-api.polymarket.com/public-search');
184
+ (0, vitest_1.expect)(url).toContain('q=recession');
185
+ (0, vitest_1.expect)(url).toContain('limit_per_type=5');
186
+ });
187
+ (0, vitest_1.it)('throws on error response', async () => {
188
+ mockFetch.mockResolvedValue({ ok: false, status: 500 });
189
+ await (0, vitest_1.expect)((0, polymarket_js_1.polymarketSearch)('test')).rejects.toThrow('500');
190
+ });
191
+ });
192
+ (0, vitest_1.describe)('polymarketListEvents', () => {
193
+ (0, vitest_1.it)('fetches active events with pagination', async () => {
194
+ mockFetch.mockResolvedValue({
195
+ ok: true,
196
+ json: () => Promise.resolve([{ id: '1' }, { id: '2' }]),
197
+ });
198
+ const events = await (0, polymarket_js_1.polymarketListEvents)({ limit: 10, offset: 20, order: 'volume_24hr' });
199
+ const url = mockFetch.mock.calls[0][0];
200
+ (0, vitest_1.expect)(url).toContain('active=true');
201
+ (0, vitest_1.expect)(url).toContain('limit=10');
202
+ (0, vitest_1.expect)(url).toContain('offset=20');
203
+ (0, vitest_1.expect)(url).toContain('order=volume_24hr');
204
+ (0, vitest_1.expect)(events).toHaveLength(2);
205
+ });
206
+ });
207
+ (0, vitest_1.describe)('polymarketGetEvent', () => {
208
+ (0, vitest_1.it)('fetches single event by ID', async () => {
209
+ mockFetch.mockResolvedValue({
210
+ ok: true,
211
+ json: () => Promise.resolve({ id: '42', title: 'Fed Decision' }),
212
+ });
213
+ const event = await (0, polymarket_js_1.polymarketGetEvent)('42');
214
+ (0, vitest_1.expect)(event.id).toBe('42');
215
+ (0, vitest_1.expect)(mockFetch.mock.calls[0][0]).toContain('/events/42');
216
+ });
217
+ });
218
+ (0, vitest_1.describe)('polymarketGetMarket', () => {
219
+ (0, vitest_1.it)('fetches single market', async () => {
220
+ mockFetch.mockResolvedValue({
221
+ ok: true,
222
+ json: () => Promise.resolve({ id: 'm1', question: 'Will X happen?' }),
223
+ });
224
+ const market = await (0, polymarket_js_1.polymarketGetMarket)('m1');
225
+ (0, vitest_1.expect)(market.question).toBe('Will X happen?');
226
+ });
227
+ });
228
+ (0, vitest_1.describe)('polymarketListTags', () => {
229
+ (0, vitest_1.it)('returns tags array', async () => {
230
+ mockFetch.mockResolvedValue({
231
+ ok: true,
232
+ json: () => Promise.resolve([
233
+ { id: '1', label: 'Politics', slug: 'politics' },
234
+ { id: '2', label: 'Crypto', slug: 'crypto' },
235
+ ]),
236
+ });
237
+ const tags = await (0, polymarket_js_1.polymarketListTags)();
238
+ (0, vitest_1.expect)(tags).toHaveLength(2);
239
+ (0, vitest_1.expect)(tags[0].label).toBe('Politics');
240
+ });
241
+ });
242
+ (0, vitest_1.describe)('polymarketGetOrderbook', () => {
243
+ (0, vitest_1.it)('fetches raw orderbook', async () => {
244
+ mockFetch.mockResolvedValue({
245
+ ok: true,
246
+ json: () => Promise.resolve({
247
+ market: 'cond1', asset_id: 'tok1', timestamp: '123', hash: 'h',
248
+ bids: [{ price: '0.45', size: '100' }],
249
+ asks: [{ price: '0.55', size: '100' }],
250
+ }),
251
+ });
252
+ const ob = await (0, polymarket_js_1.polymarketGetOrderbook)('tok1');
253
+ (0, vitest_1.expect)(ob).not.toBeNull();
254
+ (0, vitest_1.expect)(ob.bids).toHaveLength(1);
255
+ (0, vitest_1.expect)(mockFetch.mock.calls[0][0]).toContain('clob.polymarket.com/book?token_id=tok1');
256
+ });
257
+ (0, vitest_1.it)('returns null on error', async () => {
258
+ mockFetch.mockResolvedValue({ ok: false, status: 404 });
259
+ const ob = await (0, polymarket_js_1.polymarketGetOrderbook)('bad');
260
+ (0, vitest_1.expect)(ob).toBeNull();
261
+ });
262
+ (0, vitest_1.it)('returns null on API error response', async () => {
263
+ mockFetch.mockResolvedValue({
264
+ ok: true,
265
+ json: () => Promise.resolve({ error: 'No orderbook exists' }),
266
+ });
267
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetOrderbook)('bad')).toBeNull();
268
+ });
269
+ (0, vitest_1.it)('returns null on network error', async () => {
270
+ mockFetch.mockRejectedValue(new Error('network'));
271
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetOrderbook)('bad')).toBeNull();
272
+ });
273
+ });
274
+ (0, vitest_1.describe)('polymarketGetOrderbookWithDepth', () => {
275
+ (0, vitest_1.it)('fetches and computes depth in one call', async () => {
276
+ mockFetch.mockResolvedValue({
277
+ ok: true,
278
+ json: () => Promise.resolve({
279
+ market: 'c', asset_id: 't', timestamp: '0', hash: '',
280
+ bids: [{ price: '0.49', size: '300' }],
281
+ asks: [{ price: '0.51', size: '400' }],
282
+ }),
283
+ });
284
+ const depth = await (0, polymarket_js_1.polymarketGetOrderbookWithDepth)('tok1');
285
+ (0, vitest_1.expect)(depth).not.toBeNull();
286
+ (0, vitest_1.expect)(depth.bestBid).toBe(49);
287
+ (0, vitest_1.expect)(depth.bestAsk).toBe(51);
288
+ (0, vitest_1.expect)(depth.spread).toBe(2);
289
+ (0, vitest_1.expect)(depth.liquidityScore).toBe('high');
290
+ });
291
+ (0, vitest_1.it)('returns null when orderbook unavailable', async () => {
292
+ mockFetch.mockResolvedValue({ ok: false });
293
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetOrderbookWithDepth)('bad')).toBeNull();
294
+ });
295
+ });
296
+ (0, vitest_1.describe)('polymarketGetMidpoint', () => {
297
+ (0, vitest_1.it)('returns midpoint as number', async () => {
298
+ mockFetch.mockResolvedValue({
299
+ ok: true,
300
+ json: () => Promise.resolve({ mid: '0.52' }),
301
+ });
302
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetMidpoint)('tok1')).toBe(0.52);
303
+ });
304
+ (0, vitest_1.it)('returns null on error', async () => {
305
+ mockFetch.mockResolvedValue({ ok: false });
306
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetMidpoint)('bad')).toBeNull();
307
+ });
308
+ });
309
+ (0, vitest_1.describe)('polymarketGetSpread', () => {
310
+ (0, vitest_1.it)('returns spread as number', async () => {
311
+ mockFetch.mockResolvedValue({
312
+ ok: true,
313
+ json: () => Promise.resolve({ spread: '0.03' }),
314
+ });
315
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetSpread)('tok1')).toBe(0.03);
316
+ });
317
+ });
318
+ (0, vitest_1.describe)('polymarketGetMidpoints', () => {
319
+ (0, vitest_1.it)('posts batch of token IDs', async () => {
320
+ mockFetch.mockResolvedValue({
321
+ ok: true,
322
+ json: () => Promise.resolve({ tok1: 0.5, tok2: 0.7 }),
323
+ });
324
+ const result = await (0, polymarket_js_1.polymarketGetMidpoints)(['tok1', 'tok2']);
325
+ (0, vitest_1.expect)(result.tok1).toBe(0.5);
326
+ (0, vitest_1.expect)(result.tok2).toBe(0.7);
327
+ (0, vitest_1.expect)(mockFetch.mock.calls[0][1].method).toBe('POST');
328
+ });
329
+ (0, vitest_1.it)('returns empty for empty input', async () => {
330
+ const result = await (0, polymarket_js_1.polymarketGetMidpoints)([]);
331
+ (0, vitest_1.expect)(result).toEqual({});
332
+ (0, vitest_1.expect)(mockFetch).not.toHaveBeenCalled();
333
+ });
334
+ });
335
+ (0, vitest_1.describe)('polymarketGetOHLC', () => {
336
+ (0, vitest_1.it)('fetches candlestick data', async () => {
337
+ const candles = [
338
+ { t: 1000, o: 0.5, h: 0.55, l: 0.48, c: 0.52, v: 100 },
339
+ ];
340
+ mockFetch.mockResolvedValue({
341
+ ok: true,
342
+ json: () => Promise.resolve(candles),
343
+ });
344
+ const result = await (0, polymarket_js_1.polymarketGetOHLC)({
345
+ tokenId: 'tok1',
346
+ startTs: 1000,
347
+ fidelity: '1h',
348
+ });
349
+ (0, vitest_1.expect)(result).toHaveLength(1);
350
+ (0, vitest_1.expect)(result[0].c).toBe(0.52);
351
+ const url = mockFetch.mock.calls[0][0];
352
+ (0, vitest_1.expect)(url).toContain('clob.polymarket.com/ohlc');
353
+ (0, vitest_1.expect)(url).toContain('asset_id=tok1');
354
+ (0, vitest_1.expect)(url).toContain('fidelity=1h');
355
+ });
356
+ (0, vitest_1.it)('returns empty array on error', async () => {
357
+ mockFetch.mockResolvedValue({ ok: false, status: 400 });
358
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetOHLC)({ tokenId: 'bad', startTs: 0 })).toEqual([]);
359
+ });
360
+ });
361
+ (0, vitest_1.describe)('polymarketGetPriceHistory', () => {
362
+ (0, vitest_1.it)('fetches price history with interval', async () => {
363
+ mockFetch.mockResolvedValue({
364
+ ok: true,
365
+ json: () => Promise.resolve({
366
+ history: [{ t: 1000, p: 0.5 }, { t: 2000, p: 0.55 }],
367
+ }),
368
+ });
369
+ const result = await (0, polymarket_js_1.polymarketGetPriceHistory)({
370
+ tokenId: 'tok1',
371
+ interval: '1d',
372
+ });
373
+ (0, vitest_1.expect)(result).toHaveLength(2);
374
+ const url = mockFetch.mock.calls[0][0];
375
+ (0, vitest_1.expect)(url).toContain('prices-history');
376
+ (0, vitest_1.expect)(url).toContain('interval=1d');
377
+ });
378
+ (0, vitest_1.it)('fetches with absolute timestamps', async () => {
379
+ mockFetch.mockResolvedValue({
380
+ ok: true,
381
+ json: () => Promise.resolve({ history: [] }),
382
+ });
383
+ await (0, polymarket_js_1.polymarketGetPriceHistory)({
384
+ tokenId: 'tok1',
385
+ startTs: 1000,
386
+ endTs: 2000,
387
+ fidelity: 60,
388
+ });
389
+ const url = mockFetch.mock.calls[0][0];
390
+ (0, vitest_1.expect)(url).toContain('startTs=1000');
391
+ (0, vitest_1.expect)(url).toContain('endTs=2000');
392
+ (0, vitest_1.expect)(url).toContain('fidelity=60');
393
+ (0, vitest_1.expect)(url).not.toContain('interval=');
394
+ });
395
+ });
396
+ (0, vitest_1.describe)('polymarketGetPositions', () => {
397
+ (0, vitest_1.it)('fetches positions for wallet address', async () => {
398
+ mockFetch.mockResolvedValue({
399
+ ok: true,
400
+ json: () => Promise.resolve([
401
+ { asset: 'tok1', size: 100, outcome: 'Yes' },
402
+ ]),
403
+ });
404
+ const positions = await (0, polymarket_js_1.polymarketGetPositions)('0xabc123');
405
+ (0, vitest_1.expect)(positions).toHaveLength(1);
406
+ (0, vitest_1.expect)(positions[0].outcome).toBe('Yes');
407
+ (0, vitest_1.expect)(mockFetch.mock.calls[0][0]).toContain('data-api.polymarket.com/positions?user=0xabc123');
408
+ });
409
+ (0, vitest_1.it)('returns empty on error', async () => {
410
+ mockFetch.mockResolvedValue({ ok: false });
411
+ (0, vitest_1.expect)(await (0, polymarket_js_1.polymarketGetPositions)('bad')).toEqual([]);
412
+ });
413
+ });
414
+ (0, vitest_1.describe)('polymarketGetClosedPositions', () => {
415
+ (0, vitest_1.it)('fetches closed positions', async () => {
416
+ mockFetch.mockResolvedValue({
417
+ ok: true,
418
+ json: () => Promise.resolve([{ asset: 'tok1', outcome: 'No' }]),
419
+ });
420
+ const positions = await (0, polymarket_js_1.polymarketGetClosedPositions)('0xabc');
421
+ (0, vitest_1.expect)(positions).toHaveLength(1);
422
+ (0, vitest_1.expect)(mockFetch.mock.calls[0][0]).toContain('closed-positions');
423
+ });
424
+ });
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Agent bridge — connects Telegram to pi-agent-core
3
+ *
4
+ * Uses the SAME tools as sf agent --plain. Multi-turn tool calling
5
+ * is handled by pi-agent-core's Agent class (not manual OpenRouter calls).
6
+ */
7
+ import { SFClient } from '../client.js';
8
+ interface SessionState {
9
+ thesisId: string | null;
10
+ agentMessages: any[];
11
+ agent?: any;
12
+ }
13
+ export declare function getOrCreateAgent(sfClient: SFClient, session: SessionState): Promise<any>;
14
+ export declare function runAgentMessage(client: SFClient, session: SessionState, userMessage: string): Promise<string>;
15
+ export {};