@pionex/pionex-ai-kit 0.2.35 → 0.2.37

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/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // src/index.ts
4
4
  import { createInterface } from "readline";
5
5
  import { basename } from "path";
6
+ import { createRequire } from "module";
6
7
 
7
8
  // ../core/dist/index.js
8
9
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
@@ -987,8 +988,8 @@ function runSetup(options) {
987
988
  );
988
989
  }
989
990
  var PIONEX_API_DEFAULT_BASE_URL = "https://api.pionex.com";
990
- var MODULES = ["market", "account", "orders", "bot"];
991
- var DEFAULT_MODULES = ["market", "account", "orders", "bot"];
991
+ var MODULES = ["market", "account", "orders", "bot", "earn_dual"];
992
+ var DEFAULT_MODULES = ["market", "account", "orders", "bot", "earn_dual"];
992
993
  var ConfigError = class extends Error {
993
994
  suggestion;
994
995
  constructor(message, suggestion) {
@@ -1163,6 +1164,16 @@ var PionexRestClient = class {
1163
1164
  const data = await res.json();
1164
1165
  return { endpoint: path2, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
1165
1166
  }
1167
+ async signedDeleteQuery(path2, query = {}) {
1168
+ const { url, headers } = buildSignedRequest(this.config, "DELETE", path2, query, null);
1169
+ const res = await fetch(url, { method: "DELETE", headers });
1170
+ if (!res.ok) {
1171
+ const txt = await readTextSafe(res);
1172
+ throw new PionexApiError(`HTTP ${res.status}: ${txt || res.statusText}`, { status: res.status, endpoint: path2, responseText: txt });
1173
+ }
1174
+ const data = await res.json();
1175
+ return { endpoint: path2, requestTime: (/* @__PURE__ */ new Date()).toISOString(), data };
1176
+ }
1166
1177
  };
1167
1178
  function registerMarketTools() {
1168
1179
  return [
@@ -2065,8 +2076,291 @@ function registerBotTools() {
2065
2076
  }
2066
2077
  ];
2067
2078
  }
2079
+ function registerEarnDualTools() {
2080
+ return [
2081
+ // ─── Public endpoints ────────────────────────────────────────────────────
2082
+ {
2083
+ name: "pionex_earn_dual_symbols",
2084
+ module: "earn_dual",
2085
+ isWrite: false,
2086
+ description: "List all trading pairs supported by Dual Investment, optionally filtered by base currency. Supported quote currencies: USDT, USDC, USD, USDXO. No authentication required.",
2087
+ inputSchema: {
2088
+ type: "object",
2089
+ additionalProperties: false,
2090
+ properties: {
2091
+ base: { type: "string", description: "Base currency filter (e.g. BTC, ETH). Omit to return all supported pairs." }
2092
+ }
2093
+ },
2094
+ async handler(args, { client }) {
2095
+ const base = args.base;
2096
+ return (await client.publicGet("/api/v1/earn/dual/symbols", base ? { base } : {})).data;
2097
+ }
2098
+ },
2099
+ {
2100
+ name: "pionex_earn_dual_open_products",
2101
+ module: "earn_dual",
2102
+ isWrite: false,
2103
+ description: "List currently open Dual Investment products for a specific trading pair and direction. DUAL_BASE: invest in base currency (e.g. BTC); DUAL_CURRENCY: invest in investment currency (e.g. USDT). Product ID format: {BASE}-{QUOTE}-{YYMMDD}-{STRIKE}-{C|P}-{CURRENCY}, where C=DUAL_BASE, P=DUAL_CURRENCY. For BTC/ETH use quote=USDXO with currency=USDT or USDC. For other bases use quote=USDT with currency=USDT. No authentication required.",
2104
+ inputSchema: {
2105
+ type: "object",
2106
+ additionalProperties: false,
2107
+ required: ["base", "quote", "type"],
2108
+ properties: {
2109
+ base: { type: "string", description: "Base currency (e.g. BTC, ETH, XRP)" },
2110
+ quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency. Use USDXO for BTC/ETH; use USDT for all other base currencies." },
2111
+ type: { type: "string", enum: ["DUAL_BASE", "DUAL_CURRENCY"], description: "DUAL_BASE: invest in base currency (product ID suffix C); DUAL_CURRENCY: invest in investment currency (product ID suffix P)" },
2112
+ currency: { type: "string", description: "Investment currency filter. For BTC/ETH: USDT or USDC. For other pairs: USDT." }
2113
+ }
2114
+ },
2115
+ async handler(args, { client }) {
2116
+ const base = args.base;
2117
+ const quote = args.quote;
2118
+ const type = args.type;
2119
+ const currency = args.currency;
2120
+ const query = { base, quote, type };
2121
+ if (currency) query.currency = currency;
2122
+ return (await client.publicGet("/api/v1/earn/dual/openProducts", query)).data;
2123
+ }
2124
+ },
2125
+ {
2126
+ name: "pionex_earn_dual_prices",
2127
+ module: "earn_dual",
2128
+ isWrite: false,
2129
+ description: "Get latest yield rates and investability status for Dual Investment products. All three parameters (base, quote, productIds) are required. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies. When canInvest is false, profit and baseSize will be empty strings. Always call this before placing an order \u2014 the profit value returned here must be passed unchanged to pionex_earn_dual_invest. No authentication required.",
2130
+ inputSchema: {
2131
+ type: "object",
2132
+ additionalProperties: false,
2133
+ required: ["base", "quote", "productIds"],
2134
+ properties: {
2135
+ base: { type: "string", description: "Base currency (e.g. BTC, ETH, LRC)" },
2136
+ quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies." },
2137
+ productIds: {
2138
+ type: "array",
2139
+ items: { type: "string" },
2140
+ description: 'List of product IDs obtained from pionex_earn_dual_open_products (e.g. ["ETH-USDXO-260410-3000-C-USDT", "ETH-USDXO-260410-2900-C-USDT"]).'
2141
+ }
2142
+ }
2143
+ },
2144
+ async handler(args, { client }) {
2145
+ const base = args.base;
2146
+ const quote = args.quote;
2147
+ const productIds = args.productIds;
2148
+ const query = { base, quote };
2149
+ if (productIds && productIds.length > 0) query.productIds = productIds.join(",");
2150
+ return (await client.publicGet("/api/v1/earn/dual/prices", query)).data;
2151
+ }
2152
+ },
2153
+ {
2154
+ name: "pionex_earn_dual_index",
2155
+ module: "earn_dual",
2156
+ isWrite: false,
2157
+ description: "Get real-time index price for a Dual Investment underlying asset. Both base and quote are required. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies. The index price is the reference price used at settlement to determine payout direction. No authentication required.",
2158
+ inputSchema: {
2159
+ type: "object",
2160
+ additionalProperties: false,
2161
+ required: ["base", "quote"],
2162
+ properties: {
2163
+ base: { type: "string", description: "Base currency (e.g. BTC, ETH, LRC)" },
2164
+ quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies." }
2165
+ }
2166
+ },
2167
+ async handler(args, { client }) {
2168
+ const base = args.base;
2169
+ const quote = args.quote;
2170
+ return (await client.publicGet("/api/v1/earn/dual/index", { base, quote })).data;
2171
+ }
2172
+ },
2173
+ {
2174
+ name: "pionex_earn_dual_delivery_prices",
2175
+ module: "earn_dual",
2176
+ isWrite: false,
2177
+ description: "Get historical settlement delivery prices for a Dual Investment pair. base is required; quote is optional but recommended to narrow results. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies. The delivery price is the index price recorded at expiry, used to determine the settlement direction. No authentication required.",
2178
+ inputSchema: {
2179
+ type: "object",
2180
+ additionalProperties: false,
2181
+ required: ["base"],
2182
+ properties: {
2183
+ base: { type: "string", description: "Base currency (e.g. BTC, XRP)" },
2184
+ quote: { type: "string", enum: ["USDT", "USDC", "USDXO"], description: "Quote currency filter. Use USDXO for BTC/ETH pairs; use USDT for all other base currencies." },
2185
+ startTime: { type: "integer", description: "Start timestamp in milliseconds" },
2186
+ endTime: { type: "integer", description: "End timestamp in milliseconds" }
2187
+ }
2188
+ },
2189
+ async handler(args, { client }) {
2190
+ const base = args.base;
2191
+ const quote = args.quote;
2192
+ const startTime = args.startTime;
2193
+ const endTime = args.endTime;
2194
+ const query = { base };
2195
+ if (quote) query.quote = quote;
2196
+ if (startTime != null) query.startTime = String(startTime);
2197
+ if (endTime != null) query.endTime = String(endTime);
2198
+ return (await client.publicGet("/api/v1/earn/dual/deliveryPrices", query)).data;
2199
+ }
2200
+ },
2201
+ // ─── View endpoints (authentication required) ────────────────────────────
2202
+ {
2203
+ name: "pionex_earn_dual_balances",
2204
+ module: "earn_dual",
2205
+ isWrite: false,
2206
+ description: "Get authenticated user's Dual Investment account balances. Requires View permission (API key + secret).",
2207
+ inputSchema: {
2208
+ type: "object",
2209
+ additionalProperties: false,
2210
+ properties: {
2211
+ merge: { type: "boolean", description: "When true, merges balances with the same coin across different base currencies." }
2212
+ }
2213
+ },
2214
+ async handler(args, { client }) {
2215
+ const merge = args.merge;
2216
+ const query = {};
2217
+ if (merge != null) query.merge = String(merge);
2218
+ return (await client.signedGet("/api/v1/earn/dual/balances", query)).data;
2219
+ }
2220
+ },
2221
+ {
2222
+ name: "pionex_earn_dual_get_invests",
2223
+ module: "earn_dual",
2224
+ isWrite: false,
2225
+ description: "Batch query Dual Investment orders by client order IDs. Requires View permission (API key + secret).",
2226
+ inputSchema: {
2227
+ type: "object",
2228
+ additionalProperties: false,
2229
+ properties: {
2230
+ base: { type: "string", description: "Base currency (e.g. BTC)" },
2231
+ clientDualIds: {
2232
+ type: "array",
2233
+ items: { type: "string" },
2234
+ description: "List of client-assigned dual investment order IDs to query."
2235
+ }
2236
+ }
2237
+ },
2238
+ async handler(args, { client }) {
2239
+ const base = args.base;
2240
+ const clientDualIds = args.clientDualIds;
2241
+ const body = {};
2242
+ if (base) body.base = base;
2243
+ if (clientDualIds) body.clientDualIds = clientDualIds;
2244
+ return (await client.signedPost("/api/v1/earn/dual/invests", body)).data;
2245
+ }
2246
+ },
2247
+ {
2248
+ name: "pionex_earn_dual_records",
2249
+ module: "earn_dual",
2250
+ isWrite: false,
2251
+ description: "Get paginated Dual Investment history for the authenticated user. Requires View permission (API key + secret).",
2252
+ inputSchema: {
2253
+ type: "object",
2254
+ additionalProperties: false,
2255
+ required: ["base", "endTime"],
2256
+ properties: {
2257
+ base: { type: "string", description: "Base currency (e.g. BTC)" },
2258
+ quote: { type: "string", description: "Quote currency filter. Use USDXO for BTC/ETH; use USDT for others." },
2259
+ currency: { type: "string", description: "Investment currency filter (e.g. USDT, BTC)" },
2260
+ filter: { type: "string", description: "Status filter" },
2261
+ startTime: { type: "integer", description: "Start timestamp in milliseconds" },
2262
+ endTime: { type: "integer", description: "End timestamp in milliseconds (required)" },
2263
+ limit: { type: "integer", description: "Maximum number of records per page (e.g. 20)" }
2264
+ }
2265
+ },
2266
+ async handler(args, { client }) {
2267
+ const base = args.base;
2268
+ const endTime = args.endTime;
2269
+ const quote = args.quote;
2270
+ const currency = args.currency;
2271
+ const filter = args.filter;
2272
+ const startTime = args.startTime;
2273
+ const limit = args.limit;
2274
+ const query = { base, endTime: String(endTime) };
2275
+ if (quote) query.quote = quote;
2276
+ if (currency) query.currency = currency;
2277
+ if (filter) query.filter = filter;
2278
+ if (startTime != null) query.startTime = String(startTime);
2279
+ if (limit != null) query.limit = String(limit);
2280
+ return (await client.signedGet("/api/v1/earn/dual/records", query)).data;
2281
+ }
2282
+ },
2283
+ // ─── Earn/write endpoints (authentication required) ──────────────────────
2284
+ {
2285
+ name: "pionex_earn_dual_invest",
2286
+ module: "earn_dual",
2287
+ isWrite: true,
2288
+ description: "Create a new Dual Investment order. Requires Earn permission (API key + secret). Provide either baseAmount (invest in base currency) or currencyAmount (invest in investment currency), not both. profit must be obtained from pionex_earn_dual_prices and passed unchanged \u2014 a stale or mismatched value will be rejected. Product ID format: {BASE}-{QUOTE}-{YYMMDD}-{STRIKE}-{C|P}-{CURRENCY}, where C=DUAL_BASE, P=DUAL_CURRENCY.",
2289
+ inputSchema: {
2290
+ type: "object",
2291
+ additionalProperties: false,
2292
+ required: ["base"],
2293
+ properties: {
2294
+ base: { type: "string", description: "Base currency (e.g. BTC)" },
2295
+ productId: { type: "string", description: "Product ID to invest in (e.g. BTC-USDXO-260402-68000-P-USDT). Obtain from pionex_earn_dual_open_products." },
2296
+ clientDualId: { type: "string", description: "Client-assigned order ID used as an idempotency key. Recommended to avoid duplicate orders." },
2297
+ baseAmount: { type: "string", description: "Investment amount in base currency (e.g. '0.01'). Mutually exclusive with currencyAmount." },
2298
+ currencyAmount: { type: "string", description: "Investment amount in investment currency (e.g. '100'). Mutually exclusive with baseAmount." },
2299
+ profit: { type: "string", description: "Yield rate from pionex_earn_dual_prices (e.g. '0.0039'). Must be current \u2014 stale values are rejected." }
2300
+ }
2301
+ },
2302
+ async handler(args, { client }) {
2303
+ const body = { base: args.base };
2304
+ if (args.productId) body.productId = args.productId;
2305
+ if (args.clientDualId) body.clientDualId = args.clientDualId;
2306
+ if (args.baseAmount) body.baseAmount = args.baseAmount;
2307
+ if (args.currencyAmount) body.currencyAmount = args.currencyAmount;
2308
+ if (args.profit) body.profit = args.profit;
2309
+ return (await client.signedPost("/api/v1/earn/dual/invest", body)).data;
2310
+ }
2311
+ },
2312
+ {
2313
+ name: "pionex_earn_dual_revoke_invest",
2314
+ module: "earn_dual",
2315
+ isWrite: true,
2316
+ description: "Revoke a pending Dual Investment order before it is matched. Requires Earn permission (API key + secret). Parameters are sent as a JSON request body. Only orders in a pending/unmatched state can be revoked.",
2317
+ inputSchema: {
2318
+ type: "object",
2319
+ additionalProperties: false,
2320
+ required: ["base", "productId", "clientDualId"],
2321
+ properties: {
2322
+ base: { type: "string", description: "Base currency (e.g. BTC)" },
2323
+ clientDualId: { type: "string", description: "Client-assigned dual investment order ID" },
2324
+ productId: { type: "string", description: "Product ID of the order to revoke (e.g. BTC-USDXO-260402-68000-P-USDT)" }
2325
+ }
2326
+ },
2327
+ async handler(args, { client }) {
2328
+ const body = {
2329
+ base: args.base,
2330
+ productId: args.productId,
2331
+ clientDualId: args.clientDualId
2332
+ };
2333
+ return (await client.signedDelete("/api/v1/earn/dual/invest", body)).data;
2334
+ }
2335
+ },
2336
+ {
2337
+ name: "pionex_earn_dual_collect",
2338
+ module: "earn_dual",
2339
+ isWrite: true,
2340
+ description: "Collect settled Dual Investment earnings into the user's spot account. Requires Earn permission (API key + secret). Only orders in a settled state can be collected.",
2341
+ inputSchema: {
2342
+ type: "object",
2343
+ additionalProperties: false,
2344
+ required: ["base", "clientDualId", "productId"],
2345
+ properties: {
2346
+ base: { type: "string", description: "Base currency (e.g. BTC)" },
2347
+ clientDualId: { type: "string", description: "Client-assigned dual investment order ID to collect" },
2348
+ productId: { type: "string", description: "Product ID (e.g. BTC-USDXO-260402-68000-P-USDT)" }
2349
+ }
2350
+ },
2351
+ async handler(args, { client }) {
2352
+ const body = {
2353
+ base: args.base,
2354
+ clientDualId: args.clientDualId,
2355
+ productId: args.productId
2356
+ };
2357
+ return (await client.signedPost("/api/v1/earn/dual/collect", body)).data;
2358
+ }
2359
+ }
2360
+ ];
2361
+ }
2068
2362
  function allToolSpecs() {
2069
- return [...registerMarketTools(), ...registerAccountTools(), ...registerOrdersTools(), ...registerBotTools()];
2363
+ return [...registerMarketTools(), ...registerAccountTools(), ...registerOrdersTools(), ...registerBotTools(), ...registerEarnDualTools()];
2070
2364
  }
2071
2365
  function createToolRunner(client, config) {
2072
2366
  const fullConfig = { ...config, modules: [...MODULES] };
@@ -2080,6 +2374,8 @@ function createToolRunner(client, config) {
2080
2374
  }
2081
2375
 
2082
2376
  // src/index.ts
2377
+ var _require = createRequire(import.meta.url);
2378
+ var { version } = _require("../package.json");
2083
2379
  var DEFAULT_PROFILE_NAME = "pionx-prod";
2084
2380
  var DEFAULT_BASE_URL = "https://api.pionex.com";
2085
2381
  function ask(rl, question, defaultValue = "") {
@@ -2088,7 +2384,9 @@ function ask(rl, question, defaultValue = "") {
2088
2384
  }
2089
2385
  async function cmdOnboard() {
2090
2386
  const rl = createInterface({ input: process.stdin, output: process.stdout });
2091
- process.stdout.write("\n pionex-ai-kit v0.2.x\n");
2387
+ process.stdout.write(`
2388
+ pionex-ai-kit v${version}
2389
+ `);
2092
2390
  process.stdout.write(" \u26A0\uFE0F Security Tips: NEVER send API keys in agent chat. Create a dedicated API Key for your agent. Please test thoroughly before connecting to large real-money accounts.\n");
2093
2391
  process.stdout.write(" \u26A0\uFE0F \u5B89\u5168\u63D0\u793A\uFF1A\u5207\u52FF\u5728 Agent \u5BF9\u8BDD\u4E2D\u53D1\u9001 API Key\u3002\u8BF7\u4E3A Agent \u521B\u5EFA\u4E13\u7528API Key\u63A5\u5165\uFF0C\u5148\u7528\u5C0F\u91D1\u989D\u5145\u5206\u9A8C\u8BC1\u540E\u518D\u63A5\u5165\u5B9E\u76D8\u3002\n\n");
2094
2392
  process.stdout.write("Pionex CLI \u2014 Configuration Wizard\n\n");
@@ -2228,6 +2526,7 @@ Groups:
2228
2526
  account Account data (requires auth)
2229
2527
  orders Spot orders (requires auth)
2230
2528
  bot Bot commands (requires auth) \u2014 use sub-route futures_grid (more bot types may be added later)
2529
+ earn Dual Investment (requires auth for most commands) \u2014 use sub-route dual
2231
2530
 
2232
2531
  Examples:
2233
2532
  pionex-trade-cli market depth BTC_USDT --limit 5
@@ -2240,6 +2539,12 @@ Examples:
2240
2539
  pionex-trade-cli orders fills-by-order-id --symbol BTC_USDT --order-id 123
2241
2540
  pionex-trade-cli bot futures_grid get --bu-order-id <id>
2242
2541
  pionex-trade-cli bot futures_grid create --base BTC --quote USDT --bu-order-data-json '{"top":"110000","bottom":"90000","row":100,"grid_type":"arithmetic","trend":"long","leverage":5,"quoteInvestment":"100"}'
2542
+ pionex-trade-cli earn dual symbols --base BTC
2543
+ pionex-trade-cli earn dual open-products --base BTC --quote USDXO --type DUAL_BASE --currency USDT
2544
+ pionex-trade-cli earn dual prices --base BTC --quote USDXO --product-ids BTC-USDXO-260402-68000-P-USDT
2545
+ pionex-trade-cli earn dual invest --base BTC --product-id BTC-USDXO-260402-68000-P-USDT --currency-amount 100 --profit 0.0039
2546
+ pionex-trade-cli earn dual revoke-invest --base BTC --client-dual-id my-order-001 --dry-run
2547
+ pionex-trade-cli earn dual collect --base BTC --client-dual-id my-order-001 --dry-run
2243
2548
 
2244
2549
  Global flags:
2245
2550
  --profile <name> Profile in ~/.pionex/config.toml
@@ -2280,9 +2585,14 @@ function parseJsonFlag(raw, flagName) {
2280
2585
  }
2281
2586
  }
2282
2587
  async function runPionexCommand(argv) {
2588
+ const firstArg = argv[0];
2589
+ if (firstArg === "-v" || firstArg === "--version") {
2590
+ process.stdout.write(version + "\n");
2591
+ return;
2592
+ }
2283
2593
  const { positionals, flags } = parseFlags(argv);
2284
2594
  const group = positionals[0];
2285
- const command = group === "bot" ? positionals[2] : positionals[1];
2595
+ const command = group === "bot" || group === "earn" ? positionals[2] : positionals[1];
2286
2596
  if (!group || group === "help" || group === "--help" || group === "-h") {
2287
2597
  printPionexHelp();
2288
2598
  return;
@@ -2520,12 +2830,170 @@ async function runPionexCommand(argv) {
2520
2830
  }
2521
2831
  throw new Error(`Unknown futures_grid command: ${command}`);
2522
2832
  }
2833
+ if (group === "earn") {
2834
+ const earnRoute = positionals[1];
2835
+ if (!earnRoute || earnRoute !== "dual") {
2836
+ throw new Error(
2837
+ `Missing or unknown earn route: ${earnRoute ?? "(none)"}. Use: pionex-trade-cli earn dual <command>
2838
+ Commands: symbols, open-products, prices, index, delivery-prices, balances, get-invests, records, invest, revoke-invest, collect`
2839
+ );
2840
+ }
2841
+ if (!command || command === "help" || flags.help === true || flags.h === true) {
2842
+ process.stdout.write(`
2843
+ Usage: pionex-trade-cli earn dual <command> [--flags]
2844
+
2845
+ Public commands (no API key required):
2846
+ symbols List supported trading pairs [--base BTC]
2847
+ open-products List open products --base BTC --quote USDXO --type DUAL_BASE|DUAL_CURRENCY [--currency USDT]
2848
+ (BTC/ETH: --quote USDXO; others: --quote USDT)
2849
+ Product ID format: {BASE}-{QUOTE}-{YYMMDD}-{STRIKE}-{C|P}-{CURRENCY} (C=DUAL_BASE, P=DUAL_CURRENCY)
2850
+ prices Get yield rates --base BTC --quote USDXO --product-ids id1,id2
2851
+ (All three flags required. Always call before invest \u2014 profit value must be passed unchanged.)
2852
+ index Get index price --base BTC --quote USDXO
2853
+ delivery-prices Get delivery prices --base BTC [--quote USDXO] [--start-time ms] [--end-time ms]
2854
+
2855
+ Auth commands (View permission):
2856
+ balances Get Dual Investment balances [--merge]
2857
+ records Get investment history --base BTC --end-time ms [--quote USDT] [--limit 20] [--start-time ms]
2858
+ get-invests Batch query orders [--base BTC] --client-dual-ids id1,id2
2859
+
2860
+ Auth commands (Earn permission, write):
2861
+ invest Create investment --base BTC --product-id <id> (--base-amount N | --currency-amount N) --profit N [--client-dual-id id] [--dry-run]
2862
+ revoke-invest Revoke pending order --base BTC --product-id <id> --client-dual-id <id> [--dry-run]
2863
+ collect Collect settled earnings --base BTC --client-dual-id <id> --product-id <id> [--dry-run]
2864
+
2865
+ Note: For BTC/ETH: --quote USDXO --currency USDT|USDC. For other bases: --quote USDT --currency USDT.
2866
+ `);
2867
+ return;
2868
+ }
2869
+ if (command === "symbols") {
2870
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2871
+ const out = await runTool("pionex_earn_dual_symbols", { base });
2872
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2873
+ return;
2874
+ }
2875
+ if (command === "open-products" || command === "openProducts") {
2876
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2877
+ const quote = typeof flags.quote === "string" ? flags.quote : void 0;
2878
+ const type = typeof flags.type === "string" ? flags.type : void 0;
2879
+ const currency = typeof flags.currency === "string" ? flags.currency : void 0;
2880
+ if (!base || !quote || !type) throw new Error("Missing required flags: --base --quote --type (DUAL_BASE|DUAL_CURRENCY)");
2881
+ const out = await runTool("pionex_earn_dual_open_products", { base, quote, type, currency });
2882
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2883
+ return;
2884
+ }
2885
+ if (command === "prices") {
2886
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2887
+ const quote = typeof flags.quote === "string" ? flags.quote : void 0;
2888
+ const productIdsRaw = typeof flags["product-ids"] === "string" ? flags["product-ids"] : typeof flags.productIds === "string" ? flags.productIds : void 0;
2889
+ const productIds = productIdsRaw ? productIdsRaw.split(",").map((s) => s.trim()) : void 0;
2890
+ if (!base || !quote || !productIds || productIds.length === 0) throw new Error("Missing required flags: --base --quote --product-ids id1,id2");
2891
+ const out = await runTool("pionex_earn_dual_prices", { base, quote, productIds });
2892
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2893
+ return;
2894
+ }
2895
+ if (command === "index") {
2896
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2897
+ const quote = typeof flags.quote === "string" ? flags.quote : void 0;
2898
+ if (!base || !quote) throw new Error("Missing required flags: --base --quote");
2899
+ const out = await runTool("pionex_earn_dual_index", { base, quote });
2900
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2901
+ return;
2902
+ }
2903
+ if (command === "delivery-prices" || command === "deliveryPrices") {
2904
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2905
+ if (!base) throw new Error("Missing required flag: --base");
2906
+ const quote = typeof flags.quote === "string" ? flags.quote : void 0;
2907
+ const startTime = flags["start-time"] != null ? Number(flags["start-time"]) : flags.startTime != null ? Number(flags.startTime) : void 0;
2908
+ const endTime = flags["end-time"] != null ? Number(flags["end-time"]) : flags.endTime != null ? Number(flags.endTime) : void 0;
2909
+ const out = await runTool("pionex_earn_dual_delivery_prices", { base, quote, startTime, endTime });
2910
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2911
+ return;
2912
+ }
2913
+ if (command === "balances") {
2914
+ const merge = typeof flags.merge === "boolean" ? flags.merge : void 0;
2915
+ const out = await runTool("pionex_earn_dual_balances", { merge });
2916
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2917
+ return;
2918
+ }
2919
+ if (command === "get-invests" || command === "getInvests") {
2920
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2921
+ const clientDualIdsRaw = typeof flags["client-dual-ids"] === "string" ? flags["client-dual-ids"] : typeof flags.clientDualIds === "string" ? flags.clientDualIds : void 0;
2922
+ const clientDualIds = clientDualIdsRaw ? clientDualIdsRaw.split(",").map((s) => s.trim()) : void 0;
2923
+ const out = await runTool("pionex_earn_dual_get_invests", { base, clientDualIds });
2924
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2925
+ return;
2926
+ }
2927
+ if (command === "records") {
2928
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2929
+ const endTime = flags["end-time"] != null ? Number(flags["end-time"]) : flags.endTime != null ? Number(flags.endTime) : void 0;
2930
+ if (!base || endTime == null) throw new Error("Missing required flags: --base --end-time <ms>");
2931
+ const quote = typeof flags.quote === "string" ? flags.quote : void 0;
2932
+ const currency = typeof flags.currency === "string" ? flags.currency : void 0;
2933
+ const filter = typeof flags.filter === "string" ? flags.filter : void 0;
2934
+ const startTime = flags["start-time"] != null ? Number(flags["start-time"]) : flags.startTime != null ? Number(flags.startTime) : void 0;
2935
+ const limit = flags.limit != null ? Number(flags.limit) : void 0;
2936
+ const out = await runTool("pionex_earn_dual_records", { base, quote, currency, filter, startTime, endTime, limit });
2937
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2938
+ return;
2939
+ }
2940
+ if (command === "invest") {
2941
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2942
+ if (!base) throw new Error("Missing required flag: --base");
2943
+ const productId = typeof flags["product-id"] === "string" ? flags["product-id"] : typeof flags.productId === "string" ? flags.productId : void 0;
2944
+ const clientDualId = typeof flags["client-dual-id"] === "string" ? flags["client-dual-id"] : typeof flags.clientDualId === "string" ? flags.clientDualId : void 0;
2945
+ const baseAmount = typeof flags["base-amount"] === "string" ? flags["base-amount"] : typeof flags.baseAmount === "string" ? flags.baseAmount : void 0;
2946
+ const currencyAmount = typeof flags["currency-amount"] === "string" ? flags["currency-amount"] : typeof flags.currencyAmount === "string" ? flags.currencyAmount : void 0;
2947
+ const profit = typeof flags.profit === "string" ? flags.profit : void 0;
2948
+ const payload = { base, productId, clientDualId, baseAmount, currencyAmount, profit };
2949
+ if (dryRun) {
2950
+ process.stdout.write(JSON.stringify({ tool: "pionex_earn_dual_invest", args: payload }, null, 2) + "\n");
2951
+ return;
2952
+ }
2953
+ const out = await runTool("pionex_earn_dual_invest", payload);
2954
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2955
+ return;
2956
+ }
2957
+ if (command === "revoke-invest" || command === "revokeInvest") {
2958
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2959
+ const clientDualId = typeof flags["client-dual-id"] === "string" ? flags["client-dual-id"] : typeof flags.clientDualId === "string" ? flags.clientDualId : void 0;
2960
+ const productId = typeof flags["product-id"] === "string" ? flags["product-id"] : typeof flags.productId === "string" ? flags.productId : void 0;
2961
+ if (!base || !clientDualId || !productId) throw new Error("Missing required flags: --base --client-dual-id --product-id");
2962
+ const payload = { base, clientDualId, productId };
2963
+ if (dryRun) {
2964
+ process.stdout.write(JSON.stringify({ tool: "pionex_earn_dual_revoke_invest", args: payload }, null, 2) + "\n");
2965
+ return;
2966
+ }
2967
+ const out = await runTool("pionex_earn_dual_revoke_invest", payload);
2968
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2969
+ return;
2970
+ }
2971
+ if (command === "collect") {
2972
+ const base = typeof flags.base === "string" ? flags.base : void 0;
2973
+ const clientDualId = typeof flags["client-dual-id"] === "string" ? flags["client-dual-id"] : typeof flags.clientDualId === "string" ? flags.clientDualId : void 0;
2974
+ const productId = typeof flags["product-id"] === "string" ? flags["product-id"] : typeof flags.productId === "string" ? flags.productId : void 0;
2975
+ if (!base || !clientDualId || !productId) throw new Error("Missing required flags: --base --client-dual-id --product-id");
2976
+ const payload = { base, clientDualId, productId };
2977
+ if (dryRun) {
2978
+ process.stdout.write(JSON.stringify({ tool: "pionex_earn_dual_collect", args: payload }, null, 2) + "\n");
2979
+ return;
2980
+ }
2981
+ const out = await runTool("pionex_earn_dual_collect", payload);
2982
+ process.stdout.write(JSON.stringify(out.data, null, 2) + "\n");
2983
+ return;
2984
+ }
2985
+ throw new Error(`Unknown earn dual command: ${command}`);
2986
+ }
2523
2987
  throw new Error(`Unknown group: ${group}`);
2524
2988
  }
2525
2989
  function main() {
2526
2990
  const invokedAs = basename(process.argv[1] || "");
2527
2991
  const cmd = process.argv[2];
2528
2992
  if (invokedAs.includes("pionex-ai-kit")) {
2993
+ if (cmd === "-v" || cmd === "--version") {
2994
+ process.stdout.write(version + "\n");
2995
+ return;
2996
+ }
2529
2997
  if (cmd === "onboard") {
2530
2998
  cmdOnboard().catch((e) => {
2531
2999
  process.stderr.write(String(e) + "\n");