perp-cli 0.12.3 → 0.12.5
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 +41 -36
- package/dist/commands/wallet.js +35 -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,15 @@ function askChoice(rl, question, choices) {
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
// ── Exchange → env var mapping ────────────────────────────────
|
|
73
|
+
// Aster removed in v0.12.4: legacy HMAC API-key signing path was retired in
|
|
74
|
+
// Plan v3.0. New users must onboard via `perp wallet agent approve aster`
|
|
75
|
+
// (off-chain agent wallet, same flow as HL/PAC/LT). The setup wizard
|
|
76
|
+
// surfaces a redirect when Aster is selected; `wallet set aster <key>`
|
|
77
|
+
// throws INVALID_PARAMS pointing at the agent flow.
|
|
73
78
|
export const EXCHANGE_ENV_MAP = {
|
|
74
79
|
pacifica: { envKey: "PACIFICA_PRIVATE_KEY", chain: "solana", label: "Pacifica (Solana)" },
|
|
75
80
|
hyperliquid: { envKey: "HL_PRIVATE_KEY", chain: "evm", label: "Hyperliquid (EVM)" },
|
|
76
81
|
lighter: { envKey: "LIGHTER_PRIVATE_KEY", chain: "evm", label: "Lighter (EVM)" },
|
|
77
|
-
aster: { envKey: "ASTER_API_KEY", envKeySecret: "ASTER_API_SECRET", chain: "apikey", label: "Aster (BNB Chain)" },
|
|
78
82
|
};
|
|
79
83
|
// ── Validate keys ─────────────────────────────────────────────
|
|
80
84
|
export async function validateKey(chain, key) {
|
|
@@ -122,13 +126,24 @@ export function registerInitCommand(program) {
|
|
|
122
126
|
.option("--wallet-name <name>", "OWS wallet alias to create (default: 'main')")
|
|
123
127
|
.option("--passphrase <pp>", "OWS vault passphrase (fallback: OWS_PASSPHRASE env / stdin)")
|
|
124
128
|
.option("--default-exchange <name>", "Default exchange (pacifica/hyperliquid/lighter/aster)")
|
|
125
|
-
.action(async (opts) => {
|
|
129
|
+
.action(async (opts, command) => {
|
|
130
|
+
// optsWithGlobals() merges parent + subcommand options so --passphrase
|
|
131
|
+
// works whether placed on `perp --passphrase X setup` (parent) or
|
|
132
|
+
// `perp setup --passphrase X` (subcommand). Commander v13 silently
|
|
133
|
+
// shadows the subcommand flag when the parent defines an identically
|
|
134
|
+
// named flag — the visible bug was non-interactive setup throwing
|
|
135
|
+
// PASSPHRASE_REQUIRED with --passphrase set on the subcommand.
|
|
136
|
+
const mergedOpts = command.optsWithGlobals();
|
|
137
|
+
const resolvedOpts = {
|
|
138
|
+
...opts,
|
|
139
|
+
passphrase: mergedOpts.passphrase ?? opts.passphrase,
|
|
140
|
+
};
|
|
126
141
|
// Non-interactive mode: skip all readline prompts, drive everything from
|
|
127
142
|
// flags + OWS_PASSPHRASE env. This is the agent-friendly onboarding path
|
|
128
143
|
// (CI / Docker / scripted init) — see plan v3.0 "agent-friendly five
|
|
129
144
|
// guarantees" P1 (no-prompt) and P3 (passphrase 3-path).
|
|
130
|
-
if (
|
|
131
|
-
return runNonInteractiveSetup(
|
|
145
|
+
if (resolvedOpts.nonInteractive) {
|
|
146
|
+
return runNonInteractiveSetup(resolvedOpts);
|
|
132
147
|
}
|
|
133
148
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
134
149
|
try {
|
|
@@ -161,12 +176,28 @@ export function registerInitCommand(program) {
|
|
|
161
176
|
console.log(` 1) ${chalk.cyan("Pacifica")} ${chalk.gray("— Solana perps (USDC on Solana)")}`);
|
|
162
177
|
console.log(` 2) ${chalk.cyan("Hyperliquid")} ${chalk.gray("— EVM perps (USDC on Arbitrum)")}`);
|
|
163
178
|
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 (
|
|
179
|
+
console.log(` 4) ${chalk.cyan("Aster")} ${chalk.gray("— BNB Chain perps (use 'wallet agent approve aster')")}`);
|
|
165
180
|
console.log();
|
|
166
181
|
const exchangeChoice = await ask(rl, " Which exchange(s)? (1,2,3,4 or 'all'): ");
|
|
167
|
-
const
|
|
182
|
+
const selectedRaw = parseExchangeChoice(exchangeChoice);
|
|
183
|
+
// v0.12.4: legacy Aster HMAC API-key path is removed. The wizard can
|
|
184
|
+
// no longer collect ASTER_API_KEY/ASTER_API_SECRET — Aster onboarding
|
|
185
|
+
// is now exclusively via `perp wallet agent approve aster --master`.
|
|
186
|
+
// Print a redirect and drop Aster from the env-write set.
|
|
187
|
+
const asterRequested = selectedRaw.includes("aster");
|
|
188
|
+
const selected = selectedRaw.filter((e) => e !== "aster");
|
|
189
|
+
if (asterRequested) {
|
|
190
|
+
console.log(chalk.yellow.bold("\n Aster onboarding is via agent wallet (no API keys):"));
|
|
191
|
+
console.log(` ${chalk.green("perp wallet agent approve aster --master <name>")}`);
|
|
192
|
+
console.log(chalk.gray(" Run that after this wizard completes.\n"));
|
|
193
|
+
}
|
|
168
194
|
if (selected.length === 0) {
|
|
169
|
-
|
|
195
|
+
if (asterRequested) {
|
|
196
|
+
console.log(chalk.gray(" No additional exchanges to configure here. See the Aster instructions above.\n"));
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
console.log(chalk.red("\n No exchanges selected.\n"));
|
|
200
|
+
}
|
|
170
201
|
rl.close();
|
|
171
202
|
return;
|
|
172
203
|
}
|
|
@@ -174,7 +205,6 @@ export function registerInitCommand(program) {
|
|
|
174
205
|
// EVM exchanges can share the same key
|
|
175
206
|
const evmExchanges = selected.filter((e) => EXCHANGE_ENV_MAP[e].chain === "evm");
|
|
176
207
|
const solExchanges = selected.filter((e) => EXCHANGE_ENV_MAP[e].chain === "solana");
|
|
177
|
-
const apikeyExchanges = selected.filter((e) => EXCHANGE_ENV_MAP[e].chain === "apikey");
|
|
178
208
|
const newEnv = { ...env };
|
|
179
209
|
if (evmExchanges.length > 0) {
|
|
180
210
|
console.log(chalk.cyan.bold("\n EVM Private Key") + chalk.gray(` (for ${evmExchanges.join(", ")})`));
|
|
@@ -230,34 +260,9 @@ export function registerInitCommand(program) {
|
|
|
230
260
|
}
|
|
231
261
|
}
|
|
232
262
|
}
|
|
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
|
-
}
|
|
263
|
+
// v0.12.4: API-key (Aster legacy HMAC) onboarding removed from the
|
|
264
|
+
// wizard. Aster users follow the redirect above to
|
|
265
|
+
// `perp wallet agent approve aster`.
|
|
261
266
|
// Default exchange
|
|
262
267
|
const settings = loadSettings();
|
|
263
268
|
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,32 @@ 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();
|
|
616
|
+
// v0.12.4: Aster legacy HMAC API-key path is removed entirely. Plan v3.0
|
|
617
|
+
// de-wired the runtime/signing surface but left the wallet set entry
|
|
618
|
+
// accepting keys that no longer drive any signing path. Fail loudly
|
|
619
|
+
// (SSOT Rule #2) and point at the agent flow.
|
|
620
|
+
if (resolved === "aster") {
|
|
621
|
+
const msg = "'wallet set aster' is removed in v0.13. Use 'wallet agent approve aster' instead.";
|
|
622
|
+
const remediation = "perp wallet agent approve aster --master <wallet>";
|
|
623
|
+
if (isJson()) {
|
|
624
|
+
const { jsonError } = await import("../utils.js");
|
|
625
|
+
return printJson(jsonError("INVALID_PARAMS", msg, { details: { remediation } }));
|
|
626
|
+
}
|
|
627
|
+
console.error(chalk.red(`\n ${msg}`));
|
|
628
|
+
console.error(chalk.gray(` Run: ${remediation}\n`));
|
|
629
|
+
process.exit(1);
|
|
630
|
+
}
|
|
604
631
|
const info = EXCHANGE_ENV_MAP[resolved];
|
|
605
632
|
if (!info) {
|
|
606
633
|
if (isJson()) {
|
|
607
634
|
const { jsonError } = await import("../utils.js");
|
|
608
|
-
return printJson(jsonError("INVALID_PARAMS", `Unknown exchange: ${exchange}. Use: pacifica, hyperliquid, lighter
|
|
635
|
+
return printJson(jsonError("INVALID_PARAMS", `Unknown exchange: ${exchange}. Use: pacifica, hyperliquid, lighter (or hl, pac, lt). For Aster: 'perp wallet agent approve aster'.`));
|
|
609
636
|
}
|
|
610
637
|
console.error(chalk.red(`\n Unknown exchange: ${exchange}`));
|
|
611
|
-
console.error(chalk.gray(` Use: pacifica, hyperliquid, lighter
|
|
638
|
+
console.error(chalk.gray(` Use: pacifica, hyperliquid, lighter (or hl, pac, lt). For Aster: 'perp wallet agent approve aster'.\n`));
|
|
612
639
|
process.exit(1);
|
|
613
640
|
}
|
|
614
641
|
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.5",
|
|
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.5"
|
|
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.5)
|
|
45
45
|
npm install -g perp-cli@latest 2>/dev/null || npx -y perp-cli@latest --json --version
|
|
46
46
|
```
|
|
47
47
|
|