pandora-cli-skills 1.1.34 → 1.1.36
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.md +62 -13
- package/README_FOR_SHARING.md +56 -2
- package/SKILL.md +68 -9
- package/cli/lib/command_executor_service.cjs +83 -17
- package/cli/lib/command_router.cjs +48 -1
- package/cli/lib/doctor_service.cjs +413 -0
- package/cli/lib/error_recovery_service.cjs +4 -0
- package/cli/lib/lp_command_service.cjs +2 -2
- package/cli/lib/market_admin_service.cjs +70 -8
- package/cli/lib/mcp_tool_registry.cjs +39 -6
- package/cli/lib/parsers/stream_flags.cjs +107 -0
- package/cli/lib/polymarket_command_service.cjs +90 -14
- package/cli/lib/resolve_command_service.cjs +2 -2
- package/cli/lib/schema_command_service.cjs +68 -8
- package/cli/lib/stream_command_service.cjs +278 -0
- package/cli/lib/trade_command_service.cjs +28 -0
- package/cli/pandora.cjs +69 -363
- package/package.json +2 -2
- package/tests/cli/cli.integration.test.cjs +148 -0
- package/tests/cli/mcp.integration.test.cjs +38 -0
- package/tests/cli/stream.integration.test.cjs +218 -0
- package/tests/unit/new-features.test.cjs +47 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Pandora CLI & Skills
|
|
2
2
|
|
|
3
|
-
Production CLI for Pandora prediction markets with mirror + hedge tooling.
|
|
3
|
+
Production CLI for Pandora prediction markets with mirror + hedge tooling and agent-native interfaces.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -15,7 +15,58 @@ Or run without installing:
|
|
|
15
15
|
npx pandora-cli-skills@latest --help
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Agent-Native Features
|
|
19
|
+
|
|
20
|
+
- `pandora --output json schema`
|
|
21
|
+
- emits machine-readable envelope schema + command descriptors.
|
|
22
|
+
- `pandora mcp`
|
|
23
|
+
- runs an MCP server over stdio (`tools/list`, `tools/call`) for direct agent tool execution.
|
|
24
|
+
- JSON errors include optional next-best-action recovery hints:
|
|
25
|
+
- `error.recovery = { action, command, retryable }`.
|
|
26
|
+
- Attach-only fork runtime for on-chain command families:
|
|
27
|
+
- `--fork`, `--fork-rpc-url`, `--fork-chain-id`.
|
|
28
|
+
- Event-driven streaming:
|
|
29
|
+
- `pandora stream prices|events` emits NDJSON lines on stdout.
|
|
30
|
+
|
|
31
|
+
## Quickstart
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# schema for typed consumers (Pydantic/Zod/etc.)
|
|
35
|
+
pandora --output json schema
|
|
36
|
+
|
|
37
|
+
# MCP server mode
|
|
38
|
+
pandora mcp
|
|
39
|
+
|
|
40
|
+
# Read-only market discovery
|
|
41
|
+
pandora --output json markets list --active --limit 10
|
|
42
|
+
|
|
43
|
+
# Dry-run trade with fork runtime
|
|
44
|
+
pandora --output json trade --dry-run \
|
|
45
|
+
--market-address 0x... --side yes --amount-usdc 10 \
|
|
46
|
+
--fork --fork-rpc-url http://127.0.0.1:8545
|
|
47
|
+
|
|
48
|
+
# NDJSON stream
|
|
49
|
+
pandora stream prices --indexer-url https://pandoraindexer.up.railway.app/ --interval-ms 1000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Fork Mode Notes
|
|
53
|
+
|
|
54
|
+
- Runtime marker is included in payloads: `data.runtime.mode = "fork" | "live"`.
|
|
55
|
+
- Fork RPC precedence:
|
|
56
|
+
1. `--fork-rpc-url`
|
|
57
|
+
2. `FORK_RPC_URL` (when `--fork` is set)
|
|
58
|
+
3. command default live RPC path
|
|
59
|
+
- `polymarket trade --execute` in fork mode is simulation-only unless `--polymarket-mock-url` is provided.
|
|
60
|
+
|
|
61
|
+
## Streaming Contract
|
|
62
|
+
|
|
63
|
+
`pandora stream prices|events` outputs NDJSON only (one JSON object per line), for example:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{"type":"stream.tick","channel":"prices","seq":1,"ts":"2026-03-01T12:00:00.000Z","source":{"transport":"polling"},"data":{"id":"market-1","yesPct":58.12}}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Command Surface
|
|
19
70
|
|
|
20
71
|
- `pandora markets list|get`
|
|
21
72
|
- `pandora quote`
|
|
@@ -26,17 +77,15 @@ npx pandora-cli-skills@latest --help
|
|
|
26
77
|
- `pandora autopilot run|once`
|
|
27
78
|
- `pandora mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close`
|
|
28
79
|
- `pandora polymarket check|approve|preflight|trade`
|
|
80
|
+
- `pandora resolve`
|
|
81
|
+
- `pandora lp add|remove|positions`
|
|
82
|
+
- `pandora stream prices|events`
|
|
83
|
+
- `pandora schema`
|
|
84
|
+
- `pandora mcp`
|
|
29
85
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
- `pandora mirror lp-explain --liquidity-usdc 10000 --source-yes-pct 58`
|
|
33
|
-
- `pandora mirror hedge-calc --reserve-yes-usdc 8 --reserve-no-usdc 12 --excess-no-usdc 2 --polymarket-yes-pct 60`
|
|
34
|
-
- `pandora mirror simulate --liquidity-usdc 10000 --source-yes-pct 58 --target-yes-pct 58 --volume-scenarios 1000,5000,10000`
|
|
86
|
+
## Docs
|
|
35
87
|
|
|
36
|
-
|
|
88
|
+
- Full command contract and workflows: [`SKILL.md`](./SKILL.md)
|
|
89
|
+
- Operator + package documentation: [`README_FOR_SHARING.md`](./README_FOR_SHARING.md)
|
|
37
90
|
|
|
38
|
-
|
|
39
|
-
- Full CLI skill contract, error semantics, and workflow references are documented in
|
|
40
|
-
[`SKILL.md`](./SKILL.md).
|
|
41
|
-
- Full operational and JSON contract documentation is in
|
|
42
|
-
[`README_FOR_SHARING.md`](./README_FOR_SHARING.md).
|
|
91
|
+
Node.js `>=18` required.
|
package/README_FOR_SHARING.md
CHANGED
|
@@ -56,6 +56,20 @@ Prerequisite: Node.js `>=18`.
|
|
|
56
56
|
- `pandora --output json doctor`
|
|
57
57
|
- `pandora --output table polls list --limit 10`
|
|
58
58
|
- `--output json` is supported for all commands except `launch`/`clone-bet`; those stream script output directly.
|
|
59
|
+
- Agent-native schema + MCP:
|
|
60
|
+
- `pandora --output json schema`
|
|
61
|
+
- `pandora mcp`
|
|
62
|
+
- MCP exposes one tool per command family entrypoint and reuses CLI JSON envelopes.
|
|
63
|
+
- Next Best Action recovery hints:
|
|
64
|
+
- JSON errors can include `error.recovery = { action, command, retryable }`.
|
|
65
|
+
- existing `error.code`, `error.message`, and `error.details` fields are preserved.
|
|
66
|
+
- Attach-only fork runtime:
|
|
67
|
+
- shared flags: `--fork`, `--fork-rpc-url <url>`, `--fork-chain-id <id>`.
|
|
68
|
+
- payloads include runtime marker (`runtime.mode = "fork" | "live"`).
|
|
69
|
+
- precedence: `--fork-rpc-url` > `FORK_RPC_URL` (when `--fork`) > normal runtime path.
|
|
70
|
+
- NDJSON streaming:
|
|
71
|
+
- `pandora stream prices|events [--indexer-url <url>] [--indexer-ws-url <url>] [--interval-ms <ms>] [--limit <n>]`.
|
|
72
|
+
- always emits NDJSON lines to stdout; WebSocket-first with polling fallback.
|
|
59
73
|
- Guided setup:
|
|
60
74
|
- `pandora setup`
|
|
61
75
|
- `pandora setup --check-usdc-code`
|
|
@@ -77,7 +91,9 @@ Prerequisite: Node.js `>=18`.
|
|
|
77
91
|
- `pandora markets list --with-odds` (include YES/NO percentage odds inline)
|
|
78
92
|
- `pandora markets list --active|--resolved|--expiring-soon` (lifecycle convenience filters)
|
|
79
93
|
- `pandora markets get --id <market-id> --id <market-id>` (batch lookup in one call)
|
|
80
|
-
|
|
94
|
+
- `pandora --output json scan` (single-pass market discovery payload for automation)
|
|
95
|
+
- `pandora stream prices` (reactive market ticks in NDJSON)
|
|
96
|
+
- `pandora stream events` (reactive event feed in NDJSON)
|
|
81
97
|
- Phase 2 trading helpers:
|
|
82
98
|
- `pandora quote --market-address <0x...> --side yes|no --amount-usdc <amount>`
|
|
83
99
|
- `pandora trade --dry-run --market-address <0x...> --side yes|no --amount-usdc <amount> [--max-amount-usdc <amount>] [--min-probability-pct <0-100>] [--max-probability-pct <0-100>]`
|
|
@@ -97,7 +113,45 @@ Prerequisite: Node.js `>=18`.
|
|
|
97
113
|
- `pandora analyze --market-address <0x...> --provider <name>`
|
|
98
114
|
- `pandora suggest --wallet <0x...> --risk low|medium|high --budget <amount>`
|
|
99
115
|
- `pandora resolve`
|
|
100
|
-
|
|
116
|
+
- `pandora lp add|remove|positions`
|
|
117
|
+
|
|
118
|
+
## Agent-native expansion details
|
|
119
|
+
|
|
120
|
+
### MCP server (`pandora mcp`)
|
|
121
|
+
- Runs MCP server over stdio with tool discovery + execution.
|
|
122
|
+
- `tools/list` includes JSON-capable command tools (for example `markets.list`, `trade`, `mirror.plan`, `polymarket.check`).
|
|
123
|
+
- `launch` and `clone-bet` are intentionally not exposed over MCP because they stream script output.
|
|
124
|
+
- MCP safety rails:
|
|
125
|
+
- mutating tools require explicit execute intent (`intent.execute=true`) for live execution.
|
|
126
|
+
- long-running modes are blocked in v1 (`watch`, `autopilot run`, `mirror sync run/start`).
|
|
127
|
+
|
|
128
|
+
### Next Best Action recovery hints
|
|
129
|
+
- Structured JSON errors may include:
|
|
130
|
+
- `error.recovery.action`
|
|
131
|
+
- `error.recovery.command`
|
|
132
|
+
- `error.recovery.retryable`
|
|
133
|
+
- This supports automatic recovery flows in agents while keeping backward compatibility for existing JSON parsers.
|
|
134
|
+
|
|
135
|
+
### Fork runtime support
|
|
136
|
+
- Supported families:
|
|
137
|
+
- `trade`
|
|
138
|
+
- `resolve`
|
|
139
|
+
- `lp`
|
|
140
|
+
- `polymarket check|approve|preflight|trade`
|
|
141
|
+
- `polymarket trade --execute` in fork mode is simulation-only unless `--polymarket-mock-url` is provided.
|
|
142
|
+
|
|
143
|
+
### NDJSON stream command
|
|
144
|
+
- `pandora stream prices|events` always emits NDJSON (ignores table rendering path for active stream output).
|
|
145
|
+
- Tick envelope includes:
|
|
146
|
+
- `type`
|
|
147
|
+
- `ts`
|
|
148
|
+
- `seq`
|
|
149
|
+
- `channel`
|
|
150
|
+
- `source.transport`
|
|
151
|
+
- `data`
|
|
152
|
+
- Transport behavior:
|
|
153
|
+
- primary: WebSocket (`--indexer-ws-url` or derived URL)
|
|
154
|
+
- fallback: polling (`source.transport = "polling"`)
|
|
101
155
|
|
|
102
156
|
Mirror advanced flags (for operator tuning):
|
|
103
157
|
- `--sync-interval-ms <ms>` on `mirror go` to control auto-sync tick cadence.
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pandora-cli-skills
|
|
3
3
|
summary: Canonical skill and operator guide for Pandora CLI including mirror, polymarket, resolve, and LP flows.
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.36
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Pandora CLI & Skills
|
|
@@ -38,6 +38,8 @@ npm link
|
|
|
38
38
|
## CLI ergonomics
|
|
39
39
|
- Global output mode: `--output table|json` (default `table`)
|
|
40
40
|
- `--output json` is supported for all commands except `launch`/`clone-bet`; those stream script output directly.
|
|
41
|
+
- Agent schema command: `pandora --output json schema`
|
|
42
|
+
- MCP server mode: `pandora mcp`
|
|
41
43
|
- Guided setup command: `pandora setup`
|
|
42
44
|
- Phase 1 discovery command: `pandora scan`
|
|
43
45
|
- Phase 1 lifecycle filters: `pandora markets list --active|--resolved|--expiring-soon`
|
|
@@ -58,6 +60,13 @@ npm link
|
|
|
58
60
|
- `pandora suggest`
|
|
59
61
|
- `pandora resolve`
|
|
60
62
|
- `pandora lp add|remove|positions`
|
|
63
|
+
- `pandora stream prices|events`
|
|
64
|
+
- Fork runtime flags for transaction families:
|
|
65
|
+
- `--fork`
|
|
66
|
+
- `--fork-rpc-url <url>`
|
|
67
|
+
- `--fork-chain-id <id>`
|
|
68
|
+
- JSON errors can include additive recovery hints:
|
|
69
|
+
- `error.recovery = { action, command, retryable }`
|
|
61
70
|
- Doctor checks:
|
|
62
71
|
- env presence + format validation
|
|
63
72
|
- RPC reachability and chain id match
|
|
@@ -83,7 +92,7 @@ pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--
|
|
|
83
92
|
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]
|
|
84
93
|
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]
|
|
85
94
|
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>]
|
|
86
|
-
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>]
|
|
95
|
+
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] [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]
|
|
87
96
|
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]
|
|
88
97
|
pandora [--output table|json] export --wallet <address> --format csv|json [--chain-id <id>] [--year <yyyy>] [--from <unix>] [--to <unix>] [--out <path>]
|
|
89
98
|
pandora [--output table|json] arbitrage [--chain-id <id>] [--venues pandora,polymarket] [--limit <n>] [--min-spread-pct <n>] [--min-liquidity-usdc <n>] [--max-close-diff-hours <n>] [--similarity-threshold <0-1>] [--cross-venue-only|--allow-same-venue] [--with-rules] [--include-similarity] [--question-contains <text>] [--polymarket-host <url>] [--polymarket-mock-url <url>]
|
|
@@ -94,8 +103,11 @@ pandora [--output table|json] webhook test [--webhook-url <url>] [--webhook-temp
|
|
|
94
103
|
pandora [--output table|json] leaderboard [--metric profit|volume|win-rate] [--chain-id <id>] [--limit <n>] [--min-trades <n>]
|
|
95
104
|
pandora [--output table|json] analyze --market-address <address> [--provider <name>] [--model <id>] [--max-cost-usd <n>] [--temperature <n>] [--timeout-ms <ms>]
|
|
96
105
|
pandora [--output table|json] suggest --wallet <address> --risk low|medium|high --budget <amount> [--count <n>] [--include-venues pandora,polymarket]
|
|
97
|
-
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>]
|
|
98
|
-
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>]
|
|
106
|
+
pandora [--output table|json] resolve --poll-address <address> --answer yes|no|invalid --reason <text> --dry-run|--execute [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>]
|
|
107
|
+
pandora [--output table|json] lp add|remove|positions [--market-address <address>] [--wallet <address>] [--amount-usdc <n>] [--lp-tokens <n>] [--dry-run|--execute] [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>] [--deadline-seconds <n>] [--indexer-url <url>] [--timeout-ms <ms>]
|
|
108
|
+
pandora stream prices|events [--indexer-url <url>] [--indexer-ws-url <url>] [--timeout-ms <ms>] [--interval-ms <ms>] [--market-address <address>] [--chain-id <id>] [--limit <n>]
|
|
109
|
+
pandora [--output json] schema
|
|
110
|
+
pandora mcp
|
|
99
111
|
pandora launch [--dotenv-path <path>] [--skip-dotenv] [script args...]
|
|
100
112
|
pandora clone-bet [--dotenv-path <path>] [--skip-dotenv] [script args...]
|
|
101
113
|
```
|
|
@@ -126,10 +138,10 @@ sync status --pid-file <path>|--strategy-hash <hash>
|
|
|
126
138
|
Polymarket subcommand detail:
|
|
127
139
|
|
|
128
140
|
```text
|
|
129
|
-
check [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
130
|
-
approve --dry-run|--execute [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
131
|
-
preflight [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
132
|
-
trade --condition-id <id>|--slug <slug>|--token-id <id> --token yes|no --amount-usdc <n> --dry-run|--execute [--side buy|sell] [--polymarket-host <url>] [--timeout-ms <ms>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
141
|
+
check [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
142
|
+
approve --dry-run|--execute [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
143
|
+
preflight [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
144
|
+
trade --condition-id <id>|--slug <slug>|--token-id <id> --token yes|no --amount-usdc <n> --dry-run|--execute [--side buy|sell] [--polymarket-host <url>] [--polymarket-mock-url <url>] [--timeout-ms <ms>] [--fork] [--fork-rpc-url <url>] [--fork-chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
|
|
133
145
|
```
|
|
134
146
|
|
|
135
147
|
## Read-only indexer commands
|
|
@@ -188,6 +200,7 @@ pandora --output json webhook test --webhook-url https://example.com/hook
|
|
|
188
200
|
pandora --output json leaderboard --metric profit --limit 20
|
|
189
201
|
pandora --output json analyze --market-address <0x...> --provider mock
|
|
190
202
|
pandora --output json suggest --wallet <0x...> --risk medium --budget 50 --include-venues pandora
|
|
203
|
+
pandora --output json schema
|
|
191
204
|
```
|
|
192
205
|
|
|
193
206
|
## Phase 1 JSON contracts
|
|
@@ -343,7 +356,53 @@ Common structured error codes for automation:
|
|
|
343
356
|
- `WEBHOOK_DELIVERY_FAILED`: webhook hard-fail when `--fail-on-webhook-error` is set.
|
|
344
357
|
|
|
345
358
|
Error envelope:
|
|
346
|
-
- `{ ok: false, error: { code, message, details
|
|
359
|
+
- `{ ok: false, error: { code, message, details?, recovery?: { action, command, retryable } } }`
|
|
360
|
+
|
|
361
|
+
## MCP server mode
|
|
362
|
+
- Start server:
|
|
363
|
+
- `pandora mcp`
|
|
364
|
+
- Transport:
|
|
365
|
+
- MCP stdio JSON-RPC.
|
|
366
|
+
- Tool model:
|
|
367
|
+
- one tool per command family entrypoint (for example `markets.list`, `trade`, `mirror.plan`, `polymarket.check`).
|
|
368
|
+
- Exclusions in v1:
|
|
369
|
+
- `launch`, `clone-bet` are intentionally not exposed because they stream interactive script output.
|
|
370
|
+
- Guardrails in v1:
|
|
371
|
+
- mutating tools require explicit execute intent (`intent.execute=true`) for live execution.
|
|
372
|
+
- long-running modes are blocked (`watch`, `autopilot run`, `mirror sync run|start`) with actionable structured errors.
|
|
373
|
+
|
|
374
|
+
## Next Best Action recovery hints
|
|
375
|
+
- JSON errors may include additive recovery guidance:
|
|
376
|
+
- `error.recovery.action`: short recovery label.
|
|
377
|
+
- `error.recovery.command`: copy-pasteable remediation command.
|
|
378
|
+
- `error.recovery.retryable`: whether retry is expected to succeed after recovery.
|
|
379
|
+
- `details.hints` is preserved for human operators.
|
|
380
|
+
|
|
381
|
+
## Fork runtime (attach-only)
|
|
382
|
+
- Shared flags:
|
|
383
|
+
- `--fork`
|
|
384
|
+
- `--fork-rpc-url <url>`
|
|
385
|
+
- `--fork-chain-id <id>`
|
|
386
|
+
- URL precedence in fork mode:
|
|
387
|
+
1. `--fork-rpc-url`
|
|
388
|
+
2. `FORK_RPC_URL` (when `--fork`)
|
|
389
|
+
3. command live RPC path
|
|
390
|
+
- Commands annotate runtime mode in payload:
|
|
391
|
+
- `runtime.mode = "fork" | "live"`.
|
|
392
|
+
- `polymarket trade` in fork mode:
|
|
393
|
+
- default is simulation-only.
|
|
394
|
+
- `--execute` requires `--polymarket-mock-url`.
|
|
395
|
+
|
|
396
|
+
## Stream command (NDJSON)
|
|
397
|
+
- Usage:
|
|
398
|
+
- `pandora stream prices|events [--indexer-url <url>] [--indexer-ws-url <url>] [--timeout-ms <ms>] [--interval-ms <ms>] [--market-address <address>] [--chain-id <id>] [--limit <n>]`
|
|
399
|
+
- Output:
|
|
400
|
+
- NDJSON only (one JSON object per line to stdout), regardless of table/json global mode.
|
|
401
|
+
- Tick envelope fields:
|
|
402
|
+
- `type`, `ts`, `seq`, `channel`, `source.transport`, `source.url`, `data`.
|
|
403
|
+
- Transport behavior:
|
|
404
|
+
- WebSocket-first when `--indexer-ws-url` is provided/derivable.
|
|
405
|
+
- polling fallback with `source.transport = "polling"`.
|
|
347
406
|
|
|
348
407
|
## Additional JSON response shapes
|
|
349
408
|
- `doctor`:
|
|
@@ -11,28 +11,73 @@ function coerceErrorMessage(value) {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
function
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
function parseEnvelopeCandidate(text) {
|
|
15
|
+
const candidate = String(text || '').trim();
|
|
16
|
+
if (!candidate) return null;
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(candidate);
|
|
19
|
+
if (parsed && typeof parsed === 'object' && typeof parsed.ok === 'boolean') {
|
|
20
|
+
return parsed;
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
// continue with extraction path
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
function extractLastJsonObject(text) {
|
|
29
|
+
const source = String(text || '');
|
|
30
|
+
if (!source) return null;
|
|
31
|
+
|
|
32
|
+
let depth = 0;
|
|
33
|
+
let start = -1;
|
|
34
|
+
let inString = false;
|
|
35
|
+
let escaped = false;
|
|
36
|
+
let last = null;
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < source.length; i += 1) {
|
|
39
|
+
const ch = source[i];
|
|
40
|
+
if (inString) {
|
|
41
|
+
if (escaped) {
|
|
42
|
+
escaped = false;
|
|
43
|
+
} else if (ch === '\\') {
|
|
44
|
+
escaped = true;
|
|
45
|
+
} else if (ch === '"') {
|
|
46
|
+
inString = false;
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (ch === '"') {
|
|
52
|
+
inString = true;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (ch === '{') {
|
|
57
|
+
if (depth === 0) start = i;
|
|
58
|
+
depth += 1;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (ch === '}' && depth > 0) {
|
|
63
|
+
depth -= 1;
|
|
64
|
+
if (depth === 0 && start >= 0) {
|
|
65
|
+
last = source.slice(start, i + 1);
|
|
32
66
|
}
|
|
33
67
|
}
|
|
34
68
|
}
|
|
35
69
|
|
|
70
|
+
return parseEnvelopeCandidate(last);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseEnvelopeFromOutput(stdout, stderr) {
|
|
74
|
+
const candidates = [stdout, stderr, `${stdout || ''}\n${stderr || ''}`];
|
|
75
|
+
for (const candidate of candidates) {
|
|
76
|
+
const direct = parseEnvelopeCandidate(candidate);
|
|
77
|
+
if (direct) return direct;
|
|
78
|
+
const extracted = extractLastJsonObject(candidate);
|
|
79
|
+
if (extracted) return extracted;
|
|
80
|
+
}
|
|
36
81
|
return null;
|
|
37
82
|
}
|
|
38
83
|
|
|
@@ -81,6 +126,27 @@ function createCommandExecutorService(options = {}) {
|
|
|
81
126
|
};
|
|
82
127
|
}
|
|
83
128
|
|
|
129
|
+
if (result.error) {
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
exitCode: typeof result.status === 'number' ? result.status : 1,
|
|
133
|
+
stdout: result.stdout || '',
|
|
134
|
+
stderr: result.stderr || '',
|
|
135
|
+
envelope: {
|
|
136
|
+
ok: false,
|
|
137
|
+
error: {
|
|
138
|
+
code: 'COMMAND_EXEC_FAILED',
|
|
139
|
+
message: result.error.message || 'CLI command execution failed.',
|
|
140
|
+
details: {
|
|
141
|
+
commandArgs,
|
|
142
|
+
code: result.error.code || null,
|
|
143
|
+
signal: result.signal || null,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
84
150
|
const envelope = parseEnvelopeFromOutput(result.stdout, result.stderr);
|
|
85
151
|
if (!envelope) {
|
|
86
152
|
return {
|
|
@@ -50,6 +50,45 @@ function createCommandRouter(deps = {}) {
|
|
|
50
50
|
throw new Error('createCommandRouter requires emitSuccess.');
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function requireFn(name, fn) {
|
|
54
|
+
if (typeof fn !== 'function') {
|
|
55
|
+
throw new Error(`createCommandRouter requires ${name}.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
requireFn('helpJsonPayload', helpJsonPayload);
|
|
60
|
+
requireFn('printHelpTable', printHelpTable);
|
|
61
|
+
requireFn('includesHelpFlag', includesHelpFlag);
|
|
62
|
+
requireFn('commandHelpPayload', commandHelpPayload);
|
|
63
|
+
requireFn('runInitEnv', runInitEnv);
|
|
64
|
+
requireFn('runDoctor', runDoctor);
|
|
65
|
+
requireFn('runSetup', runSetup);
|
|
66
|
+
requireFn('runMarketsCommand', runMarketsCommand);
|
|
67
|
+
requireFn('runScanCommand', runScanCommand);
|
|
68
|
+
requireFn('runQuoteCommand', runQuoteCommand);
|
|
69
|
+
requireFn('runTradeCommand', runTradeCommand);
|
|
70
|
+
requireFn('runPollsCommand', runPollsCommand);
|
|
71
|
+
requireFn('runEventsCommand', runEventsCommand);
|
|
72
|
+
requireFn('runPositionsCommand', runPositionsCommand);
|
|
73
|
+
requireFn('runPortfolioCommand', runPortfolioCommand);
|
|
74
|
+
requireFn('runWatchCommand', runWatchCommand);
|
|
75
|
+
requireFn('runHistoryCommand', runHistoryCommand);
|
|
76
|
+
requireFn('runExportCommand', runExportCommand);
|
|
77
|
+
requireFn('runArbitrageCommand', runArbitrageCommand);
|
|
78
|
+
requireFn('runAutopilotCommand', runAutopilotCommand);
|
|
79
|
+
requireFn('runMirrorCommand', runMirrorCommand);
|
|
80
|
+
requireFn('runPolymarketCommand', runPolymarketCommand);
|
|
81
|
+
requireFn('runWebhookCommand', runWebhookCommand);
|
|
82
|
+
requireFn('runLeaderboardCommand', runLeaderboardCommand);
|
|
83
|
+
requireFn('runAnalyzeCommand', runAnalyzeCommand);
|
|
84
|
+
requireFn('runSuggestCommand', runSuggestCommand);
|
|
85
|
+
requireFn('runResolveCommand', runResolveCommand);
|
|
86
|
+
requireFn('runLpCommand', runLpCommand);
|
|
87
|
+
requireFn('runMcpCommand', runMcpCommand);
|
|
88
|
+
requireFn('runStreamCommand', runStreamCommand);
|
|
89
|
+
requireFn('runSchemaCommand', deps.runSchemaCommand);
|
|
90
|
+
requireFn('runScriptCommand', runScriptCommand);
|
|
91
|
+
|
|
53
92
|
return async function dispatch(command, args, context) {
|
|
54
93
|
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
55
94
|
if (context.outputMode === 'json') {
|
|
@@ -133,7 +172,15 @@ function createCommandRouter(deps = {}) {
|
|
|
133
172
|
suggest: async (handlerArgs, handlerContext) => runSuggestCommand(handlerArgs, handlerContext),
|
|
134
173
|
resolve: async (handlerArgs, handlerContext) => runResolveCommand(handlerArgs, handlerContext),
|
|
135
174
|
lp: async (handlerArgs, handlerContext) => runLpCommand(handlerArgs, handlerContext),
|
|
136
|
-
mcp: async (handlerArgs, handlerContext) =>
|
|
175
|
+
mcp: async (handlerArgs, handlerContext) => {
|
|
176
|
+
if (handlerContext.outputMode === 'json') {
|
|
177
|
+
throw new CliError(
|
|
178
|
+
'UNSUPPORTED_OUTPUT_MODE',
|
|
179
|
+
'--output json is not supported for mcp because MCP uses raw stdio transport.',
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
await runMcpCommand(handlerArgs, handlerContext);
|
|
183
|
+
},
|
|
137
184
|
stream: async (handlerArgs, handlerContext) => runStreamCommand(handlerArgs, handlerContext),
|
|
138
185
|
schema: async (handlerArgs, handlerContext) => deps.runSchemaCommand(handlerArgs, handlerContext),
|
|
139
186
|
launch: async (handlerArgs, handlerContext) => {
|