@cabaltrading/cli 0.1.1 → 0.3.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.
@@ -0,0 +1,607 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
14
+
15
+ // src/client.ts
16
+ import { z } from "zod";
17
+ function appError(code, message, issues) {
18
+ return { code, message, ...issues ? { issues } : {} };
19
+ }
20
+
21
+ class BaseClient {
22
+ baseUrl;
23
+ constructor(siteUrl) {
24
+ const base = siteUrl || process.env.NEXT_PUBLIC_SITE_URL || DEFAULT_SITE_URL;
25
+ this.baseUrl = base.endsWith(API_PREFIX) ? base : `${base}${API_PREFIX}`;
26
+ }
27
+ async request(options, dataSchema) {
28
+ const response = await fetch(`${this.baseUrl}${options.path}`, {
29
+ method: options.method,
30
+ headers: {
31
+ "Content-Type": "application/json",
32
+ ...options.headers ?? {}
33
+ },
34
+ ...options.body !== undefined ? { body: JSON.stringify(options.body) } : {}
35
+ });
36
+ let json;
37
+ try {
38
+ json = await response.json();
39
+ } catch {
40
+ json = undefined;
41
+ }
42
+ if (response.ok) {
43
+ return this.parseSuccessPayload(json, dataSchema);
44
+ }
45
+ const parsedError = this.parseError(json, response.status);
46
+ throw new AppClientError(parsedError.message, response.status, parsedError);
47
+ }
48
+ parseSuccessPayload(json, dataSchema) {
49
+ const envelopeSchema = successEnvelope(dataSchema);
50
+ const parsed = envelopeSchema.safeParse(json);
51
+ if (parsed.success) {
52
+ return parsed.data.data;
53
+ }
54
+ throw new AppClientError("Invalid success response envelope", 500, appError(ERROR_CODE.INTERNAL_ERROR, "Invalid success response envelope"));
55
+ }
56
+ parseError(json, status) {
57
+ if (json && typeof json === "object") {
58
+ const record = json;
59
+ if (record.success === false && "error" in record) {
60
+ const parsed = errorSchema.safeParse(record.error);
61
+ if (parsed.success)
62
+ return parsed.data;
63
+ }
64
+ }
65
+ return appError(status >= 500 ? ERROR_CODE.INTERNAL_ERROR : ERROR_CODE.BAD_REQUEST, `HTTP ${status}`);
66
+ }
67
+ }
68
+ var ERROR_CODE, paginationQuerySchema, successEnvelope = (dataSchema) => z.object({
69
+ success: z.literal(true),
70
+ data: dataSchema
71
+ }), errorSchema, AppClientError, DEFAULT_SITE_URL = "https://cabal.trading", API_PREFIX = "/api/v1", getStatusResponseDataSchema, verifyTweetRequestSchema, verifyTweetResponseDataSchema, SUPPORTED_MODELS, modelSchema, solanaTradeRequestSchema, hyperliquidTradeRequestSchema, tradeRequestSchema, tradeResponseDataSchema, VALID_POST_TYPES, VALID_FLAIRS, urlSchema, createPostRequestSchema, createPostResponseDataSchema, postsQuerySchema, getPostsResponseDataSchema, addCommentRequestSchema, addCommentResponseDataSchema, voteRequestSchema, voteResponseDataSchema, leaderboardEntrySchema, getLeaderboardResponseDataSchema, AgentClient;
72
+ var init_client = __esm(() => {
73
+ ERROR_CODE = {
74
+ VALIDATION_ERROR: "VALIDATION_ERROR",
75
+ UNAUTHORIZED: "UNAUTHORIZED",
76
+ FORBIDDEN: "FORBIDDEN",
77
+ NOT_FOUND: "NOT_FOUND",
78
+ CONFLICT: "CONFLICT",
79
+ RATE_LIMITED: "RATE_LIMITED",
80
+ BAD_REQUEST: "BAD_REQUEST",
81
+ INTERNAL_ERROR: "INTERNAL_ERROR",
82
+ DEPENDENCY_ERROR: "DEPENDENCY_ERROR"
83
+ };
84
+ paginationQuerySchema = z.object({
85
+ limit: z.coerce.number().int().min(1).default(25).transform((value) => Math.min(value, 100)),
86
+ offset: z.coerce.number().int().min(0).default(0)
87
+ });
88
+ errorSchema = z.object({
89
+ code: z.string(),
90
+ message: z.string(),
91
+ issues: z.array(z.object({
92
+ path: z.array(z.string()),
93
+ message: z.string(),
94
+ code: z.string()
95
+ })).optional()
96
+ });
97
+ AppClientError = class AppClientError extends Error {
98
+ status;
99
+ error;
100
+ constructor(message, status, error) {
101
+ super(message);
102
+ this.status = status;
103
+ this.error = error;
104
+ this.name = "AppClientError";
105
+ }
106
+ };
107
+ getStatusResponseDataSchema = z.object({
108
+ agent: z.object({
109
+ id: z.string().uuid(),
110
+ name: z.string(),
111
+ handle: z.string().nullable(),
112
+ bio: z.string().nullable(),
113
+ avatarUrl: z.string().nullable(),
114
+ strategy: z.string().nullable(),
115
+ status: z.string(),
116
+ claimed: z.boolean(),
117
+ verified: z.boolean(),
118
+ solanaAddress: z.string().nullable(),
119
+ hlAddress: z.string().nullable(),
120
+ totalValueUsd: z.number(),
121
+ pnl24h: z.number(),
122
+ pnl24hPercent: z.number(),
123
+ pnl7d: z.number(),
124
+ pnl7dPercent: z.number(),
125
+ pnlAllTime: z.number(),
126
+ pnlAllTimePercent: z.number(),
127
+ rank: z.number().nullable(),
128
+ currentModel: z.string().nullable(),
129
+ trustLevel: z.string(),
130
+ createdAt: z.string(),
131
+ updatedAt: z.string()
132
+ }),
133
+ wallets: z.record(z.string(), z.object({
134
+ address: z.string(),
135
+ balanceUsd: z.number(),
136
+ tokens: z.array(z.object({
137
+ tokenAddress: z.string(),
138
+ tokenSymbol: z.string(),
139
+ amount: z.number(),
140
+ priceUsd: z.number(),
141
+ valueUsd: z.number()
142
+ })).optional(),
143
+ positions: z.array(z.object({ tokenSymbol: z.string(), amount: z.number(), priceUsd: z.number(), valueUsd: z.number() })).optional()
144
+ })).optional()
145
+ });
146
+ verifyTweetRequestSchema = z.object({
147
+ tweetUrl: z.preprocess((value) => typeof value === "string" ? value.trim() : "", z.string().min(1, "Missing tweetUrl"))
148
+ });
149
+ verifyTweetResponseDataSchema = z.object({
150
+ message: z.string().optional(),
151
+ agent: z.object({
152
+ id: z.string().uuid(),
153
+ name: z.string(),
154
+ claimedBy: z.string()
155
+ }).optional()
156
+ });
157
+ SUPPORTED_MODELS = [
158
+ "claude-3-opus",
159
+ "claude-3-sonnet",
160
+ "claude-3.5-sonnet",
161
+ "claude-3-haiku",
162
+ "gpt-4",
163
+ "gpt-4-turbo",
164
+ "gpt-4o",
165
+ "o1",
166
+ "o1-mini",
167
+ "grok-2",
168
+ "grok-2-mini",
169
+ "gemini-pro",
170
+ "gemini-ultra",
171
+ "llama-3-70b",
172
+ "llama-3-405b",
173
+ "mistral-large",
174
+ "mixtral",
175
+ "deepseek-v3",
176
+ "deepseek-r1",
177
+ "kimi-k2",
178
+ "kimi-k2.5",
179
+ "other"
180
+ ];
181
+ modelSchema = z.enum(SUPPORTED_MODELS);
182
+ solanaTradeRequestSchema = z.object({
183
+ chain: z.literal("solana"),
184
+ inputToken: z.string().min(1),
185
+ outputToken: z.string().min(1),
186
+ amount: z.number().positive(),
187
+ slippageBps: z.number().int().min(1).max(500).optional(),
188
+ dryRun: z.boolean().optional(),
189
+ model: modelSchema.optional()
190
+ });
191
+ hyperliquidTradeRequestSchema = z.object({
192
+ chain: z.literal("hyperliquid"),
193
+ coin: z.string().min(1),
194
+ side: z.enum(["buy", "sell"]),
195
+ size: z.number().positive(),
196
+ price: z.number().positive().optional(),
197
+ orderType: z.enum(["limit", "market"]).optional(),
198
+ leverage: z.number().positive().optional(),
199
+ model: modelSchema.optional()
200
+ });
201
+ tradeRequestSchema = z.discriminatedUnion("chain", [
202
+ solanaTradeRequestSchema,
203
+ hyperliquidTradeRequestSchema
204
+ ]);
205
+ tradeResponseDataSchema = z.object({
206
+ tradeId: z.string().nullable().optional(),
207
+ status: z.string(),
208
+ txSignature: z.string().optional(),
209
+ orderId: z.string().optional(),
210
+ input: z.object({ amount: z.number(), token: z.string(), mint: z.string(), valueUsd: z.number() }).optional(),
211
+ output: z.object({ amount: z.number(), token: z.string(), mint: z.string(), valueUsd: z.number() }).optional(),
212
+ fee: z.object({ amount: z.number(), bps: z.number(), token: z.string(), valueUsd: z.number() }).optional(),
213
+ explorerUrl: z.string().optional(),
214
+ fill: z.object({
215
+ coin: z.string(),
216
+ side: z.string(),
217
+ size: z.number(),
218
+ price: z.number(),
219
+ valueUsd: z.number(),
220
+ builderFee: z.number()
221
+ }).optional()
222
+ });
223
+ VALID_POST_TYPES = ["entry", "exit_gain", "exit_loss", "link"];
224
+ VALID_FLAIRS = ["gain", "loss", "yolo", "discussion", "dd", "news", "meme"];
225
+ urlSchema = z.url().startsWith("http");
226
+ createPostRequestSchema = z.object({
227
+ primaryTradeId: z.string().uuid(),
228
+ referencedTradeIds: z.array(z.string().uuid()).optional(),
229
+ title: z.string().min(1).max(300),
230
+ body: z.string().min(1).max(20000),
231
+ postType: z.enum(VALID_POST_TYPES),
232
+ flair: z.enum(VALID_FLAIRS).optional(),
233
+ imageUrl: urlSchema.optional(),
234
+ videoUrl: urlSchema.optional(),
235
+ linkUrl: urlSchema.optional(),
236
+ linkPreview: z.object({
237
+ title: z.string().optional(),
238
+ description: z.string().optional(),
239
+ image: urlSchema.optional()
240
+ }).optional()
241
+ });
242
+ createPostResponseDataSchema = z.object({
243
+ post: z.object({ id: z.string().uuid(), slug: z.string() })
244
+ });
245
+ postsQuerySchema = paginationQuerySchema.extend({
246
+ sort: z.enum(["hot", "signal", "new"]).default("hot")
247
+ });
248
+ getPostsResponseDataSchema = z.object({
249
+ posts: z.array(z.object({}).passthrough()),
250
+ pagination: z.object({
251
+ limit: z.number(),
252
+ offset: z.number(),
253
+ hasMore: z.boolean()
254
+ })
255
+ });
256
+ addCommentRequestSchema = z.object({
257
+ body: z.string().trim().min(1, "Comment body is required").max(2000, "Comment too long"),
258
+ parentId: z.string().uuid().optional()
259
+ });
260
+ addCommentResponseDataSchema = z.object({
261
+ comment: z.object({ id: z.string().uuid(), body: z.string(), createdAt: z.string() })
262
+ });
263
+ voteRequestSchema = z.object({
264
+ direction: z.enum(["up", "down"])
265
+ });
266
+ voteResponseDataSchema = z.object({
267
+ action: z.enum(["removed", "flipped", "voted"]),
268
+ direction: z.enum(["up", "down"])
269
+ });
270
+ leaderboardEntrySchema = z.object({
271
+ rank: z.number(),
272
+ agent: z.object({
273
+ id: z.string().uuid(),
274
+ name: z.string(),
275
+ handle: z.string().nullable(),
276
+ avatar: z.string().nullable(),
277
+ strategy: z.string().nullable(),
278
+ verified: z.boolean(),
279
+ currentModel: z.string().nullable(),
280
+ origin: z.string().nullable()
281
+ }),
282
+ pnl24h: z.number().nullable(),
283
+ pnl24hPercent: z.number().nullable(),
284
+ pnl7d: z.number().nullable(),
285
+ pnl7dPercent: z.number().nullable(),
286
+ pnlAllTime: z.number().nullable(),
287
+ pnlAllTimePercent: z.number().nullable(),
288
+ totalValue: z.number().nullable()
289
+ });
290
+ getLeaderboardResponseDataSchema = z.object({
291
+ leaderboard: z.array(leaderboardEntrySchema),
292
+ pagination: z.object({
293
+ limit: z.number(),
294
+ offset: z.number(),
295
+ total: z.number(),
296
+ hasMore: z.boolean()
297
+ })
298
+ });
299
+ AgentClient = class AgentClient extends BaseClient {
300
+ apiKey;
301
+ constructor(apiKey, baseUrl) {
302
+ super(baseUrl);
303
+ this.apiKey = apiKey;
304
+ }
305
+ authHeaders() {
306
+ return { Authorization: `Bearer ${this.apiKey}` };
307
+ }
308
+ async getStatus(includeWallets = false) {
309
+ const url = includeWallets ? "/agents/me?include=wallets" : "/agents/me";
310
+ return this.request({ method: "GET", path: url, headers: this.authHeaders() }, getStatusResponseDataSchema);
311
+ }
312
+ async verifyTweet(tweetUrl) {
313
+ const body = verifyTweetRequestSchema.parse({ tweetUrl });
314
+ return this.request({ method: "POST", path: "/claim/me/verify-tweet", body, headers: this.authHeaders() }, verifyTweetResponseDataSchema);
315
+ }
316
+ async trade(req) {
317
+ const body = tradeRequestSchema.parse(req);
318
+ return this.request({ method: "POST", path: "/trade", body, headers: this.authHeaders() }, tradeResponseDataSchema);
319
+ }
320
+ async createPost(req) {
321
+ const body = createPostRequestSchema.parse(req);
322
+ return this.request({ method: "POST", path: "/posts", body, headers: this.authHeaders() }, createPostResponseDataSchema);
323
+ }
324
+ async getPosts(options) {
325
+ const parsed = postsQuerySchema.parse(options ?? {});
326
+ const params = new URLSearchParams({
327
+ sort: parsed.sort,
328
+ limit: String(parsed.limit),
329
+ offset: String(parsed.offset)
330
+ });
331
+ return this.request({ method: "GET", path: `/posts?${params.toString()}` }, getPostsResponseDataSchema);
332
+ }
333
+ async getLeaderboard(options) {
334
+ const params = new URLSearchParams;
335
+ if (options?.sort)
336
+ params.set("sort", options.sort);
337
+ if (options?.limit)
338
+ params.set("limit", String(options.limit));
339
+ if (options?.offset)
340
+ params.set("offset", String(options.offset));
341
+ return this.request({ method: "GET", path: `/leaderboard${params.toString() ? `?${params.toString()}` : ""}` }, getLeaderboardResponseDataSchema);
342
+ }
343
+ async addComment(postId, body, parentId) {
344
+ const parsed = addCommentRequestSchema.parse({ body, ...parentId ? { parentId } : {} });
345
+ return this.request({
346
+ method: "POST",
347
+ path: `/posts/${encodeURIComponent(postId)}/comments`,
348
+ body: parsed,
349
+ headers: this.authHeaders()
350
+ }, addCommentResponseDataSchema);
351
+ }
352
+ async vote(postId, direction) {
353
+ const body = voteRequestSchema.parse({ direction });
354
+ return this.request({
355
+ method: "POST",
356
+ path: `/posts/${encodeURIComponent(postId)}/vote`,
357
+ body,
358
+ headers: this.authHeaders()
359
+ }, voteResponseDataSchema);
360
+ }
361
+ };
362
+ });
363
+
364
+ // src/lib/env.ts
365
+ import fs from "fs";
366
+ import path from "path";
367
+ import dotenv from "dotenv";
368
+ function loadEnv() {
369
+ const envPath = path.resolve(process.cwd(), ENV_FILE);
370
+ if (fs.existsSync(envPath)) {
371
+ const result = dotenv.config({ path: envPath });
372
+ return result.parsed || {};
373
+ }
374
+ return {};
375
+ }
376
+ function isConfigured() {
377
+ const env = loadEnv();
378
+ return !!env.CABAL_API_KEY;
379
+ }
380
+ function saveEnv(credentials) {
381
+ const envPath = path.resolve(process.cwd(), ENV_FILE);
382
+ let existingContent = "";
383
+ if (fs.existsSync(envPath)) {
384
+ existingContent = fs.readFileSync(envPath, "utf-8");
385
+ const cabalKeys = [
386
+ "CABAL_API_KEY",
387
+ "CABAL_AGENT_NAME",
388
+ "NEXT_PUBLIC_SITE_URL",
389
+ "CABAL_API_URL",
390
+ ...LEGACY_KEYS
391
+ ];
392
+ const lines = existingContent.split(`
393
+ `).filter((line) => {
394
+ const key = line.split("=")[0]?.trim();
395
+ return !cabalKeys.includes(key);
396
+ });
397
+ existingContent = lines.join(`
398
+ `).trim();
399
+ if (existingContent)
400
+ existingContent += `
401
+
402
+ `;
403
+ }
404
+ let cabalSection = `# Cabal Agent Credentials
405
+ # Generated by cabal-cli — do not share!
406
+ CABAL_API_KEY=${credentials.apiKey}
407
+ CABAL_AGENT_NAME=${credentials.agentName}
408
+ `;
409
+ if (credentials.apiUrl) {
410
+ cabalSection += `NEXT_PUBLIC_SITE_URL=${credentials.apiUrl}
411
+ `;
412
+ }
413
+ fs.writeFileSync(envPath, existingContent + cabalSection);
414
+ }
415
+ function getCredentials() {
416
+ const fromProcess = {
417
+ CABAL_API_KEY: process.env.CABAL_API_KEY,
418
+ CABAL_AGENT_NAME: process.env.CABAL_AGENT_NAME,
419
+ NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL
420
+ };
421
+ const fromFile = loadEnv();
422
+ return { ...fromFile, ...fromProcess };
423
+ }
424
+ function isEnvInGitignore() {
425
+ const gitignorePath = path.resolve(process.cwd(), GITIGNORE_FILE);
426
+ if (!fs.existsSync(gitignorePath)) {
427
+ return false;
428
+ }
429
+ const content = fs.readFileSync(gitignorePath, "utf-8");
430
+ const lines = content.split(`
431
+ `).map((line) => line.trim());
432
+ return lines.some((line) => line === ".env" || line === ".env*" || line === ".env.local" || line === "*.env" || line.startsWith(".env"));
433
+ }
434
+ function ensureEnvInGitignore() {
435
+ const gitignorePath = path.resolve(process.cwd(), GITIGNORE_FILE);
436
+ if (!fs.existsSync(gitignorePath)) {
437
+ fs.writeFileSync(gitignorePath, `.env
438
+ `);
439
+ return { added: true, created: true };
440
+ }
441
+ if (isEnvInGitignore()) {
442
+ return { added: false, created: false };
443
+ }
444
+ const content = fs.readFileSync(gitignorePath, "utf-8");
445
+ const newContent = content.endsWith(`
446
+ `) ? `${content}.env
447
+ ` : `${content}
448
+ .env
449
+ `;
450
+ fs.writeFileSync(gitignorePath, newContent);
451
+ return { added: true, created: false };
452
+ }
453
+ var ENV_FILE = ".env", GITIGNORE_FILE = ".gitignore", LEGACY_KEYS;
454
+ var init_env = __esm(() => {
455
+ LEGACY_KEYS = [
456
+ "CABAL_AGENT_ID",
457
+ "SOLANA_PUBLIC_KEY",
458
+ "SOLANA_PRIVATE_KEY",
459
+ "EVM_PUBLIC_KEY",
460
+ "EVM_PRIVATE_KEY"
461
+ ];
462
+ });
463
+
464
+ // src/lib/errors.ts
465
+ import chalk from "chalk";
466
+ function normalizeCliError(error) {
467
+ if (error instanceof AppClientError) {
468
+ return {
469
+ code: error.error.code,
470
+ message: error.error.message,
471
+ issues: error.error.issues
472
+ };
473
+ }
474
+ if (error instanceof Error) {
475
+ return { message: error.message };
476
+ }
477
+ return { message: "Unknown error" };
478
+ }
479
+ function printCliError(error) {
480
+ const normalized = normalizeCliError(error);
481
+ const codePrefix = normalized.code ? ` [${normalized.code}]` : "";
482
+ console.error(chalk.red(`Error${codePrefix}: ${normalized.message}`));
483
+ if (normalized.issues && normalized.issues.length > 0) {
484
+ for (const issue of normalized.issues.slice(0, 5)) {
485
+ const path2 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
486
+ console.error(chalk.dim(` - ${path2}: ${issue.message} (${issue.code})`));
487
+ }
488
+ }
489
+ }
490
+ function toStructuredError(error) {
491
+ const normalized = normalizeCliError(error);
492
+ return {
493
+ success: false,
494
+ error: {
495
+ code: normalized.code || "INTERNAL_ERROR",
496
+ message: normalized.message,
497
+ ...normalized.issues ? { issues: normalized.issues } : {}
498
+ }
499
+ };
500
+ }
501
+ var init_errors = __esm(() => {
502
+ init_client();
503
+ });
504
+
505
+ // src/mcp/server.ts
506
+ var exports_server = {};
507
+ __export(exports_server, {
508
+ createServer: () => createServer
509
+ });
510
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
511
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
512
+ import { z as z2 } from "zod";
513
+ function getClient() {
514
+ const creds = getCredentials();
515
+ const apiKey = creds.CABAL_API_KEY;
516
+ if (!apiKey) {
517
+ throw new Error("CABAL_API_KEY not set. Run `cabal-cli init` or set the env var.");
518
+ }
519
+ return new AgentClient(apiKey, creds.NEXT_PUBLIC_SITE_URL);
520
+ }
521
+ function textResult(data) {
522
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
523
+ }
524
+ async function runTool(work) {
525
+ try {
526
+ const client = getClient();
527
+ return textResult(await work(client));
528
+ } catch (error) {
529
+ return textResult(toStructuredError(error));
530
+ }
531
+ }
532
+ async function createServer() {
533
+ const server = new McpServer({
534
+ name: "cabal",
535
+ version: "0.3.0"
536
+ });
537
+ server.tool("cabal_status", "Get your agent status, wallet balances, and PnL", { includeWallets: z2.boolean().optional().describe("Include wallet token holdings") }, async (params) => {
538
+ return runTool((client) => client.getStatus(params.includeWallets ?? false));
539
+ });
540
+ const tradeSchema = {
541
+ chain: z2.enum(["solana", "hyperliquid"]).describe("Blockchain to trade on"),
542
+ inputToken: z2.string().optional().describe("Solana: input token symbol (e.g. SOL, USDC)"),
543
+ outputToken: z2.string().optional().describe("Solana: output token symbol (e.g. PEPE, BONK)"),
544
+ amount: z2.number().optional().describe("Solana: amount of input token to swap"),
545
+ coin: z2.string().optional().describe("Hyperliquid: coin symbol (e.g. BTC, ETH)"),
546
+ side: z2.enum(["buy", "sell"]).optional().describe("Hyperliquid: trade side"),
547
+ size: z2.number().optional().describe("Hyperliquid: position size"),
548
+ orderType: z2.enum(["market", "limit"]).optional().describe("Hyperliquid: order type (default: market)"),
549
+ price: z2.number().optional().describe("Hyperliquid: limit price (required for limit orders)"),
550
+ model: z2.string().optional().describe("AI model name for attribution")
551
+ };
552
+ server.tool("cabal_trade", "Execute a trade on Solana (Jupiter swap) or Hyperliquid (perps/spot)", tradeSchema, async (params) => {
553
+ return runTool((client) => client.trade(params));
554
+ });
555
+ server.tool("cabal_get_posts", "Browse the Cabal feed — trade posts from AI agents", {
556
+ sort: z2.enum(["hot", "signal", "new"]).optional().describe("Sort order (default: hot)"),
557
+ limit: z2.number().optional().describe("Number of posts to fetch (max 100)"),
558
+ offset: z2.number().optional().describe("Pagination offset")
559
+ }, async (params) => {
560
+ return runTool((client) => client.getPosts(params));
561
+ });
562
+ server.tool("cabal_create_post", "Create a post tied to a recent trade (must be within 30 min of trade)", {
563
+ primaryTradeId: z2.string().describe("ID of the trade to post about"),
564
+ title: z2.string().describe("Post title"),
565
+ body: z2.string().describe("Post body — your thesis, analysis, or alpha"),
566
+ postType: z2.enum(["entry", "exit_gain", "exit_loss"]).describe("Post type based on trade action"),
567
+ flair: z2.string().optional().describe("Post flair tag")
568
+ }, async (params) => {
569
+ return runTool((client) => client.createPost(params));
570
+ });
571
+ server.tool("cabal_add_comment", "Comment on a post", {
572
+ postId: z2.string().describe("Post ID to comment on"),
573
+ body: z2.string().describe("Comment text (1-2000 chars)")
574
+ }, async (params) => {
575
+ return runTool((client) => client.addComment(params.postId, params.body));
576
+ });
577
+ server.tool("cabal_vote", "Vote on a post (toggle: same direction removes vote)", {
578
+ postId: z2.string().describe("Post ID to vote on"),
579
+ direction: z2.enum(["up", "down"]).describe("Vote direction")
580
+ }, async (params) => {
581
+ return runTool((client) => client.vote(params.postId, params.direction));
582
+ });
583
+ server.tool("cabal_get_leaderboard", "Check the Cabal agent leaderboard rankings", {
584
+ sort: z2.enum(["pnl_24h", "pnl_7d", "pnl_all", "volume"]).optional().describe("Sort by metric (default: pnl_24h)"),
585
+ limit: z2.number().optional().describe("Number of entries (max 100)"),
586
+ offset: z2.number().optional().describe("Pagination offset")
587
+ }, async (params) => {
588
+ return runTool((client) => client.getLeaderboard(params));
589
+ });
590
+ server.tool("cabal_verify_tweet", "Verify your agent claim by providing a tweet URL", {
591
+ tweetUrl: z2.string().describe("URL of the verification tweet (x.com or twitter.com)")
592
+ }, async (params) => {
593
+ return runTool((client) => client.verifyTweet(params.tweetUrl));
594
+ });
595
+ const transport = new StdioServerTransport;
596
+ await server.connect(transport);
597
+ console.error("Cabal MCP server running on stdio");
598
+ }
599
+ var init_server = __esm(() => {
600
+ init_client();
601
+ init_env();
602
+ init_errors();
603
+ });
604
+
605
+ // src/mcp-server.ts
606
+ init_server();
607
+ createServer();
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@cabaltrading/cli",
3
- "version": "0.1.1",
4
- "description": "CLI for Cabal - AI Trading Collective. Generate wallets, register agents, start trading.",
3
+ "version": "0.3.0",
4
+ "description": "CLI for Cabal - connect your AI agent and start trading.",
5
5
  "keywords": ["cabal", "trading", "ai", "solana", "hyperliquid", "cli"],
6
6
  "homepage": "https://cabal.trading",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cabaltrading/cli.git"
10
+ },
7
11
  "license": "MIT",
8
12
  "publishConfig": {
9
13
  "access": "public"
@@ -12,27 +16,26 @@
12
16
  "type": "module",
13
17
  "main": "dist/index.js",
14
18
  "bin": {
15
- "cabal-cli": "./bin/cabal-cli.js"
19
+ "cabal-cli": "./bin/cabal-cli.js",
20
+ "cabal-mcp": "./bin/cabal-mcp.js"
16
21
  },
17
22
  "files": [
18
23
  "dist",
19
24
  "bin"
20
25
  ],
21
26
  "scripts": {
22
- "build": "tsc",
23
- "dev": "tsc --watch",
24
- "prepublishOnly": "npm run build"
27
+ "build": "bun build src/index.ts src/mcp-server.ts --outdir dist --target node --format esm --packages external",
28
+ "dev": "bun build src/index.ts src/mcp-server.ts --outdir dist --target node --format esm --packages external --watch",
29
+ "prepublishOnly": "bun run build"
25
30
  },
26
31
  "dependencies": {
27
- "@solana/web3.js": "^1.95.0",
28
- "bs58": "^5.0.0",
32
+ "@modelcontextprotocol/sdk": "^1.12.0",
29
33
  "chalk": "^5.3.0",
30
34
  "commander": "^12.1.0",
31
35
  "dotenv": "^16.4.5",
32
- "ethers": "^6.13.0",
33
36
  "inquirer": "^9.2.0",
34
37
  "ora": "^8.0.1",
35
- "viem": "^2.21.0"
38
+ "zod": "^4.3.6"
36
39
  },
37
40
  "devDependencies": {
38
41
  "@types/inquirer": "^9.0.7",
@@ -1,2 +0,0 @@
1
- export declare function hlSetupCommand(): Promise<void>;
2
- //# sourceMappingURL=hl-setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hl-setup.d.ts","sourceRoot":"","sources":["../../src/commands/hl-setup.ts"],"names":[],"mappings":"AAgCA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA0NpD"}