pandora-cli-skills 1.1.27 → 1.1.28
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_FOR_SHARING.md +22 -3
- package/SKILL.md +32 -9
- package/cli/lib/cli_output_service.cjs +1 -6
- package/cli/lib/market_admin_service.cjs +0 -23
- package/cli/lib/mirror_service.cjs +0 -4
- package/cli/lib/scan_command_service.cjs +52 -0
- package/cli/pandora.cjs +128 -48
- package/package.json +1 -1
package/README_FOR_SHARING.md
CHANGED
|
@@ -55,7 +55,7 @@ Prerequisite: Node.js `>=18`.
|
|
|
55
55
|
- Global machine-readable output:
|
|
56
56
|
- `pandora --output json doctor`
|
|
57
57
|
- `pandora --output table polls list --limit 10`
|
|
58
|
-
- `--output json` is for
|
|
58
|
+
- `--output json` is supported for all commands except `launch`/`clone-bet`; those stream script output directly.
|
|
59
59
|
- Guided setup:
|
|
60
60
|
- `pandora setup`
|
|
61
61
|
- `pandora setup --check-usdc-code`
|
|
@@ -143,7 +143,8 @@ Prerequisite: Node.js `>=18`.
|
|
|
143
143
|
- each scan item includes at minimum `id`, `chainId`, `marketType`, `question`, `marketCloseTimestamp`, and `odds`.
|
|
144
144
|
|
|
145
145
|
## Phase 1 limitations
|
|
146
|
-
-
|
|
146
|
+
- `scan` always includes odds; `--with-odds` is accepted for backward compatibility.
|
|
147
|
+
- `--expand` is supported on both `markets list` and `scan`.
|
|
147
148
|
- `--active|--resolved|--expiring-soon` are client-side filters on fetched list pages.
|
|
148
149
|
- Odds are indexer-derived and read-only; missing upstream liquidity/price fields can produce partial odds in some environments.
|
|
149
150
|
- `scan` is indexer-backed only (no direct chain reads); freshness depends on indexer sync state.
|
|
@@ -215,7 +216,7 @@ Prerequisite: Node.js `>=18`.
|
|
|
215
216
|
- `mirror sync`:
|
|
216
217
|
- envelope is `ok=true`, `command="mirror.sync"`, with `data.strategyHash`, `data.stateFile`, `data.parameters`, `data.snapshots[]`, and `data.actions[]`.
|
|
217
218
|
- hedge controls: `--hedge-trigger-usdc`, `--max-hedge-usdc`, `--hedge-ratio <n>` (default `1`), and `--no-hedge` to disable hedge execution while keeping drift rebalancing active.
|
|
218
|
-
- trust controls: `--trust-deploy
|
|
219
|
+
- trust controls: `--trust-deploy`.
|
|
219
220
|
- streaming: `--stream` emits per-tick logs for `mirror sync run` (table mode streams by default).
|
|
220
221
|
- rebalance sizing is pool-aware: drift notional scales with `reserveYes + reserveNo`, then bounded by `--max-rebalance-usdc`.
|
|
221
222
|
- Polymarket resilience: when Polymarket endpoints are unreachable, cached snapshots under `~/.pandora/polymarket` are reused for read paths; live sync blocks execution if source data is cached/stale.
|
|
@@ -239,6 +240,24 @@ Prerequisite: Node.js `>=18`.
|
|
|
239
240
|
## Resolve/LP commands
|
|
240
241
|
- `resolve` and `lp` are active command paths with strict flag validation, runtime preflight checks, and decoded on-chain revert reporting.
|
|
241
242
|
|
|
243
|
+
## Compatibility aliases
|
|
244
|
+
- `--env-file` = `--dotenv-path`
|
|
245
|
+
- `--no-env-file` = `--skip-dotenv`
|
|
246
|
+
- `--amount` = `--amount-usdc` (trade/watch/autopilot paths)
|
|
247
|
+
- `--market-id` = `--condition-id` (polymarket trade)
|
|
248
|
+
- `--force-gate` = `--skip-gate` (deprecated; prefer `--skip-gate`)
|
|
249
|
+
|
|
250
|
+
## Additional JSON response shapes
|
|
251
|
+
- `doctor`: `{ ok: true, command: "doctor", data: { schemaVersion, generatedAt, env, rpc, codeChecks, polymarket, summary } }`
|
|
252
|
+
- `resolve`:
|
|
253
|
+
- dry-run: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "dry-run", txPlan } }`
|
|
254
|
+
- execute: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "execute", tx } }`
|
|
255
|
+
- `lp`:
|
|
256
|
+
- add/remove: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "add"|"remove", mode, txPlan, tx? } }`
|
|
257
|
+
- positions: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "positions", mode: "read", wallet, count, items } }`
|
|
258
|
+
- `polymarket` (`check|preflight|approve|trade`):
|
|
259
|
+
- `{ ok: true, command, data: { schemaVersion, generatedAt, ... } }`
|
|
260
|
+
|
|
242
261
|
### Resolve command
|
|
243
262
|
- Usage:
|
|
244
263
|
- `pandora [--output table|json] resolve --poll-address <address> --answer yes|no --reason <text> --dry-run|--execute [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]`
|
package/SKILL.md
CHANGED
|
@@ -36,7 +36,7 @@ npm link
|
|
|
36
36
|
|
|
37
37
|
## CLI ergonomics
|
|
38
38
|
- Global output mode: `--output table|json` (default `table`)
|
|
39
|
-
- `--output json`
|
|
39
|
+
- `--output json` is supported for all commands except `launch`/`clone-bet`; those stream script output directly.
|
|
40
40
|
- Guided setup command: `pandora setup`
|
|
41
41
|
- Phase 1 discovery command: `pandora scan`
|
|
42
42
|
- Phase 1 lifecycle filters: `pandora markets list --active|--resolved|--expiring-soon`
|
|
@@ -67,6 +67,7 @@ This section mirrors live CLI help output so agent runs can rely on one source o
|
|
|
67
67
|
|
|
68
68
|
```text
|
|
69
69
|
pandora [--output table|json] --version
|
|
70
|
+
pandora [--output table|json] help
|
|
70
71
|
pandora [--output table|json] init-env [--force] [--dotenv-path <path>] [--example <path>]
|
|
71
72
|
pandora [--output table|json] doctor [--dotenv-path <path>] [--skip-dotenv] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
|
|
72
73
|
pandora [--output table|json] setup [--force] [--dotenv-path <path>] [--example <path>] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
|
|
@@ -79,7 +80,7 @@ pandora [--output table|json] events get --id <id> [--type all|liquidity|oracle-
|
|
|
79
80
|
pandora [--output table|json] positions list [--wallet <address>] [--market-address <address>] [--chain-id <id>] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--where-json <json>]
|
|
80
81
|
pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]
|
|
81
82
|
pandora [--output table|json] watch [--wallet <address>] [--market-address <address>] [--side yes|no] [--amount-usdc <amount>] [--iterations <n>] [--interval-ms <ms>] [--chain-id <id>] [--include-events|--no-events] [--yes-pct <0-100>] [--alert-yes-below <0-100>] [--alert-yes-above <0-100>] [--alert-net-liquidity-below <amount>] [--alert-net-liquidity-above <amount>] [--fail-on-alert]
|
|
82
|
-
pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--
|
|
83
|
+
pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--active|--resolved|--expiring-soon] [--expiring-hours <n>] [--expand]
|
|
83
84
|
pandora [--output table|json] quote [--indexer-url <url>] [--timeout-ms <ms>] --market-address <address> --side yes|no --amount-usdc <amount> [--yes-pct <0-100>] [--slippage-bps <0-10000>]
|
|
84
85
|
pandora [--output table|json] trade [--indexer-url <url>] [--timeout-ms <ms>] [--dotenv-path <path>] [--skip-dotenv] --market-address <address> --side yes|no --amount-usdc <amount> --dry-run|--execute [--yes-pct <0-100>] [--slippage-bps <0-10000>] [--min-shares-out-raw <uint>] [--max-amount-usdc <amount>] [--min-probability-pct <0-100>] [--max-probability-pct <0-100>] [--allow-unquoted-execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]
|
|
85
86
|
pandora [--output table|json] history --wallet <address> [--chain-id <id>] [--market-address <address>] [--side yes|no|both] [--status all|open|won|lost|closed] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by timestamp|pnl|entry-price|mark-price] [--order-direction asc|desc] [--include-seed]
|
|
@@ -94,21 +95,23 @@ pandora [--output table|json] analyze --market-address <address> [--provider <na
|
|
|
94
95
|
pandora [--output table|json] suggest --wallet <address> --risk low|medium|high --budget <amount> [--count <n>] [--include-venues pandora,polymarket]
|
|
95
96
|
pandora [--output table|json] resolve --poll-address <address> --answer yes|no --reason <text> --dry-run|--execute [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]
|
|
96
97
|
pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>] [--dry-run|--execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]
|
|
98
|
+
pandora launch [--dotenv-path <path>] [--skip-dotenv] [script args...]
|
|
99
|
+
pandora clone-bet [--dotenv-path <path>] [--skip-dotenv] [script args...]
|
|
97
100
|
```
|
|
98
101
|
|
|
99
102
|
Mirror subcommand detail:
|
|
100
103
|
|
|
101
104
|
```text
|
|
102
105
|
browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>] [--closes-before <date>] [--question-contains <text>] [--limit <n>]
|
|
103
|
-
plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--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>]
|
|
104
|
-
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>]
|
|
106
|
+
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>]
|
|
107
|
+
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>] [--min-close-lead-seconds <n>] [--manifest-file <path>]
|
|
105
108
|
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]
|
|
106
109
|
lp-explain --liquidity-usdc <n> [--source-yes-pct <0-100>] [--distribution-yes <parts>] [--distribution-no <parts>]
|
|
107
|
-
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>]
|
|
108
|
-
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>]
|
|
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
|
+
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>]
|
|
109
112
|
go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--paper|--dry-run|--execute-live|--execute] [--private-key <hex>] [--funder <address>] [--auto-sync] [--sync-once] [--skip-gate]
|
|
110
|
-
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>] [--trust-deploy] [--skip-gate] [--daemon] [--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>] [--state-file <path>] [--kill-switch-file <path>]
|
|
111
|
-
status --state-file <path>|--strategy-hash <hash> [--with-live] [--
|
|
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>] [--trust-deploy] [--skip-gate] [--daemon] [--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>] [--min-time-to-close-sec <n>] [--state-file <path>] [--kill-switch-file <path>]
|
|
114
|
+
status --state-file <path>|--strategy-hash <hash> [--with-live] [--pandora-market-address <address>|--market-address <address>] [--polymarket-market-id <id>|--polymarket-slug <slug>]
|
|
112
115
|
close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute
|
|
113
116
|
```
|
|
114
117
|
|
|
@@ -189,7 +192,8 @@ pandora --output json suggest --wallet <0x...> --risk medium --budget 50 --inclu
|
|
|
189
192
|
- each scan item includes at minimum `id`, `chainId`, `marketType`, `question`, `marketCloseTimestamp`, and `odds`.
|
|
190
193
|
|
|
191
194
|
## Phase 1 limitations
|
|
192
|
-
-
|
|
195
|
+
- `scan` always includes odds; `--with-odds` is accepted for backward compatibility.
|
|
196
|
+
- `--expand` is supported on both `markets list` and `scan`.
|
|
193
197
|
- `--active|--resolved|--expiring-soon` are client-side filters over fetched pages.
|
|
194
198
|
- Odds are indexer-derived and read-only; missing upstream liquidity/price fields can produce partial odds in some environments.
|
|
195
199
|
- `scan` is indexer-backed only (no direct chain reads); freshness depends on indexer sync state.
|
|
@@ -303,6 +307,13 @@ Mode aliases:
|
|
|
303
307
|
- Mirror commands accept either market flag name:
|
|
304
308
|
- `--pandora-market-address` or `--market-address`
|
|
305
309
|
|
|
310
|
+
Common compatibility aliases:
|
|
311
|
+
- `--env-file` = `--dotenv-path`
|
|
312
|
+
- `--no-env-file` = `--skip-dotenv`
|
|
313
|
+
- `--amount` = `--amount-usdc` (trade/watch/autopilot paths)
|
|
314
|
+
- `--market-id` = `--condition-id` (polymarket trade)
|
|
315
|
+
- `--force-gate` = `--skip-gate` (deprecated; use `--skip-gate`)
|
|
316
|
+
|
|
306
317
|
## Distribution format (ppb)
|
|
307
318
|
- Distribution inputs use parts-per-billion (ppb):
|
|
308
319
|
- `--distribution-yes 580000000` means YES seed weight = `58%`.
|
|
@@ -325,6 +336,18 @@ Common structured error codes for automation:
|
|
|
325
336
|
Error envelope:
|
|
326
337
|
- `{ ok: false, error: { code, message, details? } }`
|
|
327
338
|
|
|
339
|
+
## Additional JSON response shapes
|
|
340
|
+
- `doctor`:
|
|
341
|
+
- `{ ok: true, command: "doctor", data: { schemaVersion, generatedAt, env, rpc, codeChecks, polymarket, summary } }`
|
|
342
|
+
- `resolve`:
|
|
343
|
+
- dry-run: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "dry-run", txPlan } }`
|
|
344
|
+
- execute: `{ ok: true, command: "resolve", data: { schemaVersion, generatedAt, mode: "execute", tx } }`
|
|
345
|
+
- `lp`:
|
|
346
|
+
- `lp add|remove`: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "add"|"remove", mode, txPlan, tx? } }`
|
|
347
|
+
- `lp positions`: `{ ok: true, command: "lp", data: { schemaVersion, generatedAt, action: "positions", mode: "read", wallet, count, items[] } }`
|
|
348
|
+
- `polymarket`:
|
|
349
|
+
- `check|preflight|approve|trade` all return `{ ok: true, command, data }` with `schemaVersion` and `generatedAt`; execute paths include `result`/`tx` blocks.
|
|
350
|
+
|
|
328
351
|
## Pandora mainnet deployment reference
|
|
329
352
|
- PredictionOracle (Factory): `0x259308E7d8557e4Ba192De1aB8Cf7e0E21896442`
|
|
330
353
|
- PredictionPoll (Implementation): `0xC49c177736107fD8351ed6564136B9ADbE5B1eC3`
|
|
@@ -15,10 +15,6 @@ function createCliOutputService(options = {}) {
|
|
|
15
15
|
console.log(JSON.stringify(payload, null, 2));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function emitJsonError(payload) {
|
|
19
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
18
|
function formatErrorValue(value) {
|
|
23
19
|
if (typeof value === 'string') return value;
|
|
24
20
|
if (value && typeof value.message === 'string') return value.message;
|
|
@@ -75,7 +71,7 @@ function createCliOutputService(options = {}) {
|
|
|
75
71
|
const envelope = toErrorEnvelope(error);
|
|
76
72
|
|
|
77
73
|
if (outputMode === 'json') {
|
|
78
|
-
|
|
74
|
+
emitJson(envelope);
|
|
79
75
|
} else {
|
|
80
76
|
console.error(`[${envelope.error.code}] ${envelope.error.message}`);
|
|
81
77
|
if (envelope.error.details && Array.isArray(envelope.error.details.errors) && envelope.error.details.errors.length) {
|
|
@@ -127,4 +123,3 @@ function createCliOutputService(options = {}) {
|
|
|
127
123
|
module.exports = {
|
|
128
124
|
createCliOutputService,
|
|
129
125
|
};
|
|
130
|
-
|
|
@@ -186,29 +186,6 @@ function toFiniteNumber(value) {
|
|
|
186
186
|
return numeric;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
function toBigIntOrNull(value) {
|
|
190
|
-
if (value === null || value === undefined || value === '') return null;
|
|
191
|
-
if (typeof value === 'bigint') return value;
|
|
192
|
-
try {
|
|
193
|
-
return BigInt(value);
|
|
194
|
-
} catch {
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function normalizeBigIntForJson(value) {
|
|
200
|
-
if (typeof value === 'bigint') return value.toString();
|
|
201
|
-
if (Array.isArray(value)) return value.map((item) => normalizeBigIntForJson(item));
|
|
202
|
-
if (value && typeof value === 'object') {
|
|
203
|
-
const output = {};
|
|
204
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
205
|
-
output[key] = normalizeBigIntForJson(nested);
|
|
206
|
-
}
|
|
207
|
-
return output;
|
|
208
|
-
}
|
|
209
|
-
return value;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
189
|
function normalizeResolveOutcome(answer) {
|
|
213
190
|
const normalized = String(answer || '').trim().toLowerCase();
|
|
214
191
|
if (normalized === 'yes') return true;
|
|
@@ -18,10 +18,6 @@ function round(value, decimals = 6) {
|
|
|
18
18
|
return Math.round(numeric * factor) / factor;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function clamp(value, min, max) {
|
|
22
|
-
return Math.max(min, Math.min(max, value));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
21
|
function createServiceError(code, message, details = undefined) {
|
|
26
22
|
const err = new Error(message);
|
|
27
23
|
err.code = code;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const SCAN_USAGE =
|
|
2
|
+
'pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--active|--resolved|--expiring-soon] [--expiring-hours <n>] [--expand]';
|
|
3
|
+
|
|
4
|
+
function createRunScanCommand(deps) {
|
|
5
|
+
const {
|
|
6
|
+
parseIndexerSharedFlags,
|
|
7
|
+
includesHelpFlag,
|
|
8
|
+
emitSuccess,
|
|
9
|
+
commandHelpPayload,
|
|
10
|
+
maybeLoadIndexerEnv,
|
|
11
|
+
resolveIndexerUrl,
|
|
12
|
+
parseMarketsListFlags,
|
|
13
|
+
fetchMarketsListPage,
|
|
14
|
+
buildMarketsEnrichmentContext,
|
|
15
|
+
buildMarketsListPayload,
|
|
16
|
+
renderScanTable,
|
|
17
|
+
} = deps;
|
|
18
|
+
|
|
19
|
+
return async function runScanCommand(args, context) {
|
|
20
|
+
const shared = parseIndexerSharedFlags(args);
|
|
21
|
+
if (includesHelpFlag(shared.rest)) {
|
|
22
|
+
if (context.outputMode === 'json') {
|
|
23
|
+
emitSuccess(context.outputMode, 'scan.help', commandHelpPayload(SCAN_USAGE));
|
|
24
|
+
} else {
|
|
25
|
+
console.log(`Usage: ${SCAN_USAGE}`);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
maybeLoadIndexerEnv(shared);
|
|
31
|
+
const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
|
|
32
|
+
|
|
33
|
+
const options = parseMarketsListFlags(shared.rest);
|
|
34
|
+
options.withOdds = true;
|
|
35
|
+
|
|
36
|
+
const { items, pageInfo, unfilteredCount } = await fetchMarketsListPage(indexerUrl, options, shared.timeoutMs);
|
|
37
|
+
const enrichmentContext = await buildMarketsEnrichmentContext(indexerUrl, items, options, shared.timeoutMs);
|
|
38
|
+
const payload = buildMarketsListPayload(indexerUrl, options, items, pageInfo, {
|
|
39
|
+
includeEnrichedItems: true,
|
|
40
|
+
scanMode: true,
|
|
41
|
+
enrichmentContext,
|
|
42
|
+
unfilteredCount,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
emitSuccess(context.outputMode, 'scan', payload, renderScanTable);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
SCAN_USAGE,
|
|
51
|
+
createRunScanCommand,
|
|
52
|
+
};
|
package/cli/pandora.cjs
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const { spawnSync } = require('child_process');
|
|
6
6
|
const { createCommandRouter } = require('./lib/command_router.cjs');
|
|
7
7
|
const { createCliOutputService } = require('./lib/cli_output_service.cjs');
|
|
8
|
+
const { createRunScanCommand } = require('./lib/scan_command_service.cjs');
|
|
8
9
|
const {
|
|
9
10
|
fetchHistory,
|
|
10
11
|
} = require('./lib/history_service.cjs');
|
|
@@ -258,7 +259,7 @@ Usage:
|
|
|
258
259
|
pandora [--output table|json] positions list [--wallet <address>] [--market-address <address>] [--chain-id <id>] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--where-json <json>]
|
|
259
260
|
pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]
|
|
260
261
|
pandora [--output table|json] watch [--wallet <address>] [--market-address <address>] [--side yes|no] [--amount-usdc <amount>] [--iterations <n>] [--interval-ms <ms>] [--chain-id <id>] [--include-events|--no-events] [--yes-pct <0-100>] [--alert-yes-below <0-100>] [--alert-yes-above <0-100>] [--alert-net-liquidity-below <amount>] [--alert-net-liquidity-above <amount>] [--fail-on-alert]
|
|
261
|
-
pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--
|
|
262
|
+
pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--active|--resolved|--expiring-soon] [--expiring-hours <n>] [--expand]
|
|
262
263
|
pandora [--output table|json] quote [--indexer-url <url>] [--timeout-ms <ms>] --market-address <address> --side yes|no --amount-usdc <amount> [--yes-pct <0-100>] [--slippage-bps <0-10000>]
|
|
263
264
|
pandora [--output table|json] trade [--indexer-url <url>] [--timeout-ms <ms>] [--dotenv-path <path>] [--skip-dotenv] --market-address <address> --side yes|no --amount-usdc <amount> --dry-run|--execute [--yes-pct <0-100>] [--slippage-bps <0-10000>] [--min-shares-out-raw <uint>] [--max-amount-usdc <amount>] [--min-probability-pct <0-100>] [--max-probability-pct <0-100>] [--allow-unquoted-execute] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]
|
|
264
265
|
pandora [--output table|json] history --wallet <address> [--chain-id <id>] [--market-address <address>] [--side yes|no|both] [--status all|open|won|lost|closed] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by timestamp|pnl|entry-price|mark-price] [--order-direction asc|desc] [--include-seed]
|
|
@@ -286,7 +287,7 @@ Examples:
|
|
|
286
287
|
pandora positions list --wallet 0x1234...
|
|
287
288
|
pandora portfolio --wallet 0x1234... --chain-id 1 --with-lp
|
|
288
289
|
pandora watch --market-address 0xabc... --side yes --amount-usdc 10 --iterations 5 --interval-ms 2000
|
|
289
|
-
pandora scan --limit 25 --chain-id 1
|
|
290
|
+
pandora scan --active --limit 25 --chain-id 1
|
|
290
291
|
pandora quote --market-address 0xabc... --side yes --amount-usdc 50
|
|
291
292
|
pandora trade --dry-run --market-address 0xabc... --side no --amount-usdc 25 --max-amount-usdc 50 --min-probability-pct 20
|
|
292
293
|
pandora history --wallet 0x1234... --chain-id 1 --limit 50
|
|
@@ -560,6 +561,40 @@ function inferRequestedOutputMode(argv) {
|
|
|
560
561
|
|
|
561
562
|
function expandEqualsStyleFlags(argv) {
|
|
562
563
|
const expanded = [];
|
|
564
|
+
const unaryBooleanFlags = new Set([
|
|
565
|
+
'--force',
|
|
566
|
+
'--skip-dotenv',
|
|
567
|
+
'--no-env-file',
|
|
568
|
+
'--check-usdc-code',
|
|
569
|
+
'--check-polymarket',
|
|
570
|
+
'--stdin',
|
|
571
|
+
'--active',
|
|
572
|
+
'--resolved',
|
|
573
|
+
'--expiring-soon',
|
|
574
|
+
'--expand',
|
|
575
|
+
'--with-odds',
|
|
576
|
+
'--include-events',
|
|
577
|
+
'--no-events',
|
|
578
|
+
'--fail-on-alert',
|
|
579
|
+
'--dry-run',
|
|
580
|
+
'--execute',
|
|
581
|
+
'--allow-unquoted-execute',
|
|
582
|
+
'--cross-venue-only',
|
|
583
|
+
'--allow-same-venue',
|
|
584
|
+
'--with-rules',
|
|
585
|
+
'--include-similarity',
|
|
586
|
+
'--paper',
|
|
587
|
+
'--execute-live',
|
|
588
|
+
'--trust-deploy',
|
|
589
|
+
'--allow-rule-mismatch',
|
|
590
|
+
'--auto-sync',
|
|
591
|
+
'--sync-once',
|
|
592
|
+
'--skip-gate',
|
|
593
|
+
'--force-gate',
|
|
594
|
+
'--daemon',
|
|
595
|
+
'--stream',
|
|
596
|
+
'--no-hedge',
|
|
597
|
+
]);
|
|
563
598
|
for (const token of Array.isArray(argv) ? argv : []) {
|
|
564
599
|
if (
|
|
565
600
|
typeof token === 'string' &&
|
|
@@ -571,6 +606,17 @@ function expandEqualsStyleFlags(argv) {
|
|
|
571
606
|
const flag = token.slice(0, eqIndex);
|
|
572
607
|
const value = token.slice(eqIndex + 1);
|
|
573
608
|
if (flag && flag !== '--') {
|
|
609
|
+
const normalizedValue = value.trim().toLowerCase();
|
|
610
|
+
if (
|
|
611
|
+
unaryBooleanFlags.has(flag) &&
|
|
612
|
+
(normalizedValue === 'true' || normalizedValue === 'false')
|
|
613
|
+
) {
|
|
614
|
+
// Preserve explicit boolean-literal assignments for unary flags so they
|
|
615
|
+
// are handled as invalid/explicit input by command parsers instead of
|
|
616
|
+
// silently flipping behavior (for example, --dry-run=false).
|
|
617
|
+
expanded.push(token);
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
574
620
|
expanded.push(flag);
|
|
575
621
|
if (value.length) expanded.push(value);
|
|
576
622
|
continue;
|
|
@@ -627,13 +673,29 @@ function parseDateLikeFlag(value, flagName) {
|
|
|
627
673
|
`${flagName} must be a date/time string (for example: "2026-03-15" or "2026-03-15T18:00:00Z"), not a bare number.`,
|
|
628
674
|
);
|
|
629
675
|
}
|
|
630
|
-
const
|
|
676
|
+
const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(text);
|
|
677
|
+
const isDateTime =
|
|
678
|
+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2})$/.test(text);
|
|
679
|
+
if (!isDateOnly && !isDateTime) {
|
|
680
|
+
throw new CliError(
|
|
681
|
+
'INVALID_FLAG_VALUE',
|
|
682
|
+
`${flagName} must be an ISO date/time string (for example: "2026-03-15" or "2026-03-15T18:00:00Z"). Received: "${text}"`,
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
const normalized = isDateOnly ? `${text}T00:00:00Z` : text;
|
|
686
|
+
const parsed = Date.parse(normalized);
|
|
631
687
|
if (Number.isNaN(parsed)) {
|
|
632
688
|
throw new CliError(
|
|
633
689
|
'INVALID_FLAG_VALUE',
|
|
634
690
|
`${flagName} must be a valid date/time string (for example: "2026-03-15" or "2026-03-15T18:00:00Z"). Received: "${text}"`,
|
|
635
691
|
);
|
|
636
692
|
}
|
|
693
|
+
if (isDateOnly && new Date(parsed).toISOString().slice(0, 10) !== text) {
|
|
694
|
+
throw new CliError(
|
|
695
|
+
'INVALID_FLAG_VALUE',
|
|
696
|
+
`${flagName} must be a real calendar date (YYYY-MM-DD). Received: "${text}"`,
|
|
697
|
+
);
|
|
698
|
+
}
|
|
637
699
|
return text;
|
|
638
700
|
}
|
|
639
701
|
|
|
@@ -1429,7 +1491,6 @@ function parseTradeFlags(args) {
|
|
|
1429
1491
|
chainId: null,
|
|
1430
1492
|
rpcUrl: null,
|
|
1431
1493
|
privateKey: null,
|
|
1432
|
-
funder: null,
|
|
1433
1494
|
usdc: null,
|
|
1434
1495
|
};
|
|
1435
1496
|
|
|
@@ -5571,7 +5632,11 @@ function renderMarketsListTable(data) {
|
|
|
5571
5632
|
}
|
|
5572
5633
|
|
|
5573
5634
|
function renderScanTable(data) {
|
|
5574
|
-
const items = Array.isArray(data.enrichedItems)
|
|
5635
|
+
const items = Array.isArray(data.enrichedItems)
|
|
5636
|
+
? data.enrichedItems
|
|
5637
|
+
: Array.isArray(data.items)
|
|
5638
|
+
? data.items
|
|
5639
|
+
: [];
|
|
5575
5640
|
if (!items.length) {
|
|
5576
5641
|
console.log('No markets found.');
|
|
5577
5642
|
return;
|
|
@@ -6976,6 +7041,38 @@ function buildMarketExpansion(item) {
|
|
|
6976
7041
|
};
|
|
6977
7042
|
}
|
|
6978
7043
|
|
|
7044
|
+
function normalizeNumericLikeValue(value) {
|
|
7045
|
+
if (typeof value === 'number') {
|
|
7046
|
+
return Number.isFinite(value) ? value : value;
|
|
7047
|
+
}
|
|
7048
|
+
if (typeof value !== 'string') {
|
|
7049
|
+
return value;
|
|
7050
|
+
}
|
|
7051
|
+
const trimmed = value.trim();
|
|
7052
|
+
if (!trimmed.length) {
|
|
7053
|
+
return value;
|
|
7054
|
+
}
|
|
7055
|
+
const numeric = Number(trimmed);
|
|
7056
|
+
if (!Number.isFinite(numeric)) {
|
|
7057
|
+
return value;
|
|
7058
|
+
}
|
|
7059
|
+
return numeric;
|
|
7060
|
+
}
|
|
7061
|
+
|
|
7062
|
+
function normalizeMarketNumericFields(item) {
|
|
7063
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
7064
|
+
return item;
|
|
7065
|
+
}
|
|
7066
|
+
return {
|
|
7067
|
+
...item,
|
|
7068
|
+
chainId: normalizeNumericLikeValue(item.chainId),
|
|
7069
|
+
marketCloseTimestamp: normalizeNumericLikeValue(item.marketCloseTimestamp),
|
|
7070
|
+
createdAt: normalizeNumericLikeValue(item.createdAt),
|
|
7071
|
+
totalVolume: normalizeNumericLikeValue(item.totalVolume),
|
|
7072
|
+
currentTvl: normalizeNumericLikeValue(item.currentTvl),
|
|
7073
|
+
};
|
|
7074
|
+
}
|
|
7075
|
+
|
|
6979
7076
|
function enrichMarketItem(item, options, enrichmentContext) {
|
|
6980
7077
|
const baseItem = item && typeof item === 'object' ? item : { value: item };
|
|
6981
7078
|
const enriched = { ...baseItem };
|
|
@@ -7039,6 +7136,7 @@ function buildEnrichedMarketItems(items, options, enrichmentContext) {
|
|
|
7039
7136
|
function buildMarketsListPayload(indexerUrl, options, items, pageInfo, opts = {}) {
|
|
7040
7137
|
const isScanMode = Boolean(opts.scanMode);
|
|
7041
7138
|
const enrichmentContext = opts.enrichmentContext || null;
|
|
7139
|
+
const normalizedItems = Array.isArray(items) ? items.map((item) => normalizeMarketNumericFields(item)) : [];
|
|
7042
7140
|
const filters = { ...(options.where || {}) };
|
|
7043
7141
|
if (options.lifecycle && options.lifecycle !== 'all') {
|
|
7044
7142
|
filters.lifecycle = options.lifecycle;
|
|
@@ -7050,23 +7148,27 @@ function buildMarketsListPayload(indexerUrl, options, items, pageInfo, opts = {}
|
|
|
7050
7148
|
indexerUrl,
|
|
7051
7149
|
pagination: buildMarketsPagination(options),
|
|
7052
7150
|
filters,
|
|
7053
|
-
count:
|
|
7151
|
+
count: normalizedItems.length,
|
|
7054
7152
|
pageInfo,
|
|
7055
|
-
items,
|
|
7153
|
+
items: normalizedItems,
|
|
7056
7154
|
};
|
|
7057
7155
|
|
|
7058
|
-
if (
|
|
7156
|
+
if (
|
|
7157
|
+
typeof opts.unfilteredCount === 'number' &&
|
|
7158
|
+
opts.unfilteredCount >= normalizedItems.length &&
|
|
7159
|
+
options.lifecycle !== 'all'
|
|
7160
|
+
) {
|
|
7059
7161
|
payload.lifecycle = {
|
|
7060
7162
|
mode: options.lifecycle,
|
|
7061
7163
|
expiringHours: options.lifecycle === 'expiring-soon' ? options.expiringSoonHours : null,
|
|
7062
7164
|
unfilteredCount: opts.unfilteredCount,
|
|
7063
|
-
filteredOut: opts.unfilteredCount -
|
|
7165
|
+
filteredOut: opts.unfilteredCount - normalizedItems.length,
|
|
7064
7166
|
};
|
|
7065
7167
|
}
|
|
7066
7168
|
|
|
7067
7169
|
const includeEnrichedItems = Boolean(opts.includeEnrichedItems || options.expand || options.withOdds);
|
|
7068
7170
|
if (includeEnrichedItems) {
|
|
7069
|
-
const enrichedItems = buildEnrichedMarketItems(
|
|
7171
|
+
const enrichedItems = buildEnrichedMarketItems(normalizedItems, options, enrichmentContext);
|
|
7070
7172
|
payload.enrichment = {
|
|
7071
7173
|
expand: Boolean(options.expand),
|
|
7072
7174
|
withOdds: Boolean(options.withOdds),
|
|
@@ -7687,7 +7789,7 @@ async function runMarketsCommand(args, context) {
|
|
|
7687
7789
|
const responses = await Promise.all(
|
|
7688
7790
|
ids.map(async (id) => {
|
|
7689
7791
|
const data = await graphqlRequest(indexerUrl, query, { id }, shared.timeoutMs);
|
|
7690
|
-
return { id, item: data.markets || null };
|
|
7792
|
+
return { id, item: normalizeMarketNumericFields(data.markets || null) };
|
|
7691
7793
|
}),
|
|
7692
7794
|
);
|
|
7693
7795
|
|
|
@@ -7724,43 +7826,19 @@ async function runMarketsCommand(args, context) {
|
|
|
7724
7826
|
throw new CliError('INVALID_ARGS', 'markets requires a subcommand: list|get');
|
|
7725
7827
|
}
|
|
7726
7828
|
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
'Usage: pandora [--output table|json] scan [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--poll-address <address>] [--market-type <type>] [--where-json <json>] [--expand] [--with-odds]',
|
|
7741
|
-
);
|
|
7742
|
-
}
|
|
7743
|
-
return;
|
|
7744
|
-
}
|
|
7745
|
-
|
|
7746
|
-
maybeLoadIndexerEnv(shared);
|
|
7747
|
-
const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
|
|
7748
|
-
|
|
7749
|
-
const options = parseMarketsListFlags(shared.rest);
|
|
7750
|
-
options.expand = true;
|
|
7751
|
-
options.withOdds = true;
|
|
7752
|
-
|
|
7753
|
-
const { items, pageInfo, unfilteredCount } = await fetchMarketsListPage(indexerUrl, options, shared.timeoutMs);
|
|
7754
|
-
const enrichmentContext = await buildMarketsEnrichmentContext(indexerUrl, items, options, shared.timeoutMs);
|
|
7755
|
-
const payload = buildMarketsListPayload(indexerUrl, options, items, pageInfo, {
|
|
7756
|
-
includeEnrichedItems: true,
|
|
7757
|
-
scanMode: true,
|
|
7758
|
-
enrichmentContext,
|
|
7759
|
-
unfilteredCount,
|
|
7760
|
-
});
|
|
7761
|
-
|
|
7762
|
-
emitSuccess(context.outputMode, 'scan', payload, renderScanTable);
|
|
7763
|
-
}
|
|
7829
|
+
const runScanCommand = createRunScanCommand({
|
|
7830
|
+
parseIndexerSharedFlags,
|
|
7831
|
+
includesHelpFlag,
|
|
7832
|
+
emitSuccess,
|
|
7833
|
+
commandHelpPayload,
|
|
7834
|
+
maybeLoadIndexerEnv,
|
|
7835
|
+
resolveIndexerUrl,
|
|
7836
|
+
parseMarketsListFlags,
|
|
7837
|
+
fetchMarketsListPage,
|
|
7838
|
+
buildMarketsEnrichmentContext,
|
|
7839
|
+
buildMarketsListPayload,
|
|
7840
|
+
renderScanTable,
|
|
7841
|
+
});
|
|
7764
7842
|
|
|
7765
7843
|
async function runPollsCommand(args, context) {
|
|
7766
7844
|
const shared = parseIndexerSharedFlags(args);
|
|
@@ -10600,6 +10678,8 @@ async function main() {
|
|
|
10600
10678
|
outputMode = parsed.outputMode;
|
|
10601
10679
|
args = parsed.args;
|
|
10602
10680
|
} catch (err) {
|
|
10681
|
+
// Keep parse-time failures machine-readable for agent callers even when
|
|
10682
|
+
// --output itself is malformed or missing.
|
|
10603
10683
|
emitFailure('json', err);
|
|
10604
10684
|
return;
|
|
10605
10685
|
}
|