@raintree-technology/perps 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # perps
2
2
 
3
- Universal CLI for perpetual DEXes — by [Raintree Technology](https://raintree.technology).
3
+ Universal CLI for perpetual DEXes.
4
4
 
5
5
  [![CI](https://github.com/raintree-technology/perps/actions/workflows/ci.yml/badge.svg)](https://github.com/raintree-technology/perps/actions/workflows/ci.yml)
6
6
  [![npm](https://img.shields.io/npm/v/@raintree-technology/perps)](https://www.npmjs.com/package/@raintree-technology/perps)
@@ -12,6 +12,22 @@ npm install -g @raintree-technology/perps
12
12
  perps --help
13
13
  ```
14
14
 
15
+ ## Install Paths
16
+
17
+ Canonical install path:
18
+
19
+ ```bash
20
+ npm install -g @raintree-technology/perps
21
+ ```
22
+
23
+ GitHub install path (for source snapshots and pre-release testing):
24
+
25
+ ```bash
26
+ npm install github:raintree-technology/perps
27
+ ```
28
+
29
+ The package includes lifecycle build hooks so git installs produce runnable CLI artifacts (`dist/`) automatically.
30
+
15
31
  ## Supported Exchanges
16
32
 
17
33
  | Exchange | Chain | Status | Onboarding |
@@ -136,6 +152,18 @@ perps doctor # Health check
136
152
 
137
153
  Every command supports `--json` for machine-readable output and `--help` for usage details.
138
154
 
155
+ Agent-friendly mode:
156
+
157
+ ```bash
158
+ # Non-interactive execution
159
+ perps setup wizard --yes --json
160
+
161
+ # Machine-readable responses with schema version
162
+ perps markets ls --json
163
+ ```
164
+
165
+ JSON payloads are schema-versioned, and failures return deterministic exit codes.
166
+
139
167
  ## Exit Codes
140
168
 
141
169
  | Code | Meaning |
@@ -169,7 +197,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. Please read our [Code of
169
197
 
170
198
  ## Author
171
199
 
172
- [Raintree Technology](https://raintree.technology) ([@raintree_tech](https://x.com/raintree_tech)) — created by [Zachary Roth](https://zacharyr0th.com) ([@zacharyr0th](https://x.com/zacharyr0th))
200
+ [Raintree Technology](https://raintree.technology)
173
201
 
174
202
  ## License
175
203
 
@@ -107,7 +107,6 @@ export function printSessionBanner(args) {
107
107
  for (const line of getPerpsAsciiMark()) {
108
108
  console.log(highlighter.brand(line));
109
109
  }
110
- console.log(highlighter.dim("by Raintree Technology"));
111
110
  console.log("");
112
111
  console.log(`${highlighter.dim("mode")} ${networkTag} ${highlighter.dim("exchange")} ${exchangeTag}`);
113
112
  console.log(`${highlighter.dim("command")} ${commandTag} ${highlighter.dim("version")} ${highlighter.dim(`v${args.version}`)}`);
@@ -8,6 +8,8 @@ import { createContext } from "./context.js";
8
8
  import { highlighter, printCommandCompletion, printSessionBanner } from "./experience.js";
9
9
  import { commandPathFromCommand, resolveTestnetMode } from "./network-defaults.js";
10
10
  import { outputError } from "./output.js";
11
+ import { inferExitCode } from "../lib/exit-codes.js";
12
+ import { withJsonContract } from "../lib/contracts.js";
11
13
  const require = createRequire(import.meta.url);
12
14
  const pkg = require("../../package.json");
13
15
  const commandMeta = new WeakMap();
@@ -30,6 +32,7 @@ export function createProgram() {
30
32
  .description("Universal CLI for perpetual DEXes")
31
33
  .showSuggestionAfterError(true)
32
34
  .showHelpAfterError()
35
+ .exitOverride()
33
36
  .version(pkg.version)
34
37
  .option("--json", "Output in JSON format", false)
35
38
  .option("--human", "Enable human-first output UX (banner, timing, rich formatting)", false)
@@ -62,17 +65,37 @@ Examples:
62
65
  setExchange(opts.exchange);
63
66
  }
64
67
  catch (err) {
65
- outputError(err instanceof Error ? err.message : String(err));
66
- console.error(highlighter.dim(`Available exchanges: ${getAvailableExchanges().join(", ")}`));
67
- process.exit(1);
68
+ const message = err instanceof Error ? err.message : String(err);
69
+ const exitCode = inferExitCode(err);
70
+ if (opts.json) {
71
+ console.error(JSON.stringify(withJsonContract("perps.error", {
72
+ status: "error",
73
+ error: { message, exitCode },
74
+ }), null, 2));
75
+ }
76
+ else {
77
+ outputError(message);
78
+ console.error(highlighter.dim(`Available exchanges: ${getAvailableExchanges().join(", ")}`));
79
+ }
80
+ process.exit(exitCode);
68
81
  }
69
82
  let config;
70
83
  try {
71
84
  config = loadConfig(isTestnet, opts.exchange);
72
85
  }
73
86
  catch (err) {
74
- outputError(err instanceof Error ? err.message : String(err));
75
- process.exit(1);
87
+ const message = err instanceof Error ? err.message : String(err);
88
+ const exitCode = inferExitCode(err);
89
+ if (opts.json) {
90
+ console.error(JSON.stringify(withJsonContract("perps.error", {
91
+ status: "error",
92
+ error: { message, exitCode },
93
+ }), null, 2));
94
+ }
95
+ else {
96
+ outputError(message);
97
+ }
98
+ process.exit(exitCode);
76
99
  }
77
100
  const context = createContext(config);
78
101
  // Store metadata on the root command
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") ||
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.3",
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",