@perpscope/percolator-adapter 0.9.0 → 1.0.1

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,11 +1,18 @@
1
1
  # @perpscope/percolator-adapter
2
2
 
3
- Read-only adapter helpers for Solana perps terminals that want PerpScope DTOs without adopting the cockpit UI.
3
+ Read-only CLI and adapter helpers for Solana perps terminals that want PerpScope compatibility checks and DTOs without adopting the cockpit UI.
4
4
 
5
5
  ```bash
6
6
  npm install @perpscope/percolator-adapter
7
+ npx perpscope init perpscope.capture.json
8
+ npx perpscope compat doctor perpscope.capture.json --strict
9
+ npx perpscope compat badge perpscope.capture.json
7
10
  ```
8
11
 
12
+ `perpscope init` creates a sanitized starter capture. `compat doctor` tells you whether required fields pass and what to map next. `compat badge` gives you a tiny Markdown/JSON summary for READMEs, PRs, and issue handoffs.
13
+
14
+ Doctor exit codes are CI-ready: `0` means required fields pass, `1` means rejected or required fields missing, and `2` means strict mode found useful-field gaps, unknown fields, or alias suggestions.
15
+
9
16
  ```js
10
17
  import {
11
18
  buildPercolatorCompatibilityReport,
@@ -72,12 +79,15 @@ The full field-level contract is documented in `../../docs/field-compatibility-m
72
79
  ## CLI
73
80
 
74
81
  ```bash
82
+ perpscope init perpscope.capture.json
75
83
  perpscope compat report capture.json
76
84
  perpscope compat diff previous.json current.json
77
- perpscope compat doctor capture.json
85
+ perpscope compat doctor capture.json --strict
78
86
  perpscope compat badge capture.json --json
79
87
  ```
80
88
 
89
+ Doctor exit codes are CI-ready: `0` means required fields pass, `1` means rejected or required fields missing, and `2` means strict mode found useful-field gaps, unknown fields, or alias suggestions.
90
+
81
91
  Try it locally with:
82
92
 
83
93
  ```bash
package/bin/perpscope.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import {
4
4
  buildCompatibilityBadge,
5
5
  buildCompatibilityDoctor,
@@ -13,19 +13,104 @@ import {
13
13
  } from "../index.js";
14
14
 
15
15
  const [, , ...args] = process.argv;
16
+ const CAPTURE_TEMPLATE = {
17
+ label: "My terminal read-only capture",
18
+ cluster: "mainnet-beta",
19
+ market: {
20
+ symbol: "SOL-PERP",
21
+ base: "SOL",
22
+ quote: "USDC",
23
+ slab: "PERCOLAT_SOL_...",
24
+ program: "Perco1ator111111111111111111111111111111111"
25
+ },
26
+ oracle: {
27
+ priceUsd: 181.61,
28
+ ageSecs: 2
29
+ },
30
+ engine: {
31
+ currentSlot: 346892118,
32
+ lastMarketSlot: 346892090,
33
+ fundingRateBpsPerHour: 0.82,
34
+ openInterestUsd: 2430000,
35
+ longOpenInterestUsd: 1320000,
36
+ shortOpenInterestUsd: 1110000,
37
+ insuranceUsd: 148000,
38
+ stressConsumedBps: 118,
39
+ stressLimitBps: 500
40
+ },
41
+ execution: {
42
+ bestBid: 181.52,
43
+ bestAsk: 181.71,
44
+ receipts: [
45
+ {
46
+ label: "latest fill",
47
+ sourceTimestamp: "2026-06-20T13:24:12Z",
48
+ spreadBps: 10.5,
49
+ impactBps: 8.4,
50
+ markout1mBps: 4.2,
51
+ markout5mBps: -1.7,
52
+ routeLatencyMs: 132,
53
+ priorityFeeMicrolamports: 2200
54
+ }
55
+ ]
56
+ },
57
+ account: {
58
+ side: "long",
59
+ positionSize: 420,
60
+ positionNotionalUsd: 76276.2,
61
+ collateralUsd: 8400,
62
+ unrealizedPnlUsd: 3067.2,
63
+ liquidationPrice: 162.94
64
+ },
65
+ history: {
66
+ fundingSkew: [
67
+ {
68
+ sourceTimestamp: "2026-06-20T13:24:00Z",
69
+ slot: 346892086,
70
+ fundingBpsPerHour: 0.82,
71
+ longOpenInterestUsd: 1320000,
72
+ shortOpenInterestUsd: 1110000,
73
+ stressConsumedBps: 118,
74
+ stressLimitBps: 500,
75
+ oracleAgeSec: 2.1
76
+ }
77
+ ]
78
+ }
79
+ };
16
80
 
17
81
  function usage() {
18
82
  return [
19
83
  "Usage:",
84
+ " perpscope init [output.json] [--force]",
20
85
  " perpscope compat report <capture.json>",
21
86
  " perpscope compat diff <previous.json> <current.json>",
22
- " perpscope compat doctor <capture.json>",
87
+ " perpscope compat doctor <capture.json> [--strict|--json]",
23
88
  " perpscope compat badge <capture.json> [--json|--markdown]",
24
89
  "",
25
90
  "Read-only only: the adapter rejects wallet, signer, transaction, instruction, order, private key, seed, mnemonic, and API key fields."
26
91
  ].join("\n");
27
92
  }
28
93
 
94
+ function initMessage(path) {
95
+ return [
96
+ `Created ${path}`,
97
+ "",
98
+ "Next:",
99
+ ` perpscope compat doctor ${path}`,
100
+ ` perpscope compat badge ${path}`,
101
+ "",
102
+ "Edit the capture with sanitized read-only decoded state before sharing it."
103
+ ].join("\n");
104
+ }
105
+
106
+ function initCapture(path = "perpscope.capture.json", options = {}) {
107
+ if (existsSync(path) && !options.force) {
108
+ throw new Error(`${path} already exists. Use --force to overwrite.`);
109
+ }
110
+ writeFileSync(path, `${JSON.stringify(CAPTURE_TEMPLATE, null, 2)}\n`);
111
+ return path;
112
+ }
113
+
29
114
  function readCapture(path) {
30
115
  if (!path) throw new Error("Missing capture path.");
31
116
  return parsePercolatorJson(readFileSync(path, "utf8"));
@@ -56,12 +141,28 @@ function formatDoctor(doctor) {
56
141
  return lines.join("\n");
57
142
  }
58
143
 
144
+ function doctorExitCode(doctor, options = {}) {
145
+ if (doctor.status === "rejected" || doctor.required.mapped < doctor.required.total) return 1;
146
+ if (options.strict && (
147
+ doctor.useful.mapped < doctor.useful.total ||
148
+ doctor.unknownFields.length ||
149
+ doctor.aliasSuggestions.length
150
+ )) return 2;
151
+ return 0;
152
+ }
153
+
59
154
  function main() {
60
155
  const [scope, command, ...rest] = args;
61
156
  if (!scope || scope === "--help" || scope === "-h") {
62
157
  console.log(usage());
63
158
  return;
64
159
  }
160
+ if (scope === "init") {
161
+ const output = command && !command.startsWith("--") ? command : "perpscope.capture.json";
162
+ const flags = [command, ...rest].filter(Boolean);
163
+ console.log(initMessage(initCapture(output, { force: flags.includes("--force") })));
164
+ return;
165
+ }
65
166
  if (scope !== "compat") throw new Error(`Unknown scope: ${scope}`);
66
167
  if (command === "report") {
67
168
  const input = readCapture(rest[0]);
@@ -76,7 +177,9 @@ function main() {
76
177
  }
77
178
  if (command === "doctor") {
78
179
  const input = readCapture(rest[0]);
79
- console.log(formatDoctor(buildCompatibilityDoctor(buildReport(input), { input })));
180
+ const doctor = buildCompatibilityDoctor(buildReport(input), { input });
181
+ console.log(rest.includes("--json") ? JSON.stringify(doctor, null, 2) : formatDoctor(doctor));
182
+ process.exitCode = doctorExitCode(doctor, { strict: rest.includes("--strict") });
80
183
  return;
81
184
  }
82
185
  if (command === "badge") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perpscope/percolator-adapter",
3
- "version": "0.9.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "Read-only Percolator adapter helpers for Solana perps terminals.",
6
6
  "main": "./index.js",
@@ -1,6 +1,6 @@
1
1
  import { normalizeFundingSkewHistory } from "./funding-history.js";
2
2
 
3
- export const PERPSCOPE_ADAPTER_VERSION = "0.9.0";
3
+ export const PERPSCOPE_ADAPTER_VERSION = "1.0.1";
4
4
 
5
5
  const KEYPAIR_FIELD_PATTERN = /(^|_)(secret|private|keypair|mnemonic|seed|walletPath|wallet)(_|$)/i;
6
6
  const HISTORY_COMMAND_KEYS = new Set([