@raintree-technology/perps 0.1.2 → 0.1.4

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 (36) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/README.md +42 -4
  3. package/dist/adapters/aevo.d.ts +1 -6
  4. package/dist/adapters/aevo.js +0 -21
  5. package/dist/adapters/certification.js +55 -9
  6. package/dist/adapters/decibel/order-manager.d.ts +41 -0
  7. package/dist/adapters/decibel/order-manager.js +216 -0
  8. package/dist/adapters/decibel/rest-client.d.ts +21 -0
  9. package/dist/adapters/decibel/rest-client.js +15 -8
  10. package/dist/adapters/decibel/ws-feed.js +19 -4
  11. package/dist/adapters/decibel.d.ts +15 -10
  12. package/dist/adapters/decibel.js +371 -50
  13. package/dist/adapters/hyperliquid.d.ts +1 -6
  14. package/dist/adapters/hyperliquid.js +0 -18
  15. package/dist/adapters/interface.d.ts +15 -14
  16. package/dist/adapters/orderly.d.ts +1 -9
  17. package/dist/adapters/orderly.js +0 -33
  18. package/dist/adapters/paradex.d.ts +1 -9
  19. package/dist/adapters/paradex.js +1 -34
  20. package/dist/cli/experience.js +0 -1
  21. package/dist/cli/program.js +28 -5
  22. package/dist/commands/arb/alert.d.ts +0 -4
  23. package/dist/commands/arb/alert.js +4 -14
  24. package/dist/commands/markets/index.js +2 -0
  25. package/dist/commands/markets/read-simple.d.ts +2 -0
  26. package/dist/commands/markets/read-simple.js +130 -0
  27. package/dist/commands/order/advanced-simple.d.ts +2 -0
  28. package/dist/commands/order/advanced-simple.js +820 -0
  29. package/dist/commands/order/index.js +2 -0
  30. package/dist/index.js +35 -1
  31. package/dist/lib/exit-codes.js +5 -1
  32. package/dist/lib/prompts.d.ts +0 -18
  33. package/dist/lib/prompts.js +1 -22
  34. package/dist/lib/schema.d.ts +8 -8
  35. package/dist/lib/schema.js +47 -8
  36. package/package.json +5 -2
@@ -2,6 +2,7 @@ import { registerLimitOrderSimpleCommand } from "./limit-simple.js";
2
2
  import { registerMarketOrderSimpleCommand } from "./market-simple.js";
3
3
  import { registerCancelOrderSimpleCommand } from "./cancel-simple.js";
4
4
  import { registerTriggerOrderSimpleCommands } from "./trigger-simple.js";
5
+ import { registerAdvancedOrderSimpleCommands } from "./advanced-simple.js";
5
6
  export function registerOrderCommands(program) {
6
7
  const order = program
7
8
  .command("order")
@@ -10,4 +11,5 @@ export function registerOrderCommands(program) {
10
11
  registerMarketOrderSimpleCommand(order);
11
12
  registerTriggerOrderSimpleCommands(order);
12
13
  registerCancelOrderSimpleCommand(order);
14
+ registerAdvancedOrderSimpleCommands(order);
13
15
  }
package/dist/index.js CHANGED
@@ -1,4 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  import { createProgram } from "./cli/program.js";
3
+ import { withJsonContract } from "./lib/contracts.js";
4
+ import { inferExitCode } from "./lib/exit-codes.js";
5
+ const isJsonMode = process.argv.includes("--json");
3
6
  const program = createProgram();
4
- program.parse();
7
+ if (isJsonMode) {
8
+ program.configureOutput({
9
+ writeOut: () => undefined,
10
+ writeErr: () => undefined,
11
+ });
12
+ }
13
+ try {
14
+ await program.parseAsync(process.argv);
15
+ }
16
+ catch (err) {
17
+ const message = err instanceof Error ? err.message : String(err);
18
+ const commanderExitCode = typeof err === "object" && err !== null && "exitCode" in err
19
+ ? Number(err.exitCode)
20
+ : Number.NaN;
21
+ const commanderCode = typeof err === "object" && err !== null && "code" in err
22
+ ? String(err.code ?? "")
23
+ : "";
24
+ const exitCode = Number.isFinite(commanderExitCode) && commanderExitCode === 0
25
+ ? 0
26
+ : commanderCode.startsWith("commander.")
27
+ ? inferExitCode(err)
28
+ : Number.isFinite(commanderExitCode)
29
+ ? commanderExitCode
30
+ : inferExitCode(err);
31
+ if (isJsonMode && exitCode !== 0) {
32
+ console.error(JSON.stringify(withJsonContract("perps.error", {
33
+ status: "error",
34
+ error: { message, exitCode },
35
+ }), null, 2));
36
+ }
37
+ process.exit(exitCode);
38
+ }
@@ -25,7 +25,11 @@ export function inferExitCode(err) {
25
25
  const message = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();
26
26
  if (message.includes("must be") ||
27
27
  message.includes("invalid") ||
28
- message.includes("use --yes with --json")) {
28
+ message.includes("use --yes with --json") ||
29
+ message.includes("unknown option") ||
30
+ message.includes("too many arguments") ||
31
+ message.includes("missing required argument") ||
32
+ message.includes("option argument missing")) {
29
33
  return EXIT_CODES.VALIDATION_ERROR;
30
34
  }
31
35
  if (message.includes("not configured") ||
@@ -1,32 +1,14 @@
1
- /**
2
- * Prompt for text input
3
- */
4
1
  export declare function prompt(question: string): Promise<string>;
5
- /**
6
- * Prompt for selection from a list of options with arrow key navigation
7
- */
8
2
  export declare function select<T extends string>(question: string, options: {
9
3
  value: T;
10
4
  label: string;
11
5
  description?: string;
12
6
  }[]): Promise<T>;
13
- /**
14
- * Prompt for multiple selections with checkboxes
15
- */
16
7
  export declare function multiSelect<T extends string>(question: string, options: {
17
8
  value: T;
18
9
  label: string;
19
10
  description?: string;
20
11
  }[]): Promise<T[]>;
21
- /**
22
- * Prompt for yes/no confirmation
23
- */
24
12
  export declare function confirm(question: string, defaultValue?: boolean): Promise<boolean>;
25
- /**
26
- * Wait for user to press Enter
27
- */
28
13
  export declare function waitForEnter(message?: string): Promise<void>;
29
- /**
30
- * Wait for user to press Enter (returns true) or Escape (returns false)
31
- */
32
14
  export declare function pressEnterOrEsc(message: string): Promise<boolean>;
@@ -1,4 +1,4 @@
1
- import { inquirerTheme, highlighter } from "./ui-tokens.js";
1
+ import { highlighter, inquirerTheme } from "./ui-tokens.js";
2
2
  let promptsModule = null;
3
3
  async function loadPrompts() {
4
4
  if (promptsModule)
@@ -6,9 +6,6 @@ async function loadPrompts() {
6
6
  promptsModule = await import("@inquirer/prompts");
7
7
  return promptsModule;
8
8
  }
9
- /**
10
- * Prompt for text input
11
- */
12
9
  export async function prompt(question) {
13
10
  const { input } = await loadPrompts();
14
11
  const answer = await input({
@@ -17,9 +14,6 @@ export async function prompt(question) {
17
14
  });
18
15
  return answer.trim();
19
16
  }
20
- /**
21
- * Prompt for selection from a list of options with arrow key navigation
22
- */
23
17
  export async function select(question, options) {
24
18
  const { select: inquirerSelect } = await loadPrompts();
25
19
  const result = await inquirerSelect({
@@ -33,9 +27,6 @@ export async function select(question, options) {
33
27
  });
34
28
  return result;
35
29
  }
36
- /**
37
- * Prompt for multiple selections with checkboxes
38
- */
39
30
  export async function multiSelect(question, options) {
40
31
  const { checkbox } = await loadPrompts();
41
32
  const results = await checkbox({
@@ -49,9 +40,6 @@ export async function multiSelect(question, options) {
49
40
  });
50
41
  return results;
51
42
  }
52
- /**
53
- * Prompt for yes/no confirmation
54
- */
55
43
  export async function confirm(question, defaultValue = false) {
56
44
  const { confirm: inquirerConfirm } = await loadPrompts();
57
45
  return inquirerConfirm({
@@ -60,9 +48,6 @@ export async function confirm(question, defaultValue = false) {
60
48
  theme: inquirerTheme,
61
49
  });
62
50
  }
63
- /**
64
- * Wait for user to press Enter
65
- */
66
51
  export async function waitForEnter(message = "Press Enter to continue...") {
67
52
  const { input } = await loadPrompts();
68
53
  await input({
@@ -76,9 +61,6 @@ export async function waitForEnter(message = "Press Enter to continue...") {
76
61
  },
77
62
  });
78
63
  }
79
- /**
80
- * Wait for user to press Enter (returns true) or Escape (returns false)
81
- */
82
64
  export async function pressEnterOrEsc(message) {
83
65
  return new Promise((resolve) => {
84
66
  console.log(`${highlighter.warn("→")} ${message}`);
@@ -88,19 +70,16 @@ export async function pressEnterOrEsc(message) {
88
70
  stdin.resume();
89
71
  const onData = (key) => {
90
72
  const char = key.toString();
91
- // Enter key
92
73
  if (char === "\r" || char === "\n") {
93
74
  cleanup();
94
75
  console.log(`${highlighter.success("✔")} Opening browser...`);
95
76
  resolve(true);
96
77
  }
97
- // Escape key
98
78
  else if (char === "\x1b") {
99
79
  cleanup();
100
80
  console.log(`${highlighter.dimWhite("✔")} Skipped`);
101
81
  resolve(false);
102
82
  }
103
- // Ctrl+C
104
83
  else if (char === "\x03") {
105
84
  cleanup();
106
85
  process.exit(0);
@@ -5,15 +5,15 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
5
5
  HYPERLIQUID_NETWORK: z.ZodOptional<z.ZodEnum<["testnet", "mainnet"]>>;
6
6
  DECIBEL_API_WALLET_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
7
7
  DECIBEL_API_WALLET_ADDRESS: z.ZodOptional<z.ZodString>;
8
- DECIBEL_API_BEARER_TOKEN: z.ZodOptional<z.ZodString>;
8
+ DECIBEL_API_BEARER_TOKEN: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
9
9
  DECIBEL_SUBACCOUNT_ADDRESS: z.ZodOptional<z.ZodString>;
10
10
  DECIBEL_PACKAGE_ADDRESS: z.ZodOptional<z.ZodString>;
11
11
  DECIBEL_REST_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
12
12
  DECIBEL_WS_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
13
13
  DECIBEL_FULLNODE_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
14
14
  DECIBEL_NETWORK: z.ZodOptional<z.ZodEnum<["testnet", "mainnet"]>>;
15
- AEVO_API_KEY: z.ZodOptional<z.ZodString>;
16
- AEVO_API_SECRET: z.ZodOptional<z.ZodString>;
15
+ AEVO_API_KEY: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
16
+ AEVO_API_SECRET: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
17
17
  AEVO_SIGNING_KEY: z.ZodOptional<z.ZodString>;
18
18
  AEVO_ACCOUNT_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
19
19
  AEVO_REST_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
@@ -21,13 +21,13 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
21
21
  ORDERLY_BROKER_ID: z.ZodOptional<z.ZodString>;
22
22
  ORDERLY_CHAIN_ID: z.ZodOptional<z.ZodString>;
23
23
  ORDERLY_ACCOUNT_ID: z.ZodOptional<z.ZodString>;
24
- ORDERLY_KEY: z.ZodOptional<z.ZodString>;
25
- ORDERLY_SECRET: z.ZodOptional<z.ZodString>;
26
- ORDERLY_TRADING_KEY: z.ZodOptional<z.ZodString>;
27
- ORDERLY_TRADING_SECRET: z.ZodOptional<z.ZodString>;
24
+ ORDERLY_KEY: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
25
+ ORDERLY_SECRET: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
26
+ ORDERLY_TRADING_KEY: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
27
+ ORDERLY_TRADING_SECRET: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
28
28
  ORDERLY_REST_URL: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
29
29
  ORDERLY_NETWORK: z.ZodOptional<z.ZodEnum<["testnet", "mainnet"]>>;
30
- PARADEX_API_BEARER_TOKEN: z.ZodOptional<z.ZodString>;
30
+ PARADEX_API_BEARER_TOKEN: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
31
31
  PARADEX_ACCOUNT_ADDRESS: z.ZodOptional<z.ZodString>;
32
32
  PARADEX_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
33
33
  PARADEX_ETHEREUM_ACCOUNT: z.ZodOptional<z.ZodString>;
@@ -11,6 +11,45 @@ const aptosAddress = z
11
11
  const starknetFieldElement = z
12
12
  .string()
13
13
  .regex(/^0x[a-fA-F0-9]{1,64}$/, "must be a 0x-prefixed Starknet field element");
14
+ const PLACEHOLDER_SECRET_VALUES = new Set([
15
+ "...",
16
+ "your-api-key",
17
+ "your_api_key",
18
+ "your-api-secret",
19
+ "your_api_secret",
20
+ "your-token",
21
+ "your_token",
22
+ "your-secret",
23
+ "your_secret",
24
+ "api-key-here",
25
+ "api_key_here",
26
+ "token-here",
27
+ "token_here",
28
+ "secret-here",
29
+ "secret_here",
30
+ "changeme",
31
+ "change-me",
32
+ "change_me",
33
+ "replace-me",
34
+ "replace_me",
35
+ "placeholder",
36
+ ]);
37
+ function isPlaceholderSecret(value) {
38
+ const normalized = value.trim().toLowerCase();
39
+ if (!normalized) {
40
+ return true;
41
+ }
42
+ const canonical = normalized.startsWith("<") && normalized.endsWith(">")
43
+ ? normalized.slice(1, -1)
44
+ : normalized;
45
+ return PLACEHOLDER_SECRET_VALUES.has(canonical);
46
+ }
47
+ const userProvidedSecret = z
48
+ .string()
49
+ .min(1)
50
+ .refine((value) => !isPlaceholderSecret(value), {
51
+ message: "must be user-provided (placeholder values like '...' are not allowed)",
52
+ });
14
53
  function isLoopbackHost(hostname) {
15
54
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
16
55
  }
@@ -72,7 +111,7 @@ export const envSchema = z
72
111
  // Decibel credentials/settings
73
112
  DECIBEL_API_WALLET_PRIVATE_KEY: hexPrivateKey.optional(),
74
113
  DECIBEL_API_WALLET_ADDRESS: aptosAddress.optional(),
75
- DECIBEL_API_BEARER_TOKEN: z.string().min(1).optional(),
114
+ DECIBEL_API_BEARER_TOKEN: userProvidedSecret.optional(),
76
115
  DECIBEL_SUBACCOUNT_ADDRESS: aptosAddress.optional(),
77
116
  DECIBEL_PACKAGE_ADDRESS: aptosAddress.optional(),
78
117
  DECIBEL_REST_URL: secureHttpUrl.optional(),
@@ -80,8 +119,8 @@ export const envSchema = z
80
119
  DECIBEL_FULLNODE_URL: secureHttpUrl.optional(),
81
120
  DECIBEL_NETWORK: z.enum(["testnet", "mainnet"]).optional(),
82
121
  // Aevo credentials
83
- AEVO_API_KEY: z.string().min(1).optional(),
84
- AEVO_API_SECRET: z.string().min(1).optional(),
122
+ AEVO_API_KEY: userProvidedSecret.optional(),
123
+ AEVO_API_SECRET: userProvidedSecret.optional(),
85
124
  AEVO_SIGNING_KEY: hexPrivateKey.optional(),
86
125
  AEVO_ACCOUNT_PRIVATE_KEY: hexPrivateKey.optional(),
87
126
  AEVO_REST_URL: secureHttpUrl.optional(),
@@ -90,14 +129,14 @@ export const envSchema = z
90
129
  ORDERLY_BROKER_ID: z.string().min(1).optional(),
91
130
  ORDERLY_CHAIN_ID: z.string().min(1).optional(),
92
131
  ORDERLY_ACCOUNT_ID: z.string().min(1).optional(),
93
- ORDERLY_KEY: z.string().min(1).optional(),
94
- ORDERLY_SECRET: z.string().min(1).optional(),
95
- ORDERLY_TRADING_KEY: z.string().min(1).optional(),
96
- ORDERLY_TRADING_SECRET: z.string().min(1).optional(),
132
+ ORDERLY_KEY: userProvidedSecret.optional(),
133
+ ORDERLY_SECRET: userProvidedSecret.optional(),
134
+ ORDERLY_TRADING_KEY: userProvidedSecret.optional(),
135
+ ORDERLY_TRADING_SECRET: userProvidedSecret.optional(),
97
136
  ORDERLY_REST_URL: secureHttpUrl.optional(),
98
137
  ORDERLY_NETWORK: z.enum(["testnet", "mainnet"]).optional(),
99
138
  // Paradex credentials
100
- PARADEX_API_BEARER_TOKEN: z.string().min(1).optional(),
139
+ PARADEX_API_BEARER_TOKEN: userProvidedSecret.optional(),
101
140
  PARADEX_ACCOUNT_ADDRESS: starknetFieldElement.optional(),
102
141
  PARADEX_PRIVATE_KEY: starknetFieldElement.optional(),
103
142
  PARADEX_ETHEREUM_ACCOUNT: hexAddress.optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raintree-technology/perps",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Universal CLI for perpetual DEXes",
5
5
  "author": "Raintree Technology (https://raintree.technology)",
6
6
  "contributors": [
@@ -63,6 +63,8 @@
63
63
  "sideEffects": false,
64
64
  "scripts": {
65
65
  "build": "tsc",
66
+ "prepare": "npm run build",
67
+ "prepack": "npm run build",
66
68
  "dev": "tsx src/index.ts",
67
69
  "typecheck": "tsc --noEmit",
68
70
  "smoke": "node scripts/smoke.mjs",
@@ -82,7 +84,7 @@
82
84
  "lint:fix": "biome lint --write src scripts",
83
85
  "publint": "publint",
84
86
  "attw": "attw --pack . --profile esm-only",
85
- "pack:check": "npm pack --dry-run",
87
+ "pack:check": "npm run build && npm pack --dry-run && node scripts/verify-pack.mjs",
86
88
  "prepublishOnly": "npm run build && npm run publint && npm run attw"
87
89
  },
88
90
  "dependencies": {
@@ -105,6 +107,7 @@
105
107
  "zod": "^3.24.0"
106
108
  },
107
109
  "devDependencies": {
110
+ "next": "^16.1.6",
108
111
  "@arethetypeswrong/cli": "^0.18.2",
109
112
  "@biomejs/biome": "^1.9.4",
110
113
  "@types/better-sqlite3": "^7.6.13",