pandora-cli-skills 1.1.29 → 1.1.30

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.
Files changed (38) hide show
  1. package/SKILL.md +27 -1
  2. package/cli/lib/arbitrage_service.cjs +1 -12
  3. package/cli/lib/autopilot_service.cjs +1 -11
  4. package/cli/lib/history_service.cjs +1 -6
  5. package/cli/lib/leaderboard_service.cjs +1 -6
  6. package/cli/lib/market_admin_service.cjs +45 -5
  7. package/cli/lib/mirror_command_service.cjs +133 -0
  8. package/cli/lib/mirror_econ_service.cjs +1 -10
  9. package/cli/lib/mirror_handlers/browse.cjs +40 -0
  10. package/cli/lib/mirror_handlers/close.cjs +25 -0
  11. package/cli/lib/mirror_handlers/deploy.cjs +47 -0
  12. package/cli/lib/mirror_handlers/go.cjs +270 -0
  13. package/cli/lib/mirror_handlers/hedge_calc.cjs +110 -0
  14. package/cli/lib/mirror_handlers/lp_explain.cjs +31 -0
  15. package/cli/lib/mirror_handlers/plan.cjs +40 -0
  16. package/cli/lib/mirror_handlers/simulate.cjs +31 -0
  17. package/cli/lib/mirror_handlers/status.cjs +159 -0
  18. package/cli/lib/mirror_handlers/sync.cjs +293 -0
  19. package/cli/lib/mirror_handlers/verify.cjs +67 -0
  20. package/cli/lib/mirror_service.cjs +2 -4
  21. package/cli/lib/mirror_sizing_service.cjs +1 -16
  22. package/cli/lib/mirror_sync_service.cjs +110 -40
  23. package/cli/lib/mirror_verify_service.cjs +1 -6
  24. package/cli/lib/pandora_deploy_service.cjs +6 -8
  25. package/cli/lib/parsers/autopilot_flags.cjs +256 -0
  26. package/cli/lib/parsers/mirror_deploy_flags.cjs +239 -0
  27. package/cli/lib/parsers/mirror_go_flags.cjs +341 -0
  28. package/cli/lib/parsers/mirror_sync_flags.cjs +444 -0
  29. package/cli/lib/parsers/watch_flags.cjs +211 -0
  30. package/cli/lib/polymarket_adapter.cjs +1 -6
  31. package/cli/lib/polymarket_trade_adapter.cjs +98 -6
  32. package/cli/lib/shared/constants.cjs +17 -0
  33. package/cli/lib/shared/utils.cjs +27 -0
  34. package/cli/lib/similarity_service.cjs +1 -12
  35. package/cli/pandora.cjs +4352 -6383
  36. package/package.json +5 -2
  37. package/tests/smoke/consumer-json-smoke.cjs +217 -0
  38. package/tests/smoke/pack-install-smoke.cjs +302 -0
package/SKILL.md CHANGED
@@ -110,7 +110,7 @@ lp-explain --liquidity-usdc <n> [--source-yes-pct <0-100>] [--distribution-yes <
110
110
  hedge-calc [--reserve-yes-usdc <n> --reserve-no-usdc <n>] [--excess-yes-usdc <n>] [--excess-no-usdc <n>] [--polymarket-yes-pct <0-100>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--volume-scenarios <csv>] [--pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug>] [--trust-deploy] [--manifest-file <path>]
111
111
  simulate --liquidity-usdc <n> [--source-yes-pct <0-100>] [--target-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>] [--fee-tier 500|3000|10000] [--volume-scenarios <csv>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--polymarket-yes-pct <0-100>]
112
112
  go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]
113
- sync run|once|start|stop|status --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--trust-deploy] [--manifest-file <path>] [--skip-gate] [--daemon] [--stream|--no-stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--depth-slippage-bps <n>] [--min-time-to-close-sec <n>] [--iterations <n>] [--state-file <path>] [--kill-switch-file <path>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--webhook-url <url>] [--telegram-bot-token <token>] [--discord-webhook-url <url>]
113
+ sync run|once|start --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--trust-deploy] [--manifest-file <path>] [--skip-gate] [--daemon] [--stream|--no-stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--depth-slippage-bps <n>] [--min-time-to-close-sec <n>] [--iterations <n>] [--state-file <path>] [--kill-switch-file <path>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--webhook-url <url>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>]
114
114
  status --state-file <path>|--strategy-hash <hash> [--with-live] [--pandora-market-address <address>|--market-address <address>] [--polymarket-market-id <id>|--polymarket-slug <slug>]
115
115
  close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute
116
116
  ```
@@ -257,6 +257,7 @@ pandora --output json suggest --wallet <0x...> --risk medium --budget 50 --inclu
257
257
  - Polymarket trading collateral is Polygon USDC.e on the proxy wallet.
258
258
  - rebalance sizing is pool-aware and bounded by `--max-rebalance-usdc`.
259
259
  - endpoint resilience: Polymarket snapshots are cached under `~/.pandora/polymarket`; paper/read flows can reuse cache during outages, while live sync blocks cached sources.
260
+ - daemon verification note: `mirror sync start` launches a child process that executes `cli/pandora.cjs` and enters the same CLI `main()` handlers, so crash/error handling remains covered by the normal command dispatcher + structured error envelopes.
260
261
  - `mirror status`: local mirror state inspection with optional live market diagnostics (`--with-live`).
261
262
  - `--with-live` uses the same `POLYMARKET_*` env keys for optional position visibility (YES/NO balances, open orders count, estimated value) and adds `netDeltaApprox` / `pnlApprox`.
262
263
  - missing credentials or unavailable position endpoints do not hard-fail status; diagnostics are returned with null position fields.
@@ -346,6 +347,25 @@ Error envelope:
346
347
  ## Additional JSON response shapes
347
348
  - `doctor`:
348
349
  - `{ ok: true, command: "doctor", data: { schemaVersion, generatedAt, env, rpc, codeChecks, polymarket, summary } }`
350
+ - `history`:
351
+ - `{ ok: true, command: "history", data: { schemaVersion, generatedAt, indexerUrl, wallet, chainId, filters, pagination, pageInfo, summary, count, items[] } }`
352
+ - each `items[]` row includes `entryPriceUsdcPerToken`, `markPriceUsdcPerToken`, `currentValueUsdc`, `pnlUnrealizedApproxUsdc`, `pnlRealizedApproxUsdc`, `status`, and `diagnostics[]`.
353
+ - `export`:
354
+ - `{ ok: true, command: "export", data: { schemaVersion, generatedAt, format: "csv"|"json", wallet, chainId, count, filters, columns[], outPath, rows[], content } }`
355
+ - `content` is the deterministic serialized payload written to `outPath` when `--out` is provided.
356
+ - `arbitrage`:
357
+ - `{ ok: true, command: "arbitrage", data: { schemaVersion, generatedAt, indexerUrl, venues, filters, count, opportunities[], diagnostics[] } }`
358
+ - `autopilot`:
359
+ - `{ ok: true, command: "autopilot", data: { schemaVersion, generatedAt, strategyHash, mode, executeLive, stateFile, killSwitchFile, iterationsRequested, iterationsCompleted, stoppedReason?, parameters: { marketAddress, side, amountUsdc, triggerYesBelow?, triggerYesAbove?, intervalMs, cooldownMs, maxAmountUsdc?, maxOpenExposureUsdc, dailySpendCapUsdc, maxTradesPerDay }, state, actionCount, actions[], snapshots[], webhookReports[] } }`
360
+ - `mirror browse`:
361
+ - `{ ok: true, command: "mirror.browse", data: { schemaVersion, generatedAt, source, gammaApiError, filters, count, items[], diagnostics[] } }`
362
+ - each candidate row can include `existingMirror: { marketAddress, similarity } | null`.
363
+ - `mirror go`:
364
+ - `{ ok: true, command: "mirror.go", data: { schemaVersion, generatedAt, mode, plan, deploy, verify, sync, polymarketPreflight, suggestedSyncCommand, trustManifest, diagnostics[] } }`
365
+ - `plan` is the same payload shape as `mirror.plan`; `deploy` is the same payload shape as `mirror.deploy`; `sync` is null unless `--auto-sync` is used.
366
+ - close peers:
367
+ - `mirror close`: `{ ok: true, command: "mirror.close", data: { schemaVersion, generatedAt, mode, pandoraMarketAddress, polymarketMarketId?, polymarketSlug?, steps[], diagnostics[] } }`
368
+ - `mirror sync start|status|stop`: `{ ok: true, command: "mirror.sync.start|mirror.sync.status|mirror.sync.stop", data: { strategyHash, pid?, pidFile, logFile?, alive, status, metadata? } }`
349
369
  - `resolve`:
350
370
  - dry-run: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "dry-run", txPlan } }`
351
371
  - execute: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "execute", tx } }`
@@ -354,6 +374,12 @@ Error envelope:
354
374
  - `lp positions`: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "positions", mode: "read", wallet, count, items[] } }`
355
375
  - `polymarket`:
356
376
  - `check|preflight|approve|trade` all return `{ ok: true, command, data }` with `schemaVersion` and `generatedAt`; execute paths include `result`/`tx` blocks.
377
+ - `leaderboard`:
378
+ - `{ ok: true, command: "leaderboard", data: { schemaVersion, generatedAt, indexerUrl, metric, limit, minTrades, count, items[], diagnostics[] } }`
379
+ - `analyze`:
380
+ - `{ ok: true, command: "analyze", data: { schemaVersion, generatedAt, indexerUrl, marketAddress, provider, model, market, quote, result } }`
381
+ - `suggest`:
382
+ - `{ ok: true, command: "suggest", data: { schemaVersion, generatedAt, wallet, risk, budget, count, items[], indexerUrl, includeVenues, historySummary, arbitrageCount } }`
357
383
 
358
384
  ## Pandora mainnet deployment reference
359
385
  - PredictionOracle (Factory): `0x259308E7d8557e4Ba192De1aB8Cf7e0E21896442`
@@ -5,21 +5,10 @@ const {
5
5
  questionSimilarityBreakdown,
6
6
  questionSimilarity,
7
7
  } = require('./similarity_service.cjs');
8
+ const { toNumber, round } = require('./shared/utils.cjs');
8
9
 
9
10
  const ARBITRAGE_SCHEMA_VERSION = '1.1.0';
10
11
 
11
- function toNumber(value) {
12
- const numeric = Number(value);
13
- if (!Number.isFinite(numeric)) return null;
14
- return numeric;
15
- }
16
-
17
- function round(value, decimals = 6) {
18
- if (!Number.isFinite(value)) return null;
19
- const factor = 10 ** decimals;
20
- return Math.round(value * factor) / factor;
21
- }
22
-
23
12
  function toUsdc(raw) {
24
13
  const numeric = toNumber(raw);
25
14
  if (numeric === null) return null;
@@ -9,17 +9,7 @@ const {
9
9
  pruneIdempotencyKeys,
10
10
  resetDailyCountersIfNeeded,
11
11
  } = require('./autopilot_state_store.cjs');
12
-
13
- function toNumber(value) {
14
- const numeric = Number(value);
15
- if (!Number.isFinite(numeric)) return null;
16
- return numeric;
17
- }
18
-
19
- function sleepMs(ms) {
20
- if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve();
21
- return new Promise((resolve) => setTimeout(resolve, ms));
22
- }
12
+ const { toNumber, sleepMs } = require('./shared/utils.cjs');
23
13
 
24
14
  function buildIdempotencyKey(options, quote, nowMs) {
25
15
  const yesPct = toNumber(quote && quote.odds && quote.odds.yesPct);
@@ -1,4 +1,5 @@
1
1
  const { createIndexerClient } = require('./indexer_client.cjs');
2
+ const { round } = require('./shared/utils.cjs');
2
3
 
3
4
  const USDC_DECIMALS = 6;
4
5
  const HISTORY_SCHEMA_VERSION = '1.0.0';
@@ -10,12 +11,6 @@ function toNumber(value) {
10
11
  return numeric;
11
12
  }
12
13
 
13
- function round(value, decimals = 6) {
14
- if (!Number.isFinite(value)) return null;
15
- const factor = 10 ** decimals;
16
- return Math.round(value * factor) / factor;
17
- }
18
-
19
14
  function toUsdc(raw) {
20
15
  const numeric = toNumber(raw);
21
16
  if (numeric === null) return null;
@@ -1,13 +1,8 @@
1
1
  const { createIndexerClient } = require('./indexer_client.cjs');
2
+ const { toNumber } = require('./shared/utils.cjs');
2
3
 
3
4
  const LEADERBOARD_SCHEMA_VERSION = '1.0.0';
4
5
 
5
- function toNumber(value) {
6
- const numeric = Number(value);
7
- if (!Number.isFinite(numeric)) return null;
8
- return numeric;
9
- }
10
-
11
6
  function toCount(value) {
12
7
  const numeric = toNumber(value);
13
8
  if (numeric === null || numeric < 0) return 0;
@@ -1,8 +1,4 @@
1
- const DEFAULT_INDEXER_URL = 'https://pandoraindexer.up.railway.app/';
2
- const DEFAULT_RPC_BY_CHAIN_ID = {
3
- 1: 'https://ethereum.publicnode.com',
4
- 146: 'https://rpc.soniclabs.com',
5
- };
1
+ const { DEFAULT_INDEXER_URL, DEFAULT_RPC_BY_CHAIN_ID } = require('./shared/constants.cjs');
6
2
 
7
3
  const ERC20_ABI = [
8
4
  {
@@ -180,6 +176,12 @@ function normalizeTimeoutMs(value) {
180
176
  return Math.trunc(numeric);
181
177
  }
182
178
 
179
+ /**
180
+ * Convert numeric-like input into a finite number.
181
+ * Used by LP/resolve input normalization paths.
182
+ * @param {*} value
183
+ * @returns {number|null}
184
+ */
183
185
  function toFiniteNumber(value) {
184
186
  const numeric = Number(value);
185
187
  if (!Number.isFinite(numeric)) return null;
@@ -313,6 +315,14 @@ async function readDecimals(publicClient, address, fallback = 18) {
313
315
  }
314
316
  }
315
317
 
318
+ /**
319
+ * Read optional `calcRemoveLiquidity` preview outputs.
320
+ * Returned values are raw on-chain integers as decimal strings.
321
+ * @param {object} publicClient
322
+ * @param {string} marketAddress
323
+ * @param {bigint} sharesRaw
324
+ * @returns {Promise<{collateralOutRaw: string|null, yesOutRaw: string|null, noOutRaw: string|null}|null>}
325
+ */
316
326
  async function readCalcRemoveLiquidity(publicClient, marketAddress, sharesRaw) {
317
327
  for (const abi of CALC_REMOVE_LIQUIDITY_ABI_CANDIDATES) {
318
328
  try {
@@ -395,6 +405,11 @@ async function discoverLiquidityMarkets(indexerUrl, wallet, chainId, timeoutMs)
395
405
  return Array.from(new Set(addresses));
396
406
  }
397
407
 
408
+ /**
409
+ * Resolve a market outcome by calling `resolveMarket(bool)`.
410
+ * @param {object} [options]
411
+ * @returns {Promise<object>}
412
+ */
398
413
  async function runResolve(options = {}) {
399
414
  const schemaVersion = '1.0.0';
400
415
  const generatedAt = new Date().toISOString();
@@ -468,6 +483,13 @@ async function runResolve(options = {}) {
468
483
  }
469
484
  }
470
485
 
486
+ /**
487
+ * Read LP balances and remove-liquidity previews for a wallet.
488
+ * LP/share values are raw and human-readable decimal strings in the payload.
489
+ * `collateralOutUsdc` is decimal USDC (6 decimals).
490
+ * @param {object} [options]
491
+ * @returns {Promise<object>}
492
+ */
471
493
  async function runLpPositions(options = {}) {
472
494
  const schemaVersion = '1.0.0';
473
495
  const generatedAt = new Date().toISOString();
@@ -570,6 +592,12 @@ async function runLpPositions(options = {}) {
570
592
  };
571
593
  }
572
594
 
595
+ /**
596
+ * Build or execute `addLiquidity`.
597
+ * `amountUsdc` is decimal USDC input; `collateralAmountRaw` is 6-decimal raw units.
598
+ * @param {object} [options]
599
+ * @returns {Promise<object>}
600
+ */
573
601
  async function runLpAdd(options = {}) {
574
602
  const schemaVersion = '1.0.0';
575
603
  const generatedAt = new Date().toISOString();
@@ -719,6 +747,12 @@ async function runLpAdd(options = {}) {
719
747
  }
720
748
  }
721
749
 
750
+ /**
751
+ * Build or execute `removeLiquidity`.
752
+ * `lpTokens` is decimal LP token amount; `sharesToBurnRaw` is on-chain raw units.
753
+ * @param {object} [options]
754
+ * @returns {Promise<object>}
755
+ */
722
756
  async function runLpRemove(options = {}) {
723
757
  const schemaVersion = '1.0.0';
724
758
  const generatedAt = new Date().toISOString();
@@ -825,6 +859,11 @@ async function runLpRemove(options = {}) {
825
859
  }
826
860
  }
827
861
 
862
+ /**
863
+ * Dispatch LP admin action (`positions`, `add`, `remove`).
864
+ * @param {object} [options]
865
+ * @returns {Promise<object>}
866
+ */
828
867
  async function runLp(options = {}) {
829
868
  if (options.action === 'positions') {
830
869
  return runLpPositions(options);
@@ -838,6 +877,7 @@ async function runLp(options = {}) {
838
877
  throw createServiceError('INVALID_ARGS', 'lp requires action add|remove|positions.');
839
878
  }
840
879
 
880
+ /** Public market admin API consumed by CLI `resolve` and `lp` commands. */
841
881
  module.exports = {
842
882
  runResolve,
843
883
  runLp,
@@ -0,0 +1,133 @@
1
+ const MIRROR_GO_USAGE =
2
+ 'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]';
3
+
4
+ const MIRROR_SYNC_USAGE =
5
+ 'pandora [--output table|json] mirror sync run|once|start --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--trust-deploy] [--manifest-file <path>] [--skip-gate] [--daemon] [--stream|--no-stream] [--interval-ms <ms>] [--drift-trigger-bps <n>] [--hedge-trigger-usdc <n>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--depth-slippage-bps <n>] [--min-time-to-close-sec <n>] [--iterations <n>] [--state-file <path>] [--kill-switch-file <path>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--webhook-url <url>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>]';
6
+
7
+ const INVALID_SUBCOMMAND_MESSAGE =
8
+ 'mirror requires subcommand: browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close';
9
+
10
+ function createRunMirrorCommand(deps) {
11
+ const {
12
+ CliError,
13
+ emitSuccess,
14
+ commandHelpPayload,
15
+ parseIndexerSharedFlags,
16
+ } = deps;
17
+
18
+ const handlerLoaders = {
19
+ browse: () => require('./mirror_handlers/browse.cjs'),
20
+ plan: () => require('./mirror_handlers/plan.cjs'),
21
+ deploy: () => require('./mirror_handlers/deploy.cjs'),
22
+ verify: () => require('./mirror_handlers/verify.cjs'),
23
+ 'lp-explain': () => require('./mirror_handlers/lp_explain.cjs'),
24
+ 'hedge-calc': () => require('./mirror_handlers/hedge_calc.cjs'),
25
+ simulate: () => require('./mirror_handlers/simulate.cjs'),
26
+ go: () => require('./mirror_handlers/go.cjs'),
27
+ sync: () => require('./mirror_handlers/sync.cjs'),
28
+ status: () => require('./mirror_handlers/status.cjs'),
29
+ close: () => require('./mirror_handlers/close.cjs'),
30
+ };
31
+ const handlerCache = new Map();
32
+
33
+ function getHandler(action) {
34
+ if (handlerCache.has(action)) {
35
+ return handlerCache.get(action);
36
+ }
37
+
38
+ const load = handlerLoaders[action];
39
+ if (!load) {
40
+ return null;
41
+ }
42
+
43
+ const loaded = load();
44
+ const handler =
45
+ typeof loaded === 'function'
46
+ ? loaded
47
+ : loaded && typeof loaded.handle === 'function'
48
+ ? loaded.handle
49
+ : null;
50
+
51
+ if (!handler) {
52
+ throw new Error(`Invalid mirror handler module for action: ${action}`);
53
+ }
54
+
55
+ handlerCache.set(action, handler);
56
+ return handler;
57
+ }
58
+
59
+ return async function runMirrorCommand(args, context) {
60
+ const action = args[0];
61
+ const actionArgs = args.slice(1);
62
+
63
+ if (!action || action === '--help' || action === '-h') {
64
+ if (context.outputMode === 'json') {
65
+ emitSuccess(
66
+ context.outputMode,
67
+ 'mirror.help',
68
+ commandHelpPayload(
69
+ 'pandora [--output table|json] mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close ...',
70
+ ),
71
+ );
72
+ } else {
73
+ console.log(
74
+ 'Usage: pandora [--output table|json] mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close ...',
75
+ );
76
+ console.log('');
77
+ console.log('Subcommands:');
78
+ console.log(
79
+ ' browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>] [--chain-id <id>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
80
+ );
81
+ console.log(
82
+ ' plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
83
+ );
84
+ console.log(
85
+ ' deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--oracle <address>] [--factory <address>] [--usdc <address>] [--distribution-yes <parts>] [--distribution-no <parts>] [--sources <url...>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
86
+ );
87
+ console.log(
88
+ ' verify --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--trust-deploy] [--manifest-file <path>] [--include-similarity] [--with-rules] [--allow-rule-mismatch] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
89
+ );
90
+ console.log(
91
+ ' lp-explain --liquidity-usdc <n> [--source-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>]',
92
+ );
93
+ console.log(
94
+ ' hedge-calc [--reserve-yes-usdc <n> --reserve-no-usdc <n>] [--excess-yes-usdc <n>] [--excess-no-usdc <n>] [--polymarket-yes-pct <0-100>] [--hedge-ratio <n>] [--hedge-cost-bps <n>] [--volume-scenarios <csv>] [--pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug>]',
95
+ );
96
+ console.log(
97
+ ' simulate --liquidity-usdc <n> [--source-yes-pct <0-100>] [--target-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>] [--fee-tier 500|3000|10000] [--volume-scenarios <csv>] [--hedge-ratio <n>] [--polymarket-yes-pct <0-100>]',
98
+ );
99
+ console.log(
100
+ ` go ${MIRROR_GO_USAGE.replace('pandora [--output table|json] mirror go ', '')}`,
101
+ );
102
+ console.log(
103
+ ` sync ${MIRROR_SYNC_USAGE.replace('pandora [--output table|json] mirror sync ', '')}`,
104
+ );
105
+ console.log(' stop|status selector: --pid-file <path>|--strategy-hash <hash>');
106
+ console.log(' status --state-file <path>|--strategy-hash <hash> [--with-live] [--trust-deploy]');
107
+ console.log(' close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute');
108
+ }
109
+ return;
110
+ }
111
+
112
+ const handler = getHandler(action);
113
+ if (!handler) {
114
+ throw new CliError('INVALID_ARGS', INVALID_SUBCOMMAND_MESSAGE);
115
+ }
116
+ const shared = parseIndexerSharedFlags(actionArgs);
117
+
118
+ return handler({
119
+ actionArgs: shared.rest,
120
+ shared,
121
+ context,
122
+ deps,
123
+ mirrorGoUsage: MIRROR_GO_USAGE,
124
+ mirrorSyncUsage: MIRROR_SYNC_USAGE,
125
+ });
126
+ };
127
+ }
128
+
129
+ module.exports = {
130
+ MIRROR_GO_USAGE,
131
+ MIRROR_SYNC_USAGE,
132
+ createRunMirrorCommand,
133
+ };
@@ -1,4 +1,5 @@
1
1
  const { computeDistributionHint, normalizeProbability } = require('./mirror_sizing_service.cjs');
2
+ const { round, clamp } = require('./shared/utils.cjs');
2
3
 
3
4
  const MIRROR_LP_EXPLAIN_SCHEMA_VERSION = '1.0.0';
4
5
  const MIRROR_HEDGE_CALC_SCHEMA_VERSION = '1.0.0';
@@ -13,16 +14,6 @@ function toNumber(value) {
13
14
  return numeric;
14
15
  }
15
16
 
16
- function round(value, decimals = 6) {
17
- if (!Number.isFinite(value)) return null;
18
- const factor = 10 ** decimals;
19
- return Math.round(value * factor) / factor;
20
- }
21
-
22
- function clamp(value, min, max) {
23
- return Math.max(min, Math.min(max, value));
24
- }
25
-
26
17
  function toRawUsdc(usdc) {
27
18
  const numeric = toNumber(usdc);
28
19
  if (numeric === null) return null;
@@ -0,0 +1,40 @@
1
+ module.exports = async function handleMirrorBrowse({ shared, context, deps }) {
2
+ const {
3
+ includesHelpFlag,
4
+ emitSuccess,
5
+ commandHelpPayload,
6
+ maybeLoadIndexerEnv,
7
+ resolveIndexerUrl,
8
+ parseMirrorBrowseFlags,
9
+ browseMirrorMarkets,
10
+ renderMirrorBrowseTable,
11
+ } = deps;
12
+
13
+ if (includesHelpFlag(shared.rest)) {
14
+ if (context.outputMode === 'json') {
15
+ emitSuccess(
16
+ context.outputMode,
17
+ 'mirror.browse.help',
18
+ commandHelpPayload(
19
+ 'pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]',
20
+ ),
21
+ );
22
+ } else {
23
+ console.log(
24
+ 'Usage: pandora [--output table|json] mirror browse [--min-yes-pct <n>] [--max-yes-pct <n>] [--min-volume-24h <n>] [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]',
25
+ );
26
+ }
27
+ return;
28
+ }
29
+
30
+ maybeLoadIndexerEnv(shared);
31
+ const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
32
+ const options = parseMirrorBrowseFlags(shared.rest);
33
+ const payload = await browseMirrorMarkets({
34
+ ...options,
35
+ indexerUrl,
36
+ timeoutMs: shared.timeoutMs,
37
+ });
38
+
39
+ emitSuccess(context.outputMode, 'mirror.browse', payload, renderMirrorBrowseTable);
40
+ };
@@ -0,0 +1,25 @@
1
+ module.exports = async function handleMirrorClose({ shared, context, deps }) {
2
+ const {
3
+ includesHelpFlag,
4
+ emitSuccess,
5
+ commandHelpPayload,
6
+ parseMirrorCloseFlags,
7
+ buildMirrorClosePlan,
8
+ renderMirrorCloseTable,
9
+ } = deps;
10
+
11
+ if (includesHelpFlag(shared.rest)) {
12
+ const usage =
13
+ 'pandora [--output table|json] mirror close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute';
14
+ if (context.outputMode === 'json') {
15
+ emitSuccess(context.outputMode, 'mirror.close.help', commandHelpPayload(usage));
16
+ } else {
17
+ console.log(`Usage: ${usage}`);
18
+ }
19
+ return;
20
+ }
21
+
22
+ const options = parseMirrorCloseFlags(shared.rest);
23
+ const payload = buildMirrorClosePlan(options);
24
+ emitSuccess(context.outputMode, 'mirror.close', payload, renderMirrorCloseTable);
25
+ };
@@ -0,0 +1,47 @@
1
+ module.exports = async function handleMirrorDeploy({ shared, context, deps }) {
2
+ const {
3
+ includesHelpFlag,
4
+ emitSuccess,
5
+ commandHelpPayload,
6
+ maybeLoadTradeEnv,
7
+ resolveIndexerUrl,
8
+ parseMirrorDeployFlags,
9
+ deployMirror,
10
+ coerceMirrorServiceError,
11
+ renderMirrorDeployTable,
12
+ } = deps;
13
+
14
+ if (includesHelpFlag(shared.rest)) {
15
+ if (context.outputMode === 'json') {
16
+ emitSuccess(
17
+ context.outputMode,
18
+ 'mirror.deploy.help',
19
+ commandHelpPayload(
20
+ 'pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
21
+ ),
22
+ );
23
+ } else {
24
+ console.log(
25
+ 'Usage: pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier 500|3000|10000] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
26
+ );
27
+ }
28
+ return;
29
+ }
30
+
31
+ maybeLoadTradeEnv(shared);
32
+ const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
33
+ const options = parseMirrorDeployFlags(shared.rest);
34
+ let payload;
35
+ try {
36
+ payload = await deployMirror({
37
+ ...options,
38
+ indexerUrl,
39
+ timeoutMs: shared.timeoutMs,
40
+ execute: options.execute,
41
+ });
42
+ } catch (err) {
43
+ throw coerceMirrorServiceError(err, 'MIRROR_DEPLOY_FAILED');
44
+ }
45
+
46
+ emitSuccess(context.outputMode, 'mirror.deploy', payload, renderMirrorDeployTable);
47
+ };