@hedge-layer/cli 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -0
- package/dist/index.mjs +796 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command5 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/auth.ts
|
|
7
7
|
import readline from "readline/promises";
|
|
@@ -838,11 +838,12 @@ function registerResearchCommands(program2) {
|
|
|
838
838
|
});
|
|
839
839
|
table(rows, ["ID", "Status", "Brief", "Markets", "Created"]);
|
|
840
840
|
});
|
|
841
|
-
research.command("show <id>").description("Show research session details").action(async (id) => {
|
|
841
|
+
research.command("show <id>").description("Show research session details (accepts the short ID shown by `hl research list`)").action(async (id) => {
|
|
842
842
|
const globalOpts = program2.opts();
|
|
843
843
|
const client = new ApiClient(globalOpts);
|
|
844
844
|
requireAuth3(client);
|
|
845
|
-
const
|
|
845
|
+
const fullId = await resolveOrExit(client, id);
|
|
846
|
+
const assessment = await client.get(`/api/assessments/${fullId}`);
|
|
846
847
|
if (globalOpts.json) {
|
|
847
848
|
json(assessment);
|
|
848
849
|
return;
|
|
@@ -857,14 +858,53 @@ function registerResearchCommands(program2) {
|
|
|
857
858
|
displayMarketBrief(assessment.market_brief, globalOpts);
|
|
858
859
|
}
|
|
859
860
|
});
|
|
860
|
-
research.command("delete <id>").description("Delete a research session").action(async (id) => {
|
|
861
|
+
research.command("delete <id>").description("Delete a research session (accepts the short ID shown by `hl research list`)").action(async (id) => {
|
|
861
862
|
const globalOpts = program2.opts();
|
|
862
863
|
const client = new ApiClient(globalOpts);
|
|
863
864
|
requireAuth3(client);
|
|
864
|
-
await client
|
|
865
|
+
const fullId = await resolveOrExit(client, id);
|
|
866
|
+
await client.delete(`/api/assessments/${fullId}`);
|
|
865
867
|
success("Research session deleted.");
|
|
866
868
|
});
|
|
867
869
|
}
|
|
870
|
+
var FULL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
871
|
+
function matchAssessmentId(idOrPrefix, ids) {
|
|
872
|
+
const needle = idOrPrefix.trim().toLowerCase();
|
|
873
|
+
if (!needle) {
|
|
874
|
+
throw new Error("No research session ID provided.");
|
|
875
|
+
}
|
|
876
|
+
const exact = ids.find((id) => id.toLowerCase() === needle);
|
|
877
|
+
if (exact) return exact;
|
|
878
|
+
const matches = ids.filter((id) => id.toLowerCase().startsWith(needle));
|
|
879
|
+
if (matches.length === 1) return matches[0];
|
|
880
|
+
if (matches.length === 0) {
|
|
881
|
+
throw new Error(
|
|
882
|
+
`No research session found matching "${idOrPrefix}". Run \`hl research list\` to see available sessions.`
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
const shortIds = matches.map((id) => id.slice(0, 8)).join(", ");
|
|
886
|
+
throw new Error(
|
|
887
|
+
`"${idOrPrefix}" matches ${matches.length} research sessions (${shortIds}). Use a longer ID prefix to disambiguate.`
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
async function resolveAssessmentId(client, idOrPrefix) {
|
|
891
|
+
const trimmed = idOrPrefix.trim();
|
|
892
|
+
if (FULL_UUID.test(trimmed)) return trimmed;
|
|
893
|
+
const data = await client.get("/api/assessments", { list: "true" });
|
|
894
|
+
return matchAssessmentId(
|
|
895
|
+
trimmed,
|
|
896
|
+
data.assessments.map((a) => a.id)
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
async function resolveOrExit(client, idOrPrefix) {
|
|
900
|
+
try {
|
|
901
|
+
return await resolveAssessmentId(client, idOrPrefix);
|
|
902
|
+
} catch (e) {
|
|
903
|
+
if (e instanceof Error && e.message.includes("API error")) throw e;
|
|
904
|
+
error(e instanceof Error ? e.message : String(e));
|
|
905
|
+
process.exit(1);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
868
908
|
function requireAuth3(client) {
|
|
869
909
|
if (!client.isAuthenticated) {
|
|
870
910
|
error("Not logged in. Run " + bold("hl auth login") + " first.");
|
|
@@ -1333,15 +1373,764 @@ function registerAllocatorCommands(program2) {
|
|
|
1333
1373
|
});
|
|
1334
1374
|
}
|
|
1335
1375
|
|
|
1376
|
+
// src/commands/lp.ts
|
|
1377
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
1378
|
+
import { writeFile } from "fs/promises";
|
|
1379
|
+
|
|
1380
|
+
// src/lp-display.ts
|
|
1381
|
+
import chalk7 from "chalk";
|
|
1382
|
+
function num2(value, fallback = 0) {
|
|
1383
|
+
const n = Number(value);
|
|
1384
|
+
return Number.isFinite(n) ? n : fallback;
|
|
1385
|
+
}
|
|
1386
|
+
function signedCurrency2(value) {
|
|
1387
|
+
const formatted = currency(Math.abs(value));
|
|
1388
|
+
if (value > 0) return chalk7.green(`+${formatted}`);
|
|
1389
|
+
if (value < 0) return chalk7.yellow(`-${formatted}`);
|
|
1390
|
+
return chalk7.dim("$0.00");
|
|
1391
|
+
}
|
|
1392
|
+
function actionSummary(actions) {
|
|
1393
|
+
if (!actions) return "none";
|
|
1394
|
+
return Object.entries(actions).filter(([, count]) => num2(count) > 0).map(([action, count]) => `${action}:${num2(count)}`).join(" ");
|
|
1395
|
+
}
|
|
1396
|
+
function displayLpScanResult(result, globalOpts) {
|
|
1397
|
+
if (globalOpts.json) {
|
|
1398
|
+
json(result);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
heading("LP Scan");
|
|
1402
|
+
process.stdout.write(
|
|
1403
|
+
chalk7.dim(
|
|
1404
|
+
` scan ${result.scanId} \xB7 strategy ${result.strategyId} \xB7 ${result.evidenceSaved} evidence rows saved
|
|
1405
|
+
`
|
|
1406
|
+
)
|
|
1407
|
+
);
|
|
1408
|
+
process.stdout.write(
|
|
1409
|
+
chalk7.dim(
|
|
1410
|
+
` ${result.totalScanned.toLocaleString()} scanned \xB7 ${result.totalAfterFilter.toLocaleString()} after filters \xB7 profile ${result.profile}
|
|
1411
|
+
|
|
1412
|
+
`
|
|
1413
|
+
)
|
|
1414
|
+
);
|
|
1415
|
+
if (result.markets.length === 0) {
|
|
1416
|
+
warn("No markets matched the LP scan.");
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
table(
|
|
1420
|
+
result.markets.slice(0, 10).map((market) => [
|
|
1421
|
+
String(market.rank),
|
|
1422
|
+
truncate(market.question, 46),
|
|
1423
|
+
String(Math.round(num2(market.score))),
|
|
1424
|
+
compactCurrency(num2(market.liquidity)),
|
|
1425
|
+
compactCurrency(num2(market.rewardsDailyRate)) + "/day",
|
|
1426
|
+
`${num2(market.lpExpectedReturnDailyPct).toFixed(3)}%`
|
|
1427
|
+
]),
|
|
1428
|
+
["#", "Market", "Score", "Liq", "Rewards", "Exp/day"]
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
function displayLpRecommendResult(result, globalOpts) {
|
|
1432
|
+
if (globalOpts.json) {
|
|
1433
|
+
json(result);
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
heading("LP Recommendations");
|
|
1437
|
+
process.stdout.write(
|
|
1438
|
+
chalk7.dim(
|
|
1439
|
+
` cycle ${result.cycleId} \xB7 strategy ${result.strategyId} \xB7 ${result.candidatesSubmitted} markets \xB7 ${result.allocationsSubmitted} current allocations
|
|
1440
|
+
`
|
|
1441
|
+
)
|
|
1442
|
+
);
|
|
1443
|
+
process.stdout.write(
|
|
1444
|
+
chalk7.dim(
|
|
1445
|
+
` PnL context ${result.pnlContextCount} rows${result.pnlSynced ? " \xB7 synced" : ""} \xB7 approvals required
|
|
1446
|
+
|
|
1447
|
+
`
|
|
1448
|
+
)
|
|
1449
|
+
);
|
|
1450
|
+
displayAllocatorCycleResult(result.result ?? { decisions: result.decisions }, globalOpts);
|
|
1451
|
+
}
|
|
1452
|
+
function displayLpEvaluateResult(result, globalOpts) {
|
|
1453
|
+
if (globalOpts.json) {
|
|
1454
|
+
json(result);
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
heading("LP Evaluation");
|
|
1458
|
+
process.stdout.write(
|
|
1459
|
+
chalk7.dim(
|
|
1460
|
+
` strategy ${result.strategyId} \xB7 ${result.summary.snapshots} snapshots \xB7 ${result.summary.markets} markets`
|
|
1461
|
+
)
|
|
1462
|
+
);
|
|
1463
|
+
if (result.pnlSynced) process.stdout.write(chalk7.dim(" \xB7 synced"));
|
|
1464
|
+
process.stdout.write("\n\n");
|
|
1465
|
+
if (result.syncError) warn(result.syncError);
|
|
1466
|
+
table(
|
|
1467
|
+
[
|
|
1468
|
+
["Realized PnL", signedCurrency2(result.summary.realizedPnl)],
|
|
1469
|
+
["Unrealized PnL", signedCurrency2(result.summary.unrealizedPnl)],
|
|
1470
|
+
["Net PnL", signedCurrency2(result.summary.netPnl)],
|
|
1471
|
+
["Capital locked", currency(result.summary.capitalLocked)],
|
|
1472
|
+
["Current value", currency(result.summary.currentValue)],
|
|
1473
|
+
["Outcomes", actionSummary(result.summary.outcomes)]
|
|
1474
|
+
]
|
|
1475
|
+
);
|
|
1476
|
+
if (result.lessons.length === 0) {
|
|
1477
|
+
warn("No PnL lessons available yet.");
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
process.stdout.write("\n");
|
|
1481
|
+
table(
|
|
1482
|
+
result.lessons.slice(0, 8).map((lesson) => [
|
|
1483
|
+
truncate(String(lesson.market_slug ?? "portfolio"), 26),
|
|
1484
|
+
String(lesson.outcome ?? "flat"),
|
|
1485
|
+
signedCurrency2(num2(lesson.net_pnl)),
|
|
1486
|
+
truncate(String(lesson.lesson ?? "no lesson"), 64)
|
|
1487
|
+
]),
|
|
1488
|
+
["Market", "Outcome", "Net", "Lesson"]
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
function displayLpRunResult(result, globalOpts) {
|
|
1492
|
+
if (globalOpts.json) {
|
|
1493
|
+
json(result);
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
heading("LP Run");
|
|
1497
|
+
process.stdout.write(
|
|
1498
|
+
chalk7.dim(
|
|
1499
|
+
` cycle ${result.run.cycleId} \xB7 scan ${result.run.scanId ?? "n/a"} \xB7 strategy ${result.run.strategyId}
|
|
1500
|
+
`
|
|
1501
|
+
)
|
|
1502
|
+
);
|
|
1503
|
+
process.stdout.write(
|
|
1504
|
+
chalk7.dim(
|
|
1505
|
+
` ${result.run.opportunitiesFound} opportunities \xB7 PnL ${result.run.pnlSynced ? "synced" : "not synced"} \xB7 approvals required
|
|
1506
|
+
|
|
1507
|
+
`
|
|
1508
|
+
)
|
|
1509
|
+
);
|
|
1510
|
+
displayAllocatorCycleResult(result.run.result, globalOpts);
|
|
1511
|
+
if (result.evaluation) {
|
|
1512
|
+
process.stdout.write("\n");
|
|
1513
|
+
process.stdout.write(
|
|
1514
|
+
chalk7.dim(
|
|
1515
|
+
` Evaluation: ${signedCurrency2(result.evaluation.summary.netPnl)} net PnL across ${result.evaluation.summary.markets} markets
|
|
1516
|
+
`
|
|
1517
|
+
)
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
// src/commands/lp.ts
|
|
1523
|
+
function requireAuth6(client) {
|
|
1524
|
+
if (!client.isAuthenticated) {
|
|
1525
|
+
error("Not authenticated. Run `hl auth login` first.");
|
|
1526
|
+
process.exit(1);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
function parseNonNegative2(value) {
|
|
1530
|
+
const n = Number(value);
|
|
1531
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
1532
|
+
throw new InvalidArgumentError2("Expected a non-negative number");
|
|
1533
|
+
}
|
|
1534
|
+
return n;
|
|
1535
|
+
}
|
|
1536
|
+
function parsePositiveInt2(value) {
|
|
1537
|
+
const n = Number(value);
|
|
1538
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
1539
|
+
throw new InvalidArgumentError2("Expected a positive integer");
|
|
1540
|
+
}
|
|
1541
|
+
return n;
|
|
1542
|
+
}
|
|
1543
|
+
function compact(payload) {
|
|
1544
|
+
return Object.fromEntries(
|
|
1545
|
+
Object.entries(payload).filter(([, value]) => value !== void 0 && value !== "")
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
async function writeArtifact(path, data, jsonMode) {
|
|
1549
|
+
if (!path) return;
|
|
1550
|
+
await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
1551
|
+
if (!jsonMode) {
|
|
1552
|
+
process.stderr.write(dim(` Saved artifact to ${path}
|
|
1553
|
+
`));
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
function buildLpScanPayload(topic, opts) {
|
|
1557
|
+
return compact({
|
|
1558
|
+
topic: topic?.trim() || void 0,
|
|
1559
|
+
strategyId: opts.strategyId,
|
|
1560
|
+
profile: opts.profile,
|
|
1561
|
+
sortBy: opts.sortBy,
|
|
1562
|
+
tag: opts.tag,
|
|
1563
|
+
minVolume: opts.minVolume,
|
|
1564
|
+
minLiquidity: opts.minLiquidity,
|
|
1565
|
+
maxLiquidity: opts.maxLiquidity,
|
|
1566
|
+
minRewardsDailyRate: opts.minRewardsDailyRate,
|
|
1567
|
+
minDaysToEnd: opts.minDaysToEnd,
|
|
1568
|
+
maxDaysToEnd: opts.maxDaysToEnd,
|
|
1569
|
+
maxMarketAgeHours: opts.maxMarketAgeHours,
|
|
1570
|
+
liquidProfile: opts.liquidProfile,
|
|
1571
|
+
limit: opts.limit
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
function buildLpRecommendPayload(opts) {
|
|
1575
|
+
return compact({
|
|
1576
|
+
strategyId: opts.strategyId,
|
|
1577
|
+
scanId: opts.scanId,
|
|
1578
|
+
limit: opts.limit,
|
|
1579
|
+
syncPnl: opts.syncPnl
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
function buildLpEvaluatePayload(opts) {
|
|
1583
|
+
return compact({
|
|
1584
|
+
strategyId: opts.strategyId,
|
|
1585
|
+
walletAddress: opts.walletAddress,
|
|
1586
|
+
syncPnl: opts.syncPnl,
|
|
1587
|
+
limit: opts.limit
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
function buildLpRunPayload(opts) {
|
|
1591
|
+
return compact({
|
|
1592
|
+
strategyId: opts.strategyId,
|
|
1593
|
+
limit: opts.limit,
|
|
1594
|
+
syncPnl: opts.syncPnl
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
function registerLpCommands(program2) {
|
|
1598
|
+
const lp = program2.command("lp").description("Run persisted liquidity-provider scan, recommendation, and evaluation workflows");
|
|
1599
|
+
lp.command("scan").description("Scan LP candidates and persist them as evidence").argument("[topic]", "Human label for this scan, e.g. liquidity opportunities").option("--profile <name>", "lp-opportunity | liquidity-provider | liquid-new-or-long", "liquidity-provider").option("--sort-by <key>", "score | volume | liquidity | movement | spread | rewards | rewardYield | lpExpectedReturn | horizon").option("--tag <slug>", "Polymarket category tag, e.g. crypto, politics").option("--min-volume <usd>", "Minimum 24h volume (USD)", parseNonNegative2).option("--min-liquidity <usd>", "Minimum displayed liquidity (USD)", parseNonNegative2).option("--max-liquidity <usd>", "Maximum displayed liquidity (USD)", parseNonNegative2).option("--min-rewards-daily-rate <usd>", "Minimum LP rewards USD/day", parseNonNegative2).option("--min-days-to-end <n>", "Minimum days until resolution", parseNonNegative2).option("--max-days-to-end <n>", "Maximum days until resolution", parseNonNegative2).option("--max-market-age-hours <n>", "With liquid-new-or-long: max age for the new branch", parseNonNegative2).option("--liquid-profile <mode>", "new-or-long").option("--limit <n>", "Max markets to scan/save (1-100, default 15)", parsePositiveInt2, 15).option("--strategy-id <uuid>", "LP strategy id").option("--output <file>", "Write the evidence JSON response to a local file").action(async (topic, opts) => {
|
|
1600
|
+
const globalOpts = program2.opts();
|
|
1601
|
+
const client = new ApiClient(globalOpts);
|
|
1602
|
+
requireAuth6(client);
|
|
1603
|
+
const result = await client.post(
|
|
1604
|
+
"/api/lp/scan",
|
|
1605
|
+
buildLpScanPayload(topic, opts)
|
|
1606
|
+
);
|
|
1607
|
+
await writeArtifact(opts.output, result, Boolean(globalOpts.json));
|
|
1608
|
+
displayLpScanResult(result, globalOpts);
|
|
1609
|
+
});
|
|
1610
|
+
lp.command("recommend").description("Run allocator recommendations from saved LP evidence and current allocations").option("--strategy-id <uuid>", "LP strategy id").option("--scan-id <uuid>", "Use candidates from a specific saved scan").option("--limit <n>", "Max saved candidates to submit (1-25, default 15)", parsePositiveInt2, 15).option("--sync-pnl", "Refresh wallet PnL before recommending").option("--output <file>", "Write the recommendation JSON response to a local file").action(async (opts) => {
|
|
1611
|
+
const globalOpts = program2.opts();
|
|
1612
|
+
const client = new ApiClient(globalOpts);
|
|
1613
|
+
requireAuth6(client);
|
|
1614
|
+
const result = await client.post(
|
|
1615
|
+
"/api/lp/recommend",
|
|
1616
|
+
buildLpRecommendPayload(opts)
|
|
1617
|
+
);
|
|
1618
|
+
await writeArtifact(opts.output, result, Boolean(globalOpts.json));
|
|
1619
|
+
displayLpRecommendResult(result, globalOpts);
|
|
1620
|
+
});
|
|
1621
|
+
lp.command("evaluate").description("Evaluate LP performance and return compact lessons").option("--strategy-id <uuid>", "LP strategy id").option("--wallet-address <address>", "Wallet address to sync PnL from").option("--no-sync-pnl", "Use existing PnL snapshots without refreshing").option("--limit <n>", "Max PnL snapshots to evaluate (1-100, default 50)", parsePositiveInt2, 50).option("--output <file>", "Write the evaluation JSON response to a local file").action(async (opts) => {
|
|
1622
|
+
const globalOpts = program2.opts();
|
|
1623
|
+
const client = new ApiClient(globalOpts);
|
|
1624
|
+
requireAuth6(client);
|
|
1625
|
+
const result = await client.post(
|
|
1626
|
+
"/api/lp/evaluate",
|
|
1627
|
+
buildLpEvaluatePayload(opts)
|
|
1628
|
+
);
|
|
1629
|
+
await writeArtifact(opts.output, result, Boolean(globalOpts.json));
|
|
1630
|
+
displayLpEvaluateResult(result, globalOpts);
|
|
1631
|
+
});
|
|
1632
|
+
lp.command("run").description("Run scan, recommendation, and evaluation as one dry-run chain").option("--strategy-id <uuid>", "LP strategy id").option("--limit <n>", "Max candidates to scan/recommend (1-25, default 15)", parsePositiveInt2, 15).option("--no-sync-pnl", "Use existing PnL snapshots without refreshing").option("--output <file>", "Write the chained run JSON response to a local file").action(async (opts) => {
|
|
1633
|
+
const globalOpts = program2.opts();
|
|
1634
|
+
const client = new ApiClient(globalOpts);
|
|
1635
|
+
requireAuth6(client);
|
|
1636
|
+
const run = await client.post(
|
|
1637
|
+
"/api/lp/run",
|
|
1638
|
+
buildLpRunPayload(opts)
|
|
1639
|
+
);
|
|
1640
|
+
let evaluation = null;
|
|
1641
|
+
try {
|
|
1642
|
+
evaluation = await client.post("/api/lp/evaluate", {
|
|
1643
|
+
strategyId: run.strategyId,
|
|
1644
|
+
syncPnl: false
|
|
1645
|
+
});
|
|
1646
|
+
} catch (error2) {
|
|
1647
|
+
if (!globalOpts.json) {
|
|
1648
|
+
warn(
|
|
1649
|
+
`Could not load evaluation summary: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
const combined = { run, evaluation };
|
|
1654
|
+
await writeArtifact(opts.output, combined, Boolean(globalOpts.json));
|
|
1655
|
+
displayLpRunResult(combined, globalOpts);
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// src/commands/wallet.ts
|
|
1660
|
+
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
1661
|
+
import { spawn } from "child_process";
|
|
1662
|
+
var NETWORK_NAME = "Polygon";
|
|
1663
|
+
var POLYGON_NATIVE_USDC = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
|
|
1664
|
+
var SUPPORTED_ASSETS = ["pUsd", "usdcE", "matic"];
|
|
1665
|
+
var POLYGON_NATIVE_ASSET_LABEL = "POL";
|
|
1666
|
+
var TERMINAL_WITHDRAWAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "expired"]);
|
|
1667
|
+
function requireAuth7(client) {
|
|
1668
|
+
if (!client.isAuthenticated) {
|
|
1669
|
+
error("Not authenticated. Run `hl auth login` first.");
|
|
1670
|
+
process.exit(1);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
function parsePositiveNumber2(value) {
|
|
1674
|
+
const n = Number(value);
|
|
1675
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
1676
|
+
throw new InvalidArgumentError3("Expected a positive number");
|
|
1677
|
+
}
|
|
1678
|
+
return n;
|
|
1679
|
+
}
|
|
1680
|
+
function parseWalletAddress(value) {
|
|
1681
|
+
const trimmed = value.trim();
|
|
1682
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(trimmed)) {
|
|
1683
|
+
throw new InvalidArgumentError3("Expected a valid EVM wallet address");
|
|
1684
|
+
}
|
|
1685
|
+
return trimmed;
|
|
1686
|
+
}
|
|
1687
|
+
function normalizeWalletAsset(value) {
|
|
1688
|
+
if (!value) return void 0;
|
|
1689
|
+
const normalized = value.trim().toLowerCase().replace(/[._\s-]/g, "");
|
|
1690
|
+
if (["pusd", "polymarketusd"].includes(normalized)) return "pUsd";
|
|
1691
|
+
if (["usdce", "usdcebridged", "bridgedusdc", "usdc"].includes(normalized)) return "usdcE";
|
|
1692
|
+
if (["matic", "pol"].includes(normalized)) return "matic";
|
|
1693
|
+
throw new InvalidArgumentError3("Unknown wallet asset. Use pUSD, USDC.e, or POL.");
|
|
1694
|
+
}
|
|
1695
|
+
function assetByKey(balances, key) {
|
|
1696
|
+
return balances.assets[key];
|
|
1697
|
+
}
|
|
1698
|
+
function tokenAddress(asset) {
|
|
1699
|
+
return asset.address ?? "native token";
|
|
1700
|
+
}
|
|
1701
|
+
function displayAmount(asset) {
|
|
1702
|
+
if (!asset.ok) {
|
|
1703
|
+
return `unavailable${asset.error ? ` (${asset.error})` : ""}`;
|
|
1704
|
+
}
|
|
1705
|
+
const numeric = Number(asset.balance);
|
|
1706
|
+
if (asset.address === null && asset.decimals === 18) {
|
|
1707
|
+
return Number.isFinite(numeric) ? numeric.toLocaleString("en-US", { maximumFractionDigits: 6 }) : asset.balance;
|
|
1708
|
+
}
|
|
1709
|
+
return Number.isFinite(numeric) ? currency(numeric) : asset.balance;
|
|
1710
|
+
}
|
|
1711
|
+
function displayAssetSymbol(asset) {
|
|
1712
|
+
return asset.address === null && asset.decimals === 18 ? POLYGON_NATIVE_ASSET_LABEL : asset.symbol;
|
|
1713
|
+
}
|
|
1714
|
+
function buildDepositInstructions(balances, assetKey) {
|
|
1715
|
+
const assets = assetKey ? [assetByKey(balances, assetKey)] : SUPPORTED_ASSETS.map((key) => assetByKey(balances, key));
|
|
1716
|
+
return {
|
|
1717
|
+
action: "deposit",
|
|
1718
|
+
publicDepositAddress: balances.walletAddress,
|
|
1719
|
+
walletAddress: balances.walletAddress,
|
|
1720
|
+
chainId: balances.chainId,
|
|
1721
|
+
network: NETWORK_NAME,
|
|
1722
|
+
asset: assetKey ? assetByKey(balances, assetKey) : null,
|
|
1723
|
+
assets,
|
|
1724
|
+
warning: "Send only Polygon assets to this address. Hedge Layer cannot reverse external transfers."
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
function buildWithdrawRequestPayload(opts) {
|
|
1728
|
+
const assetKey = normalizeWalletAsset(opts.asset) ?? "pUsd";
|
|
1729
|
+
if (assetKey !== "pUsd") {
|
|
1730
|
+
throw new InvalidArgumentError3("Bridge withdrawals currently send pUSD. Use --asset pUSD.");
|
|
1731
|
+
}
|
|
1732
|
+
return {
|
|
1733
|
+
amount: opts.amount,
|
|
1734
|
+
recipientAddress: opts.to,
|
|
1735
|
+
toChainId: opts.toChainId ?? "137",
|
|
1736
|
+
toTokenAddress: opts.toTokenAddress ?? POLYGON_NATIVE_USDC
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function displayWalletStatus(status) {
|
|
1740
|
+
const ownerWallet = status.ownerWallet ?? status.wallet;
|
|
1741
|
+
const depositWallet = status.depositWallet ?? status.tradingWallet ?? null;
|
|
1742
|
+
heading("Wallet");
|
|
1743
|
+
table([
|
|
1744
|
+
["Owner linked", status.linked ? "yes" : "no"],
|
|
1745
|
+
["Provider", status.provider],
|
|
1746
|
+
["Owner wallet", ownerWallet?.wallet_address ?? "(none)"],
|
|
1747
|
+
["Owner chain", ownerWallet ? `${NETWORK_NAME} (${ownerWallet.chain_id})` : "(none)"],
|
|
1748
|
+
["Owner linked at", ownerWallet?.linked_at ? new Date(ownerWallet.linked_at).toLocaleString() : "(none)"],
|
|
1749
|
+
["Deposit wallet", depositWallet?.wallet_address ?? "(none)"],
|
|
1750
|
+
["Deposit deployed", status.depositWalletDeployed ? "yes" : "no"],
|
|
1751
|
+
["Deposit approved", status.depositWalletApproved ? "yes" : "no"],
|
|
1752
|
+
["Deposit ready", status.depositWalletReady ? "yes" : "no"],
|
|
1753
|
+
["Relayer configured", status.relayerConfigured ? "yes" : "no"]
|
|
1754
|
+
]);
|
|
1755
|
+
}
|
|
1756
|
+
function displayWalletBalances(balances) {
|
|
1757
|
+
heading("Wallet Funds");
|
|
1758
|
+
table([
|
|
1759
|
+
["Wallet", balances.walletAddress],
|
|
1760
|
+
["Chain", `${NETWORK_NAME} (${balances.chainId})`],
|
|
1761
|
+
["Available pUSD", displayAmount(balances.available)],
|
|
1762
|
+
["USDC.e", displayAmount(balances.assets.usdcE)],
|
|
1763
|
+
[displayAssetSymbol(balances.assets.matic), displayAmount(balances.assets.matic)],
|
|
1764
|
+
["Updated", new Date(balances.updatedAt).toLocaleString()]
|
|
1765
|
+
]);
|
|
1766
|
+
}
|
|
1767
|
+
function displayDepositInstructions(instructions) {
|
|
1768
|
+
heading("Deposit");
|
|
1769
|
+
table([
|
|
1770
|
+
["Public deposit address", instructions.publicDepositAddress],
|
|
1771
|
+
["Network", `${instructions.network} (${instructions.chainId})`]
|
|
1772
|
+
]);
|
|
1773
|
+
process.stdout.write("\n");
|
|
1774
|
+
table(
|
|
1775
|
+
instructions.assets.map((asset) => [
|
|
1776
|
+
displayAssetSymbol(asset),
|
|
1777
|
+
tokenAddress(asset),
|
|
1778
|
+
displayAmount(asset)
|
|
1779
|
+
]),
|
|
1780
|
+
["Asset", "Token contract", "Current balance"]
|
|
1781
|
+
);
|
|
1782
|
+
process.stdout.write("\n");
|
|
1783
|
+
warn(instructions.warning);
|
|
1784
|
+
}
|
|
1785
|
+
function displayBridgeDeposit(result) {
|
|
1786
|
+
const bridgeAddresses = result.bridge?.address ?? {};
|
|
1787
|
+
const rows = Object.entries(bridgeAddresses).filter(([, value]) => typeof value === "string" && value.length > 0).map(([network, value]) => [network.toUpperCase(), value]);
|
|
1788
|
+
if (rows.length === 0) return;
|
|
1789
|
+
process.stdout.write("\n");
|
|
1790
|
+
table(rows, ["Bridge network", "Bridge deposit address"]);
|
|
1791
|
+
if (result.bridge?.note) {
|
|
1792
|
+
process.stdout.write("\n" + dim(` ${result.bridge.note}
|
|
1793
|
+
`));
|
|
1794
|
+
}
|
|
1795
|
+
for (const warning of result.bridge?.warnings ?? []) {
|
|
1796
|
+
if (warning.message) warn(warning.message);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
function displayWithdrawIntent(intent) {
|
|
1800
|
+
heading("Withdraw");
|
|
1801
|
+
table([
|
|
1802
|
+
["Intent", intent.id],
|
|
1803
|
+
["Status", intent.status],
|
|
1804
|
+
["Wallet", intent.walletAddress],
|
|
1805
|
+
["Amount", `${intent.amount} pUSD`],
|
|
1806
|
+
["Recipient", intent.recipientAddress],
|
|
1807
|
+
["Destination", `chain ${intent.toChainId} \xB7 ${intent.toTokenAddress}`],
|
|
1808
|
+
["Bridge address", intent.bridgeAddresses.evm ?? "(none)"],
|
|
1809
|
+
["Signing URL", intent.signingUrl]
|
|
1810
|
+
]);
|
|
1811
|
+
const quote = intent.quote ?? {};
|
|
1812
|
+
if (quote.estOutputUsd !== void 0 || quote.estCheckoutTimeMs !== void 0) {
|
|
1813
|
+
process.stdout.write("\n");
|
|
1814
|
+
table([
|
|
1815
|
+
["Estimated output", quote.estOutputUsd !== void 0 ? currency(Number(quote.estOutputUsd)) : "-"],
|
|
1816
|
+
["Estimated checkout", quote.estCheckoutTimeMs !== void 0 ? `${Math.round(Number(quote.estCheckoutTimeMs) / 1e3)}s` : "-"]
|
|
1817
|
+
]);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
function openBrowser(url) {
|
|
1821
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
1822
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
1823
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore" });
|
|
1824
|
+
child.unref();
|
|
1825
|
+
}
|
|
1826
|
+
function sleep(ms) {
|
|
1827
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1828
|
+
}
|
|
1829
|
+
async function pollWithdrawalIntent(client, id, opts) {
|
|
1830
|
+
const started = Date.now();
|
|
1831
|
+
let lastStatus = "";
|
|
1832
|
+
while (Date.now() - started <= opts.timeoutSeconds * 1e3) {
|
|
1833
|
+
const result = await client.get(
|
|
1834
|
+
`/api/wallet/withdraw/intents/${encodeURIComponent(id)}`
|
|
1835
|
+
);
|
|
1836
|
+
const intent = result.intent;
|
|
1837
|
+
if (!opts.jsonMode && intent.status !== lastStatus) {
|
|
1838
|
+
process.stderr.write(dim(` Withdrawal status: ${intent.status}
|
|
1839
|
+
`));
|
|
1840
|
+
lastStatus = intent.status;
|
|
1841
|
+
}
|
|
1842
|
+
if (TERMINAL_WITHDRAWAL_STATUSES.has(intent.status)) return intent;
|
|
1843
|
+
await sleep(opts.intervalSeconds * 1e3);
|
|
1844
|
+
}
|
|
1845
|
+
throw new Error(`Timed out waiting for withdrawal intent ${id}`);
|
|
1846
|
+
}
|
|
1847
|
+
function registerWalletCommands(program2) {
|
|
1848
|
+
const wallet = program2.command("wallet").description("Inspect linked wallet funds and funding instructions");
|
|
1849
|
+
wallet.command("status").description("Show linked owner and Polymarket deposit wallet status").action(async () => {
|
|
1850
|
+
const globalOpts = program2.opts();
|
|
1851
|
+
const client = new ApiClient(globalOpts);
|
|
1852
|
+
requireAuth7(client);
|
|
1853
|
+
const status = await client.get("/api/wallet/status");
|
|
1854
|
+
if (globalOpts.json) {
|
|
1855
|
+
json(status);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
displayWalletStatus(status);
|
|
1859
|
+
});
|
|
1860
|
+
wallet.command("balances").alias("funds").description("Show available wallet funds").action(async () => {
|
|
1861
|
+
const globalOpts = program2.opts();
|
|
1862
|
+
const client = new ApiClient(globalOpts);
|
|
1863
|
+
requireAuth7(client);
|
|
1864
|
+
const balances = await client.get("/api/wallet/balances");
|
|
1865
|
+
if (globalOpts.json) {
|
|
1866
|
+
json(balances);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
displayWalletBalances(balances);
|
|
1870
|
+
});
|
|
1871
|
+
wallet.command("deposit").description("Show deposit address and supported Polygon assets").option("--asset <asset>", "pUSD | USDC.e | POL").option("--bridge", "Also request Polymarket Bridge deposit addresses").action(async (opts) => {
|
|
1872
|
+
const globalOpts = program2.opts();
|
|
1873
|
+
const client = new ApiClient(globalOpts);
|
|
1874
|
+
requireAuth7(client);
|
|
1875
|
+
const asset = normalizeWalletAsset(opts.asset);
|
|
1876
|
+
const result = await client.post("/api/wallet/deposit", {
|
|
1877
|
+
bridge: Boolean(opts.bridge)
|
|
1878
|
+
});
|
|
1879
|
+
const balances = {
|
|
1880
|
+
walletAddress: result.direct.walletAddress,
|
|
1881
|
+
chainId: result.direct.chainId,
|
|
1882
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1883
|
+
available: result.direct.assets.pUsd,
|
|
1884
|
+
assets: result.direct.assets
|
|
1885
|
+
};
|
|
1886
|
+
const instructions = buildDepositInstructions(balances, asset);
|
|
1887
|
+
if (globalOpts.json) {
|
|
1888
|
+
json({ ...result, instructions });
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
displayDepositInstructions(instructions);
|
|
1892
|
+
displayBridgeDeposit(result);
|
|
1893
|
+
});
|
|
1894
|
+
wallet.command("withdraw").description("Create a browser-signed withdrawal intent and poll for completion").requiredOption("--to <address>", "Recipient EVM wallet address", parseWalletAddress).requiredOption("--amount <amount>", "Amount to withdraw", parsePositiveNumber2).option("--asset <asset>", "Source asset; currently only pUSD is supported", "pUSD").option("--to-chain-id <id>", "Destination chain id", "137").option("--to-token-address <address>", "Destination token address", parseWalletAddress, POLYGON_NATIVE_USDC).option("--no-open", "Do not open the browser signing URL").option("--no-wait", "Do not poll for completion after creating the intent").option("--poll-interval <seconds>", "Polling interval in seconds", parsePositiveNumber2, 5).option("--timeout <seconds>", "Maximum seconds to wait for completion", parsePositiveNumber2, 600).action(async (opts) => {
|
|
1895
|
+
const globalOpts = program2.opts();
|
|
1896
|
+
const client = new ApiClient(globalOpts);
|
|
1897
|
+
requireAuth7(client);
|
|
1898
|
+
const created = await client.post(
|
|
1899
|
+
"/api/wallet/withdraw/intents",
|
|
1900
|
+
buildWithdrawRequestPayload(opts)
|
|
1901
|
+
);
|
|
1902
|
+
let intent = created.intent;
|
|
1903
|
+
if (opts.open !== false) {
|
|
1904
|
+
openBrowser(intent.signingUrl);
|
|
1905
|
+
}
|
|
1906
|
+
if (opts.wait !== false) {
|
|
1907
|
+
intent = await pollWithdrawalIntent(client, intent.id, {
|
|
1908
|
+
timeoutSeconds: opts.timeout ?? 600,
|
|
1909
|
+
intervalSeconds: opts.pollInterval ?? 5,
|
|
1910
|
+
jsonMode: Boolean(globalOpts.json)
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
if (globalOpts.json) {
|
|
1914
|
+
json(intent);
|
|
1915
|
+
return;
|
|
1916
|
+
} else {
|
|
1917
|
+
displayWithdrawIntent(intent);
|
|
1918
|
+
}
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
// src/commands/signal.ts
|
|
1923
|
+
import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
1924
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1925
|
+
|
|
1926
|
+
// src/signal-display.ts
|
|
1927
|
+
import chalk8 from "chalk";
|
|
1928
|
+
function pctFromSignalValue(value) {
|
|
1929
|
+
if (value === null || value === void 0 || Number.isNaN(value)) return null;
|
|
1930
|
+
return Math.abs(value) <= 1 ? value * 100 : value;
|
|
1931
|
+
}
|
|
1932
|
+
function formatProbability(value) {
|
|
1933
|
+
const pct = pctFromSignalValue(value);
|
|
1934
|
+
return pct === null ? "n/a" : `${pct.toFixed(1)}%`;
|
|
1935
|
+
}
|
|
1936
|
+
function formatGap(value) {
|
|
1937
|
+
if (value === null || value === void 0 || Number.isNaN(value)) return "n/a";
|
|
1938
|
+
const points = Math.abs(value) <= 1 ? value * 100 : value;
|
|
1939
|
+
const formatted = `${points >= 0 ? "+" : ""}${points.toFixed(1)}pp`;
|
|
1940
|
+
if (points > 0) return chalk8.green(formatted);
|
|
1941
|
+
if (points < 0) return chalk8.red(formatted);
|
|
1942
|
+
return chalk8.dim(formatted);
|
|
1943
|
+
}
|
|
1944
|
+
function formatStrength(value) {
|
|
1945
|
+
if (value === "strong") return chalk8.green("strong");
|
|
1946
|
+
if (value === "weak") return chalk8.yellow("weak");
|
|
1947
|
+
return value ? chalk8.dim(value) : "n/a";
|
|
1948
|
+
}
|
|
1949
|
+
function analysisItems(result) {
|
|
1950
|
+
if (!result) return [];
|
|
1951
|
+
if (result.analysis) return [result];
|
|
1952
|
+
return result.analyses ?? [];
|
|
1953
|
+
}
|
|
1954
|
+
function titleFor(analysis) {
|
|
1955
|
+
return analysis.market_name || analysis.market_slug || "market";
|
|
1956
|
+
}
|
|
1957
|
+
function displaySignalAnalysis(response, globalOpts) {
|
|
1958
|
+
if (globalOpts.json) {
|
|
1959
|
+
json(response);
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
if (response.error) {
|
|
1963
|
+
error(response.error);
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
const result = response.result;
|
|
1967
|
+
if (result?.error) {
|
|
1968
|
+
error(result.error);
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
const items = analysisItems(result);
|
|
1972
|
+
if (items.length === 0) {
|
|
1973
|
+
warn("No signal analysis returned.");
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
heading(
|
|
1977
|
+
items.length === 1 ? "Signal Analysis" : `Signal Analysis \u2014 ${items.length} markets`
|
|
1978
|
+
);
|
|
1979
|
+
const rows = items.map((item) => {
|
|
1980
|
+
const analysis = item.analysis ?? {};
|
|
1981
|
+
return [
|
|
1982
|
+
truncate(titleFor(analysis), 44),
|
|
1983
|
+
formatProbability(analysis.current_yes_prob),
|
|
1984
|
+
formatProbability(analysis.predicted_prob),
|
|
1985
|
+
formatGap(analysis.probability_gap),
|
|
1986
|
+
formatStrength(analysis.signal_strength),
|
|
1987
|
+
analysis.confidence ?? "n/a"
|
|
1988
|
+
];
|
|
1989
|
+
});
|
|
1990
|
+
table(rows, ["Market", "Market YES", "Agent YES", "Gap", "Signal", "Conf"]);
|
|
1991
|
+
for (const item of items.slice(0, 3)) {
|
|
1992
|
+
const analysis = item.analysis;
|
|
1993
|
+
if (!analysis) continue;
|
|
1994
|
+
process.stdout.write("\n " + chalk8.bold(truncate(titleFor(analysis), 76)) + "\n");
|
|
1995
|
+
if (analysis.market_link) {
|
|
1996
|
+
process.stdout.write(" " + chalk8.dim("Polymarket: ") + analysis.market_link + "\n");
|
|
1997
|
+
}
|
|
1998
|
+
if (analysis.key_factors && analysis.key_factors.length > 0) {
|
|
1999
|
+
process.stdout.write(" " + chalk8.dim("Key factors: ") + analysis.key_factors.slice(0, 4).join("; ") + "\n");
|
|
2000
|
+
}
|
|
2001
|
+
if (analysis.research_findings) {
|
|
2002
|
+
process.stdout.write(
|
|
2003
|
+
" " + chalk8.dim("Research: ") + truncate(analysis.research_findings, 180) + "\n"
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
if (items.length > 3) {
|
|
2008
|
+
process.stdout.write(chalk8.dim(`
|
|
2009
|
+
... and ${items.length - 3} more
|
|
2010
|
+
`));
|
|
2011
|
+
}
|
|
2012
|
+
if (result?.strong_signal_count !== void 0) {
|
|
2013
|
+
process.stdout.write(
|
|
2014
|
+
chalk8.dim(`
|
|
2015
|
+
Strong signals: ${result.strong_signal_count}
|
|
2016
|
+
`)
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
// src/commands/signal.ts
|
|
2022
|
+
function requireAuth8(client) {
|
|
2023
|
+
if (!client.isAuthenticated) {
|
|
2024
|
+
error("Not authenticated. Run `hl auth login` first.");
|
|
2025
|
+
process.exit(1);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
function collect(value, previous = []) {
|
|
2029
|
+
return [...previous, value];
|
|
2030
|
+
}
|
|
2031
|
+
function parseProbability(value) {
|
|
2032
|
+
const n = Number(value);
|
|
2033
|
+
if (!Number.isFinite(n) || n < 0 || n > 100) {
|
|
2034
|
+
throw new InvalidArgumentError4("Expected a probability between 0 and 100");
|
|
2035
|
+
}
|
|
2036
|
+
return n;
|
|
2037
|
+
}
|
|
2038
|
+
async function readStdin2() {
|
|
2039
|
+
const chunks = [];
|
|
2040
|
+
for await (const chunk of process.stdin) {
|
|
2041
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2042
|
+
}
|
|
2043
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
2044
|
+
}
|
|
2045
|
+
async function readMarketPayload(path) {
|
|
2046
|
+
if (!path) return {};
|
|
2047
|
+
const raw = path === "-" ? await readStdin2() : await readFile2(path, "utf8");
|
|
2048
|
+
const parsed = JSON.parse(raw);
|
|
2049
|
+
if (Array.isArray(parsed)) {
|
|
2050
|
+
return { markets: parsed };
|
|
2051
|
+
}
|
|
2052
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.markets)) {
|
|
2053
|
+
return { markets: parsed.markets };
|
|
2054
|
+
}
|
|
2055
|
+
if (parsed && typeof parsed === "object" && parsed.market) {
|
|
2056
|
+
return { market: parsed.market };
|
|
2057
|
+
}
|
|
2058
|
+
if (parsed && typeof parsed === "object") {
|
|
2059
|
+
return { market: parsed };
|
|
2060
|
+
}
|
|
2061
|
+
throw new Error("Market JSON must be an object, an array, or { market | markets }");
|
|
2062
|
+
}
|
|
2063
|
+
function inlineMarketFromOptions(opts) {
|
|
2064
|
+
const market = {};
|
|
2065
|
+
if (opts.question) market.question = opts.question;
|
|
2066
|
+
if (opts.description) market.description = opts.description;
|
|
2067
|
+
if (opts.yesProb !== void 0) market.yesPrice = opts.yesProb;
|
|
2068
|
+
if (opts.noProb !== void 0) market.noPrice = opts.noProb;
|
|
2069
|
+
if (opts.slug) market.slug = opts.slug;
|
|
2070
|
+
if (opts.link) market.link = opts.link;
|
|
2071
|
+
return Object.keys(market).length > 0 ? market : void 0;
|
|
2072
|
+
}
|
|
2073
|
+
async function buildSignalPayload(positionalUrl, opts) {
|
|
2074
|
+
const urls = [positionalUrl, ...opts.url ?? []].filter(
|
|
2075
|
+
(value) => Boolean(value)
|
|
2076
|
+
);
|
|
2077
|
+
const filePayload = await readMarketPayload(opts.market);
|
|
2078
|
+
const inlineMarket = inlineMarketFromOptions(opts);
|
|
2079
|
+
const hasMarketInput = Boolean(filePayload.market || filePayload.markets || inlineMarket);
|
|
2080
|
+
if (urls.length > 0 && hasMarketInput) {
|
|
2081
|
+
throw new Error("Use either URL input or market JSON/options, not both.");
|
|
2082
|
+
}
|
|
2083
|
+
if (filePayload.market && inlineMarket) {
|
|
2084
|
+
throw new Error("Use either --market or inline market options, not both.");
|
|
2085
|
+
}
|
|
2086
|
+
if (filePayload.markets && inlineMarket) {
|
|
2087
|
+
throw new Error("Use either --market or inline market options, not both.");
|
|
2088
|
+
}
|
|
2089
|
+
if (urls.length === 0 && !hasMarketInput) {
|
|
2090
|
+
throw new Error("Provide a Polymarket URL or a market payload.");
|
|
2091
|
+
}
|
|
2092
|
+
const payload = urls.length === 1 ? { url: urls[0] } : urls.length > 1 ? { urls } : filePayload.market ? { market: filePayload.market } : filePayload.markets ? { markets: filePayload.markets } : { market: inlineMarket };
|
|
2093
|
+
if (opts.context) {
|
|
2094
|
+
payload.previous_analysis_context = opts.context;
|
|
2095
|
+
}
|
|
2096
|
+
return payload;
|
|
2097
|
+
}
|
|
2098
|
+
async function runSignalAnalysis(client, payload) {
|
|
2099
|
+
return client.post("/api/signal/analyze", payload);
|
|
2100
|
+
}
|
|
2101
|
+
function registerSignalCommands(program2) {
|
|
2102
|
+
const signal = program2.command("signal").description("Analyze Polymarket probability gaps with the signal agent");
|
|
2103
|
+
signal.command("analyze").description("Estimate true YES probability and compare it with market pricing").argument("[url]", "Polymarket market/event URL to analyze").option("--url <url>", "Additional Polymarket URL; repeat for multiple markets", collect, []).option("--market <file>", "Inline market JSON object/array; use '-' to read stdin").option("--context <text>", "Prior search notes or analysis context for the agent").option("--question <text>", "Inline market question when not using a URL").option("--description <text>", "Inline market description or resolution criteria").option("--yes-prob <prob>", "Current YES probability or price, e.g. 0.52 or 52", parseProbability).option("--no-prob <prob>", "Current NO probability or price, e.g. 0.48 or 48", parseProbability).option("--slug <slug>", "Inline market slug").option("--link <url>", "Inline market link").action(async (url, o) => {
|
|
2104
|
+
const globalOpts = program2.opts();
|
|
2105
|
+
const client = new ApiClient(globalOpts);
|
|
2106
|
+
requireAuth8(client);
|
|
2107
|
+
let payload;
|
|
2108
|
+
try {
|
|
2109
|
+
payload = await buildSignalPayload(url, o);
|
|
2110
|
+
} catch (e) {
|
|
2111
|
+
error(e instanceof Error ? e.message : String(e));
|
|
2112
|
+
process.exit(1);
|
|
2113
|
+
}
|
|
2114
|
+
if (!globalOpts.json) {
|
|
2115
|
+
process.stderr.write(dim(" Running signal analysis...\n"));
|
|
2116
|
+
}
|
|
2117
|
+
const result = await runSignalAnalysis(client, payload);
|
|
2118
|
+
displaySignalAnalysis(result, globalOpts);
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
|
|
1336
2122
|
// src/index.ts
|
|
1337
|
-
var program = new
|
|
1338
|
-
program.name("hl").description("Hedge Layer CLI \u2014 prediction market intelligence from the terminal").version("1.
|
|
2123
|
+
var program = new Command5();
|
|
2124
|
+
program.name("hl").description("Hedge Layer CLI \u2014 prediction market intelligence from the terminal").version("1.8.0").option("--json", "Output as JSON (machine-readable)").option("--api-url <url>", "Override API base URL").option("--token <token>", "Override stored API token").option("--verbose", "Show HTTP request details").option("--no-color", "Disable colored output");
|
|
1339
2125
|
registerAuthCommands(program);
|
|
1340
2126
|
registerBriefCommands(program);
|
|
1341
2127
|
registerProfileCommand(program);
|
|
1342
2128
|
registerResearchCommands(program);
|
|
1343
2129
|
registerFeedCommand(program);
|
|
2130
|
+
registerWalletCommands(program);
|
|
2131
|
+
registerLpCommands(program);
|
|
1344
2132
|
registerAllocatorCommands(program);
|
|
2133
|
+
registerSignalCommands(program);
|
|
1345
2134
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
1346
2135
|
const opts = program.opts();
|
|
1347
2136
|
if (opts.color === false) {
|