@liquiditytech/rapidx-cli 1.0.26

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 (64) hide show
  1. package/README.md +81 -0
  2. package/dist/cli/audit.js +10 -0
  3. package/dist/cli/bin.js +41 -0
  4. package/dist/cli/commands/account.js +34 -0
  5. package/dist/cli/commands/algo.js +35 -0
  6. package/dist/cli/commands/auth.js +22 -0
  7. package/dist/cli/commands/config.js +46 -0
  8. package/dist/cli/commands/doctor.js +42 -0
  9. package/dist/cli/commands/index.js +73 -0
  10. package/dist/cli/commands/market.js +26 -0
  11. package/dist/cli/commands/order.js +81 -0
  12. package/dist/cli/commands/position.js +35 -0
  13. package/dist/cli/commands/schema.js +5 -0
  14. package/dist/cli/commands/self-check.js +24 -0
  15. package/dist/cli/commands/trade-gate.js +26 -0
  16. package/dist/cli/commands/trade.js +30 -0
  17. package/dist/cli/commands/update.js +27 -0
  18. package/dist/cli/envelope.js +29 -0
  19. package/dist/cli/help.js +34 -0
  20. package/dist/cli/invocation-checker.js +39 -0
  21. package/dist/cli/mcp-entry.js +4 -0
  22. package/dist/cli/parser.js +87 -0
  23. package/dist/core/audit/redaction.js +56 -0
  24. package/dist/core/audit/writer.js +27 -0
  25. package/dist/core/client/capability-executor.js +400 -0
  26. package/dist/core/client/rapid-x-client.js +156 -0
  27. package/dist/core/client/signing.js +24 -0
  28. package/dist/core/client/symbol.js +36 -0
  29. package/dist/core/config/credential.js +42 -0
  30. package/dist/core/config/resolve.js +24 -0
  31. package/dist/core/contracts/capabilities.js +77 -0
  32. package/dist/core/contracts/compatibility.js +65 -0
  33. package/dist/core/contracts/events.js +29 -0
  34. package/dist/core/contracts/evidence.js +7 -0
  35. package/dist/core/contracts/input-schema.js +370 -0
  36. package/dist/core/contracts/types.js +1 -0
  37. package/dist/core/errors/product-error.js +74 -0
  38. package/dist/core/index.js +24 -0
  39. package/dist/core/safety/policy.js +215 -0
  40. package/dist/core/safety/raw-api.js +19 -0
  41. package/dist/core/self-check/live-read-only-probes.js +25 -0
  42. package/dist/core/self-check/live-trading-verification-probes.js +252 -0
  43. package/dist/core/self-check/run-self-check.js +91 -0
  44. package/dist/core/trading/preview.js +330 -0
  45. package/dist/core/trading/trading-verification.js +137 -0
  46. package/dist/core/update/check-update.js +295 -0
  47. package/dist/core/version.js +1 -0
  48. package/dist/mcp/audit.js +10 -0
  49. package/dist/mcp/server.js +73 -0
  50. package/dist/mcp/tool-registry.js +31 -0
  51. package/dist/mcp/tool-runner.js +144 -0
  52. package/package.json +48 -0
  53. package/packages/distribution/docs/cli-only-agent.md +12 -0
  54. package/packages/distribution/docs/cli.md +49 -0
  55. package/packages/distribution/docs/index.md +58 -0
  56. package/packages/distribution/docs/mcp.md +36 -0
  57. package/packages/distribution/docs/quickstart.md +129 -0
  58. package/packages/distribution/docs/self-check.md +7 -0
  59. package/packages/distribution/docs/skills.md +140 -0
  60. package/packages/distribution/docs/tools.md +35 -0
  61. package/packages/distribution/docs/trading-verification.md +17 -0
  62. package/packages/distribution/docs/troubleshooting/index.md +27 -0
  63. package/packages/distribution/manifests/offline-manifest.json +26 -0
  64. package/packages/distribution/registry/rapidx.mcp.json +26 -0
@@ -0,0 +1,295 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { RAPIDX_SKILLS_SCHEMA_VERSION, RAPIDX_SKILLS_VERSION } from "../contracts/compatibility.js";
4
+ import { SCHEMA_VERSION } from "../contracts/types.js";
5
+ import { RAPIDX_VERSION } from "../version.js";
6
+ export const DEFAULT_RELEASE_MANIFEST_URL = "https://raw.githubusercontent.com/LiquidityTech/ltp-rapidx-skill/main/releases/stable.json";
7
+ export const DEFAULT_UPDATE_CACHE_TTL_SECONDS = 86_400;
8
+ export const BUILTIN_MINIMUM_SUPPORTED_VERSION = "1.0.20";
9
+ export const BUILTIN_MINIMUM_WRITE_VERSION = "1.0.24";
10
+ export async function checkForUpdate(input = {}, env = process.env, cwd = process.cwd()) {
11
+ const manifestUrl = resolveManifestUrl(input, env);
12
+ const cacheTtlSeconds = resolveCacheTtl(input);
13
+ const cacheFile = resolveUpdateCacheFile(env, cwd);
14
+ const cached = loadCachedUpdateState(cacheFile);
15
+ if (!input.force && cached && !isCacheExpired(cached, cacheTtlSeconds)) {
16
+ return {
17
+ ...cached.result,
18
+ manifestSource: "cache",
19
+ cacheTtlSeconds
20
+ };
21
+ }
22
+ try {
23
+ const manifest = await fetchReleaseManifest(manifestUrl);
24
+ const result = buildUpdateCheckResult(manifest, {
25
+ checkedAt: new Date().toISOString(),
26
+ cacheTtlSeconds,
27
+ manifestUrl,
28
+ manifestSource: "remote"
29
+ });
30
+ saveCachedUpdateState(cacheFile, {
31
+ checkedAt: result.checkedAt,
32
+ cacheTtlSeconds,
33
+ result
34
+ });
35
+ return result;
36
+ }
37
+ catch (error) {
38
+ if (cached) {
39
+ return {
40
+ ...cached.result,
41
+ manifestSource: "cache",
42
+ cacheTtlSeconds,
43
+ error: error instanceof Error ? error.message : String(error)
44
+ };
45
+ }
46
+ return buildUnknownUpdateCheckResult({
47
+ checkedAt: new Date().toISOString(),
48
+ cacheTtlSeconds,
49
+ manifestUrl,
50
+ manifestSource: "fallback",
51
+ error: error instanceof Error ? error.message : String(error)
52
+ });
53
+ }
54
+ }
55
+ export function buildUpdateCheckResult(manifest, metadata) {
56
+ const minimumSupportedVersion = manifest.minimumSupportedVersion || BUILTIN_MINIMUM_SUPPORTED_VERSION;
57
+ const minimumWriteVersion = maxVersion(manifest.minimumWriteVersion || BUILTIN_MINIMUM_WRITE_VERSION, BUILTIN_MINIMUM_WRITE_VERSION);
58
+ const status = computeUpdateStatus({
59
+ currentVersion: RAPIDX_VERSION,
60
+ latestVersion: manifest.latestCliVersion,
61
+ minimumSupportedVersion,
62
+ minimumWriteVersion
63
+ });
64
+ const writeAllowed = status !== "WRITE_BLOCKED";
65
+ return {
66
+ currentVersion: RAPIDX_VERSION,
67
+ latestVersion: manifest.latestCliVersion,
68
+ minimumSupportedVersion,
69
+ minimumWriteVersion,
70
+ latestMcpSchemaVersion: manifest.latestMcpSchemaVersion,
71
+ currentMcpSchemaVersion: SCHEMA_VERSION,
72
+ skillsVersion: manifest.skillsVersion,
73
+ currentSkillsVersion: RAPIDX_SKILLS_VERSION,
74
+ skillsSchemaVersion: manifest.skillsSchemaVersion,
75
+ currentSkillsSchemaVersion: RAPIDX_SKILLS_SCHEMA_VERSION,
76
+ updateAvailable: compareVersions(RAPIDX_VERSION, manifest.latestCliVersion) < 0,
77
+ writeAllowed,
78
+ status,
79
+ severity: manifest.severity,
80
+ breaking: manifest.breaking,
81
+ checkedAt: metadata.checkedAt,
82
+ cacheTtlSeconds: metadata.cacheTtlSeconds,
83
+ manifestUrl: metadata.manifestUrl,
84
+ manifestSource: metadata.manifestSource,
85
+ releaseNotesUrl: manifest.releaseNotesUrl,
86
+ skillsUpdateRecommended: manifest.skillsUpdateRecommended || compareVersions(RAPIDX_SKILLS_VERSION, manifest.skillsVersion) < 0,
87
+ upgrade: manifest.upgrade,
88
+ ...(metadata.error ? { error: metadata.error } : {})
89
+ };
90
+ }
91
+ export function resolveUpdateCacheFile(env = process.env, cwd = process.cwd()) {
92
+ const stateDir = env.RAPIDX_STATE_DIR ? resolve(env.RAPIDX_STATE_DIR) : resolve(cwd, ".rapidx");
93
+ return join(stateDir, "update-state.json");
94
+ }
95
+ function resolveManifestUrl(input, env) {
96
+ const url = firstNonEmpty(input.manifestUrl, env.RAPIDX_RELEASE_MANIFEST_URL, DEFAULT_RELEASE_MANIFEST_URL);
97
+ assertHttpsUrl(url);
98
+ return url;
99
+ }
100
+ function resolveCacheTtl(input) {
101
+ if (typeof input.maxCacheAgeSeconds === "number" && Number.isFinite(input.maxCacheAgeSeconds) && input.maxCacheAgeSeconds >= 0) {
102
+ return input.maxCacheAgeSeconds;
103
+ }
104
+ return DEFAULT_UPDATE_CACHE_TTL_SECONDS;
105
+ }
106
+ async function fetchReleaseManifest(url) {
107
+ const response = await fetch(url);
108
+ if (!response.ok) {
109
+ throw new Error(`Release manifest request failed with HTTP ${response.status}.`);
110
+ }
111
+ const raw = JSON.parse(await response.text());
112
+ return normalizeReleaseManifest(raw);
113
+ }
114
+ function normalizeReleaseManifest(raw) {
115
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
116
+ throw new Error("Release manifest must be a JSON object.");
117
+ }
118
+ const value = raw;
119
+ const upgrade = isPlainObject(value.upgrade) ? value.upgrade : {};
120
+ const manifest = {
121
+ product: readLiteral(value.product, "rapidx", "product"),
122
+ channel: readLiteral(value.channel, "stable", "channel"),
123
+ latestCliVersion: readRequiredString(value.latestCliVersion, "latestCliVersion"),
124
+ minimumSupportedVersion: readOptionalString(value.minimumSupportedVersion, BUILTIN_MINIMUM_SUPPORTED_VERSION),
125
+ minimumWriteVersion: readOptionalString(value.minimumWriteVersion, BUILTIN_MINIMUM_WRITE_VERSION),
126
+ latestMcpSchemaVersion: readOptionalString(value.latestMcpSchemaVersion, SCHEMA_VERSION),
127
+ skillsVersion: readOptionalString(value.skillsVersion, RAPIDX_SKILLS_VERSION),
128
+ skillsSchemaVersion: readOptionalString(value.skillsSchemaVersion, RAPIDX_SKILLS_SCHEMA_VERSION),
129
+ skillsUpdateRecommended: value.skillsUpdateRecommended === true,
130
+ severity: readSeverity(value.severity),
131
+ breaking: value.breaking === true,
132
+ releaseNotesUrl: readOptionalString(value.releaseNotesUrl, ""),
133
+ upgrade: {
134
+ global: readOptionalString(upgrade.global, undefined),
135
+ workspace: readOptionalString(upgrade.workspace, undefined),
136
+ skills: readOptionalString(upgrade.skills, undefined)
137
+ }
138
+ };
139
+ assertSemver(manifest.latestCliVersion, "latestCliVersion");
140
+ assertSemver(manifest.minimumSupportedVersion, "minimumSupportedVersion");
141
+ assertSemver(manifest.minimumWriteVersion, "minimumWriteVersion");
142
+ return manifest;
143
+ }
144
+ function fallbackManifest() {
145
+ return {
146
+ product: "rapidx",
147
+ channel: "stable",
148
+ latestCliVersion: RAPIDX_VERSION,
149
+ minimumSupportedVersion: BUILTIN_MINIMUM_SUPPORTED_VERSION,
150
+ minimumWriteVersion: BUILTIN_MINIMUM_WRITE_VERSION,
151
+ latestMcpSchemaVersion: SCHEMA_VERSION,
152
+ skillsVersion: RAPIDX_SKILLS_VERSION,
153
+ skillsSchemaVersion: RAPIDX_SKILLS_SCHEMA_VERSION,
154
+ skillsUpdateRecommended: false,
155
+ severity: "none",
156
+ breaking: false,
157
+ releaseNotesUrl: "",
158
+ upgrade: {}
159
+ };
160
+ }
161
+ function buildUnknownUpdateCheckResult(metadata) {
162
+ const manifest = fallbackManifest();
163
+ return {
164
+ currentVersion: RAPIDX_VERSION,
165
+ latestVersion: RAPIDX_VERSION,
166
+ minimumSupportedVersion: manifest.minimumSupportedVersion,
167
+ minimumWriteVersion: manifest.minimumWriteVersion,
168
+ latestMcpSchemaVersion: manifest.latestMcpSchemaVersion,
169
+ currentMcpSchemaVersion: SCHEMA_VERSION,
170
+ skillsVersion: manifest.skillsVersion,
171
+ currentSkillsVersion: RAPIDX_SKILLS_VERSION,
172
+ skillsSchemaVersion: manifest.skillsSchemaVersion,
173
+ currentSkillsSchemaVersion: RAPIDX_SKILLS_SCHEMA_VERSION,
174
+ updateAvailable: false,
175
+ writeAllowed: true,
176
+ status: "UNKNOWN",
177
+ severity: "none",
178
+ breaking: false,
179
+ checkedAt: metadata.checkedAt,
180
+ cacheTtlSeconds: metadata.cacheTtlSeconds,
181
+ manifestUrl: metadata.manifestUrl,
182
+ manifestSource: metadata.manifestSource,
183
+ releaseNotesUrl: "",
184
+ skillsUpdateRecommended: false,
185
+ upgrade: {},
186
+ error: metadata.error
187
+ };
188
+ }
189
+ function computeUpdateStatus(input) {
190
+ if (compareVersions(input.currentVersion, input.minimumWriteVersion) < 0) {
191
+ return "WRITE_BLOCKED";
192
+ }
193
+ if (compareVersions(input.currentVersion, input.minimumSupportedVersion) < 0) {
194
+ return "UPGRADE_REQUIRED";
195
+ }
196
+ if (compareVersions(input.currentVersion, input.latestVersion) < 0) {
197
+ return "UPDATE_AVAILABLE";
198
+ }
199
+ return "CURRENT";
200
+ }
201
+ function compareVersions(left, right) {
202
+ const a = parseVersion(left);
203
+ const b = parseVersion(right);
204
+ for (const index of [0, 1, 2]) {
205
+ const leftPart = a[index];
206
+ const rightPart = b[index];
207
+ if (leftPart !== rightPart) {
208
+ return leftPart < rightPart ? -1 : 1;
209
+ }
210
+ }
211
+ return 0;
212
+ }
213
+ function maxVersion(left, right) {
214
+ return compareVersions(left, right) >= 0 ? left : right;
215
+ }
216
+ function parseVersion(version) {
217
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version);
218
+ if (!match) {
219
+ throw new Error(`Invalid semver version: ${version}`);
220
+ }
221
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
222
+ }
223
+ function assertSemver(version, field) {
224
+ try {
225
+ parseVersion(version);
226
+ }
227
+ catch {
228
+ throw new Error(`Release manifest field ${field} must be semver.`);
229
+ }
230
+ }
231
+ function loadCachedUpdateState(filePath) {
232
+ if (!existsSync(filePath)) {
233
+ return undefined;
234
+ }
235
+ try {
236
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
237
+ if (!isPlainObject(parsed) || !isPlainObject(parsed.result) || typeof parsed.checkedAt !== "string") {
238
+ return undefined;
239
+ }
240
+ return parsed;
241
+ }
242
+ catch {
243
+ return undefined;
244
+ }
245
+ }
246
+ function saveCachedUpdateState(filePath, state) {
247
+ mkdirSync(dirname(filePath), { recursive: true });
248
+ writeFileSync(filePath, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
249
+ }
250
+ function isCacheExpired(state, ttlSeconds) {
251
+ if (ttlSeconds === 0) {
252
+ return true;
253
+ }
254
+ const checkedAt = Date.parse(state.checkedAt);
255
+ if (!Number.isFinite(checkedAt)) {
256
+ return true;
257
+ }
258
+ return Date.now() - checkedAt > ttlSeconds * 1000;
259
+ }
260
+ function readRequiredString(value, field) {
261
+ if (typeof value !== "string" || value.length === 0) {
262
+ throw new Error(`Release manifest field ${field} is required.`);
263
+ }
264
+ return value;
265
+ }
266
+ function readOptionalString(value, fallback) {
267
+ return typeof value === "string" && value.length > 0 ? value : fallback ?? "";
268
+ }
269
+ function readLiteral(value, expected, field) {
270
+ if (value !== expected) {
271
+ throw new Error(`Release manifest field ${field} must be ${expected}.`);
272
+ }
273
+ return expected;
274
+ }
275
+ function readSeverity(value) {
276
+ return value === "recommended" || value === "required" || value === "critical" || value === "none" ? value : "none";
277
+ }
278
+ function firstNonEmpty(...values) {
279
+ return values.find((value) => typeof value === "string" && value.length > 0) ?? DEFAULT_RELEASE_MANIFEST_URL;
280
+ }
281
+ function isPlainObject(value) {
282
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
283
+ }
284
+ function assertHttpsUrl(value) {
285
+ let parsed;
286
+ try {
287
+ parsed = new URL(value);
288
+ }
289
+ catch {
290
+ throw new Error("Release manifest URL must be a valid HTTPS URL.");
291
+ }
292
+ if (parsed.protocol !== "https:") {
293
+ throw new Error("Release manifest URL must use HTTPS.");
294
+ }
295
+ }
@@ -0,0 +1 @@
1
+ export const RAPIDX_VERSION = "1.0.26";
@@ -0,0 +1,10 @@
1
+ import { AuditWriter } from "../core/index.js";
2
+ const writer = new AuditWriter(process.stderr);
3
+ export function writeMcpAudit(type, status, payload) {
4
+ const result = writer.write({
5
+ type,
6
+ status,
7
+ payload
8
+ });
9
+ return result.auditId;
10
+ }
@@ -0,0 +1,73 @@
1
+ import { createInterface } from "node:readline";
2
+ import { stdin, stdout } from "node:process";
3
+ import { buildCompatibilityReport, RAPIDX_VERSION } from "../core/index.js";
4
+ import { getMcpToolDefinitions } from "./tool-registry.js";
5
+ import { runMcpTool } from "./tool-runner.js";
6
+ export async function handleMcpRequest(request) {
7
+ if (request.method === "initialize") {
8
+ return {
9
+ jsonrpc: "2.0",
10
+ id: request.id,
11
+ result: {
12
+ protocolVersion: "2025-03-26",
13
+ serverInfo: { name: "rapidx", version: RAPIDX_VERSION },
14
+ capabilities: { tools: {} },
15
+ compatibility: buildCompatibilityReport()
16
+ }
17
+ };
18
+ }
19
+ if (request.method === "tools/list") {
20
+ return {
21
+ jsonrpc: "2.0",
22
+ id: request.id,
23
+ result: {
24
+ tools: getMcpToolDefinitions().map((tool) => ({
25
+ name: tool.name,
26
+ description: tool.description,
27
+ inputSchema: tool.inputSchema
28
+ }))
29
+ }
30
+ };
31
+ }
32
+ if (request.method === "tools/call") {
33
+ const params = request.params ?? {};
34
+ const name = typeof params.name === "string" ? params.name : "";
35
+ const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
36
+ const result = await runMcpTool(name, args);
37
+ return {
38
+ jsonrpc: "2.0",
39
+ id: request.id,
40
+ result: {
41
+ content: [{ type: "text", text: JSON.stringify(result) }],
42
+ isError: !result.ok
43
+ }
44
+ };
45
+ }
46
+ return {
47
+ jsonrpc: "2.0",
48
+ id: request.id,
49
+ error: { code: -32601, message: `Unsupported MCP method: ${request.method}` }
50
+ };
51
+ }
52
+ export async function startStdioServer() {
53
+ const rl = createInterface({ input: stdin });
54
+ for await (const line of rl) {
55
+ if (!line.trim()) {
56
+ continue;
57
+ }
58
+ try {
59
+ const request = JSON.parse(line);
60
+ const response = await handleMcpRequest(request);
61
+ stdout.write(`${JSON.stringify(response)}\n`);
62
+ }
63
+ catch (error) {
64
+ stdout.write(`${JSON.stringify({
65
+ jsonrpc: "2.0",
66
+ error: {
67
+ code: -32700,
68
+ message: error instanceof Error ? error.message : String(error)
69
+ }
70
+ })}\n`);
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,31 @@
1
+ import { findCapabilityById, findCapabilityByMcpTool, inputSchemaForName, listMcpCapabilities, SCHEMA_VERSION } from "../core/index.js";
2
+ export function getMcpToolDefinitions() {
3
+ const seen = new Set();
4
+ const tools = [];
5
+ for (const capability of listMcpCapabilities()) {
6
+ if (!capability.mcpTool || seen.has(capability.mcpTool)) {
7
+ continue;
8
+ }
9
+ seen.add(capability.mcpTool);
10
+ const canonical = capabilityForTool(capability.mcpTool);
11
+ tools.push({
12
+ name: capability.mcpTool,
13
+ description: `RapidX ${canonical.capabilityId} (${canonical.riskLevel}, schema ${SCHEMA_VERSION})`,
14
+ inputSchema: inputSchemaForName(canonical.inputSchema),
15
+ capability: canonical
16
+ });
17
+ }
18
+ return tools.sort((a, b) => a.name.localeCompare(b.name));
19
+ }
20
+ export function capabilityForTool(toolName) {
21
+ const overrideId = toolName === "rapidx/tools"
22
+ ? "schema.discover"
23
+ : toolName === "rapidx/self-check"
24
+ ? "self-check.run"
25
+ : undefined;
26
+ const capability = overrideId ? findCapabilityById(overrideId) : findCapabilityByMcpTool(toolName);
27
+ if (!capability) {
28
+ throw new Error(`Unknown MCP tool: ${toolName}`);
29
+ }
30
+ return capability;
31
+ }
@@ -0,0 +1,144 @@
1
+ import { consumePreview, createTargetedTradePreview, createTradePreview, defaultSafetyPolicy, executeRapidXCapability, findCapabilityById, getSchemaResult, makeEvidence, makePreviewStore, makeSafetyState, normalizeUnknownError, runSelfCheck, runTradingVerification, verifyPreview, buildLiveReadOnlySelfCheck, buildLiveTradingVerificationProbes, checkForUpdate, } from "../core/index.js";
2
+ import { capabilityForTool, getMcpToolDefinitions } from "./tool-registry.js";
3
+ import { writeMcpAudit } from "./audit.js";
4
+ const previewStore = makePreviewStore();
5
+ const safetyState = makeSafetyState();
6
+ export async function runMcpTool(toolName, input = {}) {
7
+ try {
8
+ if (toolName === "rapidx/tools") {
9
+ const data = {
10
+ schemaVersion: getSchemaResult().schemaVersion,
11
+ tools: getMcpToolDefinitions().map((tool) => ({
12
+ name: tool.name,
13
+ riskLevel: tool.capability.riskLevel,
14
+ inputSchema: tool.capability.inputSchema,
15
+ outputSchema: tool.capability.outputSchema,
16
+ previewRequired: tool.capability.previewRequired
17
+ }))
18
+ };
19
+ const auditId = writeMcpAudit("tools/list", "PASS", { toolName });
20
+ return { ok: true, status: "PASS", data, auditId, evidence: [makeEvidence("rapidx/tools")] };
21
+ }
22
+ if (toolName === "rapidx/self-check") {
23
+ const report = await runSelfCheck({
24
+ input: { scope: input.scope === "deep" ? "deep" : "quick", readOnly: true, checkUpdates: input.checkUpdates === true },
25
+ ...buildLiveReadOnlySelfCheck(input)
26
+ });
27
+ const auditId = writeMcpAudit("self-check", report.status, { toolName, status: report.status });
28
+ return { ok: report.status === "PASS", status: report.status, data: report, auditId, evidence: report.evidence };
29
+ }
30
+ if (toolName === "rapidx/update/check") {
31
+ const updateInput = { force: input.force === true };
32
+ if (typeof input.manifestUrl === "string") {
33
+ updateInput.manifestUrl = input.manifestUrl;
34
+ }
35
+ if (typeof input.maxCacheAgeSeconds === "number") {
36
+ updateInput.maxCacheAgeSeconds = input.maxCacheAgeSeconds;
37
+ }
38
+ const update = await checkForUpdate(updateInput);
39
+ const status = envelopeStatusForUpdate(update.status);
40
+ const auditId = writeMcpAudit("update-check", status, { toolName, updateStatus: update.status });
41
+ return { ok: status === "PASS", status, data: update, auditId, evidence: [makeEvidence(toolName)] };
42
+ }
43
+ if (toolName === "rapidx/trading-verification" || toolName === "rapidx/trade/verify-live") {
44
+ const report = await runTradingVerification(input, buildLiveTradingVerificationProbes(input));
45
+ const auditId = writeMcpAudit("trading-verification", report.status, { toolName, status: report.status });
46
+ return tradingVerificationEnvelope(toolName, report, auditId);
47
+ }
48
+ const capability = capabilityForTool(toolName);
49
+ if (toolName === "rapidx/order/preview" || toolName === "rapidx/order/place-preview") {
50
+ const previewCapability = findCapabilityById("order.preview");
51
+ if (!previewCapability) {
52
+ throw new Error("order.preview capability missing");
53
+ }
54
+ const preview = createTradePreview(previewCapability, input, { ...defaultSafetyPolicy(), tradingEnabled: true, readOnly: false }, safetyState, previewStore);
55
+ const auditId = writeMcpAudit("trade-preview", "PASS", { toolName, capabilityId: capability.capabilityId, previewId: preview.previewId });
56
+ return { ok: true, status: "PASS", data: preview, auditId, evidence: [makeEvidence(toolName)] };
57
+ }
58
+ const concretePreviewTarget = concretePreviewTargetForTool(toolName);
59
+ if (concretePreviewTarget) {
60
+ const targetCapability = findCapabilityById(concretePreviewTarget);
61
+ if (!targetCapability) {
62
+ throw new Error(`${concretePreviewTarget} capability missing`);
63
+ }
64
+ const preview = createTargetedTradePreview(targetCapability, input, { ...defaultSafetyPolicy(), tradingEnabled: true, readOnly: false }, safetyState, previewStore);
65
+ const auditId = writeMcpAudit("trade-preview", "PASS", { toolName, capabilityId: targetCapability.capabilityId, previewId: preview.previewId });
66
+ return { ok: true, status: "PASS", data: preview, auditId, evidence: [makeEvidence(toolName)] };
67
+ }
68
+ if (toolName === "rapidx/trade/preview") {
69
+ const targetCapabilityId = typeof input.targetCapabilityId === "string" ? input.targetCapabilityId : "";
70
+ const targetCapability = targetCapabilityId ? findCapabilityById(targetCapabilityId) : undefined;
71
+ if (!targetCapability) {
72
+ throw new Error(`Unknown trade preview target: ${targetCapabilityId || "<missing>"}`);
73
+ }
74
+ if (targetCapability.operationType !== "TRADE_WRITE" || isPreviewCapability(targetCapability.capabilityId)) {
75
+ throw new Error("trade preview target must be a non-preview trade write capability.");
76
+ }
77
+ const preview = createTargetedTradePreview(targetCapability, input, { ...defaultSafetyPolicy(), tradingEnabled: true, readOnly: false }, safetyState, previewStore);
78
+ const auditId = writeMcpAudit("trade-preview", "PASS", { toolName, capabilityId: targetCapability.capabilityId, previewId: preview.previewId });
79
+ return { ok: true, status: "PASS", data: preview, auditId, evidence: [makeEvidence(toolName)] };
80
+ }
81
+ if (capability.previewRequired) {
82
+ verifyPreview(previewStore, typeof input.previewId === "string" ? input.previewId : undefined, capability, input);
83
+ const consentId = input.continueConsentId;
84
+ if (typeof consentId !== "string" || consentId.length === 0) {
85
+ return { ok: false, status: "BLOCKED", code: "RMCP20001", message: "continueConsentId is required for trade writes.", evidence: [makeEvidence(toolName)] };
86
+ }
87
+ consumePreview(previewStore, typeof input.previewId === "string" ? input.previewId : undefined, capability, input);
88
+ }
89
+ const data = await executeRapidXCapability(capability.capabilityId, input);
90
+ const auditId = writeMcpAudit("tool-call", "PASS", { toolName, capabilityId: capability.capabilityId });
91
+ return {
92
+ ok: true,
93
+ status: "PASS",
94
+ data,
95
+ auditId,
96
+ evidence: [makeEvidence(toolName, "real_tool_call")]
97
+ };
98
+ }
99
+ catch (error) {
100
+ const productError = normalizeUnknownError(error, "RMCP12003");
101
+ return {
102
+ ok: false,
103
+ status: productError.status,
104
+ code: productError.code.replace(/^RCORE/, "RMCP"),
105
+ message: productError.message,
106
+ evidence: [makeEvidence(toolName)]
107
+ };
108
+ }
109
+ }
110
+ function concretePreviewTargetForTool(toolName) {
111
+ if (toolName === "rapidx/order/amend-preview") {
112
+ return "order.amend";
113
+ }
114
+ if (toolName === "rapidx/order/cancel-preview") {
115
+ return "order.cancel";
116
+ }
117
+ return undefined;
118
+ }
119
+ function tradingVerificationEnvelope(toolName, report, auditId) {
120
+ if (report.status === "PASS") {
121
+ return { ok: true, status: "PASS", data: report, auditId, evidence: [makeEvidence(toolName)] };
122
+ }
123
+ return {
124
+ ok: false,
125
+ status: report.status,
126
+ code: (report.errorCode ?? "RCORE10002").replace(/^RCORE/, "RMCP"),
127
+ message: report.errorMessage ?? `Trading verification failed: ${report.errorCode ?? report.status}.`,
128
+ data: report,
129
+ auditId,
130
+ evidence: [makeEvidence(toolName)]
131
+ };
132
+ }
133
+ function isPreviewCapability(capabilityId) {
134
+ return capabilityId.endsWith(".preview") || capabilityId.endsWith("-preview");
135
+ }
136
+ function envelopeStatusForUpdate(status) {
137
+ if (status === "WRITE_BLOCKED" || status === "UPGRADE_REQUIRED") {
138
+ return "BLOCKED";
139
+ }
140
+ if (status === "UNKNOWN") {
141
+ return "NOT_VERIFIED";
142
+ }
143
+ return "PASS";
144
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@liquiditytech/rapidx-cli",
3
+ "version": "1.0.26",
4
+ "description": "RapidX CLI, MCP server mode, registry, and release assets.",
5
+ "type": "module",
6
+ "private": false,
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+ssh://git@github.com/LiquidityTech/ltp-rapidx-cli-mcp.git"
10
+ },
11
+ "publishConfig": {
12
+ "registry": "https://registry.npmjs.org/",
13
+ "access": "public"
14
+ },
15
+ "bin": {
16
+ "rapidx": "dist/cli/bin.js"
17
+ },
18
+ "files": [
19
+ "dist/cli/**/*.js",
20
+ "dist/core/**/*.js",
21
+ "dist/mcp/**/*.js",
22
+ "packages/distribution/docs",
23
+ "packages/distribution/registry",
24
+ "packages/distribution/manifests",
25
+ "README.md"
26
+ ],
27
+ "scripts": {
28
+ "prebuild": "node packages/distribution/scripts/clean-dist.mjs",
29
+ "build": "tsc -p tsconfig.json && node packages/distribution/scripts/normalize-dist.mjs",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit",
31
+ "test": "vitest run",
32
+ "test:release-smoke": "vitest run packages/distribution/tests/release-smoke.test.ts",
33
+ "validate:release": "node dist/distribution/src/validate-release.js"
34
+ },
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "1.29.0",
40
+ "zod": "3.25.76"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.0.0",
44
+ "tsx": "^4.19.0",
45
+ "typescript": "^5.8.0",
46
+ "vitest": "^4.1.1"
47
+ }
48
+ }
@@ -0,0 +1,12 @@
1
+ # CLI-only Agents
2
+
3
+ Some agent hosts cannot call MCP tools. They can still call the CLI directly.
4
+
5
+ Use direct commands only:
6
+
7
+ ```bash
8
+ rapidx schema --json
9
+ rapidx self-check --read-only --json
10
+ ```
11
+
12
+ When passing trade input, use a JSON file or stdin JSON. Avoid interpreter wrappers and bridge scripts.
@@ -0,0 +1,49 @@
1
+ # CLI
2
+
3
+ The `rapidx` CLI is the atomic interface for agents that cannot use MCP.
4
+
5
+ Official environment variables:
6
+
7
+ - `LTP_ACCESS_KEY`
8
+ - `LTP_SECRET_KEY`
9
+ - `LTP_API_HOST`
10
+
11
+ `LTP_API_HOST` is required. Use the API host provided for the current environment.
12
+
13
+ Supported conventions:
14
+
15
+ - Use `--json` for machine output.
16
+ - Use `--input @/absolute/path/input.json` for structured input.
17
+ - Use stdin JSON when the host supports it.
18
+ - Do not create bridge scripts.
19
+ - Use `rapidx order place-preview` for `order.place`.
20
+ - Use `rapidx order amend-preview` for `order.amend`.
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.
23
+ - `rapidx trade preview` input is flat JSON; do not wrap target parameters under `params`.
24
+ - Treat `maxNotional` as a safety upper bound, not as the target order amount. Check symbol `minNotional` before increasing a requested amount.
25
+
26
+ Examples:
27
+
28
+ ```bash
29
+ rapidx schema --json
30
+ rapidx update check --json
31
+ rapidx self-check --read-only --json
32
+ rapidx self-check --read-only --check-updates --json
33
+ rapidx account balance --input '{"mode":"portfolio"}' --json
34
+ rapidx account balance --input '{"mode":"account"}' --json
35
+ rapidx order place-preview --input @/absolute/path/order-preview.json --json
36
+ rapidx order amend-preview --input @/absolute/path/order-amend-preview.json --json
37
+ rapidx order cancel-preview --input @/absolute/path/order-cancel-preview.json --json
38
+ rapidx trade preview --input @/absolute/path/trade-preview.json --json
39
+ rapidx position history --input @/absolute/path/position-history.json --json
40
+ rapidx account set-position-mode --input @/absolute/path/set-position-mode.json --json
41
+ ```
42
+
43
+ `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.
44
+
45
+ `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.
46
+
47
+ `rapidx account set-position-mode` is a trade write. It requires a matching preview token and a `continueConsentId` before execution.
48
+
49
+ `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.