perp-cli 0.12.3 → 0.12.6
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/dist/commands/agent.js +27 -11
- package/dist/commands/init.js +25 -34
- package/dist/commands/wallet.js +20 -8
- package/dist/config.js +5 -1
- package/package.json +3 -2
- package/skills/perp-cli/SKILL.md +2 -2
package/dist/commands/agent.js
CHANGED
|
@@ -266,8 +266,15 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
266
266
|
.option("--ip-whitelist <list>", "Comma-separated IP whitelist (pass empty string to include empty)")
|
|
267
267
|
.option("--api-key-index <n>", "Lighter slot index (4-254). Default: next free slot.")
|
|
268
268
|
.option("--json", "Machine-readable output")
|
|
269
|
-
.action(async (exchange, opts) => {
|
|
269
|
+
.action(async (exchange, opts, command) => {
|
|
270
270
|
const useJson = opts.json ?? isJson();
|
|
271
|
+
// optsWithGlobals() merges parent + subcommand options so --passphrase
|
|
272
|
+
// works whether placed on `perp --passphrase X wallet agent approve` or
|
|
273
|
+
// on the subcommand. Commander v13 silently shadows the subcommand flag
|
|
274
|
+
// when the parent defines an identically-named flag — the visible bug
|
|
275
|
+
// was approve throwing PASSPHRASE_REQUIRED even with --passphrase set.
|
|
276
|
+
const mergedOpts = command.optsWithGlobals();
|
|
277
|
+
const passphraseFlag = mergedOpts.passphrase ?? opts.passphrase;
|
|
271
278
|
// Normalize aliases
|
|
272
279
|
const exchangeNorm = exchange === "lt" ? "lighter"
|
|
273
280
|
: exchange === "hl" ? "hyperliquid"
|
|
@@ -314,7 +321,7 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
314
321
|
}
|
|
315
322
|
chosenSlot = n;
|
|
316
323
|
}
|
|
317
|
-
const passphrase = await resolvePassphrase({ flag:
|
|
324
|
+
const passphrase = await resolvePassphrase({ flag: passphraseFlag });
|
|
318
325
|
if (passphrase === null) {
|
|
319
326
|
reportErrorAndExit(new PerpError("PASSPHRASE_REQUIRED", "No passphrase provided and stdin is non-TTY", {
|
|
320
327
|
remediation: "Provide passphrase via --passphrase flag, OWS_PASSPHRASE env var, or stdin pipe",
|
|
@@ -377,7 +384,7 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
377
384
|
remediation: "Run: perp setup or use --master <name>",
|
|
378
385
|
}));
|
|
379
386
|
}
|
|
380
|
-
const passphrase = await resolvePassphrase({ flag:
|
|
387
|
+
const passphrase = await resolvePassphrase({ flag: passphraseFlag });
|
|
381
388
|
if (passphrase === null) {
|
|
382
389
|
reportErrorAndExit(new PerpError("PASSPHRASE_REQUIRED", "No passphrase provided and stdin is non-TTY", {
|
|
383
390
|
remediation: "Provide passphrase via --passphrase flag, OWS_PASSPHRASE env var, or stdin pipe",
|
|
@@ -438,7 +445,7 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
438
445
|
remediation: "Run: perp setup or use --master <name>",
|
|
439
446
|
}));
|
|
440
447
|
}
|
|
441
|
-
const passphrase = await resolvePassphrase({ flag:
|
|
448
|
+
const passphrase = await resolvePassphrase({ flag: passphraseFlag });
|
|
442
449
|
if (passphrase === null) {
|
|
443
450
|
reportErrorAndExit(new PerpError("PASSPHRASE_REQUIRED", "No passphrase provided and stdin is non-TTY", {
|
|
444
451
|
remediation: "Provide passphrase via --passphrase flag, OWS_PASSPHRASE env var, or stdin pipe",
|
|
@@ -526,7 +533,7 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
526
533
|
canSpot = opts.canSpot ?? false;
|
|
527
534
|
canWithdraw = opts.canWithdraw ?? false;
|
|
528
535
|
// 3-path passphrase resolver (non-TTY path)
|
|
529
|
-
passphrase = await resolvePassphrase({ flag:
|
|
536
|
+
passphrase = await resolvePassphrase({ flag: passphraseFlag });
|
|
530
537
|
if (passphrase === null) {
|
|
531
538
|
// TTY but non-wizard (some flags were supplied) — throw PASSPHRASE_REQUIRED
|
|
532
539
|
reportErrorAndExit(new PerpError("PASSPHRASE_REQUIRED", "No passphrase provided and stdin is non-TTY or wizard skipped", {
|
|
@@ -666,8 +673,11 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
666
673
|
.option("--force", "Skip Aster POST, only clear local state")
|
|
667
674
|
.option("--passphrase <pp>", "Master OWS passphrase")
|
|
668
675
|
.option("--json", "Machine-readable output")
|
|
669
|
-
.action(async (exchange, agentName, opts) => {
|
|
676
|
+
.action(async (exchange, agentName, opts, command) => {
|
|
670
677
|
const useJson = opts.json ?? isJson();
|
|
678
|
+
// optsWithGlobals(): see wallet agent approve for full rationale.
|
|
679
|
+
const mergedOpts = command.optsWithGlobals();
|
|
680
|
+
const passphraseFlag = mergedOpts.passphrase ?? opts.passphrase;
|
|
671
681
|
try {
|
|
672
682
|
// Step 1: Check local meta (idempotent — absent = already revoked)
|
|
673
683
|
const meta = getAgent(exchange, agentName);
|
|
@@ -683,7 +693,7 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
683
693
|
}
|
|
684
694
|
if (!opts.force) {
|
|
685
695
|
// Step 2: Resolve passphrase
|
|
686
|
-
const passphrase = await resolvePassphrase({ flag:
|
|
696
|
+
const passphrase = await resolvePassphrase({ flag: passphraseFlag });
|
|
687
697
|
if (passphrase === null) {
|
|
688
698
|
throw new PerpError("PASSPHRASE_REQUIRED", "No passphrase provided and stdin is non-TTY", {
|
|
689
699
|
remediation: "Provide passphrase via --passphrase flag, OWS_PASSPHRASE env var, or stdin pipe",
|
|
@@ -791,8 +801,11 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
791
801
|
.option("--can-withdraw", "Allow withdrawals (default: inherit or off)")
|
|
792
802
|
.option("--no-can-withdraw", "Disallow withdrawals")
|
|
793
803
|
.option("--json", "Machine-readable output")
|
|
794
|
-
.action(async (exchange, oldAgentNameArg, opts) => {
|
|
804
|
+
.action(async (exchange, oldAgentNameArg, opts, command) => {
|
|
795
805
|
const useJson = opts.json ?? isJson();
|
|
806
|
+
// optsWithGlobals(): see wallet agent approve for full rationale.
|
|
807
|
+
const mergedOpts = command.optsWithGlobals();
|
|
808
|
+
const passphraseFlag = mergedOpts.passphrase ?? opts.passphrase;
|
|
796
809
|
try {
|
|
797
810
|
// Determine which agent to rotate
|
|
798
811
|
const agentNameToRotate = oldAgentNameArg ?? opts.agentName ?? getAgent(exchange)?.agentName;
|
|
@@ -802,7 +815,7 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
802
815
|
});
|
|
803
816
|
}
|
|
804
817
|
// Resolve passphrase early (needed for both revoke and approve phases)
|
|
805
|
-
const passphrase = await resolvePassphrase({ flag:
|
|
818
|
+
const passphrase = await resolvePassphrase({ flag: passphraseFlag });
|
|
806
819
|
if (passphrase === null) {
|
|
807
820
|
throw new PerpError("PASSPHRASE_REQUIRED", "No passphrase provided and stdin is non-TTY", {
|
|
808
821
|
remediation: "Provide passphrase via --passphrase flag, OWS_PASSPHRASE env var, or stdin pipe",
|
|
@@ -1006,11 +1019,14 @@ export function registerWalletAgentCommands(walletCmd, isJson) {
|
|
|
1006
1019
|
.option("--account-index <n>", "Account index (Lighter only)")
|
|
1007
1020
|
.option("--passphrase <pp>", "Master OWS passphrase (Aster/PAC). Falls back to OWS_PASSPHRASE env.")
|
|
1008
1021
|
.option("--json", "Machine-readable output")
|
|
1009
|
-
.action(async (exchange, agentName, opts) => {
|
|
1022
|
+
.action(async (exchange, agentName, opts, command) => {
|
|
1010
1023
|
const useJson = opts.json ?? isJson();
|
|
1011
1024
|
const ts = new Date().toISOString();
|
|
1025
|
+
// optsWithGlobals(): see wallet agent approve for full rationale.
|
|
1026
|
+
const mergedOpts = command.optsWithGlobals();
|
|
1027
|
+
const passphraseFlag = mergedOpts.passphrase ?? opts.passphrase;
|
|
1012
1028
|
// Resolve passphrase via 3-path: flag > env > (no stdin prompt in verify)
|
|
1013
|
-
const passphrase =
|
|
1029
|
+
const passphrase = passphraseFlag ?? process.env["OWS_PASSPHRASE"] ?? "";
|
|
1014
1030
|
const verifyOpts = {
|
|
1015
1031
|
agentName,
|
|
1016
1032
|
master: opts.master,
|
package/dist/commands/init.js
CHANGED
|
@@ -70,11 +70,17 @@ function askChoice(rl, question, choices) {
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
// ── Exchange → env var mapping ────────────────────────────────
|
|
73
|
+
// v0.12.4 retired the legacy HMAC ASTER_API_KEY/SECRET path (Plan v3.0).
|
|
74
|
+
// v0.12.6 restores Aster as a normal EVM exchange: `ASTER_PRIVATE_KEY`
|
|
75
|
+
// (Tier 3 PK direct path) is collected like HL/LT, and the agent flow
|
|
76
|
+
// (`wallet agent approve aster`) is the recommended Tier 1 onboarding.
|
|
77
|
+
// Removing Aster entirely was over-correction — the entry must exist so
|
|
78
|
+
// `wallet import` / `wallet set` / setup wizard treat all 4 DEX uniformly.
|
|
73
79
|
export const EXCHANGE_ENV_MAP = {
|
|
74
80
|
pacifica: { envKey: "PACIFICA_PRIVATE_KEY", chain: "solana", label: "Pacifica (Solana)" },
|
|
75
81
|
hyperliquid: { envKey: "HL_PRIVATE_KEY", chain: "evm", label: "Hyperliquid (EVM)" },
|
|
76
82
|
lighter: { envKey: "LIGHTER_PRIVATE_KEY", chain: "evm", label: "Lighter (EVM)" },
|
|
77
|
-
aster: { envKey: "
|
|
83
|
+
aster: { envKey: "ASTER_PRIVATE_KEY", chain: "evm", label: "Aster (BNB Chain)" },
|
|
78
84
|
};
|
|
79
85
|
// ── Validate keys ─────────────────────────────────────────────
|
|
80
86
|
export async function validateKey(chain, key) {
|
|
@@ -122,13 +128,24 @@ export function registerInitCommand(program) {
|
|
|
122
128
|
.option("--wallet-name <name>", "OWS wallet alias to create (default: 'main')")
|
|
123
129
|
.option("--passphrase <pp>", "OWS vault passphrase (fallback: OWS_PASSPHRASE env / stdin)")
|
|
124
130
|
.option("--default-exchange <name>", "Default exchange (pacifica/hyperliquid/lighter/aster)")
|
|
125
|
-
.action(async (opts) => {
|
|
131
|
+
.action(async (opts, command) => {
|
|
132
|
+
// optsWithGlobals() merges parent + subcommand options so --passphrase
|
|
133
|
+
// works whether placed on `perp --passphrase X setup` (parent) or
|
|
134
|
+
// `perp setup --passphrase X` (subcommand). Commander v13 silently
|
|
135
|
+
// shadows the subcommand flag when the parent defines an identically
|
|
136
|
+
// named flag — the visible bug was non-interactive setup throwing
|
|
137
|
+
// PASSPHRASE_REQUIRED with --passphrase set on the subcommand.
|
|
138
|
+
const mergedOpts = command.optsWithGlobals();
|
|
139
|
+
const resolvedOpts = {
|
|
140
|
+
...opts,
|
|
141
|
+
passphrase: mergedOpts.passphrase ?? opts.passphrase,
|
|
142
|
+
};
|
|
126
143
|
// Non-interactive mode: skip all readline prompts, drive everything from
|
|
127
144
|
// flags + OWS_PASSPHRASE env. This is the agent-friendly onboarding path
|
|
128
145
|
// (CI / Docker / scripted init) — see plan v3.0 "agent-friendly five
|
|
129
146
|
// guarantees" P1 (no-prompt) and P3 (passphrase 3-path).
|
|
130
|
-
if (
|
|
131
|
-
return runNonInteractiveSetup(
|
|
147
|
+
if (resolvedOpts.nonInteractive) {
|
|
148
|
+
return runNonInteractiveSetup(resolvedOpts);
|
|
132
149
|
}
|
|
133
150
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
134
151
|
try {
|
|
@@ -161,7 +178,7 @@ export function registerInitCommand(program) {
|
|
|
161
178
|
console.log(` 1) ${chalk.cyan("Pacifica")} ${chalk.gray("— Solana perps (USDC on Solana)")}`);
|
|
162
179
|
console.log(` 2) ${chalk.cyan("Hyperliquid")} ${chalk.gray("— EVM perps (USDC on Arbitrum)")}`);
|
|
163
180
|
console.log(` 3) ${chalk.cyan("Lighter")} ${chalk.gray("— EVM perps (USDC on Ethereum/Arb)")}`);
|
|
164
|
-
console.log(` 4) ${chalk.cyan("Aster")} ${chalk.gray("— BNB Chain perps (
|
|
181
|
+
console.log(` 4) ${chalk.cyan("Aster")} ${chalk.gray("— BNB Chain perps (use 'wallet agent approve aster')")}`);
|
|
165
182
|
console.log();
|
|
166
183
|
const exchangeChoice = await ask(rl, " Which exchange(s)? (1,2,3,4 or 'all'): ");
|
|
167
184
|
const selected = parseExchangeChoice(exchangeChoice);
|
|
@@ -174,7 +191,6 @@ export function registerInitCommand(program) {
|
|
|
174
191
|
// EVM exchanges can share the same key
|
|
175
192
|
const evmExchanges = selected.filter((e) => EXCHANGE_ENV_MAP[e].chain === "evm");
|
|
176
193
|
const solExchanges = selected.filter((e) => EXCHANGE_ENV_MAP[e].chain === "solana");
|
|
177
|
-
const apikeyExchanges = selected.filter((e) => EXCHANGE_ENV_MAP[e].chain === "apikey");
|
|
178
194
|
const newEnv = { ...env };
|
|
179
195
|
if (evmExchanges.length > 0) {
|
|
180
196
|
console.log(chalk.cyan.bold("\n EVM Private Key") + chalk.gray(` (for ${evmExchanges.join(", ")})`));
|
|
@@ -230,34 +246,9 @@ export function registerInitCommand(program) {
|
|
|
230
246
|
}
|
|
231
247
|
}
|
|
232
248
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
console.log(chalk.cyan.bold(`\n ${info.label} API Credentials`));
|
|
237
|
-
const apiKey = await ask(rl, ` ${info.label} API key: `);
|
|
238
|
-
if (!apiKey) {
|
|
239
|
-
console.log(chalk.gray(" Skipped."));
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
const { valid, address } = await validateKey("apikey", apiKey);
|
|
243
|
-
if (!valid) {
|
|
244
|
-
console.log(chalk.red(" Invalid API key (must be at least 16 chars), skipped."));
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
newEnv[info.envKey] = apiKey;
|
|
248
|
-
console.log(` ${chalk.green("OK")} ${chalk.gray(address)}`);
|
|
249
|
-
if (info.envKeySecret) {
|
|
250
|
-
const secret = await ask(rl, ` ${info.label} API secret: `);
|
|
251
|
-
if (secret) {
|
|
252
|
-
newEnv[info.envKeySecret] = secret;
|
|
253
|
-
console.log(` ${chalk.green("OK")} secret saved`);
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
console.log(chalk.yellow(" No secret provided — some operations may fail."));
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
249
|
+
// v0.12.4: API-key (Aster legacy HMAC) onboarding removed from the
|
|
250
|
+
// wizard. Aster users follow the redirect above to
|
|
251
|
+
// `perp wallet agent approve aster`.
|
|
261
252
|
// Default exchange
|
|
262
253
|
const settings = loadSettings();
|
|
263
254
|
if (selected.length === 1) {
|
package/dist/commands/wallet.js
CHANGED
|
@@ -326,7 +326,7 @@ export function registerWalletCommands(program, isJson, getAdapter, getPacificaA
|
|
|
326
326
|
.option("--show-mnemonic", "Display the mnemonic phrase (CAUTION)")
|
|
327
327
|
.option("--passphrase <pp>", "Master OWS passphrase (fallback: OWS_PASSPHRASE env / stdin)")
|
|
328
328
|
.option("--legacy <chain>", "Legacy mode: generate solana or evm key (unencrypted)")
|
|
329
|
-
.action(async (name, opts) => {
|
|
329
|
+
.action(async (name, opts, command) => {
|
|
330
330
|
// Legacy mode for backward compat
|
|
331
331
|
if (opts.legacy) {
|
|
332
332
|
return _legacyGenerate(opts.legacy, name, isJson);
|
|
@@ -337,8 +337,15 @@ export function registerWalletCommands(program, isJson, getAdapter, getPacificaA
|
|
|
337
337
|
// > OWS_PASSPHRASE env > stdin pipe). When stdin is a TTY and nothing was
|
|
338
338
|
// provided, the resolver returns null; fall back to "" to preserve the
|
|
339
339
|
// historical "no passphrase" default for interactive use.
|
|
340
|
+
//
|
|
341
|
+
// optsWithGlobals() merges parent + subcommand options, so --passphrase
|
|
342
|
+
// works whether placed on `perp --passphrase X subcmd` (parent) or
|
|
343
|
+
// `perp subcmd --passphrase X` (subcommand). Without this, Commander v13
|
|
344
|
+
// shadows the subcommand flag with the parent's same-named flag and
|
|
345
|
+
// opts.passphrase is always undefined.
|
|
346
|
+
const merged = command.optsWithGlobals();
|
|
340
347
|
const { resolvePassphrase } = await import("../agent-wallet/passphrase.js");
|
|
341
|
-
const pp = (await resolvePassphrase({ flag: opts.passphrase })) ?? "";
|
|
348
|
+
const pp = (await resolvePassphrase({ flag: merged.passphrase ?? opts.passphrase })) ?? "";
|
|
342
349
|
const ows = loadOws();
|
|
343
350
|
const w = ows.createWallet(walletName, pp, parseInt(opts.words));
|
|
344
351
|
// Set as active wallet
|
|
@@ -402,7 +409,7 @@ export function registerWalletCommands(program, isJson, getAdapter, getPacificaA
|
|
|
402
409
|
.option("--mnemonic", "Import as mnemonic phrase instead of private key")
|
|
403
410
|
.option("--passphrase <pp>", "Master OWS passphrase (fallback: OWS_PASSPHRASE env / stdin)")
|
|
404
411
|
.option("--legacy <chain>", "Legacy mode: import to wallets.json (solana or evm)")
|
|
405
|
-
.action(async (privateKey, opts) => {
|
|
412
|
+
.action(async (privateKey, opts, command) => {
|
|
406
413
|
// Legacy mode (positional only)
|
|
407
414
|
if (opts.legacy) {
|
|
408
415
|
if (!privateKey) {
|
|
@@ -486,8 +493,13 @@ export function registerWalletCommands(program, isJson, getAdapter, getPacificaA
|
|
|
486
493
|
// > OWS_PASSPHRASE env > stdin pipe). When stdin is a TTY and nothing was
|
|
487
494
|
// provided, the resolver returns null; fall back to "" to preserve the
|
|
488
495
|
// historical "no passphrase" default for interactive use.
|
|
496
|
+
//
|
|
497
|
+
// optsWithGlobals() merges parent + subcommand options — see the
|
|
498
|
+
// wallet generate action for context. Without it, the parent program's
|
|
499
|
+
// --passphrase shadows the subcommand's same-named flag.
|
|
500
|
+
const merged = command.optsWithGlobals();
|
|
489
501
|
const { resolvePassphrase } = await import("../agent-wallet/passphrase.js");
|
|
490
|
-
const pp = (await resolvePassphrase({ flag: opts.passphrase })) ?? "";
|
|
502
|
+
const pp = (await resolvePassphrase({ flag: merged.passphrase ?? opts.passphrase })) ?? "";
|
|
491
503
|
let w;
|
|
492
504
|
if (opts.mnemonic) {
|
|
493
505
|
w = ows.importWalletMnemonic(opts.name, privateKey, pp);
|
|
@@ -598,17 +610,17 @@ export function registerWalletCommands(program, isJson, getAdapter, getPacificaA
|
|
|
598
610
|
.description("Set private key for an exchange")
|
|
599
611
|
.option("--default", "Also set as default exchange")
|
|
600
612
|
.action(async (exchange, key, opts) => {
|
|
601
|
-
// Resolve alias (hl → hyperliquid, pac → pacifica, lt → lighter)
|
|
602
|
-
const aliases = { hl: "hyperliquid", pac: "pacifica", lt: "lighter" };
|
|
613
|
+
// Resolve alias (hl → hyperliquid, pac → pacifica, lt → lighter, ast → aster)
|
|
614
|
+
const aliases = { hl: "hyperliquid", pac: "pacifica", lt: "lighter", ast: "aster" };
|
|
603
615
|
const resolved = aliases[exchange.toLowerCase()] || exchange.toLowerCase();
|
|
604
616
|
const info = EXCHANGE_ENV_MAP[resolved];
|
|
605
617
|
if (!info) {
|
|
606
618
|
if (isJson()) {
|
|
607
619
|
const { jsonError } = await import("../utils.js");
|
|
608
|
-
return printJson(jsonError("INVALID_PARAMS", `Unknown exchange: ${exchange}. Use: pacifica, hyperliquid, lighter, aster (or hl, pac, lt, ast)
|
|
620
|
+
return printJson(jsonError("INVALID_PARAMS", `Unknown exchange: ${exchange}. Use: pacifica, hyperliquid, lighter, aster (or hl, pac, lt, ast).`));
|
|
609
621
|
}
|
|
610
622
|
console.error(chalk.red(`\n Unknown exchange: ${exchange}`));
|
|
611
|
-
console.error(chalk.gray(` Use: pacifica, hyperliquid, lighter, aster (or hl, pac, lt, ast)
|
|
623
|
+
console.error(chalk.gray(` Use: pacifica, hyperliquid, lighter, aster (or hl, pac, lt, ast).\n`));
|
|
612
624
|
process.exit(1);
|
|
613
625
|
}
|
|
614
626
|
const { valid, address } = await validateKey(info.chain, key);
|
package/dist/config.js
CHANGED
|
@@ -74,11 +74,15 @@ export async function loadPrivateKey(exchange, pkOverride, walletName) {
|
|
|
74
74
|
// settings module not available, skip
|
|
75
75
|
}
|
|
76
76
|
// 3. Exchange-specific env vars
|
|
77
|
+
// v0.12.4: Aster's ASTER_API_KEY entry is removed — the legacy HMAC
|
|
78
|
+
// signing path was retired in Plan v3.0. Aster onboarding is now via
|
|
79
|
+
// `perp wallet agent approve aster` (off-chain agent wallet); the runtime
|
|
80
|
+
// adapter pulls credentials from the agent store, not the env.
|
|
77
81
|
const envMap = {
|
|
78
82
|
pacifica: ["PACIFICA_PRIVATE_KEY", "pk"],
|
|
79
83
|
hyperliquid: ["HYPERLIQUID_PRIVATE_KEY", "HL_PRIVATE_KEY"],
|
|
80
84
|
lighter: ["LIGHTER_PRIVATE_KEY"],
|
|
81
|
-
aster: [
|
|
85
|
+
aster: [],
|
|
82
86
|
};
|
|
83
87
|
for (const envVar of envMap[exchange]) {
|
|
84
88
|
if (process.env[envVar])
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "perp-cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.6",
|
|
4
4
|
"description": "Multi-DEX Perpetual Futures CLI - Pacifica, Hyperliquid, Lighter, Aster",
|
|
5
5
|
"bin": {
|
|
6
6
|
"perp": "dist/index.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"test": "vitest run --exclude '**/integration/**'",
|
|
19
19
|
"test:integration": "vitest run -c vitest.integration.config.ts",
|
|
20
20
|
"test:all": "vitest run",
|
|
21
|
-
"
|
|
21
|
+
"sync-skill-version": "node scripts/sync-skill-version.mjs",
|
|
22
|
+
"prepublishOnly": "node scripts/sync-skill-version.mjs && pnpm run build"
|
|
22
23
|
},
|
|
23
24
|
"keywords": [
|
|
24
25
|
"pacifica",
|
package/skills/perp-cli/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ allowed-tools: "Bash(perp:*), Bash(npx perp-cli:*), Bash(npx -y perp-cli:*)"
|
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
author: hypurrquant
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.12.6"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# perp-cli Agent Guide
|
|
@@ -41,7 +41,7 @@ Multi-DEX perpetual futures CLI + MCP server — Pacifica (Solana), Hyperliquid
|
|
|
41
41
|
## Install
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
perp --version 2>/dev/null # check if exists (must be >= 0.
|
|
44
|
+
perp --version 2>/dev/null # check if exists (must be >= 0.12.6)
|
|
45
45
|
npm install -g perp-cli@latest 2>/dev/null || npx -y perp-cli@latest --json --version
|
|
46
46
|
```
|
|
47
47
|
|