@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 +12 -2
- package/bin/perpscope.mjs +106 -3
- package/package.json +1 -1
- package/src/lib/percolator-adapter.js +1 -1
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
|
-
|
|
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
|
import { normalizeFundingSkewHistory } from "./funding-history.js";
|
|
2
2
|
|
|
3
|
-
export const PERPSCOPE_ADAPTER_VERSION = "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([
|