@palmyr/cli 1.3.1 → 1.4.0

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/cli.js CHANGED
@@ -1636,6 +1636,8 @@ async function main() {
1636
1636
  { name: 'brief', description: 'Show thesis + PnL brief for a position', hint: '<CA>' },
1637
1637
  { name: 'doctor', description: 'Health check for the wallet-trading subsystem', hint: '[--wallet <ref>]' },
1638
1638
  { name: 'smoke-test', description: 'End-to-end validation of wallet trading on Solana + Base', hint: '--wallet <ref> [--chain solana|base|all]' },
1639
+ { name: 'readiness', description: 'Go/no-go autonomous-trading readiness — sign, gas, quotes, daemon, open positions', hint: '--wallet <ref>' },
1640
+ { name: 'live-test', description: 'Execute tiny real round trips on Solana + Base, verify no leftover positions', hint: '--wallet <ref> --budget Nusdc [--chain ...]' },
1639
1641
  { name: 'daemon', description: 'Auto-monitor positions for trigger-based exits', hint: 'tick | start [--auto] | stop | status' },
1640
1642
  { name: 'triggers', description: 'List pending trigger fires from the daemon', hint: '[--ca X] [--since ISO] [--clear]' },
1641
1643
  { name: 'trading-keystore', description: 'Encrypted BIP39 keystore for HD-derived trading wallets', hint: 'init | list | status | derive | export' },
@@ -3021,14 +3023,19 @@ async function main() {
3021
3023
  const p = readPosition(inferredChain, ca, scopedAddr);
3022
3024
  if (!p)
3023
3025
  err(`Position not found: ${ca}`, EXIT.NOT_FOUND);
3026
+ // `--evaluate` degrades gracefully: a missing ANTHROPIC_API_KEY or
3027
+ // a model-API failure must NOT take down the whole brief. We surface
3028
+ // the LLM error as `llmError` so agents can branch on it, and still
3029
+ // print the deterministic brief fields.
3024
3030
  let llm;
3031
+ let llmError;
3025
3032
  if (evaluate) {
3026
3033
  const { evaluateBriefWithLLM } = await import('./wallet-brief-llm.js');
3027
3034
  try {
3028
3035
  llm = await evaluateBriefWithLLM(p);
3029
3036
  }
3030
3037
  catch (e) {
3031
- err(e.message || 'brief --evaluate failed', EXIT.GENERAL);
3038
+ llmError = e?.message ?? 'brief --evaluate failed';
3032
3039
  }
3033
3040
  }
3034
3041
  if (!AGENT_MODE) {
@@ -3061,6 +3068,10 @@ async function main() {
3061
3068
  console.log(` ${t.muted}Reasoning:${t.reset} ${llm.reasoning}`);
3062
3069
  console.log(` ${t.muted}Watch for:${t.reset} ${llm.watchFor}`);
3063
3070
  }
3071
+ else if (llmError) {
3072
+ console.log();
3073
+ console.log(` ${t.warn}LLM eval skipped: ${llmError}${t.reset}`);
3074
+ }
3064
3075
  else {
3065
3076
  console.log();
3066
3077
  console.log(` ${t.muted}Add --evaluate for an LLM thesis-health check.${t.reset}`);
@@ -3078,6 +3089,7 @@ async function main() {
3078
3089
  pnl: p.pnl,
3079
3090
  sellsCount: p.sells.length,
3080
3091
  llm,
3092
+ llmError,
3081
3093
  });
3082
3094
  }
3083
3095
  break;
@@ -3176,6 +3188,111 @@ async function main() {
3176
3188
  process.exit(EXIT.GENERAL);
3177
3189
  break;
3178
3190
  }
3191
+ case 'readiness': {
3192
+ const readyWalletRef = flags.wallet || undefined;
3193
+ if (!readyWalletRef)
3194
+ err('--wallet required. Use a vault wallet name/id or `trading:N`.', EXIT.BAD_INPUT);
3195
+ const { runWalletReadiness } = await import('./wallet-readiness.js');
3196
+ const report = await runWalletReadiness({ walletRef: readyWalletRef });
3197
+ if (AGENT_MODE) {
3198
+ print(report);
3199
+ if (!report.safeForAutonomousTrading)
3200
+ process.exit(EXIT.GENERAL);
3201
+ break;
3202
+ }
3203
+ console.log();
3204
+ section('Wallet readiness');
3205
+ kv('Wallet', report.wallet);
3206
+ if (report.solanaAddress)
3207
+ kv('Solana', report.solanaAddress);
3208
+ if (report.evmAddress)
3209
+ kv('EVM', report.evmAddress);
3210
+ kv('Verdict', report.safeForAutonomousTrading
3211
+ ? `${t.success}safe for autonomous trading${t.reset}`
3212
+ : `${t.error}NOT safe — see failing checks${t.reset}`);
3213
+ if (report.balances.solana)
3214
+ kv('SOL balance', `${report.balances.solana.sol.toFixed(6)} SOL`);
3215
+ if (report.balances.base)
3216
+ kv('ETH balance', `${report.balances.base.eth.toFixed(8)} ETH`);
3217
+ kv('Open positions', `solana=${report.openPositions.solana} base=${report.openPositions.base}`);
3218
+ kv('Daemon', report.daemon.running
3219
+ ? `${t.success}running${t.reset} (pid ${report.daemon.pid}${report.daemon.autoExecute ? ', auto-execute' : ''})`
3220
+ : `${t.warn}not running${t.reset}`);
3221
+ console.log();
3222
+ section('Checks');
3223
+ for (const c of report.checks) {
3224
+ const dot = c.status === 'pass' ? `${t.success}✓${t.reset}`
3225
+ : c.status === 'warn' ? `${t.warn}!${t.reset}`
3226
+ : c.status === 'skip' ? `${t.muted}-${t.reset}`
3227
+ : `${t.error}✗${t.reset}`;
3228
+ const tail = [c.value !== undefined ? String(c.value) : null, c.message].filter(Boolean).join(' — ');
3229
+ console.log(` ${dot} ${c.name}${tail ? `: ${tail}` : ''}`);
3230
+ }
3231
+ console.log();
3232
+ if (!report.safeForAutonomousTrading)
3233
+ process.exit(EXIT.GENERAL);
3234
+ break;
3235
+ }
3236
+ case 'live-test': {
3237
+ const liveWalletRef = flags.wallet || undefined;
3238
+ if (!liveWalletRef)
3239
+ err('--wallet required. Use a vault wallet name/id or `trading:N`.', EXIT.BAD_INPUT);
3240
+ const budgetRaw = flags.budget;
3241
+ if (!budgetRaw)
3242
+ err('--budget required, e.g. --budget 1usdc (caps total trade exposure).', EXIT.BAD_INPUT);
3243
+ const budgetMatch = (budgetRaw ?? '').trim().match(/^(\d+(?:\.\d+)?)\s*usdc$/i);
3244
+ if (!budgetMatch)
3245
+ err(`--budget must be in USDC (e.g. "0.5usdc", "1usdc"), got "${budgetRaw}".`, EXIT.BAD_INPUT);
3246
+ const budgetUsdc = Number(budgetMatch[1]);
3247
+ const liveChainFlag = (flags.chain || 'all').toLowerCase();
3248
+ if (liveChainFlag !== 'solana' && liveChainFlag !== 'base' && liveChainFlag !== 'all') {
3249
+ err(`--chain must be solana, base, or all (got ${liveChainFlag})`, EXIT.BAD_INPUT);
3250
+ }
3251
+ const { runWalletLiveTest } = await import('./wallet-live-test.js');
3252
+ let report;
3253
+ try {
3254
+ report = await runWalletLiveTest({
3255
+ walletRef: liveWalletRef,
3256
+ budgetUsdc,
3257
+ chain: liveChainFlag,
3258
+ });
3259
+ }
3260
+ catch (e) {
3261
+ err(e.message || 'live-test failed', EXIT.GENERAL);
3262
+ }
3263
+ if (AGENT_MODE) {
3264
+ print(report);
3265
+ if (!report.safeForAutonomousTrading)
3266
+ process.exit(EXIT.GENERAL);
3267
+ break;
3268
+ }
3269
+ console.log();
3270
+ section('Wallet live-test');
3271
+ kv('Wallet', report.wallet);
3272
+ kv('Budget', `${report.budgetUsdc.toFixed(6)} USDC (per leg ${report.perLegUsdc.toFixed(6)} USDC)`);
3273
+ kv('Verdict', report.safeForAutonomousTrading
3274
+ ? `${t.success}safe for autonomous trading${t.reset}`
3275
+ : `${t.error}NOT safe — see failing legs${t.reset}`);
3276
+ kv('Total realized', `${report.totalRealizedUsdc >= 0 ? '+' : ''}${report.totalRealizedUsdc.toFixed(6)} USDC`);
3277
+ kv('Open positions after', String(report.openPositionsAfter));
3278
+ console.log();
3279
+ section('Legs');
3280
+ for (const leg of report.legs) {
3281
+ const dot = leg.status === 'pass' ? `${t.success}✓${t.reset}`
3282
+ : leg.status === 'skip' ? `${t.muted}-${t.reset}`
3283
+ : `${t.error}✗${t.reset}`;
3284
+ const realizedStr = leg.realized
3285
+ ? `; realized ${leg.realized.amount >= 0 ? '+' : ''}${leg.realized.amount.toFixed(6)} ${leg.realized.asset}`
3286
+ : '';
3287
+ const txStr = leg.txHash ? ` (${leg.txHash.slice(0, 10)}…)` : '';
3288
+ const detail = [leg.message, leg.durationMs !== undefined ? `${leg.durationMs}ms` : null].filter(Boolean).join(' — ');
3289
+ console.log(` ${dot} ${leg.chain}/${leg.name}${txStr}${realizedStr}${detail ? `: ${detail}` : ''}`);
3290
+ }
3291
+ console.log();
3292
+ if (!report.safeForAutonomousTrading)
3293
+ process.exit(EXIT.GENERAL);
3294
+ break;
3295
+ }
3179
3296
  case 'daemon': {
3180
3297
  const sub = positional[0];
3181
3298
  if (!sub)