@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
package/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # RapidX CLI
2
+
3
+ RapidX CLI provides a local command-line entrypoint and MCP server mode for RapidX account, market, order, position, and portfolio workflows.
4
+
5
+ The package is distributed as `@liquiditytech/rapidx-cli`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @liquiditytech/rapidx-cli
11
+ ```
12
+
13
+ ## Configure
14
+
15
+ Provide credentials through the agent host secret mechanism or environment variables:
16
+
17
+ ```bash
18
+ LTP_ACCESS_KEY=<your-access-key>
19
+ LTP_SECRET_KEY=<your-secret-key>
20
+ LTP_API_HOST=<provided-api-host>
21
+ ```
22
+
23
+ Do not commit credentials into project files.
24
+
25
+ ## Use As CLI
26
+
27
+ ```bash
28
+ rapidx schema --json
29
+ rapidx self-check --read-only --json
30
+ rapidx market get-ticker --input '{"symbol":"BINANCE_PERP_BTC_USDT"}' --json
31
+ ```
32
+
33
+ Trade writes use a preview-first flow:
34
+
35
+ ```bash
36
+ rapidx order place-preview --input '{"symbol":"BINANCE_PERP_BTC_USDT","side":"BUY","orderType":"LIMIT","price":"65000","quantity":"0.001","maxNotional":"10","clientOrderId":"example-001"}' --json
37
+ ```
38
+
39
+ Submit write operations only after reviewing the preview result and providing the returned `previewId` plus `confirmation.submitToken` as `continueConsentId`.
40
+
41
+ ## Use As MCP
42
+
43
+ Configure the agent host to start the MCP server with:
44
+
45
+ ```json
46
+ {
47
+ "command": "rapidx",
48
+ "args": ["mcp", "serve"],
49
+ "env": {
50
+ "LTP_ACCESS_KEY": "<your-access-key>",
51
+ "LTP_SECRET_KEY": "<your-secret-key>",
52
+ "LTP_API_HOST": "<provided-api-host>"
53
+ }
54
+ }
55
+ ```
56
+
57
+ MCP tools expose the same capabilities as the CLI through structured tool schemas. Trade-write tools require preview and consent fields.
58
+
59
+ ## Skills
60
+
61
+ RapidX skills are distributed separately through GitHub. Use the config skill to guide installation, credential configuration, MCP registration, access review, and read-only self-check. Use the trading skill for normal query and preview-first trading workflows.
62
+
63
+ ```bash
64
+ npx skills add git@github.com:LiquidityTech/ltp-rapidx-skill.git \
65
+ --skill ltp-rapidx-config \
66
+ --skill ltp-rapidx-trading \
67
+ -a codex -y
68
+ ```
69
+
70
+ If the skills repository is public and the agent host has HTTPS GitHub access, `LiquidityTech/ltp-rapidx-skill` may be used instead of the SSH URL. Private repositories should use the SSH URL so Git can use the configured deploy key or user key.
71
+
72
+ ## Verification
73
+
74
+ After installation:
75
+
76
+ ```bash
77
+ rapidx schema --json
78
+ rapidx self-check --read-only --json
79
+ ```
80
+
81
+ Do not treat missing real API evidence as success. If RapidX cannot be called, the expected status is `NOT_VERIFIED`.
@@ -0,0 +1,10 @@
1
+ import { AuditWriter } from "../core/index.js";
2
+ const writer = new AuditWriter(process.stderr);
3
+ export function writeCliAudit(type, status, payload) {
4
+ const result = writer.write({
5
+ type,
6
+ status,
7
+ payload
8
+ });
9
+ return result.auditId;
10
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ import { stdin as input, stdout, stderr } from "node:process";
3
+ import { dispatchCli } from "./commands/index.js";
4
+ import { parseArgv } from "./parser.js";
5
+ import { startMcpServer } from "./mcp-entry.js";
6
+ import { formatCliHelp, formatCliVersion, isHelpArg, isVersionArg } from "./help.js";
7
+ async function readStdinIfPiped() {
8
+ if (input.isTTY) {
9
+ return "";
10
+ }
11
+ const chunks = [];
12
+ for await (const chunk of input) {
13
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
14
+ }
15
+ return Buffer.concat(chunks).toString("utf8");
16
+ }
17
+ async function main() {
18
+ const argv = process.argv.slice(2);
19
+ if (argv.some(isHelpArg)) {
20
+ stdout.write(`${formatCliHelp()}\n`);
21
+ return;
22
+ }
23
+ if (argv.some(isVersionArg)) {
24
+ stdout.write(`${formatCliVersion()}\n`);
25
+ return;
26
+ }
27
+ if (argv[0] === "mcp" && argv[1] === "serve") {
28
+ await startMcpServer();
29
+ return;
30
+ }
31
+ const parsed = parseArgv(argv, { stdin: await readStdinIfPiped() });
32
+ const result = await dispatchCli(parsed);
33
+ stdout.write(`${JSON.stringify(result)}\n`);
34
+ if (!result.ok) {
35
+ process.exitCode = 1;
36
+ }
37
+ }
38
+ main().catch((error) => {
39
+ stderr.write(`${JSON.stringify({ ok: false, status: "FAIL", code: "RCLI00001", message: error instanceof Error ? error.message : String(error) })}\n`);
40
+ process.exitCode = 1;
41
+ });
@@ -0,0 +1,34 @@
1
+ import { executeRapidXCapability, findCapabilityById, normalizeUnknownError } from "../../core/index.js";
2
+ import { writeCliAudit } from "../audit.js";
3
+ import { fail, ok } from "../envelope.js";
4
+ import { enforceCliPreviewGate } from "./trade-gate.js";
5
+ const ACCOUNT_CAPABILITY_BY_ACTION = {
6
+ overview: "account.overview",
7
+ balance: "account.balance",
8
+ "set-position-mode": "account.set-position-mode"
9
+ };
10
+ export async function runAccountCommand(action, input) {
11
+ const capabilityId = ACCOUNT_CAPABILITY_BY_ACTION[action];
12
+ if (!capabilityId) {
13
+ return fail("RCLI12001", `Unknown account command: ${action}`, "FAIL", `rapidx account ${action}`);
14
+ }
15
+ const capability = findCapabilityById(capabilityId);
16
+ if (!capability) {
17
+ return fail("RCLI30002", `Missing capability: ${capabilityId}`, "FAIL", `rapidx account ${action}`);
18
+ }
19
+ const blocked = enforceCliPreviewGate(capability, input, `rapidx account ${action}`);
20
+ if (blocked) {
21
+ return blocked;
22
+ }
23
+ try {
24
+ const data = await executeRapidXCapability(capabilityId, input);
25
+ const auditId = capability.operationType === "TRADE_WRITE"
26
+ ? writeCliAudit("trade-write", "PASS", { command: `rapidx account ${action}`, capabilityId })
27
+ : undefined;
28
+ return ok(data, `rapidx account ${action}`, "PASS", "real_tool_call", auditId);
29
+ }
30
+ catch (error) {
31
+ const productError = normalizeUnknownError(error, "RCLI01001");
32
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, `rapidx account ${action}`);
33
+ }
34
+ }
@@ -0,0 +1,35 @@
1
+ import { executeRapidXCapability, findCapabilityById, normalizeUnknownError } from "../../core/index.js";
2
+ import { writeCliAudit } from "../audit.js";
3
+ import { fail, ok } from "../envelope.js";
4
+ import { enforceCliPreviewGate } from "./trade-gate.js";
5
+ const ALGO_CAPABILITY_BY_ACTION = {
6
+ place: "algo.place",
7
+ amend: "algo.amend",
8
+ cancel: "algo.cancel",
9
+ list: "algo.list"
10
+ };
11
+ export async function runAlgoCommand(action, input) {
12
+ const capabilityId = ALGO_CAPABILITY_BY_ACTION[action];
13
+ if (!capabilityId) {
14
+ return fail("RCLI12001", `Unknown algo command: ${action}`, "FAIL", `rapidx algo ${action}`);
15
+ }
16
+ const capability = findCapabilityById(capabilityId);
17
+ if (!capability) {
18
+ return fail("RCLI30002", `Missing capability: ${capabilityId}`, "FAIL", `rapidx algo ${action}`);
19
+ }
20
+ const blocked = enforceCliPreviewGate(capability, input, `rapidx algo ${action}`);
21
+ if (blocked) {
22
+ return blocked;
23
+ }
24
+ try {
25
+ const data = await executeRapidXCapability(capabilityId, input);
26
+ const auditId = capability.operationType === "TRADE_WRITE"
27
+ ? writeCliAudit("trade-write", "PASS", { command: `rapidx algo ${action}`, capabilityId })
28
+ : undefined;
29
+ return ok(data, `rapidx algo ${action}`, "PASS", "real_tool_call", auditId);
30
+ }
31
+ catch (error) {
32
+ const productError = normalizeUnknownError(error, "RCLI12001");
33
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, `rapidx algo ${action}`);
34
+ }
35
+ }
@@ -0,0 +1,22 @@
1
+ import { makeCredentialRef, resolveCredential } from "../../core/index.js";
2
+ import { fail, ok } from "../envelope.js";
3
+ export function runAuthCheck(input) {
4
+ try {
5
+ const source = typeof input.source === "string" ? input.source : "env";
6
+ const material = {};
7
+ if (typeof input.accessKey === "string") {
8
+ material.accessKey = input.accessKey;
9
+ }
10
+ if (typeof input.secretKey === "string") {
11
+ material.secretKey = input.secretKey;
12
+ }
13
+ const credential = resolveCredential({
14
+ ref: makeCredentialRef(source),
15
+ material
16
+ });
17
+ return ok({ status: "PASS", maskedAccessKey: credential.maskedAccessKey }, "rapidx auth check");
18
+ }
19
+ catch (error) {
20
+ return fail("RCLI01001", error instanceof Error ? error.message : String(error), "NOT_VERIFIED", "rapidx auth check");
21
+ }
22
+ }
@@ -0,0 +1,46 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { makeCredentialRef } from "../../core/index.js";
4
+ import { ok } from "../envelope.js";
5
+ export function runConfigCommand(action, input) {
6
+ const filePath = resolveConfigFile();
7
+ const config = loadConfig(filePath);
8
+ if (action === "set") {
9
+ const key = String(input.key ?? "");
10
+ config[key] = shouldRedactConfigKey(key) ? "[REDACTED]" : input.value;
11
+ saveConfig(filePath, config);
12
+ return ok({ updated: [key], credentialRef: makeCredentialRef("chat-secret") }, "rapidx config set");
13
+ }
14
+ if (action === "unset") {
15
+ const key = String(input.key ?? "");
16
+ delete config[key];
17
+ saveConfig(filePath, config);
18
+ return ok({ unset: key }, "rapidx config unset");
19
+ }
20
+ if (action === "get") {
21
+ const key = String(input.key ?? "");
22
+ return ok({ key, value: config[key] ?? null }, "rapidx config get");
23
+ }
24
+ return ok({ entries: config }, "rapidx config list");
25
+ }
26
+ function resolveConfigFile(env = process.env, cwd = process.cwd()) {
27
+ const stateDir = env.RAPIDX_STATE_DIR ? resolve(env.RAPIDX_STATE_DIR) : resolve(cwd, ".rapidx");
28
+ return join(stateDir, "config.json");
29
+ }
30
+ function loadConfig(filePath) {
31
+ if (!existsSync(filePath)) {
32
+ return {};
33
+ }
34
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
35
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
36
+ return {};
37
+ }
38
+ return parsed;
39
+ }
40
+ function saveConfig(filePath, config) {
41
+ mkdirSync(dirname(filePath), { recursive: true });
42
+ writeFileSync(filePath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
43
+ }
44
+ function shouldRedactConfigKey(key) {
45
+ return /secret|token|password|credential|access[_-]?key|api[_-]?key/i.test(key);
46
+ }
@@ -0,0 +1,42 @@
1
+ import { buildCompatibilityReport, getSchemaResult } from "../../core/index.js";
2
+ import { checkInvocationMode } from "../invocation-checker.js";
3
+ import { ok } from "../envelope.js";
4
+ export function runDoctorCommand(input) {
5
+ const invocationInput = {
6
+ commandLine: typeof input.commandLine === "string" ? input.commandLine : "rapidx doctor --json"
7
+ };
8
+ if (typeof input.preflightError === "string") {
9
+ invocationInput.preflightError = input.preflightError;
10
+ }
11
+ const invocation = checkInvocationMode(invocationInput);
12
+ const compatibilityInput = {};
13
+ if (typeof input.mcpSchemaVersion === "string") {
14
+ compatibilityInput.mcpSchemaVersion = input.mcpSchemaVersion;
15
+ }
16
+ if (typeof input.skillsVersion === "string") {
17
+ compatibilityInput.skillsVersion = input.skillsVersion;
18
+ }
19
+ if (typeof input.skillsSchemaVersion === "string") {
20
+ compatibilityInput.skillsSchemaVersion = input.skillsSchemaVersion;
21
+ }
22
+ const compatibility = buildCompatibilityReport(compatibilityInput);
23
+ const credentialSource = {
24
+ LTP_ACCESS_KEY: Boolean(process.env.LTP_ACCESS_KEY),
25
+ LTP_SECRET_KEY: Boolean(process.env.LTP_SECRET_KEY),
26
+ LTP_API_HOST: Boolean(process.env.LTP_API_HOST)
27
+ };
28
+ const doctorStatus = !invocation.compliant || compatibility.status === "BLOCKED" ? "FAIL" : "PASS";
29
+ return ok({
30
+ schemaVersion: getSchemaResult().schemaVersion,
31
+ invocation,
32
+ compatibility,
33
+ credentialSource,
34
+ checks: [
35
+ { name: "node", status: "PASS" },
36
+ { name: "canonical-schema", status: "PASS" },
37
+ { name: "version-compatibility", status: compatibility.status },
38
+ { name: "credential-source", status: credentialSource.LTP_ACCESS_KEY && credentialSource.LTP_SECRET_KEY && credentialSource.LTP_API_HOST ? "PASS" : "NOT_VERIFIED" },
39
+ { name: "invocation", status: invocation.compliant ? "PASS" : "FAIL" }
40
+ ]
41
+ }, "rapidx doctor --json", doctorStatus);
42
+ }
@@ -0,0 +1,73 @@
1
+ import { checkInvocationMode } from "../invocation-checker.js";
2
+ import { fail, ok } from "../envelope.js";
3
+ import { runAccountCommand } from "./account.js";
4
+ import { runAlgoCommand } from "./algo.js";
5
+ import { runAuthCheck } from "./auth.js";
6
+ import { runConfigCommand } from "./config.js";
7
+ import { runDoctorCommand } from "./doctor.js";
8
+ import { runMarketCommand } from "./market.js";
9
+ import { runOrderCommand } from "./order.js";
10
+ import { runPositionCommand } from "./position.js";
11
+ import { runSchemaCommand } from "./schema.js";
12
+ import { runSelfCheckCommand, runTradingVerificationCommand } from "./self-check.js";
13
+ import { runTradeCommand } from "./trade.js";
14
+ import { runUpdateCommand } from "./update.js";
15
+ export async function dispatchCli(parsed) {
16
+ const [domain, action, subAction] = parsed.commandParts;
17
+ if (!domain) {
18
+ return fail("RCLI00001", "Command is required.");
19
+ }
20
+ if (domain === "schema") {
21
+ return runSchemaCommand();
22
+ }
23
+ if (domain === "doctor") {
24
+ return runDoctorCommand(parsed.input);
25
+ }
26
+ if (domain === "auth" && action === "check") {
27
+ return runAuthCheck(parsed.input);
28
+ }
29
+ if (domain === "config") {
30
+ return runConfigCommand(action ?? "list", parsed.input);
31
+ }
32
+ if (domain === "self-check" && action === "trade-verify") {
33
+ return runTradingVerificationCommand(parsed.input);
34
+ }
35
+ if (domain === "trade" && action === "verify-live") {
36
+ return runTradingVerificationCommand(parsed.input, "rapidx trade verify-live --json");
37
+ }
38
+ if (domain === "self-check") {
39
+ return runSelfCheckCommand(parsed.input);
40
+ }
41
+ if (domain === "update") {
42
+ return runUpdateCommand(action ?? "check", parsed.input);
43
+ }
44
+ if (domain === "invocation" && action === "check") {
45
+ const invocationInput = {};
46
+ if (typeof parsed.input.commandLine === "string") {
47
+ invocationInput.commandLine = parsed.input.commandLine;
48
+ }
49
+ if (typeof parsed.input.preflightError === "string") {
50
+ invocationInput.preflightError = parsed.input.preflightError;
51
+ }
52
+ return ok(checkInvocationMode(invocationInput), "rapidx invocation check");
53
+ }
54
+ if (domain === "market") {
55
+ return runMarketCommand(action ?? "get-ticker", parsed.input);
56
+ }
57
+ if (domain === "account") {
58
+ return runAccountCommand(action ?? "overview", parsed.input);
59
+ }
60
+ if (domain === "order") {
61
+ return runOrderCommand(action ?? "list", parsed.input);
62
+ }
63
+ if (domain === "trade") {
64
+ return runTradeCommand(action ?? "preview", parsed.input);
65
+ }
66
+ if (domain === "position") {
67
+ return runPositionCommand(action ?? "list", parsed.input);
68
+ }
69
+ if (domain === "algo") {
70
+ return runAlgoCommand(subAction ?? action ?? "list", parsed.input);
71
+ }
72
+ return fail("RCLI12001", `Unknown command: ${parsed.command}`);
73
+ }
@@ -0,0 +1,26 @@
1
+ import { executeRapidXCapability, normalizeUnknownError } from "../../core/index.js";
2
+ import { ok } from "../envelope.js";
3
+ import { fail } from "../envelope.js";
4
+ const MARKET_CAPABILITY_BY_ACTION = {
5
+ "get-ticker": "market.ticker",
6
+ "get-orderbook": "market.orderbook",
7
+ "get-klines": "market.klines",
8
+ "get-funding-rate": "market.funding-rate",
9
+ "get-mark-price": "market.mark-price",
10
+ "get-symbol-info": "market.symbol-info",
11
+ "get-open-interest": "market.open-interest"
12
+ };
13
+ export async function runMarketCommand(action, input) {
14
+ const capabilityId = MARKET_CAPABILITY_BY_ACTION[action];
15
+ if (!capabilityId) {
16
+ return fail("RCLI12001", `Unknown market command: ${action}`, "FAIL", `rapidx market ${action}`);
17
+ }
18
+ try {
19
+ const data = await executeRapidXCapability(capabilityId, input);
20
+ return ok(data, `rapidx market ${action}`, "PASS", "real_tool_call");
21
+ }
22
+ catch (error) {
23
+ const productError = normalizeUnknownError(error, "RCLI12001");
24
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, `rapidx market ${action}`);
25
+ }
26
+ }
@@ -0,0 +1,81 @@
1
+ import { consumePreview, createTargetedTradePreview, createTradePreview, defaultSafetyPolicy, executeRapidXCapability, findCapabilityById, loadPreviewStoreFromFile, makeSafetyState, normalizeUnknownError, resolvePreviewStoreFile, savePreviewStoreToFile, verifyPreview } from "../../core/index.js";
2
+ import { fail, ok } from "../envelope.js";
3
+ import { writeCliAudit } from "../audit.js";
4
+ const safetyState = makeSafetyState();
5
+ export async function runOrderCommand(action, input) {
6
+ const previewStoreFile = resolvePreviewStoreFile();
7
+ const previewStore = loadPreviewStoreFromFile(previewStoreFile);
8
+ if (action === "preview" || action === "place-preview") {
9
+ const capability = findCapabilityById("order.preview");
10
+ if (!capability) {
11
+ return fail("RCLI30001", "order.preview capability missing.");
12
+ }
13
+ try {
14
+ const preview = createTradePreview(capability, input, { ...defaultSafetyPolicy(), tradingEnabled: true, readOnly: false }, safetyState, previewStore);
15
+ savePreviewStoreToFile(previewStoreFile, previewStore);
16
+ return ok(preview, `rapidx order ${action}`);
17
+ }
18
+ catch (error) {
19
+ const productError = normalizeUnknownError(error, "RCLI20002");
20
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, `rapidx order ${action}`);
21
+ }
22
+ }
23
+ const previewTarget = previewTargetForAction(action);
24
+ if (previewTarget) {
25
+ const targetCapability = findCapabilityById(previewTarget);
26
+ if (!targetCapability) {
27
+ return fail("RCLI30001", `${previewTarget} capability missing.`);
28
+ }
29
+ try {
30
+ const preview = createTargetedTradePreview(targetCapability, input, { ...defaultSafetyPolicy(), tradingEnabled: true, readOnly: false }, safetyState, previewStore);
31
+ savePreviewStoreToFile(previewStoreFile, previewStore);
32
+ return ok(preview, `rapidx order ${action}`);
33
+ }
34
+ catch (error) {
35
+ const productError = normalizeUnknownError(error, "RCLI20002");
36
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, `rapidx order ${action}`);
37
+ }
38
+ }
39
+ const capability = findCapabilityById(`order.${action}`);
40
+ if (!capability) {
41
+ return fail("RCLI12001", `Unknown order command: ${action}`);
42
+ }
43
+ if (capability.previewRequired) {
44
+ try {
45
+ verifyPreview(previewStore, typeof input.previewId === "string" ? input.previewId : undefined, capability, input);
46
+ }
47
+ catch (error) {
48
+ return fail("RCLI20002", error instanceof Error ? error.message : String(error), "BLOCKED", `rapidx order ${action}`);
49
+ }
50
+ if (typeof input.continueConsentId !== "string" || input.continueConsentId.length === 0) {
51
+ return fail("RCLI20001", "continueConsentId is required for trade writes.", "BLOCKED", `rapidx order ${action}`);
52
+ }
53
+ try {
54
+ consumePreview(previewStore, typeof input.previewId === "string" ? input.previewId : undefined, capability, input);
55
+ savePreviewStoreToFile(previewStoreFile, previewStore);
56
+ }
57
+ catch (error) {
58
+ return fail("RCLI20002", error instanceof Error ? error.message : String(error), "BLOCKED", `rapidx order ${action}`);
59
+ }
60
+ }
61
+ try {
62
+ const data = await executeRapidXCapability(capability.capabilityId, input);
63
+ const auditId = capability.operationType === "TRADE_WRITE"
64
+ ? writeCliAudit("trade-write", "PASS", { command: `rapidx order ${action}`, capabilityId: capability.capabilityId, clientOrderId: input.clientOrderId })
65
+ : undefined;
66
+ return ok(data, `rapidx order ${action}`, "PASS", "real_tool_call", auditId);
67
+ }
68
+ catch (error) {
69
+ const productError = normalizeUnknownError(error, "RCLI12001");
70
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, `rapidx order ${action}`);
71
+ }
72
+ }
73
+ function previewTargetForAction(action) {
74
+ if (action === "amend-preview") {
75
+ return "order.amend";
76
+ }
77
+ if (action === "cancel-preview") {
78
+ return "order.cancel";
79
+ }
80
+ return undefined;
81
+ }
@@ -0,0 +1,35 @@
1
+ import { executeRapidXCapability, findCapabilityById, normalizeUnknownError } from "../../core/index.js";
2
+ import { writeCliAudit } from "../audit.js";
3
+ import { fail, ok } from "../envelope.js";
4
+ import { enforceCliPreviewGate } from "./trade-gate.js";
5
+ const POSITION_CAPABILITY_BY_ACTION = {
6
+ list: "position.list",
7
+ history: "position.history",
8
+ close: "position.close",
9
+ "set-leverage": "position.set-leverage"
10
+ };
11
+ export async function runPositionCommand(action, input) {
12
+ const capabilityId = POSITION_CAPABILITY_BY_ACTION[action];
13
+ if (!capabilityId) {
14
+ return fail("RCLI12001", `Unknown position command: ${action}`, "FAIL", `rapidx position ${action}`);
15
+ }
16
+ const capability = findCapabilityById(capabilityId);
17
+ if (!capability) {
18
+ return fail("RCLI30002", `Missing capability: ${capabilityId}`, "FAIL", `rapidx position ${action}`);
19
+ }
20
+ const blocked = enforceCliPreviewGate(capability, input, `rapidx position ${action}`);
21
+ if (blocked) {
22
+ return blocked;
23
+ }
24
+ try {
25
+ const data = await executeRapidXCapability(capabilityId, input);
26
+ const auditId = capability.operationType === "TRADE_WRITE"
27
+ ? writeCliAudit("trade-write", "PASS", { command: `rapidx position ${action}`, capabilityId })
28
+ : undefined;
29
+ return ok(data, `rapidx position ${action}`, "PASS", "real_tool_call", auditId);
30
+ }
31
+ catch (error) {
32
+ const productError = normalizeUnknownError(error, "RCLI12001");
33
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, `rapidx position ${action}`);
34
+ }
35
+ }
@@ -0,0 +1,5 @@
1
+ import { getSchemaResult } from "../../core/index.js";
2
+ import { ok } from "../envelope.js";
3
+ export function runSchemaCommand() {
4
+ return ok(getSchemaResult(), "rapidx schema --json");
5
+ }
@@ -0,0 +1,24 @@
1
+ import { buildLiveTradingVerificationProbes, buildLiveReadOnlySelfCheck, runSelfCheck, runTradingVerification } from "../../core/index.js";
2
+ import { failWithData, ok } from "../envelope.js";
3
+ export async function runSelfCheckCommand(input) {
4
+ const options = {
5
+ input: {
6
+ scope: input.scope === "deep" ? "deep" : "quick",
7
+ readOnly: input.readOnly !== false,
8
+ checkUpdates: input.checkUpdates === true
9
+ },
10
+ ...buildLiveReadOnlySelfCheck(input)
11
+ };
12
+ const report = await runSelfCheck(options);
13
+ return ok(report, "rapidx self-check --read-only --json", report.status);
14
+ }
15
+ export async function runTradingVerificationCommand(input, evidenceText = "rapidx self-check trade-verify --json") {
16
+ const report = await runTradingVerification(input, buildLiveTradingVerificationProbes(input));
17
+ if (report.status === "PASS") {
18
+ return ok(report, evidenceText, report.status);
19
+ }
20
+ return failWithData(tradingVerificationCliCode(report), report.errorMessage ?? `Trading verification failed: ${report.errorCode ?? report.status}.`, report, report.status, evidenceText);
21
+ }
22
+ function tradingVerificationCliCode(report) {
23
+ return (report.errorCode ?? "RCORE10002").replace(/^RCORE/, "RCLI");
24
+ }
@@ -0,0 +1,26 @@
1
+ import { consumePreview, loadPreviewStoreFromFile, resolvePreviewStoreFile, savePreviewStoreToFile, verifyPreview } from "../../core/index.js";
2
+ import { fail } from "../envelope.js";
3
+ export function enforceCliPreviewGate(capability, input, evidenceText) {
4
+ if (!capability.previewRequired) {
5
+ return undefined;
6
+ }
7
+ const previewStoreFile = resolvePreviewStoreFile();
8
+ const previewStore = loadPreviewStoreFromFile(previewStoreFile);
9
+ try {
10
+ verifyPreview(previewStore, typeof input.previewId === "string" ? input.previewId : undefined, capability, input);
11
+ }
12
+ catch (error) {
13
+ return fail("RCLI20002", error instanceof Error ? error.message : String(error), "BLOCKED", evidenceText);
14
+ }
15
+ if (typeof input.continueConsentId !== "string" || input.continueConsentId.length === 0) {
16
+ return fail("RCLI20001", "continueConsentId is required for trade writes.", "BLOCKED", evidenceText);
17
+ }
18
+ try {
19
+ consumePreview(previewStore, typeof input.previewId === "string" ? input.previewId : undefined, capability, input);
20
+ savePreviewStoreToFile(previewStoreFile, previewStore);
21
+ }
22
+ catch (error) {
23
+ return fail("RCLI20002", error instanceof Error ? error.message : String(error), "BLOCKED", evidenceText);
24
+ }
25
+ return undefined;
26
+ }
@@ -0,0 +1,30 @@
1
+ import { createTargetedTradePreview, defaultSafetyPolicy, findCapabilityById, loadPreviewStoreFromFile, makeSafetyState, normalizeUnknownError, resolvePreviewStoreFile, savePreviewStoreToFile } from "../../core/index.js";
2
+ import { fail, ok } from "../envelope.js";
3
+ const safetyState = makeSafetyState();
4
+ export async function runTradeCommand(action, input) {
5
+ if (action !== "preview") {
6
+ return fail("RCLI12001", `Unknown trade command: ${action}`, "FAIL", `rapidx trade ${action}`);
7
+ }
8
+ const targetCapabilityId = typeof input.targetCapabilityId === "string" ? input.targetCapabilityId : "";
9
+ const targetCapability = targetCapabilityId ? findCapabilityById(targetCapabilityId) : undefined;
10
+ if (!targetCapability) {
11
+ return fail("RCLI12001", `Unknown trade preview target: ${targetCapabilityId || "<missing>"}`, "FAIL", "rapidx trade preview");
12
+ }
13
+ if (targetCapability.operationType !== "TRADE_WRITE" || isPreviewCapability(targetCapability.capabilityId)) {
14
+ return fail("RCLI20002", "trade preview target must be a non-preview trade write capability.", "BLOCKED", "rapidx trade preview");
15
+ }
16
+ const previewStoreFile = resolvePreviewStoreFile();
17
+ const previewStore = loadPreviewStoreFromFile(previewStoreFile);
18
+ try {
19
+ const preview = createTargetedTradePreview(targetCapability, input, { ...defaultSafetyPolicy(), tradingEnabled: true, readOnly: false }, safetyState, previewStore);
20
+ savePreviewStoreToFile(previewStoreFile, previewStore);
21
+ return ok(preview, "rapidx trade preview");
22
+ }
23
+ catch (error) {
24
+ const productError = normalizeUnknownError(error, "RCLI20002");
25
+ return fail(productError.code.replace(/^RCORE/, "RCLI"), productError.message, productError.status, "rapidx trade preview");
26
+ }
27
+ }
28
+ function isPreviewCapability(capabilityId) {
29
+ return capabilityId.endsWith(".preview") || capabilityId.endsWith("-preview");
30
+ }
@@ -0,0 +1,27 @@
1
+ import { checkForUpdate } from "../../core/index.js";
2
+ import { fail, ok } from "../envelope.js";
3
+ export async function runUpdateCommand(action, input) {
4
+ if (action !== "check") {
5
+ return fail("RCLI12001", `Unknown update command: ${action}`, "FAIL", "rapidx update");
6
+ }
7
+ const updateInput = {
8
+ force: input.force === true
9
+ };
10
+ if (typeof input.manifestUrl === "string") {
11
+ updateInput.manifestUrl = input.manifestUrl;
12
+ }
13
+ if (typeof input.maxCacheAgeSeconds === "number") {
14
+ updateInput.maxCacheAgeSeconds = input.maxCacheAgeSeconds;
15
+ }
16
+ const result = await checkForUpdate(updateInput);
17
+ return ok(result, "rapidx update check --json", envelopeStatusForUpdate(result.status));
18
+ }
19
+ function envelopeStatusForUpdate(status) {
20
+ if (status === "WRITE_BLOCKED" || status === "UPGRADE_REQUIRED") {
21
+ return "BLOCKED";
22
+ }
23
+ if (status === "UNKNOWN") {
24
+ return "NOT_VERIFIED";
25
+ }
26
+ return "PASS";
27
+ }