pandora-cli-skills 1.1.25 → 1.1.27

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.
@@ -143,7 +143,7 @@ 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
- - `--expand` and `--with-odds` are supported on `markets list` only.
146
+ - `--expand` and `--with-odds` are supported on both `markets list` and `scan`.
147
147
  - `--active|--resolved|--expiring-soon` are client-side filters on fetched list pages.
148
148
  - Odds are indexer-derived and read-only; missing upstream liquidity/price fields can produce partial odds in some environments.
149
149
  - `scan` is indexer-backed only (no direct chain reads); freshness depends on indexer sync state.
@@ -239,6 +239,22 @@ Prerequisite: Node.js `>=18`.
239
239
  ## Resolve/LP commands
240
240
  - `resolve` and `lp` are active command paths with strict flag validation, runtime preflight checks, and decoded on-chain revert reporting.
241
241
 
242
+ ### Resolve command
243
+ - Usage:
244
+ - `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>]`
245
+ - Behavior:
246
+ - `--dry-run` returns a deterministic execution plan.
247
+ - `--execute` submits the resolution transaction with decoded revert diagnostics on failure.
248
+
249
+ ### LP command
250
+ - Usage:
251
+ - `pandora [--output table|json] lp add --market-address <address> --amount-usdc <n> --dry-run|--execute [--deadline-seconds <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]`
252
+ - `pandora [--output table|json] lp remove --market-address <address> --lp-tokens <n> --dry-run|--execute [--deadline-seconds <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]`
253
+ - `pandora [--output table|json] lp positions --wallet <address> [--market-address <address>] [--chain-id <id>] [--indexer-url <url>] [--timeout-ms <ms>]`
254
+ - Behavior:
255
+ - `add/remove` use simulation-first transaction flow.
256
+ - `positions` returns LP holdings and preview diagnostics.
257
+
242
258
  ## Pandora Mainnet Reference
243
259
  - PredictionOracle (Factory): `0x259308E7d8557e4Ba192De1aB8Cf7e0E21896442`
244
260
  - PredictionPoll (Implementation): `0xC49c177736107fD8351ed6564136B9ADbE5B1eC3`
package/SKILL.md CHANGED
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: pandora-cli-skills
3
+ summary: Canonical skill and operator guide for Pandora CLI including mirror, polymarket, resolve, and LP flows.
4
+ ---
5
+
1
6
  # Pandora CLI & Skills
2
7
 
3
8
  Create and publish Pandora prediction markets from deployed contracts.
@@ -57,6 +62,65 @@ npm link
57
62
  - RPC reachability and chain id match
58
63
  - bytecode checks for `ORACLE` + `FACTORY` (`--check-usdc-code` optional)
59
64
 
65
+ ## Complete command + flag reference (authoritative)
66
+ This section mirrors live CLI help output so agent runs can rely on one source of truth.
67
+
68
+ ```text
69
+ pandora [--output table|json] --version
70
+ pandora [--output table|json] init-env [--force] [--dotenv-path <path>] [--example <path>]
71
+ pandora [--output table|json] doctor [--dotenv-path <path>] [--skip-dotenv] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
72
+ pandora [--output table|json] setup [--force] [--dotenv-path <path>] [--example <path>] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]
73
+ pandora [--output table|json] markets list [--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] [--with-odds]
74
+ pandora [--output table|json] markets get [--id <id> ...] [--stdin]
75
+ pandora [--output table|json] polls list [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-by <field>] [--order-direction asc|desc] [--chain-id <id>] [--creator <address>] [--status <int>] [--category <int>] [--question-contains <text>] [--where-json <json>]
76
+ pandora [--output table|json] polls get --id <id>
77
+ pandora [--output table|json] events list [--type all|liquidity|oracle-fee|claim] [--limit <n>] [--after <cursor>] [--before <cursor>] [--order-direction asc|desc] [--chain-id <id>] [--wallet <address>] [--market-address <address>] [--poll-address <address>] [--tx-hash <hash>]
78
+ pandora [--output table|json] events get --id <id> [--type all|liquidity|oracle-fee|claim]
79
+ 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
+ pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]
81
+ 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>] [--expand] [--with-odds]
83
+ 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
+ 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
+ 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]
86
+ pandora [--output table|json] export --wallet <address> --format csv|json [--chain-id <id>] [--year <yyyy>] [--from <unix>] [--to <unix>] [--out <path>]
87
+ 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>]
88
+ pandora [--output table|json] autopilot run|once --market-address <address> --side yes|no --amount-usdc <amount> [--trigger-yes-below <0-100>] [--trigger-yes-above <0-100>] [--paper|--execute-live] [--interval-ms <ms>] [--cooldown-ms <ms>] [--max-amount-usdc <amount>] [--max-open-exposure-usdc <amount>] [--max-trades-per-day <n>] [--state-file <path>] [--kill-switch-file <path>] [--webhook-url <url>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>]
89
+ pandora [--output table|json] mirror browse|plan|deploy|verify|lp-explain|hedge-calc|simulate|go|sync|status|close ...
90
+ pandora [--output table|json] polymarket check|approve|preflight|trade ...
91
+ pandora [--output table|json] webhook test [--webhook-url <url>] [--webhook-template <json>] [--webhook-secret <secret>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>] [--webhook-timeout-ms <ms>] [--webhook-retries <n>]
92
+ pandora [--output table|json] leaderboard [--metric profit|volume|win-rate] [--chain-id <id>] [--limit <n>] [--min-trades <n>]
93
+ pandora [--output table|json] analyze --market-address <address> [--provider <name>] [--model <id>] [--max-cost-usd <n>] [--temperature <n>] [--timeout-ms <ms>]
94
+ pandora [--output table|json] suggest --wallet <address> --risk low|medium|high --budget <amount> [--count <n>] [--include-venues pandora,polymarket]
95
+ 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
+ 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>]
97
+ ```
98
+
99
+ Mirror subcommand detail:
100
+
101
+ ```text
102
+ 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>]
105
+ 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
+ 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>]
109
+ 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] [--trust-deploy]
112
+ close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute
113
+ ```
114
+
115
+ Polymarket subcommand detail:
116
+
117
+ ```text
118
+ check [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
119
+ approve --dry-run|--execute [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
120
+ preflight [--rpc-url <url>] [--private-key <hex>] [--funder <address>]
121
+ 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>]
122
+ ```
123
+
60
124
  ## Read-only indexer commands
61
125
  Indexer URL resolution order:
62
126
  1. `--indexer-url`
@@ -125,7 +189,7 @@ pandora --output json suggest --wallet <0x...> --risk medium --budget 50 --inclu
125
189
  - each scan item includes at minimum `id`, `chainId`, `marketType`, `question`, `marketCloseTimestamp`, and `odds`.
126
190
 
127
191
  ## Phase 1 limitations
128
- - `--expand` and `--with-odds` are supported on `markets list` only.
192
+ - `--expand` and `--with-odds` are supported on both `markets list` and `scan`.
129
193
  - `--active|--resolved|--expiring-soon` are client-side filters over fetched pages.
130
194
  - Odds are indexer-derived and read-only; missing upstream liquidity/price fields can produce partial odds in some environments.
131
195
  - `scan` is indexer-backed only (no direct chain reads); freshness depends on indexer sync state.
@@ -193,6 +257,23 @@ pandora --output json suggest --wallet <0x...> --risk medium --budget 50 --inclu
193
257
  - `suggest`: risk/budget-ranked opportunities seeded from arbitrage output and wallet history.
194
258
  - `resolve` and `lp`: enabled command paths with strict flag/runtime validation and decoded on-chain revert reporting.
195
259
 
260
+ ### Resolve command
261
+ - Usage:
262
+ - `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>]`
263
+ - Behavior:
264
+ - `--dry-run` returns the call plan and decode-ready payload.
265
+ - `--execute` submits on-chain resolution through configured oracle/operator path.
266
+ - Reverts are surfaced through decoded custom errors when ABI matches.
267
+
268
+ ### LP command
269
+ - Usage:
270
+ - `pandora [--output table|json] lp add --market-address <address> --amount-usdc <n> --dry-run|--execute [--deadline-seconds <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]`
271
+ - `pandora [--output table|json] lp remove --market-address <address> --lp-tokens <n> --dry-run|--execute [--deadline-seconds <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--usdc <address>]`
272
+ - `pandora [--output table|json] lp positions --wallet <address> [--market-address <address>] [--chain-id <id>] [--indexer-url <url>] [--timeout-ms <ms>]`
273
+ - Behavior:
274
+ - `add/remove` path runs transaction simulation before submit.
275
+ - `positions` combines indexer + on-chain LP state when available.
276
+
196
277
  ## Polymarket command group
197
278
  - `pandora polymarket check [--rpc-url <url>] [--private-key <hex>] [--funder <address>]`
198
279
  - Discovers signer + proxy wallet readiness and reports balances/allowances/ownership checks.
@@ -0,0 +1,130 @@
1
+ function createCliOutputService(options = {}) {
2
+ const defaultSchemaVersion =
3
+ typeof options.defaultSchemaVersion === 'string' && options.defaultSchemaVersion.trim()
4
+ ? options.defaultSchemaVersion.trim()
5
+ : '1.0.0';
6
+ const CliError = options.CliError;
7
+
8
+ if (typeof CliError !== 'function') {
9
+ throw new Error('createCliOutputService requires CliError class.');
10
+ }
11
+
12
+ let failureAlreadyEmitted = false;
13
+
14
+ function emitJson(payload) {
15
+ console.log(JSON.stringify(payload, null, 2));
16
+ }
17
+
18
+ function emitJsonError(payload) {
19
+ console.log(JSON.stringify(payload, null, 2));
20
+ }
21
+
22
+ function formatErrorValue(value) {
23
+ if (typeof value === 'string') return value;
24
+ if (value && typeof value.message === 'string') return value.message;
25
+
26
+ try {
27
+ return JSON.stringify(value);
28
+ } catch {
29
+ return String(value);
30
+ }
31
+ }
32
+
33
+ function toErrorEnvelope(error) {
34
+ if (error instanceof CliError) {
35
+ const envelope = {
36
+ ok: false,
37
+ error: {
38
+ code: error.code,
39
+ message: error.message,
40
+ },
41
+ };
42
+ if (error.details !== undefined) {
43
+ envelope.error.details = error.details;
44
+ }
45
+ return envelope;
46
+ }
47
+
48
+ return {
49
+ ok: false,
50
+ error: {
51
+ code: 'UNEXPECTED_ERROR',
52
+ message: formatErrorValue(error && error.message ? error.message : error),
53
+ },
54
+ };
55
+ }
56
+
57
+ function attachJsonMetadata(data) {
58
+ if (!data || typeof data !== 'object' || Array.isArray(data)) return data;
59
+ const payload = { ...data };
60
+ if (typeof payload.schemaVersion !== 'string' || !payload.schemaVersion.trim()) {
61
+ payload.schemaVersion = defaultSchemaVersion;
62
+ }
63
+ if (typeof payload.generatedAt !== 'string' || Number.isNaN(Date.parse(payload.generatedAt))) {
64
+ payload.generatedAt = new Date().toISOString();
65
+ }
66
+ return payload;
67
+ }
68
+
69
+ function emitFailure(outputMode, error) {
70
+ if (failureAlreadyEmitted) {
71
+ process.exit(error instanceof CliError ? error.exitCode : 1);
72
+ }
73
+ failureAlreadyEmitted = true;
74
+
75
+ const envelope = toErrorEnvelope(error);
76
+
77
+ if (outputMode === 'json') {
78
+ emitJsonError(envelope);
79
+ } else {
80
+ console.error(`[${envelope.error.code}] ${envelope.error.message}`);
81
+ if (envelope.error.details && Array.isArray(envelope.error.details.errors) && envelope.error.details.errors.length) {
82
+ for (const err of envelope.error.details.errors) {
83
+ console.error(`- ${formatErrorValue(err)}`);
84
+ }
85
+ }
86
+ if (envelope.error.details && Array.isArray(envelope.error.details.hints) && envelope.error.details.hints.length) {
87
+ for (const hint of envelope.error.details.hints) {
88
+ console.error(`Hint: ${hint}`);
89
+ }
90
+ }
91
+ if (
92
+ envelope.error.details &&
93
+ !Array.isArray(envelope.error.details.errors) &&
94
+ !Array.isArray(envelope.error.details.hints)
95
+ ) {
96
+ try {
97
+ console.error(`Details: ${JSON.stringify(envelope.error.details)}`);
98
+ } catch {
99
+ console.error(`Details: ${String(envelope.error.details)}`);
100
+ }
101
+ }
102
+ }
103
+
104
+ process.exit(error instanceof CliError ? error.exitCode : 1);
105
+ }
106
+
107
+ function emitSuccess(outputMode, command, data, tableRenderer) {
108
+ if (outputMode === 'json') {
109
+ emitJson({ ok: true, command, data: attachJsonMetadata(data) });
110
+ return;
111
+ }
112
+
113
+ if (typeof tableRenderer === 'function') {
114
+ tableRenderer(data);
115
+ return;
116
+ }
117
+
118
+ console.log('Done.');
119
+ }
120
+
121
+ return {
122
+ emitFailure,
123
+ emitSuccess,
124
+ };
125
+ }
126
+
127
+ module.exports = {
128
+ createCliOutputService,
129
+ };
130
+
@@ -0,0 +1,162 @@
1
+ function createCommandRouter(deps = {}) {
2
+ const {
3
+ CliError,
4
+ packageVersion,
5
+ emitSuccess,
6
+ helpJsonPayload,
7
+ printHelpTable,
8
+ includesHelpFlag,
9
+ commandHelpPayload,
10
+ runInitEnv,
11
+ runDoctor,
12
+ runSetup,
13
+ runMarketsCommand,
14
+ runScanCommand,
15
+ runQuoteCommand,
16
+ runTradeCommand,
17
+ runPollsCommand,
18
+ runEventsCommand,
19
+ runPositionsCommand,
20
+ runPortfolioCommand,
21
+ runWatchCommand,
22
+ runHistoryCommand,
23
+ runExportCommand,
24
+ runArbitrageCommand,
25
+ runAutopilotCommand,
26
+ runMirrorCommand,
27
+ runPolymarketCommand,
28
+ runWebhookCommand,
29
+ runLeaderboardCommand,
30
+ runAnalyzeCommand,
31
+ runSuggestCommand,
32
+ runResolveCommand,
33
+ runLpCommand,
34
+ runScriptCommand,
35
+ } = deps;
36
+
37
+ if (typeof CliError !== 'function') {
38
+ throw new Error('createCommandRouter requires CliError.');
39
+ }
40
+
41
+ if (typeof emitSuccess !== 'function') {
42
+ throw new Error('createCommandRouter requires emitSuccess.');
43
+ }
44
+
45
+ return async function dispatch(command, args, context) {
46
+ if (!command || command === 'help' || command === '--help' || command === '-h') {
47
+ if (context.outputMode === 'json') {
48
+ emitSuccess(context.outputMode, 'help', helpJsonPayload());
49
+ } else {
50
+ printHelpTable();
51
+ }
52
+ return;
53
+ }
54
+
55
+ if (command === '--version' || command === '-V' || command === 'version') {
56
+ if (context.outputMode === 'json') {
57
+ emitSuccess(context.outputMode, 'version', { version: packageVersion });
58
+ } else {
59
+ // eslint-disable-next-line no-console
60
+ console.log(packageVersion);
61
+ }
62
+ return;
63
+ }
64
+
65
+ const handlers = {
66
+ 'init-env': async (handlerArgs, handlerContext) => {
67
+ if (includesHelpFlag(handlerArgs)) {
68
+ const usage = 'pandora [--output table|json] init-env [--force] [--dotenv-path <path>] [--example <path>]';
69
+ if (handlerContext.outputMode === 'json') {
70
+ emitSuccess(handlerContext.outputMode, 'init-env.help', commandHelpPayload(usage));
71
+ } else {
72
+ // eslint-disable-next-line no-console
73
+ console.log(`Usage: ${usage}`);
74
+ }
75
+ return;
76
+ }
77
+ runInitEnv(handlerArgs, handlerContext.outputMode);
78
+ },
79
+ doctor: async (handlerArgs, handlerContext) => {
80
+ if (includesHelpFlag(handlerArgs)) {
81
+ const usage =
82
+ 'pandora [--output table|json] doctor [--dotenv-path <path>] [--skip-dotenv] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]';
83
+ if (handlerContext.outputMode === 'json') {
84
+ emitSuccess(handlerContext.outputMode, 'doctor.help', commandHelpPayload(usage));
85
+ } else {
86
+ // eslint-disable-next-line no-console
87
+ console.log(`Usage: ${usage}`);
88
+ }
89
+ return;
90
+ }
91
+ await runDoctor(handlerArgs, handlerContext.outputMode);
92
+ },
93
+ setup: async (handlerArgs, handlerContext) => {
94
+ if (includesHelpFlag(handlerArgs)) {
95
+ const usage =
96
+ 'pandora [--output table|json] setup [--force] [--dotenv-path <path>] [--example <path>] [--check-usdc-code] [--check-polymarket] [--rpc-timeout-ms <ms>]';
97
+ if (handlerContext.outputMode === 'json') {
98
+ emitSuccess(handlerContext.outputMode, 'setup.help', commandHelpPayload(usage));
99
+ } else {
100
+ // eslint-disable-next-line no-console
101
+ console.log(`Usage: ${usage}`);
102
+ }
103
+ return;
104
+ }
105
+ await runSetup(handlerArgs, handlerContext.outputMode);
106
+ },
107
+ markets: async (handlerArgs, handlerContext) => runMarketsCommand(handlerArgs, handlerContext),
108
+ scan: async (handlerArgs, handlerContext) => runScanCommand(handlerArgs, handlerContext),
109
+ quote: async (handlerArgs, handlerContext) => runQuoteCommand(handlerArgs, handlerContext),
110
+ trade: async (handlerArgs, handlerContext) => runTradeCommand(handlerArgs, handlerContext),
111
+ polls: async (handlerArgs, handlerContext) => runPollsCommand(handlerArgs, handlerContext),
112
+ events: async (handlerArgs, handlerContext) => runEventsCommand(handlerArgs, handlerContext),
113
+ positions: async (handlerArgs, handlerContext) => runPositionsCommand(handlerArgs, handlerContext),
114
+ portfolio: async (handlerArgs, handlerContext) => runPortfolioCommand(handlerArgs, handlerContext),
115
+ watch: async (handlerArgs, handlerContext) => runWatchCommand(handlerArgs, handlerContext),
116
+ history: async (handlerArgs, handlerContext) => runHistoryCommand(handlerArgs, handlerContext),
117
+ export: async (handlerArgs, handlerContext) => runExportCommand(handlerArgs, handlerContext),
118
+ arbitrage: async (handlerArgs, handlerContext) => runArbitrageCommand(handlerArgs, handlerContext),
119
+ autopilot: async (handlerArgs, handlerContext) => runAutopilotCommand(handlerArgs, handlerContext),
120
+ mirror: async (handlerArgs, handlerContext) => runMirrorCommand(handlerArgs, handlerContext),
121
+ polymarket: async (handlerArgs, handlerContext) => runPolymarketCommand(handlerArgs, handlerContext),
122
+ webhook: async (handlerArgs, handlerContext) => runWebhookCommand(handlerArgs, handlerContext),
123
+ leaderboard: async (handlerArgs, handlerContext) => runLeaderboardCommand(handlerArgs, handlerContext),
124
+ analyze: async (handlerArgs, handlerContext) => runAnalyzeCommand(handlerArgs, handlerContext),
125
+ suggest: async (handlerArgs, handlerContext) => runSuggestCommand(handlerArgs, handlerContext),
126
+ resolve: async (handlerArgs, handlerContext) => runResolveCommand(handlerArgs, handlerContext),
127
+ lp: async (handlerArgs, handlerContext) => runLpCommand(handlerArgs, handlerContext),
128
+ launch: async (handlerArgs, handlerContext) => {
129
+ if (handlerContext.outputMode === 'json') {
130
+ throw new CliError(
131
+ 'UNSUPPORTED_OUTPUT_MODE',
132
+ '--output json is not supported for launch/clone-bet because these commands stream script output directly.',
133
+ );
134
+ }
135
+ runScriptCommand('launch', handlerArgs);
136
+ },
137
+ 'clone-bet': async (handlerArgs, handlerContext) => {
138
+ if (handlerContext.outputMode === 'json') {
139
+ throw new CliError(
140
+ 'UNSUPPORTED_OUTPUT_MODE',
141
+ '--output json is not supported for launch/clone-bet because these commands stream script output directly.',
142
+ );
143
+ }
144
+ runScriptCommand('clone-bet', handlerArgs);
145
+ },
146
+ };
147
+
148
+ const handler = handlers[command];
149
+ if (!handler) {
150
+ throw new CliError('UNKNOWN_COMMAND', `Unknown command: ${command}`, {
151
+ hints: ['Run `pandora help` to see available commands.'],
152
+ });
153
+ }
154
+
155
+ await handler(args, context);
156
+ };
157
+ }
158
+
159
+ module.exports = {
160
+ createCommandRouter,
161
+ };
162
+
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const crypto = require('crypto');
3
4
 
4
5
  const EXPORT_SCHEMA_VERSION = '1.0.0';
5
6
 
@@ -85,7 +86,23 @@ function maybeWriteOutput(content, outPath) {
85
86
  if (!outPath) return null;
86
87
  const resolved = path.resolve(outPath);
87
88
  fs.mkdirSync(path.dirname(resolved), { recursive: true });
88
- fs.writeFileSync(resolved, content);
89
+ const tmpPath = `${resolved}.${process.pid}.${Date.now()}.${crypto.randomBytes(4).toString('hex')}.tmp`;
90
+ try {
91
+ fs.writeFileSync(tmpPath, content, { mode: 0o600 });
92
+ fs.renameSync(tmpPath, resolved);
93
+ try {
94
+ fs.chmodSync(resolved, 0o600);
95
+ } catch {
96
+ // best-effort hardening on platforms that ignore/limit chmod
97
+ }
98
+ } catch (err) {
99
+ try {
100
+ if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
101
+ } catch {
102
+ // best-effort temp cleanup
103
+ }
104
+ throw err;
105
+ }
89
106
  return resolved;
90
107
  }
91
108
 
@@ -56,9 +56,23 @@ function saveManifest(filePath, manifest) {
56
56
  const resolved = resolveManifestPath(filePath);
57
57
  fs.mkdirSync(path.dirname(resolved), { recursive: true });
58
58
  const tmpPath = `${resolved}.${process.pid}.${Date.now()}.${crypto.randomBytes(4).toString('hex')}.tmp`;
59
- fs.writeFileSync(tmpPath, JSON.stringify(manifest, null, 2));
60
- fs.renameSync(tmpPath, resolved);
61
- return resolved;
59
+ try {
60
+ fs.writeFileSync(tmpPath, JSON.stringify(manifest, null, 2), { mode: 0o600 });
61
+ fs.renameSync(tmpPath, resolved);
62
+ try {
63
+ fs.chmodSync(resolved, 0o600);
64
+ } catch {
65
+ // best-effort hardening on platforms that ignore/limit chmod
66
+ }
67
+ return resolved;
68
+ } catch (err) {
69
+ try {
70
+ if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
71
+ } catch {
72
+ // best-effort temp cleanup
73
+ }
74
+ throw err;
75
+ }
62
76
  }
63
77
 
64
78
  function pairMatches(record, selector = {}) {
@@ -190,8 +190,25 @@ function writeCacheFile(cacheFile, payload) {
190
190
  const dir = path.dirname(cacheFile);
191
191
  fs.mkdirSync(dir, { recursive: true });
192
192
  const tmpPath = `${cacheFile}.${process.pid}.${Date.now()}.${crypto.randomBytes(4).toString('hex')}.tmp`;
193
- fs.writeFileSync(tmpPath, JSON.stringify(payload, null, 2));
194
- fs.renameSync(tmpPath, cacheFile);
193
+ let wroteTmp = false;
194
+ try {
195
+ fs.writeFileSync(tmpPath, JSON.stringify(payload, null, 2), { mode: 0o600 });
196
+ wroteTmp = true;
197
+ fs.renameSync(tmpPath, cacheFile);
198
+ try {
199
+ fs.chmodSync(cacheFile, 0o600);
200
+ } catch {
201
+ // best-effort hardening on platforms that ignore/limit chmod
202
+ }
203
+ } finally {
204
+ if (wroteTmp && fs.existsSync(tmpPath)) {
205
+ try {
206
+ fs.unlinkSync(tmpPath);
207
+ } catch {
208
+ // best-effort cleanup
209
+ }
210
+ }
211
+ }
195
212
  }
196
213
 
197
214
  function buildCachePayload(options, host, marketRow, orderbooks = null, sourceType = 'polymarket:clob') {
package/cli/pandora.cjs CHANGED
@@ -3,6 +3,8 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { spawnSync } = require('child_process');
6
+ const { createCommandRouter } = require('./lib/command_router.cjs');
7
+ const { createCliOutputService } = require('./lib/cli_output_service.cjs');
6
8
  const {
7
9
  fetchHistory,
8
10
  } = require('./lib/history_service.cjs');
@@ -78,6 +80,7 @@ const COMMAND_TARGETS = {
78
80
  };
79
81
 
80
82
  const OUTPUT_MODES = new Set(['table', 'json']);
83
+ const CLI_JSON_SCHEMA_VERSION = '1.0.0';
81
84
  const DEFAULT_RPC_TIMEOUT_MS = 12_000;
82
85
  const DEFAULT_INDEXER_TIMEOUT_MS = 12_000;
83
86
  const DEFAULT_POLYMARKET_HOST = 'https://clob.polymarket.com';
@@ -231,6 +234,11 @@ class CliError extends Error {
231
234
  }
232
235
  }
233
236
 
237
+ const { emitFailure, emitSuccess } = createCliOutputService({
238
+ defaultSchemaVersion: CLI_JSON_SCHEMA_VERSION,
239
+ CliError,
240
+ });
241
+
234
242
  function printHelpTable() {
235
243
  console.log(`
236
244
  pandora - Prediction market CLI
@@ -292,7 +300,7 @@ Examples:
292
300
  pandora mirror hedge-calc --reserve-yes-usdc 8 --reserve-no-usdc 12 --excess-no-usdc 2 --polymarket-yes-pct 60
293
301
  pandora mirror simulate --liquidity-usdc 10000 --source-yes-pct 58 --target-yes-pct 58 --volume-scenarios 1000,5000,10000
294
302
  pandora mirror go --polymarket-slug nba-mia-phi-2026-02-28 --liquidity-usdc 10 --paper
295
- pandora mirror sync once --pandora-market-address 0xabc... --polymarket-market-id 0xdef... --paper --hedge-ratio 1.0 --force-gate
303
+ pandora mirror sync once --pandora-market-address 0xabc... --polymarket-market-id 0xdef... --paper --hedge-ratio 1.0 --skip-gate
296
304
  pandora polymarket check --rpc-url https://polygon-bor-rpc.publicnode.com --private-key 0x... --funder 0xproxy...
297
305
  pandora polymarket approve --dry-run --rpc-url https://polygon-bor-rpc.publicnode.com --private-key 0x... --funder 0xproxy...
298
306
  pandora polymarket preflight --rpc-url https://polygon-bor-rpc.publicnode.com --private-key 0x... --funder 0xproxy...
@@ -613,6 +621,12 @@ function extractOutputMode(argv) {
613
621
 
614
622
  function parseDateLikeFlag(value, flagName) {
615
623
  const text = String(value || '').trim();
624
+ if (/^-?\d+(\.\d+)?$/.test(text)) {
625
+ throw new CliError(
626
+ 'INVALID_FLAG_VALUE',
627
+ `${flagName} must be a date/time string (for example: "2026-03-15" or "2026-03-15T18:00:00Z"), not a bare number.`,
628
+ );
629
+ }
616
630
  const parsed = Date.parse(text);
617
631
  if (Number.isNaN(parsed)) {
618
632
  throw new CliError(
@@ -623,38 +637,6 @@ function parseDateLikeFlag(value, flagName) {
623
637
  return text;
624
638
  }
625
639
 
626
- function emitJson(payload) {
627
- console.log(JSON.stringify(payload, null, 2));
628
- }
629
-
630
- function emitJsonError(payload) {
631
- console.log(JSON.stringify(payload, null, 2));
632
- }
633
-
634
- function toErrorEnvelope(error) {
635
- if (error instanceof CliError) {
636
- const envelope = {
637
- ok: false,
638
- error: {
639
- code: error.code,
640
- message: error.message,
641
- },
642
- };
643
- if (error.details !== undefined) {
644
- envelope.error.details = error.details;
645
- }
646
- return envelope;
647
- }
648
-
649
- return {
650
- ok: false,
651
- error: {
652
- code: 'UNEXPECTED_ERROR',
653
- message: formatErrorValue(error && error.message ? error.message : error),
654
- },
655
- };
656
- }
657
-
658
640
  function formatErrorValue(value) {
659
641
  if (typeof value === 'string') return value;
660
642
  if (value && typeof value.message === 'string') return value.message;
@@ -666,60 +648,6 @@ function formatErrorValue(value) {
666
648
  }
667
649
  }
668
650
 
669
- let failureAlreadyEmitted = false;
670
-
671
- function emitFailure(outputMode, error) {
672
- if (failureAlreadyEmitted) {
673
- process.exit(error instanceof CliError ? error.exitCode : 1);
674
- }
675
- failureAlreadyEmitted = true;
676
-
677
- const envelope = toErrorEnvelope(error);
678
-
679
- if (outputMode === 'json') {
680
- emitJsonError(envelope);
681
- } else {
682
- console.error(`[${envelope.error.code}] ${envelope.error.message}`);
683
- if (envelope.error.details && Array.isArray(envelope.error.details.errors) && envelope.error.details.errors.length) {
684
- for (const err of envelope.error.details.errors) {
685
- console.error(`- ${formatErrorValue(err)}`);
686
- }
687
- }
688
- if (envelope.error.details && Array.isArray(envelope.error.details.hints) && envelope.error.details.hints.length) {
689
- for (const hint of envelope.error.details.hints) {
690
- console.error(`Hint: ${hint}`);
691
- }
692
- }
693
- if (
694
- envelope.error.details &&
695
- !Array.isArray(envelope.error.details.errors) &&
696
- !Array.isArray(envelope.error.details.hints)
697
- ) {
698
- try {
699
- console.error(`Details: ${JSON.stringify(envelope.error.details)}`);
700
- } catch {
701
- console.error(`Details: ${String(envelope.error.details)}`);
702
- }
703
- }
704
- }
705
-
706
- process.exit(error instanceof CliError ? error.exitCode : 1);
707
- }
708
-
709
- function emitSuccess(outputMode, command, data, tableRenderer) {
710
- if (outputMode === 'json') {
711
- emitJson({ ok: true, command, data });
712
- return;
713
- }
714
-
715
- if (typeof tableRenderer === 'function') {
716
- tableRenderer(data);
717
- return;
718
- }
719
-
720
- console.log('Done.');
721
- }
722
-
723
651
  function parseDotEnv(content) {
724
652
  const env = {};
725
653
  const lines = content.split(/\r?\n/);
@@ -753,6 +681,22 @@ function isValidHttpUrl(value) {
753
681
  }
754
682
  }
755
683
 
684
+ function isLocalHostname(hostname) {
685
+ const host = String(hostname || '').trim().toLowerCase();
686
+ return host === 'localhost' || host === '127.0.0.1' || host === '::1';
687
+ }
688
+
689
+ function isSecureHttpUrlOrLocal(value) {
690
+ try {
691
+ const parsed = new URL(value);
692
+ if (parsed.protocol === 'https:') return true;
693
+ if (parsed.protocol !== 'http:') return false;
694
+ return isLocalHostname(parsed.hostname);
695
+ } catch {
696
+ return false;
697
+ }
698
+ }
699
+
756
700
  function isValidAddress(value) {
757
701
  return /^0x[a-fA-F0-9]{40}$/.test(value);
758
702
  }
@@ -963,6 +907,13 @@ function mergeWhere(where, jsonText, flagName) {
963
907
  throw new CliError('INVALID_JSON', `${flagName} must decode to a JSON object.`);
964
908
  }
965
909
 
910
+ const forbiddenKeys = new Set(['__proto__', 'prototype', 'constructor']);
911
+ for (const key of Object.keys(parsed)) {
912
+ if (forbiddenKeys.has(key)) {
913
+ throw new CliError('INVALID_JSON', `${flagName} contains forbidden key: "${key}".`);
914
+ }
915
+ }
916
+
966
917
  return { ...where, ...parsed };
967
918
  }
968
919
 
@@ -1837,7 +1788,14 @@ function parsePortfolioFlags(args) {
1837
1788
 
1838
1789
  function parseWebhookFlagIntoOptions(args, i, token, options) {
1839
1790
  if (token === '--webhook-url') {
1840
- options.webhookUrl = requireFlagValue(args, i, '--webhook-url');
1791
+ const value = requireFlagValue(args, i, '--webhook-url');
1792
+ if (!isSecureHttpUrlOrLocal(value)) {
1793
+ throw new CliError(
1794
+ 'INVALID_FLAG_VALUE',
1795
+ '--webhook-url must use https:// (or http://localhost/127.0.0.1 for local testing).',
1796
+ );
1797
+ }
1798
+ options.webhookUrl = value;
1841
1799
  return 1;
1842
1800
  }
1843
1801
  if (token === '--webhook-template') {
@@ -1871,7 +1829,14 @@ function parseWebhookFlagIntoOptions(args, i, token, options) {
1871
1829
  return 1;
1872
1830
  }
1873
1831
  if (token === '--discord-webhook-url') {
1874
- options.discordWebhookUrl = requireFlagValue(args, i, '--discord-webhook-url');
1832
+ const value = requireFlagValue(args, i, '--discord-webhook-url');
1833
+ if (!isSecureHttpUrlOrLocal(value)) {
1834
+ throw new CliError(
1835
+ 'INVALID_FLAG_VALUE',
1836
+ '--discord-webhook-url must use https:// (or http://localhost/127.0.0.1 for local testing).',
1837
+ );
1838
+ }
1839
+ options.discordWebhookUrl = value;
1875
1840
  return 1;
1876
1841
  }
1877
1842
  if (token === '--fail-on-webhook-error') {
@@ -3377,6 +3342,7 @@ function parseMirrorSyncFlags(args) {
3377
3342
  failOnWebhookError: false,
3378
3343
  daemon: false,
3379
3344
  forceGate: false,
3345
+ forceGateDeprecatedUsed: false,
3380
3346
  };
3381
3347
  let sawPaperModeFlag = false;
3382
3348
  let sawExecuteLiveModeFlag = false;
@@ -3524,7 +3490,14 @@ function parseMirrorSyncFlags(args) {
3524
3490
  continue;
3525
3491
  }
3526
3492
  if (token === '--rpc-url') {
3527
- options.rpcUrl = requireFlagValue(rest, i, '--rpc-url');
3493
+ const rpcUrl = requireFlagValue(rest, i, '--rpc-url');
3494
+ if (!isSecureHttpUrlOrLocal(rpcUrl)) {
3495
+ throw new CliError(
3496
+ 'INVALID_FLAG_VALUE',
3497
+ '--rpc-url must use https:// (or http://localhost/127.0.0.1 for local testing).',
3498
+ );
3499
+ }
3500
+ options.rpcUrl = rpcUrl;
3528
3501
  i += 1;
3529
3502
  continue;
3530
3503
  }
@@ -3544,7 +3517,14 @@ function parseMirrorSyncFlags(args) {
3544
3517
  continue;
3545
3518
  }
3546
3519
  if (token === '--polymarket-host') {
3547
- options.polymarketHost = requireFlagValue(rest, i, '--polymarket-host');
3520
+ const polymarketHost = requireFlagValue(rest, i, '--polymarket-host');
3521
+ if (!isSecureHttpUrlOrLocal(polymarketHost)) {
3522
+ throw new CliError(
3523
+ 'INVALID_FLAG_VALUE',
3524
+ '--polymarket-host must use https:// (or http://localhost/127.0.0.1 for local testing).',
3525
+ );
3526
+ }
3527
+ options.polymarketHost = polymarketHost;
3548
3528
  i += 1;
3549
3529
  continue;
3550
3530
  }
@@ -3567,8 +3547,13 @@ function parseMirrorSyncFlags(args) {
3567
3547
  options.trustDeploy = true;
3568
3548
  continue;
3569
3549
  }
3570
- if (token === '--force-gate' || token === '--skip-gate') {
3550
+ if (token === '--skip-gate') {
3551
+ options.forceGate = true;
3552
+ continue;
3553
+ }
3554
+ if (token === '--force-gate') {
3571
3555
  options.forceGate = true;
3556
+ options.forceGateDeprecatedUsed = true;
3572
3557
  continue;
3573
3558
  }
3574
3559
  if (token === '--manifest-file') {
@@ -3753,7 +3738,7 @@ function buildMirrorSyncDaemonCliArgs(options, shared) {
3753
3738
  if (options.polymarketGammaMockUrl) args.push('--polymarket-gamma-mock-url', options.polymarketGammaMockUrl);
3754
3739
  if (options.polymarketMockUrl) args.push('--polymarket-mock-url', options.polymarketMockUrl);
3755
3740
  if (options.trustDeploy) args.push('--trust-deploy');
3756
- if (options.forceGate) args.push('--force-gate');
3741
+ if (options.forceGate) args.push('--skip-gate');
3757
3742
  if (options.manifestFile) args.push('--manifest-file', options.manifestFile);
3758
3743
 
3759
3744
  if (options.webhookUrl) args.push('--webhook-url', options.webhookUrl);
@@ -3803,6 +3788,7 @@ function parseMirrorGoFlags(args) {
3803
3788
  manifestFile: null,
3804
3789
  trustDeploy: false,
3805
3790
  forceGate: false,
3791
+ forceGateDeprecatedUsed: false,
3806
3792
  polymarketHost: null,
3807
3793
  polymarketGammaUrl: null,
3808
3794
  polymarketGammaMockUrl: null,
@@ -3932,7 +3918,14 @@ function parseMirrorGoFlags(args) {
3932
3918
  continue;
3933
3919
  }
3934
3920
  if (token === '--rpc-url') {
3935
- options.rpcUrl = requireFlagValue(args, i, '--rpc-url');
3921
+ const rpcUrl = requireFlagValue(args, i, '--rpc-url');
3922
+ if (!isSecureHttpUrlOrLocal(rpcUrl)) {
3923
+ throw new CliError(
3924
+ 'INVALID_FLAG_VALUE',
3925
+ '--rpc-url must use https:// (or http://localhost/127.0.0.1 for local testing).',
3926
+ );
3927
+ }
3928
+ options.rpcUrl = rpcUrl;
3936
3929
  i += 1;
3937
3930
  continue;
3938
3931
  }
@@ -3984,12 +3977,24 @@ function parseMirrorGoFlags(args) {
3984
3977
  options.trustDeploy = true;
3985
3978
  continue;
3986
3979
  }
3987
- if (token === '--force-gate' || token === '--skip-gate') {
3980
+ if (token === '--skip-gate') {
3988
3981
  options.forceGate = true;
3989
3982
  continue;
3990
3983
  }
3984
+ if (token === '--force-gate') {
3985
+ options.forceGate = true;
3986
+ options.forceGateDeprecatedUsed = true;
3987
+ continue;
3988
+ }
3991
3989
  if (token === '--polymarket-host') {
3992
- options.polymarketHost = requireFlagValue(args, i, '--polymarket-host');
3990
+ const polymarketHost = requireFlagValue(args, i, '--polymarket-host');
3991
+ if (!isSecureHttpUrlOrLocal(polymarketHost)) {
3992
+ throw new CliError(
3993
+ 'INVALID_FLAG_VALUE',
3994
+ '--polymarket-host must use https:// (or http://localhost/127.0.0.1 for local testing).',
3995
+ );
3996
+ }
3997
+ options.polymarketHost = polymarketHost;
3993
3998
  i += 1;
3994
3999
  continue;
3995
4000
  }
@@ -4440,7 +4445,14 @@ function parsePolymarketSharedFlags(args, actionLabel) {
4440
4445
  for (let i = 0; i < args.length; i += 1) {
4441
4446
  const token = args[i];
4442
4447
  if (token === '--rpc-url') {
4443
- options.rpcUrl = requireFlagValue(args, i, '--rpc-url');
4448
+ const rpcUrl = requireFlagValue(args, i, '--rpc-url');
4449
+ if (!isSecureHttpUrlOrLocal(rpcUrl)) {
4450
+ throw new CliError(
4451
+ 'INVALID_FLAG_VALUE',
4452
+ '--rpc-url must use https:// (or http://localhost/127.0.0.1 for local testing).',
4453
+ );
4454
+ }
4455
+ options.rpcUrl = rpcUrl;
4444
4456
  i += 1;
4445
4457
  continue;
4446
4458
  }
@@ -4557,7 +4569,14 @@ function parsePolymarketTradeFlags(args) {
4557
4569
  continue;
4558
4570
  }
4559
4571
  if (token === '--polymarket-host') {
4560
- options.host = requireFlagValue(args, i, '--polymarket-host');
4572
+ const host = requireFlagValue(args, i, '--polymarket-host');
4573
+ if (!isSecureHttpUrlOrLocal(host)) {
4574
+ throw new CliError(
4575
+ 'INVALID_FLAG_VALUE',
4576
+ '--polymarket-host must use https:// (or http://localhost/127.0.0.1 for local testing).',
4577
+ );
4578
+ }
4579
+ options.host = host;
4561
4580
  i += 1;
4562
4581
  continue;
4563
4582
  }
@@ -5109,8 +5128,8 @@ function validateEnvValues() {
5109
5128
  }
5110
5129
 
5111
5130
  const rpcUrl = String(process.env.RPC_URL || '').trim();
5112
- if (!missingSet.has('RPC_URL') && !isValidHttpUrl(rpcUrl)) {
5113
- errors.push(`RPC_URL must be a valid http/https URL. Received: "${rpcUrl}"`);
5131
+ if (!missingSet.has('RPC_URL') && !isSecureHttpUrlOrLocal(rpcUrl)) {
5132
+ errors.push(`RPC_URL must use https:// (or http://localhost/127.0.0.1 for local testing). Received: "${rpcUrl}"`);
5114
5133
  }
5115
5134
 
5116
5135
  const privateKey = String(process.env.PANDORA_PRIVATE_KEY || process.env.PRIVATE_KEY || '').trim();
@@ -7313,8 +7332,11 @@ function resolveTradeRuntimeConfig(options) {
7313
7332
  }
7314
7333
 
7315
7334
  const rpcUrl = options.rpcUrl || process.env.RPC_URL || DEFAULT_RPC_BY_CHAIN_ID[chainIdRaw];
7316
- if (!isValidHttpUrl(rpcUrl)) {
7317
- throw new CliError('INVALID_FLAG_VALUE', `RPC URL must be a valid http/https URL. Received: "${rpcUrl}"`);
7335
+ if (!isSecureHttpUrlOrLocal(rpcUrl)) {
7336
+ throw new CliError(
7337
+ 'INVALID_FLAG_VALUE',
7338
+ `RPC URL must use https:// (or http://localhost/127.0.0.1 for local testing). Received: "${rpcUrl}"`,
7339
+ );
7318
7340
  }
7319
7341
 
7320
7342
  const privateKey = options.privateKey || process.env.PANDORA_PRIVATE_KEY || process.env.PRIVATE_KEY;
@@ -7704,6 +7726,23 @@ async function runMarketsCommand(args, context) {
7704
7726
 
7705
7727
  async function runScanCommand(args, context) {
7706
7728
  const shared = parseIndexerSharedFlags(args);
7729
+ if (includesHelpFlag(shared.rest)) {
7730
+ if (context.outputMode === 'json') {
7731
+ emitSuccess(
7732
+ context.outputMode,
7733
+ 'scan.help',
7734
+ commandHelpPayload(
7735
+ '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]',
7736
+ ),
7737
+ );
7738
+ } else {
7739
+ console.log(
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
+
7707
7746
  maybeLoadIndexerEnv(shared);
7708
7747
  const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
7709
7748
 
@@ -8300,10 +8339,12 @@ async function runPortfolioCommand(args, context) {
8300
8339
  if (context.outputMode === 'json') {
8301
8340
  emitSuccess(context.outputMode, 'portfolio.help', {
8302
8341
  usage:
8303
- 'pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events]',
8342
+ 'pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]',
8304
8343
  });
8305
8344
  } else {
8306
- console.log('Usage: pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events]');
8345
+ console.log(
8346
+ 'Usage: pandora [--output table|json] portfolio --wallet <address> [--chain-id <id>] [--limit <n>] [--include-events|--no-events] [--with-lp] [--rpc-url <url>]',
8347
+ );
8307
8348
  }
8308
8349
  return;
8309
8350
  }
@@ -8888,10 +8929,10 @@ async function runMirrorCommand(args, context) {
8888
8929
  ' 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>]',
8889
8930
  );
8890
8931
  console.log(
8891
- ' 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] [--force-gate]',
8932
+ ' 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]',
8892
8933
  );
8893
8934
  console.log(
8894
- ' 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] [--force-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>]',
8935
+ ' 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>]',
8895
8936
  );
8896
8937
  console.log(' status --state-file <path>|--strategy-hash <hash> [--with-live] [--trust-deploy]');
8897
8938
  console.log(' close --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute');
@@ -9335,7 +9376,7 @@ async function runMirrorCommand(args, context) {
9335
9376
  if (action === 'go') {
9336
9377
  if (includesHelpFlag(shared.rest)) {
9337
9378
  const usage =
9338
- 'pandora [--output table|json] mirror 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] [--force-gate]';
9379
+ 'pandora [--output table|json] mirror 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]';
9339
9380
  if (context.outputMode === 'json') {
9340
9381
  emitSuccess(context.outputMode, 'mirror.go.help', commandHelpPayload(usage));
9341
9382
  } else {
@@ -9347,6 +9388,12 @@ async function runMirrorCommand(args, context) {
9347
9388
  maybeLoadTradeEnv(shared);
9348
9389
  const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
9349
9390
  const options = parseMirrorGoFlags(shared.rest);
9391
+ const deprecatedForceGateWarning = options.forceGateDeprecatedUsed
9392
+ ? 'Flag --force-gate is deprecated; use --skip-gate instead.'
9393
+ : null;
9394
+ if (deprecatedForceGateWarning && context.outputMode === 'table') {
9395
+ console.error(`Warning: ${deprecatedForceGateWarning}`);
9396
+ }
9350
9397
 
9351
9398
  let planPayload;
9352
9399
  let deployPayload;
@@ -9385,6 +9432,7 @@ async function runMirrorCommand(args, context) {
9385
9432
  trustDeploy = true;
9386
9433
  }
9387
9434
  const diagnostics = [];
9435
+ if (deprecatedForceGateWarning) diagnostics.push(deprecatedForceGateWarning);
9388
9436
 
9389
9437
  if (pandoraMarketAddress) {
9390
9438
  if (!trustManifest && (options.executeLive || options.trustDeploy)) {
@@ -9538,7 +9586,7 @@ async function runMirrorCommand(args, context) {
9538
9586
  '--paper',
9539
9587
  `--drift-trigger-bps ${options.driftTriggerBps}`,
9540
9588
  `--hedge-trigger-usdc ${options.hedgeTriggerUsdc}`,
9541
- options.forceGate ? '--force-gate' : null,
9589
+ options.forceGate ? '--skip-gate' : null,
9542
9590
  ]
9543
9591
  .filter(Boolean)
9544
9592
  .join(' ');
@@ -9581,7 +9629,7 @@ async function runMirrorCommand(args, context) {
9581
9629
  'mirror.sync.help',
9582
9630
  {
9583
9631
  usage:
9584
- 'pandora [--output table|json] mirror 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] [--force-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>]',
9632
+ 'pandora [--output table|json] mirror 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>]',
9585
9633
  daemonLifecycle: {
9586
9634
  start:
9587
9635
  'pandora [--output table|json] mirror sync start --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [run flags]',
@@ -9610,7 +9658,7 @@ async function runMirrorCommand(args, context) {
9610
9658
  );
9611
9659
  } else {
9612
9660
  console.log(
9613
- 'Usage: pandora [--output table|json] mirror 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] [--force-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>]',
9661
+ 'Usage: pandora [--output table|json] mirror 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>]',
9614
9662
  );
9615
9663
  console.log('Daemon stop: pandora mirror sync stop --pid-file <path>|--strategy-hash <hash>');
9616
9664
  console.log('Daemon status: pandora mirror sync status --pid-file <path>|--strategy-hash <hash>');
@@ -9657,6 +9705,12 @@ async function runMirrorCommand(args, context) {
9657
9705
  maybeLoadTradeEnv(shared);
9658
9706
  const indexerUrl = resolveIndexerUrl(shared.indexerUrl);
9659
9707
  const options = parseMirrorSyncFlags(syncRunArgs);
9708
+ const deprecatedForceGateWarning = options.forceGateDeprecatedUsed
9709
+ ? 'Flag --force-gate is deprecated; use --skip-gate instead.'
9710
+ : null;
9711
+ if (deprecatedForceGateWarning && context.outputMode === 'table') {
9712
+ console.error(`Warning: ${deprecatedForceGateWarning}`);
9713
+ }
9660
9714
  if (isStartAction) {
9661
9715
  options.daemon = true;
9662
9716
  }
@@ -9723,6 +9777,10 @@ async function runMirrorCommand(args, context) {
9723
9777
  if (trustManifest) {
9724
9778
  daemonPayload.trustManifest = trustManifest;
9725
9779
  }
9780
+ if (deprecatedForceGateWarning) {
9781
+ const existingDiagnostics = Array.isArray(daemonPayload.diagnostics) ? daemonPayload.diagnostics : [];
9782
+ daemonPayload.diagnostics = [...existingDiagnostics, deprecatedForceGateWarning];
9783
+ }
9726
9784
 
9727
9785
  emitSuccess(
9728
9786
  context.outputMode,
@@ -9733,6 +9791,13 @@ async function runMirrorCommand(args, context) {
9733
9791
  return;
9734
9792
  }
9735
9793
 
9794
+ if (context.outputMode === 'json' && options.stream) {
9795
+ throw new CliError(
9796
+ 'INVALID_ARGS',
9797
+ '--stream is only supported in table output mode. Use --output table or remove --stream.',
9798
+ );
9799
+ }
9800
+
9736
9801
  const streamTicks = options.stream || (options.mode === 'run' && context.outputMode === 'table');
9737
9802
  let polymarketPreflight = null;
9738
9803
 
@@ -9814,6 +9879,10 @@ async function runMirrorCommand(args, context) {
9814
9879
  if (polymarketPreflight) {
9815
9880
  payload.polymarketPreflight = polymarketPreflight;
9816
9881
  }
9882
+ if (deprecatedForceGateWarning) {
9883
+ const existingDiagnostics = Array.isArray(payload.diagnostics) ? payload.diagnostics : [];
9884
+ payload.diagnostics = [...existingDiagnostics, deprecatedForceGateWarning];
9885
+ }
9817
9886
 
9818
9887
  emitSuccess(context.outputMode, 'mirror.sync', payload, renderMirrorSyncTable);
9819
9888
  return;
@@ -10066,6 +10135,17 @@ async function runWebhookCommand(args, context) {
10066
10135
  throw new CliError('INVALID_ARGS', 'webhook requires subcommand: test');
10067
10136
  }
10068
10137
 
10138
+ if (includesHelpFlag(actionArgs)) {
10139
+ const usage =
10140
+ 'pandora [--output table|json] webhook test [--webhook-url <url>] [--webhook-template <json>] [--webhook-secret <secret>] [--telegram-bot-token <token>] [--telegram-chat-id <id>] [--discord-webhook-url <url>] [--webhook-timeout-ms <ms>] [--webhook-retries <n>]';
10141
+ if (context.outputMode === 'json') {
10142
+ emitSuccess(context.outputMode, 'webhook.test.help', commandHelpPayload(usage));
10143
+ } else {
10144
+ console.log(`Usage: ${usage}`);
10145
+ }
10146
+ return;
10147
+ }
10148
+
10069
10149
  const options = parseWebhookTestFlags(actionArgs);
10070
10150
  const payload = await sendWebhookNotifications(options, {
10071
10151
  event: 'webhook.test',
@@ -10358,6 +10438,11 @@ function runInitEnv(args, outputMode) {
10358
10438
 
10359
10439
  fs.mkdirSync(path.dirname(options.envFile), { recursive: true });
10360
10440
  fs.copyFileSync(options.exampleFile, options.envFile);
10441
+ try {
10442
+ fs.chmodSync(options.envFile, 0o600);
10443
+ } catch {
10444
+ // best-effort hardening on platforms that ignore/limit chmod
10445
+ }
10361
10446
 
10362
10447
  emitSuccess(outputMode, 'init-env', {
10363
10448
  envFile: options.envFile,
@@ -10416,6 +10501,11 @@ async function runSetup(args, outputMode) {
10416
10501
 
10417
10502
  let envStep;
10418
10503
  if (fs.existsSync(options.envFile) && !options.force) {
10504
+ try {
10505
+ fs.chmodSync(options.envFile, 0o600);
10506
+ } catch {
10507
+ // best-effort hardening on pre-existing files
10508
+ }
10419
10509
  envStep = {
10420
10510
  status: 'skipped',
10421
10511
  message: `Env file exists at ${options.envFile}. Reusing existing file.`,
@@ -10425,6 +10515,11 @@ async function runSetup(args, outputMode) {
10425
10515
  } else {
10426
10516
  fs.mkdirSync(path.dirname(options.envFile), { recursive: true });
10427
10517
  fs.copyFileSync(options.exampleFile, options.envFile);
10518
+ try {
10519
+ fs.chmodSync(options.envFile, 0o600);
10520
+ } catch {
10521
+ // best-effort hardening on platforms that ignore/limit chmod
10522
+ }
10428
10523
  envStep = {
10429
10524
  status: 'written',
10430
10525
  message: `Wrote env file: ${options.envFile}`,
@@ -10460,160 +10555,40 @@ async function runSetup(args, outputMode) {
10460
10555
  emitSuccess(outputMode, 'setup', payload, renderSetupTable);
10461
10556
  }
10462
10557
 
10463
- async function dispatch(command, args, context) {
10464
- if (!command || command === 'help' || command === '--help' || command === '-h') {
10465
- if (context.outputMode === 'json') {
10466
- emitSuccess(context.outputMode, 'help', helpJsonPayload());
10467
- } else {
10468
- printHelpTable();
10469
- }
10470
- return;
10471
- }
10472
-
10473
- if (command === '--version' || command === '-V' || command === 'version') {
10474
- if (context.outputMode === 'json') {
10475
- emitSuccess(context.outputMode, 'version', { version: PACKAGE_VERSION });
10476
- } else {
10477
- console.log(PACKAGE_VERSION);
10478
- }
10479
- return;
10480
- }
10481
-
10482
- if (command === 'init-env') {
10483
- runInitEnv(args, context.outputMode);
10484
- return;
10485
- }
10486
-
10487
- if (command === 'doctor') {
10488
- await runDoctor(args, context.outputMode);
10489
- return;
10490
- }
10491
-
10492
- if (command === 'setup') {
10493
- await runSetup(args, context.outputMode);
10494
- return;
10495
- }
10496
-
10497
- if (command === 'markets') {
10498
- await runMarketsCommand(args, context);
10499
- return;
10500
- }
10501
-
10502
- if (command === 'scan') {
10503
- await runScanCommand(args, context);
10504
- return;
10505
- }
10506
-
10507
- if (command === 'quote') {
10508
- await runQuoteCommand(args, context);
10509
- return;
10510
- }
10511
-
10512
- if (command === 'trade') {
10513
- await runTradeCommand(args, context);
10514
- return;
10515
- }
10516
-
10517
- if (command === 'polls') {
10518
- await runPollsCommand(args, context);
10519
- return;
10520
- }
10521
-
10522
- if (command === 'events') {
10523
- await runEventsCommand(args, context);
10524
- return;
10525
- }
10526
-
10527
- if (command === 'positions') {
10528
- await runPositionsCommand(args, context);
10529
- return;
10530
- }
10531
-
10532
- if (command === 'portfolio') {
10533
- await runPortfolioCommand(args, context);
10534
- return;
10535
- }
10536
-
10537
- if (command === 'watch') {
10538
- await runWatchCommand(args, context);
10539
- return;
10540
- }
10541
-
10542
- if (command === 'history') {
10543
- await runHistoryCommand(args, context);
10544
- return;
10545
- }
10546
-
10547
- if (command === 'export') {
10548
- await runExportCommand(args, context);
10549
- return;
10550
- }
10551
-
10552
- if (command === 'arbitrage') {
10553
- await runArbitrageCommand(args, context);
10554
- return;
10555
- }
10556
-
10557
- if (command === 'autopilot') {
10558
- await runAutopilotCommand(args, context);
10559
- return;
10560
- }
10561
-
10562
- if (command === 'mirror') {
10563
- await runMirrorCommand(args, context);
10564
- return;
10565
- }
10566
-
10567
- if (command === 'polymarket') {
10568
- await runPolymarketCommand(args, context);
10569
- return;
10570
- }
10571
-
10572
- if (command === 'webhook') {
10573
- await runWebhookCommand(args, context);
10574
- return;
10575
- }
10576
-
10577
- if (command === 'leaderboard') {
10578
- await runLeaderboardCommand(args, context);
10579
- return;
10580
- }
10581
-
10582
- if (command === 'analyze') {
10583
- await runAnalyzeCommand(args, context);
10584
- return;
10585
- }
10586
-
10587
- if (command === 'suggest') {
10588
- await runSuggestCommand(args, context);
10589
- return;
10590
- }
10591
-
10592
- if (command === 'resolve') {
10593
- await runResolveCommand(args, context);
10594
- return;
10595
- }
10596
-
10597
- if (command === 'lp') {
10598
- await runLpCommand(args, context);
10599
- return;
10600
- }
10601
-
10602
- if (command === 'launch' || command === 'clone-bet') {
10603
- if (context.outputMode === 'json') {
10604
- throw new CliError(
10605
- 'UNSUPPORTED_OUTPUT_MODE',
10606
- '--output json is not supported for launch/clone-bet because these commands stream script output directly.',
10607
- );
10608
- }
10609
- runScriptCommand(command, args);
10610
- return;
10611
- }
10612
-
10613
- throw new CliError('UNKNOWN_COMMAND', `Unknown command: ${command}`, {
10614
- hints: ['Run `pandora help` to see available commands.'],
10615
- });
10616
- }
10558
+ const dispatch = createCommandRouter({
10559
+ CliError,
10560
+ packageVersion: PACKAGE_VERSION,
10561
+ emitSuccess,
10562
+ helpJsonPayload,
10563
+ printHelpTable,
10564
+ includesHelpFlag,
10565
+ commandHelpPayload,
10566
+ runInitEnv,
10567
+ runDoctor,
10568
+ runSetup,
10569
+ runMarketsCommand,
10570
+ runScanCommand,
10571
+ runQuoteCommand,
10572
+ runTradeCommand,
10573
+ runPollsCommand,
10574
+ runEventsCommand,
10575
+ runPositionsCommand,
10576
+ runPortfolioCommand,
10577
+ runWatchCommand,
10578
+ runHistoryCommand,
10579
+ runExportCommand,
10580
+ runArbitrageCommand,
10581
+ runAutopilotCommand,
10582
+ runMirrorCommand,
10583
+ runPolymarketCommand,
10584
+ runWebhookCommand,
10585
+ runLeaderboardCommand,
10586
+ runAnalyzeCommand,
10587
+ runSuggestCommand,
10588
+ runResolveCommand,
10589
+ runLpCommand,
10590
+ runScriptCommand,
10591
+ });
10617
10592
 
10618
10593
  async function main() {
10619
10594
  const rawArgv = expandEqualsStyleFlags(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pandora-cli-skills",
3
- "version": "1.1.25",
3
+ "version": "1.1.27",
4
4
  "description": "Pandora CLI & Skills",
5
5
  "main": "cli/pandora.cjs",
6
6
  "bin": {