@liquiditytech/rapidx-cli 1.0.31 → 1.0.33

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 (31) hide show
  1. package/dist/cli/commands/account.js +15 -12
  2. package/dist/cli/commands/algo.js +3 -2
  3. package/dist/cli/commands/index.js +6 -6
  4. package/dist/cli/commands/order.js +2 -2
  5. package/dist/cli/commands/position.js +3 -1
  6. package/dist/cli/commands/update.js +3 -0
  7. package/dist/cli/help.js +1 -1
  8. package/dist/core/client/capability-executor.js +109 -32
  9. package/dist/core/client/rapid-x-client.js +6 -32
  10. package/dist/core/contracts/capabilities.js +18 -10
  11. package/dist/core/contracts/compatibility.js +1 -1
  12. package/dist/core/contracts/input-schema.js +40 -14
  13. package/dist/core/safety/policy.js +9 -5
  14. package/dist/core/self-check/live-read-only-probes.js +4 -4
  15. package/dist/core/self-check/live-trading-verification-probes.js +3 -3
  16. package/dist/core/trading/preview-preflight.js +3 -3
  17. package/dist/core/trading/preview.js +2 -2
  18. package/dist/core/trading/trading-verification.js +8 -8
  19. package/dist/core/update/check-update.js +77 -10
  20. package/dist/core/version.js +1 -1
  21. package/dist/mcp/tool-runner.js +5 -2
  22. package/package.json +1 -1
  23. package/packages/distribution/docs/cli.md +36 -11
  24. package/packages/distribution/docs/mcp.md +4 -4
  25. package/packages/distribution/docs/quickstart.md +5 -5
  26. package/packages/distribution/docs/self-check.md +1 -1
  27. package/packages/distribution/docs/skills.md +1 -1
  28. package/packages/distribution/docs/tools.md +16 -14
  29. package/packages/distribution/docs/trading-verification.md +1 -1
  30. package/packages/distribution/manifests/offline-manifest.json +4 -4
  31. package/packages/distribution/registry/rapidx.mcp.json +1 -1
@@ -85,13 +85,24 @@ const targetCapabilityIdSchema = {
85
85
  };
86
86
  const orderIdSchema = {
87
87
  type: "string",
88
- description: "RapidX order id returned by order.place, order.list, or order.get.",
88
+ description: "RapidX order id returned by order.place, order.open-orders, or order.query.",
89
89
  examples: ["2173374713391199"]
90
90
  };
91
- const accountBalanceModeSchema = {
91
+ const exchangeTypeSchema = { type: "string", description: "RapidX exchange type, for example BINANCE or OKX." };
92
+ const businessTypeSchema = { type: "string", description: "RapidX business type, for example PERP." };
93
+ const pageSchema = { type: "number", description: "1-based page number." };
94
+ const pageSizeSchema = { type: "number", description: "Page size." };
95
+ const startTimeSchema = { type: "string", description: "Start timestamp accepted by the RapidX API." };
96
+ const endTimeSchema = { type: "string", description: "End timestamp accepted by the RapidX API." };
97
+ const coinSchema = { type: "string", description: "Asset coin, for example USDT." };
98
+ const statementTypeSchema = { type: "string", description: "RapidX statement type filter." };
99
+ const filterExecutedSchema = { type: "boolean", description: "Whether to filter executed order history where the endpoint supports it." };
100
+ const closeAllPositionsSchema = { type: "boolean", description: "When true, requests RapidX to close all positions allowed by the provided filters." };
101
+ const symbolListSchema = { type: "string", description: "Comma-separated RapidX symbols for close-all position requests." };
102
+ const closeAllPositionSideSchema = {
92
103
  type: "string",
93
- enum: ["portfolio", "account"],
94
- description: "Balance source. portfolio uses /api/v1/trading/portfolio/assets; account uses /api/v1/account/balance."
104
+ enum: ["LONG", "SHORT", "NONE"],
105
+ description: "Position side for close-all. Use NONE only when the RapidX close-all endpoint accepts it for one-way mode."
95
106
  };
96
107
  const positionSideSchema = {
97
108
  type: "string",
@@ -114,8 +125,10 @@ export function inputSchemaForName(name) {
114
125
  return objectSchema({ symbol: symbolSchema, depth: numberSchema }, ["symbol"]);
115
126
  case "KlinesInput":
116
127
  return objectSchema({ symbol: symbolSchema, interval: stringSchema, limit: numberSchema }, ["symbol"]);
117
- case "AccountBalanceInput":
118
- return objectSchema({ mode: accountBalanceModeSchema, currency: stringSchema });
128
+ case "PortfolioAssetsInput":
129
+ return objectSchema({ exchangeType: exchangeTypeSchema, page: pageSchema, pageSize: pageSizeSchema });
130
+ case "PositionBracketInput":
131
+ return objectSchema({ symbol: symbolSchema }, ["symbol"]);
119
132
  case "OrderLookupInput":
120
133
  return {
121
134
  ...objectSchema({ orderId: orderIdSchema, clientOrderId: clientOrderIdSchema, symbol: symbolSchema }),
@@ -123,12 +136,16 @@ export function inputSchemaForName(name) {
123
136
  };
124
137
  case "OrderListInput":
125
138
  case "OrderHistoryInput":
126
- return objectSchema({ symbol: symbolSchema, pageSize: numberSchema, startTime: stringSchema, endTime: stringSchema });
139
+ return objectSchema({ symbol: symbolSchema, exchange: stringSchema, businessType: businessTypeSchema, page: pageSchema, pageSize: pageSizeSchema, startTime: startTimeSchema, endTime: endTimeSchema, filterExecuted: filterExecutedSchema });
140
+ case "ExecutionListInput":
141
+ return objectSchema({ orderId: orderIdSchema, symbol: symbolSchema, exchange: stringSchema, businessType: businessTypeSchema, limit: numberSchema, startTime: startTimeSchema, endTime: endTimeSchema });
142
+ case "StatementInput":
143
+ return objectSchema({ coin: coinSchema, symbol: symbolSchema, statementType: statementTypeSchema, exchange: stringSchema, page: pageSchema, pageSize: pageSizeSchema, startTime: startTimeSchema, endTime: endTimeSchema });
127
144
  case "PositionListInput":
128
145
  case "AlgoListInput":
129
- return objectSchema({ symbol: symbolSchema });
146
+ return objectSchema({ symbol: symbolSchema, exchange: stringSchema, businessType: businessTypeSchema, page: pageSchema, pageSize: pageSizeSchema, startTime: startTimeSchema, endTime: endTimeSchema });
130
147
  case "PositionHistoryInput":
131
- return objectSchema({ symbol: symbolSchema, pageSize: numberSchema, startTime: stringSchema, endTime: stringSchema });
148
+ return objectSchema({ symbol: symbolSchema, pageSize: pageSizeSchema, startTime: startTimeSchema, endTime: endTimeSchema });
132
149
  case "TradePreviewInput":
133
150
  return objectSchema({
134
151
  targetCapabilityId: targetCapabilityIdSchema,
@@ -184,7 +201,7 @@ export function inputSchemaForName(name) {
184
201
  ]),
185
202
  oneOf: [{ required: ["quantity"] }, { required: ["amount"] }]
186
203
  };
187
- case "AmendOrderPreviewInput":
204
+ case "ReplaceOrderPreviewInput":
188
205
  return {
189
206
  ...objectSchema({ orderId: orderIdSchema, clientOrderId: clientOrderIdSchema, price: priceSchema, quantity: quantitySchema, ...automationProperties(), ...compatibilityProperties() }),
190
207
  allOf: [
@@ -197,7 +214,7 @@ export function inputSchemaForName(name) {
197
214
  ...objectSchema({ orderId: orderIdSchema, clientOrderId: clientOrderIdSchema, ...automationProperties(), ...compatibilityProperties() }),
198
215
  anyOf: [{ required: ["orderId"] }, { required: ["clientOrderId"] }]
199
216
  };
200
- case "AmendOrderInput":
217
+ case "ReplaceOrderInput":
201
218
  return {
202
219
  ...objectSchema({ orderId: orderIdSchema, clientOrderId: clientOrderIdSchema, price: priceSchema, quantity: quantitySchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["previewId", "continueConsentId"]),
203
220
  allOf: [
@@ -210,10 +227,14 @@ export function inputSchemaForName(name) {
210
227
  ...objectSchema({ orderId: orderIdSchema, clientOrderId: clientOrderIdSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["previewId", "continueConsentId"]),
211
228
  anyOf: [{ required: ["orderId"] }, { required: ["clientOrderId"] }]
212
229
  };
230
+ case "CancelAllOrdersInput":
231
+ return objectSchema({ exchangeType: exchangeTypeSchema, symbol: symbolSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["previewId", "continueConsentId"]);
213
232
  case "ClosePositionInput":
214
233
  return objectSchema({ symbol: symbolSchema, positionSide: positionSideSchema, reduceOnly: closePositionReduceOnlySchema, maxNotional: maxNotionalSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["symbol", "reduceOnly", "maxNotional", "previewId", "continueConsentId"]);
234
+ case "CloseAllPositionsInput":
235
+ return objectSchema({ symbolList: symbolListSchema, positionSide: closeAllPositionSideSchema, closeAllPositions: closeAllPositionsSchema, exchangeType: exchangeTypeSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["previewId", "continueConsentId"]);
215
236
  case "SetLeverageInput":
216
- return objectSchema({ symbol: symbolSchema, leverage: numberSchema, positionSide: positionSideSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["symbol", "leverage", "previewId", "continueConsentId"]);
237
+ return objectSchema({ symbol: symbolSchema, leverage: numberSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["symbol", "leverage", "previewId", "continueConsentId"]);
217
238
  case "SetPositionModeInput":
218
239
  return objectSchema({ mode: { type: "string", enum: ["BOTH", "NET"] }, exchange: stringSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["mode", "exchange", "previewId", "continueConsentId"]);
219
240
  case "AlgoPlaceInput":
@@ -242,7 +263,7 @@ export function inputSchemaForName(name) {
242
263
  ]),
243
264
  anyOf: [{ required: ["quantity"] }, { required: ["amount"] }, { required: ["conditionType"] }]
244
265
  };
245
- case "AlgoAmendInput":
266
+ case "AlgoReplaceInput":
246
267
  return objectSchema({
247
268
  algoOrderId: stringSchema,
248
269
  clientOrderId: clientOrderIdSchema,
@@ -257,6 +278,11 @@ export function inputSchemaForName(name) {
257
278
  }, ["previewId", "continueConsentId"]);
258
279
  case "AlgoCancelInput":
259
280
  return objectSchema({ algoOrderId: stringSchema, clientOrderId: clientOrderIdSchema, previewId: previewIdSchema, continueConsentId: continueConsentIdSchema }, ["previewId", "continueConsentId"]);
281
+ case "AlgoLookupInput":
282
+ return {
283
+ ...objectSchema({ algoOrderId: stringSchema, clientOrderId: clientOrderIdSchema, attachedOrderId: stringSchema }),
284
+ anyOf: [{ required: ["algoOrderId"] }, { required: ["clientOrderId"] }, { required: ["attachedOrderId"] }]
285
+ };
260
286
  case "TradingVerificationInput":
261
287
  return objectSchema({
262
288
  symbol: symbolSchema,
@@ -279,7 +305,7 @@ export function inputSchemaForName(name) {
279
305
  case "InvocationCheckInput":
280
306
  return objectSchema({ commandLine: stringSchema, preflightError: stringSchema });
281
307
  case "UpdateCheckInput":
282
- return objectSchema({ force: booleanSchema, manifestUrl: stringSchema, maxCacheAgeSeconds: numberSchema });
308
+ return objectSchema({ force: booleanSchema, manifestUrl: stringSchema, maxCacheAgeSeconds: numberSchema, installedSkillsVersion: stringSchema });
283
309
  case "SchemaQuery":
284
310
  return objectSchema({ consumer: stringSchema, consumerVersion: stringSchema, includeExamples: booleanSchema });
285
311
  case "SelfCheckInput":
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { buildCompatibilityReport } from "../contracts/compatibility.js";
3
3
  import { ProductError } from "../errors/product-error.js";
4
- const TRADING_DOMAINS = new Set(["account", "order", "position", "algo"]);
4
+ const TRADING_DOMAINS = new Set(["portfolio", "order", "position", "algo"]);
5
5
  export function defaultSafetyPolicy() {
6
6
  const policy = {
7
7
  readOnly: process.env.RAPIDX_READ_ONLY === "true",
@@ -72,7 +72,7 @@ export function evaluateSafety(capability, input, policy, state = makeSafetyStat
72
72
  return { allowed: false, code: "RCORE00001", reason: "leverage must be an integer from 1 to 125.", paramsHash };
73
73
  }
74
74
  }
75
- if (capability.capabilityId === "account.set-position-mode") {
75
+ if (capability.capabilityId === "portfolio.set-position-mode") {
76
76
  const mode = String(input.mode ?? "");
77
77
  if (mode !== "BOTH" && mode !== "NET") {
78
78
  return { allowed: false, code: "RCORE00001", reason: "mode must be BOTH or NET.", paramsHash };
@@ -124,7 +124,7 @@ function requiredTradeFields(capability, input) {
124
124
  requireString(input, "algoType", missing);
125
125
  }
126
126
  break;
127
- case "order.amend":
127
+ case "order.replace":
128
128
  requireOneOf(input, ["orderId", "clientOrderId"], "orderId or clientOrderId", missing);
129
129
  if (!hasString(input, "price") && !hasString(input, "quantity")) {
130
130
  missing.push("price or quantity");
@@ -146,11 +146,15 @@ function requiredTradeFields(capability, input) {
146
146
  missing.push("leverage");
147
147
  }
148
148
  break;
149
- case "account.set-position-mode":
149
+ case "order.cancel-all":
150
+ break;
151
+ case "position.close-all":
152
+ break;
153
+ case "portfolio.set-position-mode":
150
154
  requireString(input, "mode", missing);
151
155
  requireString(input, "exchange", missing);
152
156
  break;
153
- case "algo.amend":
157
+ case "algo.replace":
154
158
  requireOneOf(input, ["algoOrderId", "clientOrderId"], "algoOrderId or clientOrderId", missing);
155
159
  break;
156
160
  case "algo.cancel":
@@ -8,10 +8,10 @@ export function buildLiveReadOnlySelfCheck(input = {}) {
8
8
  const symbol = typeof input.symbol === "string" ? input.symbol : "BINANCE_PERP_BTC_USDT";
9
9
  const probes = {
10
10
  publicMarket: async () => executeRapidXCapability("market.ticker", { symbol }),
11
- credentialAuth: async () => executeRapidXCapability("account.overview", {}),
12
- accountRead: async () => executeRapidXCapability("account.balance", {}),
13
- orderRead: async () => executeRapidXCapability("order.list", {}),
14
- positionRead: async () => executeRapidXCapability("position.list", {})
11
+ credentialAuth: async () => executeRapidXCapability("portfolio.overview", {}),
12
+ accountRead: async () => executeRapidXCapability("portfolio.assets", {}),
13
+ orderRead: async () => executeRapidXCapability("order.open-orders", {}),
14
+ positionRead: async () => executeRapidXCapability("position.query", {})
15
15
  };
16
16
  return { credential, probes };
17
17
  }
@@ -77,7 +77,7 @@ async function buildMarketRules(input) {
77
77
  };
78
78
  }
79
79
  async function queryOrder(order) {
80
- const result = await executeRapidXCapability("order.get", { orderId: order.orderId });
80
+ const result = await executeRapidXCapability("order.query", { orderId: order.orderId });
81
81
  const data = extractDataObject(result);
82
82
  const requestId = stringField(data, "clientOrderId") ?? order.requestId;
83
83
  return {
@@ -89,8 +89,8 @@ async function queryOrder(order) {
89
89
  async function cleanupCheck(input) {
90
90
  const symbol = typeof input.symbol === "string" ? input.symbol : "BINANCE_PERP_BTC_USDT";
91
91
  const [ordersResult, positionsResult] = await Promise.all([
92
- executeRapidXCapability("order.list", { symbol, pageSize: 20 }),
93
- executeRapidXCapability("position.list", { symbol })
92
+ executeRapidXCapability("order.open-orders", { symbol, pageSize: 20 }),
93
+ executeRapidXCapability("position.query", { symbol })
94
94
  ]);
95
95
  const orders = extractList(extractDataObject(ordersResult));
96
96
  const positions = extractArrayData(positionsResult);
@@ -11,8 +11,8 @@ export async function runPreviewPreflight(capabilityId, input, options = {}) {
11
11
  return validateOrderPlacementPreflight(input, options);
12
12
  case "order.cancel":
13
13
  return validateOrderMutationPreflight(input, "cancel", options);
14
- case "order.amend":
15
- return validateOrderMutationPreflight(input, "amend", options);
14
+ case "order.replace":
15
+ return validateOrderMutationPreflight(input, "replace", options);
16
16
  case "position.close":
17
17
  return validatePositionClosePreflight(input, options);
18
18
  default:
@@ -50,7 +50,7 @@ async function validateOrderMutationPreflight(input, action, options) {
50
50
  throw new ProductError({
51
51
  code: "RCORE22004",
52
52
  status: "BLOCKED",
53
- message: "ORDER_NOT_FOUND: order.get did not return an order for preview preflight."
53
+ message: "ORDER_NOT_FOUND: order.query did not return an order for preview preflight."
54
54
  });
55
55
  }
56
56
  const state = normalizeOrderState(order.orderState ?? order.state ?? order.status);
@@ -238,7 +238,7 @@ function makeRequestSummary(capabilityId, params) {
238
238
  positionSide: params.positionSide
239
239
  });
240
240
  }
241
- if (capabilityId === "account.set-position-mode") {
241
+ if (capabilityId === "portfolio.set-position-mode") {
242
242
  return compactRecord({
243
243
  action: "set_position_mode",
244
244
  mode: params.mode,
@@ -274,7 +274,7 @@ function makeRiskNotes(capabilityId, params, automation) {
274
274
  if (capabilityId === "position.close") {
275
275
  notes.push("Do not pass side or quantity for position.close; RapidX determines BUY or SELL from the current position and closes the target symbol/positionSide.");
276
276
  notes.push("position.close uses the RapidX close-position API and may execute as a market close with slippage.");
277
- notes.push("order.get reduceOnly may not reflect the close-position API request; verify final exposure with position.list.");
277
+ notes.push("order.query reduceOnly may not reflect the close-position API request; verify final exposure with position.query.");
278
278
  }
279
279
  return notes;
280
280
  }
@@ -95,18 +95,18 @@ export async function runTradingVerification(rawInput, probes = {}) {
95
95
  }
96
96
  steps.push({ name: "query", status: "PASS", toolOrCommandEvidence: `${queried.orderId}:${queried.status}` });
97
97
  let currentOrder = queried;
98
- if (probes.amendOrder) {
99
- currentOrder = await probes.amendOrder(currentOrder);
100
- steps.push({ name: "amend", status: currentOrder.status === "UNKNOWN" ? "NOT_VERIFIED" : "PASS", toolOrCommandEvidence: `${currentOrder.orderId}:${currentOrder.status}` });
98
+ if (probes.replaceOrder) {
99
+ currentOrder = await probes.replaceOrder(currentOrder);
100
+ steps.push({ name: "replace", status: currentOrder.status === "UNKNOWN" ? "NOT_VERIFIED" : "PASS", toolOrCommandEvidence: `${currentOrder.orderId}:${currentOrder.status}` });
101
101
  if (currentOrder.status === "UNKNOWN") {
102
- return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001", errorStage: "amend", lastObservedOrderState: currentOrder.status };
102
+ return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE23001", errorStage: "replace", lastObservedOrderState: currentOrder.status };
103
103
  }
104
104
  }
105
105
  else {
106
106
  steps.push({
107
- name: "amend",
107
+ name: "replace",
108
108
  status: "EXPECTED_ERROR",
109
- toolOrCommandEvidence: "optional verify-live amend check skipped; order.amend remains available outside verify-live"
109
+ toolOrCommandEvidence: "optional verify-live replace check skipped; order.replace remains available outside verify-live"
110
110
  });
111
111
  }
112
112
  if (!probes.cancelOrder) {
@@ -122,7 +122,7 @@ export async function runTradingVerification(rawInput, probes = {}) {
122
122
  steps.push({ name: "cancel", status: "PASS", toolOrCommandEvidence: cancelConfirmation.evidence });
123
123
  if (!probes.cleanupCheck) {
124
124
  steps.push({ name: "cleanup-check", status: "NOT_VERIFIED", toolOrCommandEvidence: "cleanupCheck probe missing" });
125
- return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE24002", errorStage: "cleanup-check", recommendedAction: "Query order/list and position/list before retrying verify-live." };
125
+ return { submittedRealOrder: true, status: "NOT_VERIFIED", cleanupStatus: "NOT_VERIFIED", steps, previewId: preview.previewId, errorCode: "RCORE24002", errorStage: "cleanup-check", recommendedAction: "Query order/open-orders and position/query before retrying verify-live." };
126
126
  }
127
127
  const cleanupConfirmation = await confirmCleanup(currentOrder, probes);
128
128
  if (!cleanupConfirmation.confirmed) {
@@ -254,7 +254,7 @@ function cleanupFailureReport(steps, previewId, errorStage, lastObservedOrderSta
254
254
  errorStage,
255
255
  ...(lastObservedOrderState ? { lastObservedOrderState } : {}),
256
256
  expectedOrderStates: ["CANCELED", "EXPIRED", "REJECTED"],
257
- recommendedAction: "Query order/get, order/list, and position/list before retrying; do not submit another verification until cleanup is confirmed."
257
+ recommendedAction: "Query order/query, order/open-orders, and position/query before retrying; do not submit another verification until cleanup is confirmed."
258
258
  };
259
259
  }
260
260
  function quantityForNotional(minNotional, price) {
@@ -13,11 +13,14 @@ export async function checkForUpdate(input = {}, env = process.env, cwd = proces
13
13
  const cacheFile = resolveUpdateCacheFile(env, cwd);
14
14
  const cached = loadCachedUpdateState(cacheFile);
15
15
  if (!input.force && cached && !isCacheExpired(cached, cacheTtlSeconds)) {
16
- return {
17
- ...cached.result,
16
+ const manifest = cached.manifest ?? manifestFromCachedResult(cached.result);
17
+ return buildUpdateCheckResult(manifest, {
18
+ checkedAt: cached.checkedAt,
19
+ cacheTtlSeconds,
20
+ manifestUrl,
18
21
  manifestSource: "cache",
19
- cacheTtlSeconds
20
- };
22
+ installedSkillsVersion: input.installedSkillsVersion
23
+ });
21
24
  }
22
25
  try {
23
26
  const manifest = await fetchReleaseManifest(manifestUrl);
@@ -25,29 +28,35 @@ export async function checkForUpdate(input = {}, env = process.env, cwd = proces
25
28
  checkedAt: new Date().toISOString(),
26
29
  cacheTtlSeconds,
27
30
  manifestUrl,
28
- manifestSource: "remote"
31
+ manifestSource: "remote",
32
+ installedSkillsVersion: input.installedSkillsVersion
29
33
  });
30
34
  saveCachedUpdateState(cacheFile, {
31
35
  checkedAt: result.checkedAt,
32
36
  cacheTtlSeconds,
37
+ manifest,
33
38
  result
34
39
  });
35
40
  return result;
36
41
  }
37
42
  catch (error) {
38
43
  if (cached) {
39
- return {
40
- ...cached.result,
41
- manifestSource: "cache",
44
+ const manifest = cached.manifest ?? manifestFromCachedResult(cached.result);
45
+ return buildUpdateCheckResult(manifest, {
46
+ checkedAt: cached.checkedAt,
42
47
  cacheTtlSeconds,
48
+ manifestUrl,
49
+ manifestSource: "cache",
50
+ installedSkillsVersion: input.installedSkillsVersion,
43
51
  error: error instanceof Error ? error.message : String(error)
44
- };
52
+ });
45
53
  }
46
54
  return buildUnknownUpdateCheckResult({
47
55
  checkedAt: new Date().toISOString(),
48
56
  cacheTtlSeconds,
49
57
  manifestUrl,
50
58
  manifestSource: "fallback",
59
+ installedSkillsVersion: input.installedSkillsVersion,
51
60
  error: error instanceof Error ? error.message : String(error)
52
61
  });
53
62
  }
@@ -62,6 +71,11 @@ export function buildUpdateCheckResult(manifest, metadata) {
62
71
  minimumWriteVersion
63
72
  });
64
73
  const writeAllowed = status !== "WRITE_BLOCKED";
74
+ const installedSkillsVersion = normalizeInstalledSkillsVersion(metadata.installedSkillsVersion);
75
+ const skillsInstallStatus = computeSkillsInstallStatus(installedSkillsVersion, manifest.skillsVersion);
76
+ const skillsUpdateRecommended = manifest.skillsUpdateRecommended
77
+ || skillsInstallStatus === "UPDATE_AVAILABLE"
78
+ || (skillsInstallStatus === "NOT_VERIFIED" && compareVersions(RAPIDX_SKILLS_VERSION, manifest.skillsVersion) < 0);
65
79
  return {
66
80
  currentVersion: RAPIDX_VERSION,
67
81
  latestVersion: manifest.latestCliVersion,
@@ -71,6 +85,10 @@ export function buildUpdateCheckResult(manifest, metadata) {
71
85
  currentMcpSchemaVersion: SCHEMA_VERSION,
72
86
  skillsVersion: manifest.skillsVersion,
73
87
  currentSkillsVersion: RAPIDX_SKILLS_VERSION,
88
+ latestSkillsVersion: manifest.skillsVersion,
89
+ bundledExpectedSkillsVersion: RAPIDX_SKILLS_VERSION,
90
+ installedSkillsVersion,
91
+ skillsInstallStatus,
74
92
  skillsSchemaVersion: manifest.skillsSchemaVersion,
75
93
  currentSkillsSchemaVersion: RAPIDX_SKILLS_SCHEMA_VERSION,
76
94
  updateAvailable: compareVersions(RAPIDX_VERSION, manifest.latestCliVersion) < 0,
@@ -82,9 +100,12 @@ export function buildUpdateCheckResult(manifest, metadata) {
82
100
  cacheTtlSeconds: metadata.cacheTtlSeconds,
83
101
  manifestUrl: metadata.manifestUrl,
84
102
  manifestSource: metadata.manifestSource,
103
+ updateFreshness: freshnessForSource(metadata.manifestSource),
104
+ refreshCommand: "rapidx update check --input '{\"force\":true}' --json",
85
105
  releaseNotesUrl: manifest.releaseNotesUrl,
86
- skillsUpdateRecommended: manifest.skillsUpdateRecommended || compareVersions(RAPIDX_SKILLS_VERSION, manifest.skillsVersion) < 0,
106
+ skillsUpdateRecommended,
87
107
  upgrade: manifest.upgrade,
108
+ ...(metadata.manifestSource === "cache" ? { cacheWarning: "Result came from local cache. Use force=true to refresh the remote release manifest." } : {}),
88
109
  ...(metadata.error ? { error: metadata.error } : {})
89
110
  };
90
111
  }
@@ -160,6 +181,8 @@ function fallbackManifest() {
160
181
  }
161
182
  function buildUnknownUpdateCheckResult(metadata) {
162
183
  const manifest = fallbackManifest();
184
+ const installedSkillsVersion = normalizeInstalledSkillsVersion(metadata.installedSkillsVersion);
185
+ const skillsInstallStatus = computeSkillsInstallStatus(installedSkillsVersion, manifest.skillsVersion);
163
186
  return {
164
187
  currentVersion: RAPIDX_VERSION,
165
188
  latestVersion: RAPIDX_VERSION,
@@ -169,6 +192,10 @@ function buildUnknownUpdateCheckResult(metadata) {
169
192
  currentMcpSchemaVersion: SCHEMA_VERSION,
170
193
  skillsVersion: manifest.skillsVersion,
171
194
  currentSkillsVersion: RAPIDX_SKILLS_VERSION,
195
+ latestSkillsVersion: manifest.skillsVersion,
196
+ bundledExpectedSkillsVersion: RAPIDX_SKILLS_VERSION,
197
+ installedSkillsVersion,
198
+ skillsInstallStatus,
172
199
  skillsSchemaVersion: manifest.skillsSchemaVersion,
173
200
  currentSkillsSchemaVersion: RAPIDX_SKILLS_SCHEMA_VERSION,
174
201
  updateAvailable: false,
@@ -180,6 +207,8 @@ function buildUnknownUpdateCheckResult(metadata) {
180
207
  cacheTtlSeconds: metadata.cacheTtlSeconds,
181
208
  manifestUrl: metadata.manifestUrl,
182
209
  manifestSource: metadata.manifestSource,
210
+ updateFreshness: freshnessForSource(metadata.manifestSource),
211
+ refreshCommand: "rapidx update check --input '{\"force\":true}' --json",
183
212
  releaseNotesUrl: "",
184
213
  skillsUpdateRecommended: false,
185
214
  upgrade: {},
@@ -198,6 +227,41 @@ function computeUpdateStatus(input) {
198
227
  }
199
228
  return "CURRENT";
200
229
  }
230
+ function computeSkillsInstallStatus(installedSkillsVersion, latestSkillsVersion) {
231
+ if (installedSkillsVersion === "NOT_VERIFIED") {
232
+ return "NOT_VERIFIED";
233
+ }
234
+ return compareVersions(installedSkillsVersion, latestSkillsVersion) < 0 ? "UPDATE_AVAILABLE" : "CURRENT";
235
+ }
236
+ function normalizeInstalledSkillsVersion(value) {
237
+ return typeof value === "string" && isSemver(value) ? value : "NOT_VERIFIED";
238
+ }
239
+ function freshnessForSource(source) {
240
+ if (source === "remote") {
241
+ return "REMOTE";
242
+ }
243
+ if (source === "cache") {
244
+ return "CACHE";
245
+ }
246
+ return "FALLBACK";
247
+ }
248
+ function manifestFromCachedResult(result) {
249
+ return {
250
+ product: "rapidx",
251
+ channel: "stable",
252
+ latestCliVersion: result.latestVersion,
253
+ minimumSupportedVersion: result.minimumSupportedVersion,
254
+ minimumWriteVersion: result.minimumWriteVersion,
255
+ latestMcpSchemaVersion: result.latestMcpSchemaVersion,
256
+ skillsVersion: result.skillsVersion,
257
+ skillsSchemaVersion: result.skillsSchemaVersion,
258
+ skillsUpdateRecommended: result.skillsUpdateRecommended,
259
+ severity: result.severity,
260
+ breaking: result.breaking,
261
+ releaseNotesUrl: result.releaseNotesUrl,
262
+ upgrade: result.upgrade
263
+ };
264
+ }
201
265
  function compareVersions(left, right) {
202
266
  const a = parseVersion(left);
203
267
  const b = parseVersion(right);
@@ -220,6 +284,9 @@ function parseVersion(version) {
220
284
  }
221
285
  return [Number(match[1]), Number(match[2]), Number(match[3])];
222
286
  }
287
+ function isSemver(version) {
288
+ return /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.test(version);
289
+ }
223
290
  function assertSemver(version, field) {
224
291
  try {
225
292
  parseVersion(version);
@@ -1 +1 @@
1
- export const RAPIDX_VERSION = "1.0.31";
1
+ export const RAPIDX_VERSION = "1.0.33";
@@ -40,6 +40,9 @@ export async function runMcpTool(toolName, input = {}) {
40
40
  if (typeof input.maxCacheAgeSeconds === "number") {
41
41
  updateInput.maxCacheAgeSeconds = input.maxCacheAgeSeconds;
42
42
  }
43
+ if (typeof input.installedSkillsVersion === "string") {
44
+ updateInput.installedSkillsVersion = input.installedSkillsVersion;
45
+ }
43
46
  const update = await checkForUpdate(updateInput);
44
47
  const status = envelopeStatusForUpdate(update.status);
45
48
  const auditId = writeMcpAudit("update-check", status, { toolName, updateStatus: update.status });
@@ -136,8 +139,8 @@ export async function runMcpTool(toolName, input = {}) {
136
139
  }
137
140
  }
138
141
  function concretePreviewTargetForTool(toolName) {
139
- if (toolName === "rapidx/order/amend-preview") {
140
- return "order.amend";
142
+ if (toolName === "rapidx/order/replace-preview") {
143
+ return "order.replace";
141
144
  }
142
145
  if (toolName === "rapidx/order/cancel-preview") {
143
146
  return "order.cancel";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liquiditytech/rapidx-cli",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "RapidX CLI, MCP server mode, registry, and release assets.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -17,9 +17,9 @@ Supported conventions:
17
17
  - Use stdin JSON when the host supports it.
18
18
  - Do not create bridge scripts.
19
19
  - Use `rapidx order place-preview` for `order.place`.
20
- - Use `rapidx order amend-preview` for `order.amend`.
20
+ - Use `rapidx order replace-preview` for `order.replace`.
21
21
  - Use `rapidx order cancel-preview` for `order.cancel`.
22
- - Use `rapidx trade preview` with `targetCapabilityId` for non-order trade writes, including position, account, and algo writes.
22
+ - Use `rapidx trade preview` with `targetCapabilityId` for non-order trade writes, including position, portfolio, and algo writes.
23
23
  - `rapidx trade preview` input is flat JSON; do not wrap target parameters under `params`.
24
24
  - CLI preview ids are stored in the local CLI preview store. Do not submit MCP-created preview ids through CLI.
25
25
  - Treat `maxNotional` as a safety upper bound, not as the target order amount. Check symbol `minNotional` before increasing a requested amount.
@@ -32,28 +32,53 @@ rapidx schema --json
32
32
  rapidx update check --json
33
33
  rapidx self-check --read-only --json
34
34
  rapidx self-check --read-only --check-updates --json
35
- rapidx account balance --input '{"mode":"portfolio"}' --json
36
- rapidx account balance --input '{"mode":"account"}' --json
35
+ rapidx portfolio overview --json
36
+ rapidx portfolio assets --input '{"exchangeType":"BINANCE"}' --json
37
37
  rapidx order place-preview --input @/absolute/path/order-preview.json --json
38
- rapidx order amend-preview --input @/absolute/path/order-amend-preview.json --json
38
+ rapidx order replace-preview --input @/absolute/path/order-replace-preview.json --json
39
39
  rapidx order cancel-preview --input @/absolute/path/order-cancel-preview.json --json
40
40
  rapidx trade preview --input @/absolute/path/trade-preview.json --json
41
41
  rapidx position history --input @/absolute/path/position-history.json --json
42
- rapidx account set-position-mode --input @/absolute/path/set-position-mode.json --json
42
+ rapidx portfolio set-position-mode --input @/absolute/path/set-position-mode.json --json
43
43
  ```
44
44
 
45
45
  `rapidx update check --json` reads the RapidX release manifest and caches the result locally. Use it during setup, review, or session startup. Trade submit paths should not perform a fresh network update check.
46
46
 
47
+ When checking an existing installation, read the installed RapidX skill `version` from the loaded `SKILL.md` frontmatter and pass it to update check:
48
+
49
+ ```bash
50
+ rapidx update check --input '{"installedSkillsVersion":"1.0.8"}' --json
51
+ ```
52
+
53
+ For upgrade reviews, force a remote manifest refresh:
54
+
55
+ ```bash
56
+ rapidx update check --input '{"installedSkillsVersion":"1.0.8","force":true}' --json
57
+ ```
58
+
59
+ Update output fields:
60
+
61
+ - `latestVersion`: latest CLI version from the release manifest.
62
+ - `currentVersion`: running CLI version.
63
+ - `latestSkillsVersion` / `skillsVersion`: latest RapidX skills version from the release manifest.
64
+ - `bundledExpectedSkillsVersion` / `currentSkillsVersion`: skills version expected by this CLI build, not proof of locally installed skills.
65
+ - `installedSkillsVersion`: local skill version supplied by the caller, or `NOT_VERIFIED`.
66
+ - `skillsInstallStatus`: `CURRENT`, `UPDATE_AVAILABLE`, or `NOT_VERIFIED`.
67
+ - `manifestSource`: `remote`, `cache`, or `fallback`.
68
+ - `updateFreshness`: `REMOTE`, `CACHE`, or `FALLBACK`.
69
+ - `cacheWarning`: present when the result came from local cache.
70
+ - `refreshCommand`: command for forcing a remote manifest refresh.
71
+
47
72
  `rapidx schema --json` returns `capabilities` plus `inputSchemas`. Agents should read the concrete input schema before constructing write inputs.
48
73
 
49
- `rapidx account balance` defaults to `mode=portfolio` and reads `/api/v1/trading/portfolio/assets`. `mode=account` reads `/api/v1/account/balance` and requires credentials with account-level permission.
74
+ `rapidx portfolio assets` reads `/api/v1/trading/portfolio/assets`. It is the canonical CLI command for portfolio asset balances.
50
75
 
51
- `rapidx account set-position-mode` is a trade write. Use it only when the user explicitly asks to change account position mode. It requires a matching preview token and a `continueConsentId` before execution.
76
+ `rapidx portfolio set-position-mode` is a trade write. Use it only when the user explicitly asks to change account position mode. It requires a matching preview token and a `continueConsentId` before execution.
52
77
 
53
- `rapidx order cancel` is asynchronous. A successful response can mean cancel accepted but not terminal. Poll `rapidx order get --json` when `terminalStateConfirmed=false`.
78
+ `rapidx order cancel` is asynchronous. A successful response can mean cancel accepted but not terminal. Poll `rapidx order query --json` when `terminalStateConfirmed=false`.
54
79
 
55
- When using `orderId`, pass the RapidX 16-digit numeric order id. Invalid `orderId` format is rejected locally as `INVALID_INPUT`. `clientOrderId` can be used instead and does not need to satisfy the `orderId` format. Preview commands for amend/cancel perform a RapidX readback after local format validation; valid-but-missing orders return `NOT_FOUND` or a blocked non-open state.
80
+ When using `orderId`, pass the RapidX 16-digit numeric order id. Invalid `orderId` format is rejected locally as `INVALID_INPUT`. `clientOrderId` can be used instead and does not need to satisfy the `orderId` format. Preview commands for replace/cancel perform a RapidX readback after local format validation; valid-but-missing orders return `NOT_FOUND` or a blocked non-open state.
56
81
 
57
82
  Automation mode still uses preview. Pass `automationMode=true` and the user's exact `automationConsentText` to preview only when the user has enabled automation in chat for the current scope.
58
83
 
59
- `rapidx position close` is a close-position action. Do not pass `side` or `quantity`; RapidX determines BUY or SELL from the current position and closes the target symbol/positionSide. Use a reduce-only order flow for partial closes. Verify the final exposure with `rapidx position list --json`, and do not rely only on `order get` to interpret the close intent.
84
+ `rapidx position close` is a close-position action. Do not pass `side` or `quantity`; RapidX determines BUY or SELL from the current position and closes the target symbol/positionSide. Use a reduce-only order flow for partial closes. Verify the final exposure with `rapidx position query --json`, and do not rely only on `order query` to interpret the close intent.
@@ -28,14 +28,14 @@ Key trading tools:
28
28
  - `rapidx/update/check` reads the RapidX release manifest and returns current/latest CLI version, minimum write version, skills update advice, and upgrade commands.
29
29
  - `rapidx/self-check` can include update state when called with `{"checkUpdates": true}`.
30
30
  - `rapidx/order/preview` creates preview tokens for `order.place`.
31
- - `rapidx/order/place-preview`, `rapidx/order/amend-preview`, and `rapidx/order/cancel-preview` are concrete order preview aliases. Preview responses include `confirmation.submitToken`; pass it as `continueConsentId` when submitting the matching write tool.
32
- - `rapidx/trade/preview` creates preview tokens for non-place trade writes by `targetCapabilityId`, including position, account, and algo writes.
31
+ - `rapidx/order/place-preview`, `rapidx/order/replace-preview`, and `rapidx/order/cancel-preview` are concrete order preview aliases. Preview responses include `confirmation.submitToken`; pass it as `continueConsentId` when submitting the matching write tool.
32
+ - `rapidx/trade/preview` creates preview tokens for non-place trade writes by `targetCapabilityId`, including position, portfolio, and algo writes.
33
33
  - Preview ids are local to the running MCP server. Do not submit a CLI-created preview id through MCP, and do not submit an MCP-created preview id through CLI.
34
- - `rapidx/order/cancel` returns cancel acceptance plus terminal-state guidance. Poll `rapidx/order/get` when `terminalStateConfirmed=false`.
34
+ - `rapidx/order/cancel` returns cancel acceptance plus terminal-state guidance. Poll `rapidx/order/query` when `terminalStateConfirmed=false`.
35
35
  - Automation mode still uses preview. Set `automationMode=true` and pass the user's exact `automationConsentText` only when the user has enabled automation in chat for the current scope.
36
36
  - `rapidx/position/history` reads historical positions.
37
37
  - Hedge-mode order placement and verification use `positionSide="LONG"` or `positionSide="SHORT"` in the order or verify-live input. Do not use account mode switching as a substitute for order-level `positionSide`.
38
- - `rapidx/account/set-position-mode` changes account position mode and requires preview plus explicit continuation consent. Use it only when the user explicitly asks to change account position mode.
38
+ - `rapidx/portfolio/set-position-mode` changes account position mode and requires preview plus explicit continuation consent. Use it only when the user explicitly asks to change account position mode.
39
39
  - `rapidx/trade/verify-live` runs the optional small real-trade verification flow with `explicitUserConsent` and parameter-bound `acceptedRiskText`; it creates its own internal preview and does not accept an external `previewId`. If `positionSide` is provided, `acceptedRiskText` must include that position side. `rapidx/trading-verification` remains supported as a compatibility alias.
40
40
 
41
41
  Agents must discover the full tool list through `rapidx/tools` and must not invent tool names.