@netmind/arena-cli 0.21.0 → 0.25.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/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { readFileSync as readFileSync9 } from "fs";
5
- import { Command as Command25 } from "commander";
5
+ import { Command as Command27 } from "commander";
6
6
 
7
7
  // src/diag.ts
8
8
  import { appendFileSync } from "fs";
@@ -326,9 +326,9 @@ function charCount(value) {
326
326
  return String(value).length;
327
327
  }
328
328
  }
329
- async function api(path, opts = {}) {
329
+ async function api(path2, opts = {}) {
330
330
  const { method = "GET", body, auth = false } = opts;
331
- const url = `${getApiUrl()}${path}`;
331
+ const url = `${getApiUrl()}${path2}`;
332
332
  const headers = {
333
333
  "User-Agent": `arena-cli/${CLI_VERSION}`,
334
334
  "X-Arena-Cli-Version": CLI_VERSION
@@ -369,7 +369,7 @@ async function api(path, opts = {}) {
369
369
  emitApiDiag({
370
370
  ts: (/* @__PURE__ */ new Date()).toISOString(),
371
371
  method,
372
- path,
372
+ path: path2,
373
373
  status: res.status,
374
374
  latencyMs: Date.now() - startedAt,
375
375
  reqChars: charCount(requestBody),
@@ -684,13 +684,13 @@ function ensureDir2(dir) {
684
684
  mkdirSync3(dir, { recursive: true });
685
685
  }
686
686
  }
687
- function writeJson(path, data) {
687
+ function writeJson(path2, data) {
688
688
  ensureCacheDir();
689
- writeFileSync3(path, JSON.stringify(data, null, 2) + "\n");
689
+ writeFileSync3(path2, JSON.stringify(data, null, 2) + "\n");
690
690
  }
691
- function readJson(path) {
691
+ function readJson(path2) {
692
692
  try {
693
- return JSON.parse(readFileSync4(path, "utf-8"));
693
+ return JSON.parse(readFileSync4(path2, "utf-8"));
694
694
  } catch {
695
695
  return null;
696
696
  }
@@ -1581,9 +1581,152 @@ Champion: ${res.champion.agentName} (${res.champion.returnPct}%)`);
1581
1581
  });
1582
1582
  var gameCmd = new Command5("game").description("Interact with a live competition").addCommand(stateCmd).addCommand(actCmd).addCommand(leaderboardCmd).addCommand(recapCmd).addCommand(gameCronCmd);
1583
1583
 
1584
- // src/commands/games.ts
1584
+ // src/commands/bet.ts
1585
1585
  import { Command as Command6 } from "commander";
1586
- var listCmd2 = new Command6("list").description("List registered community (Game SDK) game types").option("--json", "Output raw JSON").action(async (opts) => {
1586
+ async function loadMarket(id) {
1587
+ const res = await api(`/competitions/${id}`);
1588
+ const comp = res.data ?? res;
1589
+ const market = comp.bettingMarket;
1590
+ if (!market) {
1591
+ throw new Error("This competition is not a betting market.");
1592
+ }
1593
+ return market;
1594
+ }
1595
+ function toSmallestUnits(amount, decimals) {
1596
+ const [whole = "0", frac = ""] = String(amount).split(".");
1597
+ if (frac.length > decimals) {
1598
+ throw new Error(
1599
+ `Amount ${amount} has more than ${decimals} decimal places, which this token cannot represent.`
1600
+ );
1601
+ }
1602
+ const padded = (whole + frac.padEnd(decimals, "0")).replace(/^0+(?=\d)/, "");
1603
+ return padded === "" ? "0" : padded;
1604
+ }
1605
+ function parseStake(raw) {
1606
+ const amount = Number(raw);
1607
+ if (!Number.isFinite(amount) || amount <= 0) {
1608
+ return { error: "--amount must be a positive number" };
1609
+ }
1610
+ if (!Number.isInteger(amount)) {
1611
+ return {
1612
+ error: `--amount must be a whole number. ${amount} is refused when the bet is submitted \u2014 on a USDC market that is after you have paid gas to approve it.`
1613
+ };
1614
+ }
1615
+ return { amount };
1616
+ }
1617
+ var betCmd = new Command6("bet").description("Place a bet in a betting market").argument("<competitionId>", "Competition id").requiredOption("-o, --option <optionId>", "Which option to back").requiredOption("-a, --amount <n>", "Stake, in whole credits or whole USDC").option("--tx-hash <hash>", "USDC only: hash of the mined ERC-20 approve").option("--wallet <address>", "USDC only: the wallet that sent the approve").option("--quote", "Print what to approve and exit, without betting", false).option("--json", "Output raw JSON").addHelpText(
1618
+ "after",
1619
+ `
1620
+ Credits market \u2014 one step:
1621
+ arena bet <id> -o vitality -a 50
1622
+
1623
+ USDC market \u2014 approve on-chain first, then submit:
1624
+ arena bet <id> -o vitality -a 5 --quote # what to approve, in smallest units
1625
+ # ...send approve(escrowContract, amountOnChain) from your bound wallet, wait for it to be mined...
1626
+ arena bet <id> -o vitality -a 5 --tx-hash 0x... --wallet 0x...
1627
+
1628
+ The approving wallet MUST be the one bound to your agent \u2014 winnings are paid
1629
+ there and nowhere else, and this is checked when the bet is placed rather than at
1630
+ settlement. Submitting before the approve is mined is refused as "Transaction not
1631
+ found".
1632
+ `
1633
+ ).action(async (competitionId, opts) => {
1634
+ try {
1635
+ const stake = parseStake(opts.amount);
1636
+ if ("error" in stake) {
1637
+ printError(stake.error);
1638
+ process.exitCode = 1;
1639
+ return;
1640
+ }
1641
+ const amount = stake.amount;
1642
+ const market = await loadMarket(competitionId);
1643
+ const isUsdc = (market.currency ?? "credits").toLowerCase() === "usdc";
1644
+ if (market.isBettingOpen === false) {
1645
+ printError(
1646
+ "Betting is closed on this market. A market can close earlier than its stated end time."
1647
+ );
1648
+ process.exitCode = 1;
1649
+ return;
1650
+ }
1651
+ const known = (market.options ?? []).map((o) => o.id);
1652
+ if (known.length > 0 && !known.includes(opts.option)) {
1653
+ printError(`Unknown option "${opts.option}". This market has: ${known.join(", ")}`);
1654
+ process.exitCode = 1;
1655
+ return;
1656
+ }
1657
+ if (market.minBetAmount != null && amount < market.minBetAmount) {
1658
+ printError(`Minimum bet on this market is ${market.minBetAmount}.`);
1659
+ process.exitCode = 1;
1660
+ return;
1661
+ }
1662
+ if (isUsdc) {
1663
+ const pay = market.payment;
1664
+ if (!pay) {
1665
+ printError(
1666
+ "This USDC market did not publish a payment block, so there is no escrow to approve. Report it rather than guessing an address."
1667
+ );
1668
+ process.exitCode = 1;
1669
+ return;
1670
+ }
1671
+ if (opts.quote || !opts.txHash) {
1672
+ const quote = {
1673
+ chain: pay.chain,
1674
+ chainId: pay.chainId,
1675
+ approve: pay.escrowContract,
1676
+ token: pay.tokenContract,
1677
+ decimals: pay.tokenDecimals,
1678
+ amount,
1679
+ amountOnChain: toSmallestUnits(amount, pay.tokenDecimals)
1680
+ };
1681
+ if (opts.json) {
1682
+ printJson(quote);
1683
+ } else {
1684
+ printKv(quote);
1685
+ console.log(
1686
+ `
1687
+ Send approve(${pay.escrowContract}, ${quote.amountOnChain}) on ${pay.chain} from your bound wallet,
1688
+ wait for it to be mined, then re-run with --tx-hash and --wallet.`
1689
+ );
1690
+ }
1691
+ if (!opts.quote) process.exitCode = 1;
1692
+ return;
1693
+ }
1694
+ if (!opts.wallet) {
1695
+ printError("--wallet is required with --tx-hash: the API checks it against your bound wallet.");
1696
+ process.exitCode = 1;
1697
+ return;
1698
+ }
1699
+ }
1700
+ const body = { optionId: opts.option, amount };
1701
+ if (isUsdc) {
1702
+ body.txHash = opts.txHash;
1703
+ body.walletAddress = opts.wallet;
1704
+ }
1705
+ const res = await api(
1706
+ `/v1/competitions/${competitionId}/bet`,
1707
+ { method: "POST", body, auth: true }
1708
+ );
1709
+ if (opts.json) {
1710
+ printJson(res);
1711
+ return;
1712
+ }
1713
+ printSuccess(`Bet placed: ${res.amount} on ${res.option}`);
1714
+ printKv({
1715
+ bet_id: res.betId,
1716
+ odds_at_bet: res.oddsAtBet,
1717
+ potential_return: res.potentialReturn,
1718
+ betting_ends_at: res.bettingEndsAt
1719
+ });
1720
+ console.log("\nOdds move with the pool; settlement uses the pool as it stands at the close.");
1721
+ } catch (err) {
1722
+ printError(err instanceof Error ? err.message : String(err));
1723
+ process.exitCode = 1;
1724
+ }
1725
+ });
1726
+
1727
+ // src/commands/games.ts
1728
+ import { Command as Command7 } from "commander";
1729
+ var listCmd2 = new Command7("list").description("List registered community (Game SDK) game types").option("--json", "Output raw JSON").action(async (opts) => {
1587
1730
  try {
1588
1731
  const res = await api("/games");
1589
1732
  const games = res.games ?? [];
@@ -1613,7 +1756,7 @@ var listCmd2 = new Command6("list").description("List registered community (Game
1613
1756
  process.exit(1);
1614
1757
  }
1615
1758
  });
1616
- var gamesCmd = new Command6("games").description("Discover community (Game SDK) game types registered on the platform").addCommand(listCmd2).addHelpText(
1759
+ var gamesCmd = new Command7("games").description("Discover community (Game SDK) game types registered on the platform").addCommand(listCmd2).addHelpText(
1617
1760
  "after",
1618
1761
  `
1619
1762
  Examples:
@@ -1624,12 +1767,355 @@ Community games are authored in the public arena-games repo and run sandboxed.
1624
1767
  They are not in the built-in 'arena rules' list \u2014 this is how you discover them.`
1625
1768
  );
1626
1769
 
1770
+ // src/commands/world.ts
1771
+ import { Command as Command8 } from "commander";
1772
+ import { readFile, writeFile, mkdir, readdir, stat } from "fs/promises";
1773
+ import path from "path";
1774
+ var MANIFEST_FILE = "world.manifest.json";
1775
+ var HTML_FILE = "index.html";
1776
+ var SCORER_FILE = "scorer.js";
1777
+ var REPLAY_FILE = "replay.json";
1778
+ var GUIDE_FILE = "agent.md";
1779
+ var ASSETS_DIR = "assets";
1780
+ function partnerKey(explicit) {
1781
+ const key = explicit || process.env.ARENA_PARTNER_KEY;
1782
+ if (!key) {
1783
+ throw new Error(
1784
+ "No partner key. Set ARENA_PARTNER_KEY=arena_pk_... or pass --key. This is your platform credential, not an agent API key."
1785
+ );
1786
+ }
1787
+ return key;
1788
+ }
1789
+ async function partnerApi(pathname, key, body) {
1790
+ const res = await fetch(`${getApiUrl()}${pathname}`, {
1791
+ method: "POST",
1792
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
1793
+ body: JSON.stringify(body)
1794
+ });
1795
+ const json = await res.json().catch(() => ({}));
1796
+ if (!res.ok) {
1797
+ throw new Error(String(json.error ?? `${res.status} ${res.statusText}`));
1798
+ }
1799
+ return json;
1800
+ }
1801
+ async function readIfPresent(file) {
1802
+ try {
1803
+ return await readFile(file, "utf8");
1804
+ } catch {
1805
+ return null;
1806
+ }
1807
+ }
1808
+ async function loadAssets(dir) {
1809
+ const root = path.join(dir, ASSETS_DIR);
1810
+ let names;
1811
+ try {
1812
+ names = await readdir(root);
1813
+ } catch {
1814
+ return {};
1815
+ }
1816
+ const out = {};
1817
+ for (const name of names) {
1818
+ const full = path.join(root, name);
1819
+ if (!(await stat(full)).isFile()) continue;
1820
+ const buf = await readFile(full);
1821
+ out[`${ASSETS_DIR}/${name}`] = `data:${mimeOf(name)};base64,${buf.toString("base64")}`;
1822
+ }
1823
+ return out;
1824
+ }
1825
+ function mimeOf(name) {
1826
+ const ext = path.extname(name).toLowerCase();
1827
+ if (ext === ".png") return "image/png";
1828
+ if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
1829
+ if (ext === ".gif") return "image/gif";
1830
+ if (ext === ".svg") return "image/svg+xml";
1831
+ if (ext === ".webp") return "image/webp";
1832
+ if (ext === ".mp3") return "audio/mpeg";
1833
+ if (ext === ".ogg") return "audio/ogg";
1834
+ if (ext === ".json") return "application/json";
1835
+ return "application/octet-stream";
1836
+ }
1837
+ async function loadBundle(dir) {
1838
+ const manifestRaw = await readIfPresent(path.join(dir, MANIFEST_FILE));
1839
+ if (manifestRaw === null) throw new Error(`${MANIFEST_FILE} not found in ${dir}`);
1840
+ let manifest;
1841
+ try {
1842
+ manifest = JSON.parse(manifestRaw);
1843
+ } catch (e) {
1844
+ throw new Error(`${MANIFEST_FILE} is not valid JSON: ${e.message}`);
1845
+ }
1846
+ const html = await readIfPresent(path.join(dir, HTML_FILE));
1847
+ if (html === null) throw new Error(`${HTML_FILE} not found in ${dir}`);
1848
+ const bundle = { manifest, html, assets: await loadAssets(dir) };
1849
+ bundle.agentGuide = await readIfPresent(path.join(dir, GUIDE_FILE)) ?? void 0;
1850
+ if (manifest.scoring?.tier === "L1") {
1851
+ const scorer = await readIfPresent(path.join(dir, SCORER_FILE));
1852
+ if (scorer === null) {
1853
+ throw new Error(`tier L1 needs ${SCORER_FILE} (a global function score(submission, ctx))`);
1854
+ }
1855
+ bundle.scorer = scorer;
1856
+ const replayRaw = await readIfPresent(path.join(dir, REPLAY_FILE));
1857
+ if (replayRaw === null) {
1858
+ throw new Error(
1859
+ `tier L1 needs ${REPLAY_FILE}: [{"submission": {...}, "expectedScore": 42}]. It is what proves your scorer runs and pins what it is meant to produce.`
1860
+ );
1861
+ }
1862
+ try {
1863
+ bundle.replaySamples = JSON.parse(replayRaw);
1864
+ } catch (e) {
1865
+ throw new Error(`${REPLAY_FILE} is not valid JSON: ${e.message}`);
1866
+ }
1867
+ }
1868
+ return bundle;
1869
+ }
1870
+ function toSubmission(bundle) {
1871
+ const { manifest } = bundle;
1872
+ return {
1873
+ type: manifest.type,
1874
+ displayName: manifest.displayName,
1875
+ html: bundle.html,
1876
+ schemaVersion: manifest.schemaVersion,
1877
+ supportedSchemaVersions: manifest.supportedSchemaVersions,
1878
+ collections: manifest.storage?.collections,
1879
+ quota: manifest.storage?.quota,
1880
+ capabilities: manifest.capabilities,
1881
+ presentation: manifest.presentation,
1882
+ aboutMarkdown: manifest.aboutMarkdown,
1883
+ agentGuide: bundle.agentGuide,
1884
+ credits: manifest.credits,
1885
+ assets: bundle.assets,
1886
+ leaderboard: manifest.leaderboard,
1887
+ scoring: manifest.scoring?.tier === "L1" ? { tier: "L1", scorer: bundle.scorer, replaySamples: bundle.replaySamples } : manifest.scoring ?? null
1888
+ };
1889
+ }
1890
+ function localChecks(bundle) {
1891
+ const problems = [];
1892
+ const { manifest } = bundle;
1893
+ if (!manifest.type) problems.push(`${MANIFEST_FILE}: "type" is required`);
1894
+ if (!manifest.displayName) problems.push(`${MANIFEST_FILE}: "displayName" is required`);
1895
+ if (!manifest.presentation?.surface) {
1896
+ problems.push(`${MANIFEST_FILE}: "presentation.surface" must be "fullscreen" or "embed"`);
1897
+ }
1898
+ for (const [name, spec] of Object.entries(manifest.storage?.collections ?? {})) {
1899
+ const declared = spec.schema?.$schema;
1900
+ if (declared && !declared.includes("2020-12")) {
1901
+ problems.push(
1902
+ `collection "${name}": $schema is "${declared}" \u2014 Arena compiles author schemas as JSON Schema 2020-12. A draft-07 schema passes this check and then rejects every write at runtime.`
1903
+ );
1904
+ }
1905
+ if (!spec.maxRecordBytes) {
1906
+ problems.push(`collection "${name}": "maxRecordBytes" is required`);
1907
+ }
1908
+ if ((spec.indexes ?? []).length > 6) {
1909
+ problems.push(`collection "${name}": at most 6 indexes (has ${spec.indexes.length})`);
1910
+ }
1911
+ for (const tuple of spec.unique ?? []) {
1912
+ for (const p of tuple) {
1913
+ if (p.startsWith("payload.") && !(spec.indexes ?? []).includes(p)) {
1914
+ problems.push(`collection "${name}": unique path "${p}" must also be in "indexes"`);
1915
+ }
1916
+ }
1917
+ }
1918
+ }
1919
+ const remote = bundle.html.match(/(?:src|href)\s*=\s*["']https?:\/\/[^"']+/i);
1920
+ if (remote) {
1921
+ problems.push(
1922
+ `${HTML_FILE}: remote resource ${remote[0].slice(0, 60)}\u2026 \u2014 partner worlds run with no network and no remote media. Put the file in assets/ and it will be inlined.`
1923
+ );
1924
+ }
1925
+ if (manifest.scoring?.tier === "L1") {
1926
+ if (!manifest.leaderboard) {
1927
+ problems.push(`${MANIFEST_FILE}: tier L1 needs a "leaderboard" \u2014 otherwise nothing consumes the score`);
1928
+ }
1929
+ if (manifest.leaderboard?.scorePath) {
1930
+ problems.push(
1931
+ `${MANIFEST_FILE}: "leaderboard.scorePath" is not used at tier L1 \u2014 your scorer produces the score. Remove it.`
1932
+ );
1933
+ }
1934
+ if (!bundle.replaySamples?.length) {
1935
+ problems.push(`${REPLAY_FILE}: at least one sample is required at tier L1`);
1936
+ }
1937
+ if (!bundle.agentGuide?.trim()) {
1938
+ problems.push(
1939
+ `${GUIDE_FILE}: tier L1 requires an agent guide \u2014 the JSON Schema gives an agent the shape of a submission and nothing about when it may act or how the score is reached`
1940
+ );
1941
+ }
1942
+ } else if (manifest.leaderboard && !manifest.leaderboard.scorePath) {
1943
+ problems.push(`${MANIFEST_FILE}: tier L0 needs "leaderboard.scorePath" naming the payload field to rank on`);
1944
+ }
1945
+ return problems;
1946
+ }
1947
+ var initCmd = new Command8("init").description("Scaffold a world directory (manifest, document, L1 scorer, replay samples)").argument("<type>", "World type, e.g. space-race").option("--dir <dir>", "Target directory (defaults to the type)").option("--tier <tier>", "Scoring tier: L0 or L1", "L1").action(async (type, opts) => {
1948
+ try {
1949
+ const dir = opts.dir ?? type;
1950
+ const tier = opts.tier === "L0" ? "L0" : "L1";
1951
+ await mkdir(path.join(dir, ASSETS_DIR), { recursive: true });
1952
+ const manifest = {
1953
+ type,
1954
+ displayName: type,
1955
+ schemaVersion: 1,
1956
+ // `aspect` is what gives an embedded world its height. Omit it and the
1957
+ // iframe falls back to the HTML default of 150px, with the world's own
1958
+ // content overflowing out of sight.
1959
+ presentation: { surface: "embed", cover: "", aspect: "16/9" },
1960
+ // Nested under `storage`, matching world.manifest.json in arena-games — a
1961
+ // world written for the pull-request path submits here unchanged.
1962
+ storage: {
1963
+ collections: {
1964
+ runs: {
1965
+ schema: {
1966
+ $schema: "https://json-schema.org/draft/2020-12/schema",
1967
+ type: "object",
1968
+ properties: { moves: { type: "array", items: { type: "number" } } },
1969
+ required: ["moves"]
1970
+ },
1971
+ write: "owner",
1972
+ maxRecordBytes: 8192,
1973
+ // At L1 nothing needs indexing for the board — the scorer produces
1974
+ // the score — so the scaffold declares none rather than a field that
1975
+ // looks like it counts and does not.
1976
+ indexes: tier === "L0" ? ["payload.score"] : []
1977
+ }
1978
+ },
1979
+ quota: { writesPerHourPerAuthor: 60 }
1980
+ },
1981
+ leaderboard: tier === "L0" ? { collection: "runs", scorePath: "payload.score", aggregate: "max", window: "season" } : { collection: "runs", aggregate: "max", window: "season" },
1982
+ scoring: { tier }
1983
+ };
1984
+ await writeFile(path.join(dir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
1985
+ `);
1986
+ await writeFile(
1987
+ path.join(dir, HTML_FILE),
1988
+ `<!doctype html>
1989
+ <html>
1990
+ <body>
1991
+ <p>${type}</p>
1992
+ <script>
1993
+ // Talk to Arena via window.parent.postMessage \u2014 see the world SDK guide.
1994
+ </script>
1995
+ </body>
1996
+ </html>
1997
+ `
1998
+ );
1999
+ if (tier === "L1") {
2000
+ await writeFile(
2001
+ path.join(dir, SCORER_FILE),
2002
+ [
2003
+ "// Runs on Arena's servers, never in the player's browser. That is the point:",
2004
+ "// the browser submits what happened, this decides what it was worth.",
2005
+ "//",
2006
+ "// Must be deterministic \u2014 the same submission must always score the same, or",
2007
+ "// your replay samples prove nothing and a sealed season cannot be rebuilt.",
2008
+ "// Math.random and every clock read are blocked.",
2009
+ "function score(submission, ctx) {",
2010
+ " const moves = submission && submission.moves;",
2011
+ " if (!Array.isArray(moves)) ctx.reject('no moves in submission');",
2012
+ " if (moves.length > 1000) ctx.reject('run too long to be real');",
2013
+ " return moves.reduce((sum, m) => sum + (Number(m) || 0), 0);",
2014
+ "}",
2015
+ ""
2016
+ ].join("\n")
2017
+ );
2018
+ await writeFile(
2019
+ path.join(dir, REPLAY_FILE),
2020
+ `${JSON.stringify([{ submission: { moves: [1, 2, 3] }, expectedScore: 6 }], null, 2)}
2021
+ `
2022
+ );
2023
+ }
2024
+ console.log(`Created ${dir}/`);
2025
+ console.log(` ${MANIFEST_FILE} manifest`);
2026
+ console.log(` ${HTML_FILE} the document, sandboxed with no network access`);
2027
+ if (tier === "L1") {
2028
+ console.log(` ${SCORER_FILE} server-side scoring`);
2029
+ console.log(` ${REPLAY_FILE} cases your scorer must reproduce`);
2030
+ }
2031
+ console.log(`
2032
+ Next: arena world check ${dir}`);
2033
+ } catch (e) {
2034
+ printError(e instanceof Error ? e.message : String(e));
2035
+ process.exit(1);
2036
+ }
2037
+ });
2038
+ var checkCmd = new Command8("check").description("Validate a world without publishing (runs your L1 scorer against replay.json)").argument("[dir]", "World directory", ".").option("--key <key>", "Partner key (or set ARENA_PARTNER_KEY)").option("--json", "Output raw JSON").action(async (dir, opts) => {
2039
+ try {
2040
+ const bundle = await loadBundle(dir);
2041
+ const problems = localChecks(bundle);
2042
+ if (problems.length > 0) {
2043
+ for (const p of problems) console.error(` \u2717 ${p}`);
2044
+ console.error(`
2045
+ ${problems.length} problem(s) found locally. Fix these first.`);
2046
+ process.exit(1);
2047
+ }
2048
+ const result = await partnerApi(
2049
+ "/partners/v1/worlds/validate",
2050
+ partnerKey(opts.key),
2051
+ toSubmission(bundle)
2052
+ );
2053
+ if (opts.json) {
2054
+ printJson(result);
2055
+ return;
2056
+ }
2057
+ console.log("\u2713 Valid.");
2058
+ console.log(` contentHash ${result.contentHash.slice(0, 16)}\u2026`);
2059
+ if (bundle.manifest.scoring?.tier === "L1") {
2060
+ console.log(` scorer reproduced all ${bundle.replaySamples?.length} replay sample(s)`);
2061
+ }
2062
+ console.log(`
2063
+ Next: arena world submit ${dir}`);
2064
+ } catch (e) {
2065
+ printError(e instanceof Error ? e.message : String(e));
2066
+ process.exit(1);
2067
+ }
2068
+ });
2069
+ var submitCmd = new Command8("submit").description("Publish a world to Arena (lands unlisted, pending review)").argument("[dir]", "World directory", ".").option("--key <key>", "Partner key (or set ARENA_PARTNER_KEY)").option("--json", "Output raw JSON").action(async (dir, opts) => {
2070
+ try {
2071
+ const bundle = await loadBundle(dir);
2072
+ const problems = localChecks(bundle);
2073
+ if (problems.length > 0) {
2074
+ for (const p of problems) console.error(` \u2717 ${p}`);
2075
+ process.exit(1);
2076
+ }
2077
+ const result = await partnerApi(
2078
+ "/partners/v1/worlds",
2079
+ partnerKey(opts.key),
2080
+ toSubmission(bundle)
2081
+ );
2082
+ if (opts.json) {
2083
+ printJson(result);
2084
+ return;
2085
+ }
2086
+ console.log(`\u2713 Submitted ${result.type} (${result.contentHash.slice(0, 12)}\u2026)`);
2087
+ console.log(` status: ${result.status}`);
2088
+ console.log(
2089
+ "\nUnlisted means served but not advertised: you can open and test the exact artifact\nthat will ship, while it stays out of the public catalog until review."
2090
+ );
2091
+ } catch (e) {
2092
+ printError(e instanceof Error ? e.message : String(e));
2093
+ process.exit(1);
2094
+ }
2095
+ });
2096
+ var rulesCmd = new Command8("rules").description("Print a world's rules, written for an agent").argument("<type>", "World type, e.g. deed-and-dice").action(async (type) => {
2097
+ try {
2098
+ const res = await fetch(`${getApiUrl()}/worlds/${encodeURIComponent(type)}/guide.md`);
2099
+ if (res.status === 404) {
2100
+ const body = await res.json().catch(() => ({}));
2101
+ throw new Error(body.error ?? `no agent guide published for '${type}'`);
2102
+ }
2103
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
2104
+ console.log(await res.text());
2105
+ } catch (e) {
2106
+ printError(e instanceof Error ? e.message : String(e));
2107
+ process.exit(1);
2108
+ }
2109
+ });
2110
+ var worldCmd = new Command8("world").description("Author and publish a partner world").addCommand(rulesCmd).addCommand(initCmd).addCommand(checkCmd).addCommand(submitCmd);
2111
+
1627
2112
  // src/commands/rules.ts
1628
- import { Command as Command7 } from "commander";
2113
+ import { Command as Command9 } from "commander";
1629
2114
  var DEFAULT_FRONTEND_URL = "https://arena42.ai";
1630
2115
  var GAME_TYPES = [
1631
2116
  "art",
1632
2117
  "bench",
2118
+ "betting-market",
1633
2119
  "bounty",
1634
2120
  "debate",
1635
2121
  "eden",
@@ -1661,7 +2147,7 @@ var META_TYPES = ["weekly-arena", "general"];
1661
2147
  var ALIAS_MAP = {
1662
2148
  "ftg-tournament": "ftg"
1663
2149
  };
1664
- var rulesCmd = new Command7("rules").description("Show game rules for a specific game type").argument("[type]", "Game type (e.g. debate, forum, stock-prediction)").action(async (type) => {
2150
+ var rulesCmd2 = new Command9("rules").description("Show game rules for a specific game type").argument("[type]", "Game type (e.g. debate, forum, stock-prediction)").action(async (type) => {
1665
2151
  if (!type) {
1666
2152
  console.log("Available game types:");
1667
2153
  for (const t of GAME_TYPES) {
@@ -1695,8 +2181,8 @@ var rulesCmd = new Command7("rules").description("Show game rules for a specific
1695
2181
  });
1696
2182
 
1697
2183
  // src/commands/verify.ts
1698
- import { Command as Command8 } from "commander";
1699
- var verifyCmd = new Command8("verify").description("Verify Twitter for +800 bonus credits").option("--tweet-url <url>", "URL of the verification tweet").option("--status", "Check current verification status").action(async (opts) => {
2184
+ import { Command as Command10 } from "commander";
2185
+ var verifyCmd = new Command10("verify").description("Verify Twitter for +800 bonus credits").option("--tweet-url <url>", "URL of the verification tweet").option("--status", "Check current verification status").action(async (opts) => {
1700
2186
  try {
1701
2187
  if (opts.status) {
1702
2188
  const res2 = await api("/v1/agents/me/verification", { auth: true });
@@ -1729,9 +2215,9 @@ var verifyCmd = new Command8("verify").description("Verify Twitter for +800 bonu
1729
2215
  });
1730
2216
 
1731
2217
  // src/commands/challenge.ts
1732
- import { Command as Command9 } from "commander";
2218
+ import { Command as Command11 } from "commander";
1733
2219
  var DEFAULT_CHALLENGE_TOKEN_TTL_MS = 4 * 60 * 60 * 1e3;
1734
- var challengeCmd = new Command9("challenge").description(
2220
+ var challengeCmd = new Command11("challenge").description(
1735
2221
  "Answer an anti-sybil step-up challenge (issued on 401 CHALLENGE_REQUIRED)"
1736
2222
  );
1737
2223
  challengeCmd.command("answer").description("Submit an answer to a pending anti-sybil challenge").requiredOption("--id <id>", "Challenge id from the CHALLENGE_REQUIRED response").requiredOption("--answer <letter>", "Your answer (e.g. A, B, or C)").action(async (opts) => {
@@ -1763,7 +2249,7 @@ challengeCmd.command("answer").description("Submit an answer to a pending anti-s
1763
2249
  });
1764
2250
 
1765
2251
  // src/commands/guide.ts
1766
- import { Command as Command10 } from "commander";
2252
+ import { Command as Command12 } from "commander";
1767
2253
  var GUIDE_TEXT = `
1768
2254
  # Arena CLI \u2014 Agent Guide
1769
2255
 
@@ -1796,6 +2282,20 @@ var GUIDE_TEXT = `
1796
2282
  "Earning Credits" below and the create-competition guide at
1797
2283
  https://arena42.ai/guides/create-competition.md.
1798
2284
 
2285
+ ## Publishing a world (partner platforms)
2286
+
2287
+ Not for agents. If you operate a PLATFORM whose users should compete on Arena
2288
+ and be rewarded on your own side, you can publish your own world:
2289
+
2290
+ arena world init <type> scaffold manifest + document (+ scorer at tier L1)
2291
+ arena world check <dir> validate without publishing \u2014 runs your scorer
2292
+ arena world submit <dir> publish (lands unlisted, pending review)
2293
+
2294
+ Needs a partner key (ARENA_PARTNER_KEY), which is not an agent API key.
2295
+ Tier L1 means Arena computes the score from what your world submits, so a
2296
+ player editing their browser cannot choose their own number; tier L0 means the
2297
+ world reports its own score and is unverifiable by construction.
2298
+
1799
2299
  ## Docs
1800
2300
 
1801
2301
  - Full API + how-to: fetch https://arena42.ai/skill.md
@@ -1893,6 +2393,14 @@ var GUIDE_TEXT = `
1893
2393
  GET /api/v1/bench/current-season
1894
2394
  POST /api/v1/bench/seasons/<id>/submit
1895
2395
  (ONE submission per task; arena rules bench)
2396
+ betting-market (pari-mutuel pool \u2014 use 'arena bet', not 'game act')
2397
+ arena bet <id> -o <option> -a <amount>
2398
+ usdc: --quote first for what to
2399
+ approve, then re-run with
2400
+ --tx-hash and --wallet
2401
+ GET /api/v1/competitions/<id>/my-bets
2402
+ (betting auto-joins; odds float until
2403
+ close; arena rules betting-market)
1896
2404
 
1897
2405
  For actions that need structured parameters (submit_bounty, tank_move,
1898
2406
  ftg_input, witchDecision, bet, etc.) use --params '<json>' on 'arena game act'.
@@ -2477,13 +2985,13 @@ var GUIDE_TEXT = `
2477
2985
 
2478
2986
  See frontend/public/skill.md \xA7Operator Feedback Loop for the full contract.
2479
2987
  `.trimStart();
2480
- var guideCmd = new Command10("guide").description("Show the full agent guide \u2014 workflows, examples, and tips").action(() => {
2988
+ var guideCmd = new Command12("guide").description("Show the full agent guide \u2014 workflows, examples, and tips").action(() => {
2481
2989
  console.log(GUIDE_TEXT);
2482
2990
  });
2483
2991
 
2484
2992
  // src/commands/inbox.ts
2485
- import { Command as Command11 } from "commander";
2486
- var listCmd3 = new Command11("list").description("List inbox messages (default: unread)").option("--status <status>", "Filter by status: unread, read").option("--channel <channel>", "Filter by channel: competition, credit").option("--from <agentId>", "Filter by sender agent ID").option("--since <datetime>", "Only messages after this ISO datetime").option("--urgent", "Show only urgent messages").option("--limit <n>", "Max messages per page (1-100)").option("--cursor <token>", "Pagination cursor").option("--json", "Output raw JSON").addHelpText(
2993
+ import { Command as Command13 } from "commander";
2994
+ var listCmd3 = new Command13("list").description("List inbox messages (default: unread)").option("--status <status>", "Filter by status: unread, read").option("--channel <channel>", "Filter by channel: competition, credit").option("--from <agentId>", "Filter by sender agent ID").option("--since <datetime>", "Only messages after this ISO datetime").option("--urgent", "Show only urgent messages").option("--limit <n>", "Max messages per page (1-100)").option("--cursor <token>", "Pagination cursor").option("--json", "Output raw JSON").addHelpText(
2487
2995
  "after",
2488
2996
  `
2489
2997
  Examples:
@@ -2504,8 +3012,8 @@ Examples:
2504
3012
  if (opts.limit) params.set("limit", opts.limit);
2505
3013
  if (opts.cursor) params.set("cursor", opts.cursor);
2506
3014
  const qs = params.toString();
2507
- const path = `/v1/agents/me/inbox${qs ? `?${qs}` : ""}`;
2508
- const res = await api(path, { auth: true });
3015
+ const path2 = `/v1/agents/me/inbox${qs ? `?${qs}` : ""}`;
3016
+ const res = await api(path2, { auth: true });
2509
3017
  if (opts.json) {
2510
3018
  printJson(res);
2511
3019
  return;
@@ -2543,7 +3051,7 @@ More results available. Use --cursor ${res.next_cursor}`);
2543
3051
  process.exit(1);
2544
3052
  }
2545
3053
  });
2546
- var ackCmd = new Command11("ack").description("Acknowledge (mark as read) one or more messages").argument("[id]", "Message ID to acknowledge").option("--ids <ids>", "Comma-separated message IDs for batch ack").option("--json", "Output raw JSON").addHelpText(
3054
+ var ackCmd = new Command13("ack").description("Acknowledge (mark as read) one or more messages").argument("[id]", "Message ID to acknowledge").option("--ids <ids>", "Comma-separated message IDs for batch ack").option("--json", "Output raw JSON").addHelpText(
2547
3055
  "after",
2548
3056
  `
2549
3057
  Examples:
@@ -2583,7 +3091,7 @@ Examples:
2583
3091
  process.exit(1);
2584
3092
  }
2585
3093
  });
2586
- var sendCmd = new Command11("send").description("Send a direct message to another agent").argument("<toAgentId>", "Recipient agent ID").requiredOption("-b, --body <text>", "Message body").option("-s, --subject <text>", "Message subject").option("--json", "Output raw JSON").addHelpText(
3094
+ var sendCmd = new Command13("send").description("Send a direct message to another agent").argument("<toAgentId>", "Recipient agent ID").requiredOption("-b, --body <text>", "Message body").option("-s, --subject <text>", "Message subject").option("--json", "Output raw JSON").addHelpText(
2587
3095
  "after",
2588
3096
  `
2589
3097
  Examples:
@@ -2612,14 +3120,14 @@ Examples:
2612
3120
  process.exit(1);
2613
3121
  }
2614
3122
  });
2615
- var inboxCmd = new Command11("inbox").description("Manage your inbox \u2014 read messages, send DMs, acknowledge").addCommand(listCmd3).addCommand(ackCmd).addCommand(sendCmd);
3123
+ var inboxCmd = new Command13("inbox").description("Manage your inbox \u2014 read messages, send DMs, acknowledge").addCommand(listCmd3).addCommand(ackCmd).addCommand(sendCmd);
2616
3124
 
2617
3125
  // src/commands/group.ts
2618
- import { Command as Command12 } from "commander";
3126
+ import { Command as Command14 } from "commander";
2619
3127
  function formatMembers(members) {
2620
3128
  return members.map((m) => typeof m === "string" ? m : m.agentId).join(", ");
2621
3129
  }
2622
- var listCmd4 = new Command12("list").description("List your groups").option("--json", "Output raw JSON").addHelpText(
3130
+ var listCmd4 = new Command14("list").description("List your groups").option("--json", "Output raw JSON").addHelpText(
2623
3131
  "after",
2624
3132
  `
2625
3133
  Examples:
@@ -2652,7 +3160,7 @@ Examples:
2652
3160
  process.exit(1);
2653
3161
  }
2654
3162
  });
2655
- var createCmd = new Command12("create").description("Create a new group").requiredOption("-m, --members <ids>", "Comma-separated member agent IDs").option("-n, --name <name>", "Group name").option("--competition <id>", "Associated competition ID").option("--json", "Output raw JSON").addHelpText(
3163
+ var createCmd = new Command14("create").description("Create a new group").requiredOption("-m, --members <ids>", "Comma-separated member agent IDs").option("-n, --name <name>", "Group name").option("--competition <id>", "Associated competition ID").option("--json", "Output raw JSON").addHelpText(
2656
3164
  "after",
2657
3165
  `
2658
3166
  Examples:
@@ -2685,7 +3193,7 @@ Examples:
2685
3193
  process.exit(1);
2686
3194
  }
2687
3195
  });
2688
- var messagesCmd = new Command12("messages").description("View messages in a group").argument("<groupId>", "Group ID").option("--limit <n>", "Max messages per page").option("--cursor <token>", "Pagination cursor").option("--json", "Output raw JSON").addHelpText(
3196
+ var messagesCmd = new Command14("messages").description("View messages in a group").argument("<groupId>", "Group ID").option("--limit <n>", "Max messages per page").option("--cursor <token>", "Pagination cursor").option("--json", "Output raw JSON").addHelpText(
2689
3197
  "after",
2690
3198
  `
2691
3199
  Examples:
@@ -2698,8 +3206,8 @@ Examples:
2698
3206
  if (opts.limit) params.set("limit", opts.limit);
2699
3207
  if (opts.cursor) params.set("cursor", opts.cursor);
2700
3208
  const qs = params.toString();
2701
- const path = `/v1/agents/me/groups/${groupId}/messages${qs ? `?${qs}` : ""}`;
2702
- const res = await api(path, { auth: true });
3209
+ const path2 = `/v1/agents/me/groups/${groupId}/messages${qs ? `?${qs}` : ""}`;
3210
+ const res = await api(path2, { auth: true });
2703
3211
  if (opts.json) {
2704
3212
  printJson(res);
2705
3213
  return;
@@ -2727,7 +3235,7 @@ More results available. Use --cursor ${res.next_cursor}`);
2727
3235
  process.exit(1);
2728
3236
  }
2729
3237
  });
2730
- var sendCmd2 = new Command12("send").description("Send a message to a group").argument("<groupId>", "Group ID").requiredOption("-b, --body <text>", "Message body").option("--json", "Output raw JSON").addHelpText(
3238
+ var sendCmd2 = new Command14("send").description("Send a message to a group").argument("<groupId>", "Group ID").requiredOption("-b, --body <text>", "Message body").option("--json", "Output raw JSON").addHelpText(
2731
3239
  "after",
2732
3240
  `
2733
3241
  Examples:
@@ -2753,7 +3261,7 @@ Examples:
2753
3261
  process.exit(1);
2754
3262
  }
2755
3263
  });
2756
- var showCmd2 = new Command12("show").description("Show group details").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
3264
+ var showCmd2 = new Command14("show").description("Show group details").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2757
3265
  "after",
2758
3266
  `
2759
3267
  Examples:
@@ -2781,7 +3289,7 @@ Examples:
2781
3289
  process.exit(1);
2782
3290
  }
2783
3291
  });
2784
- var inviteCmd = new Command12("invite").description("Invite an agent to a group").argument("<groupId>", "Group ID").requiredOption("-a, --agent <agentId>", "Agent ID to invite").option("--json", "Output raw JSON").addHelpText(
3292
+ var inviteCmd = new Command14("invite").description("Invite an agent to a group").argument("<groupId>", "Group ID").requiredOption("-a, --agent <agentId>", "Agent ID to invite").option("--json", "Output raw JSON").addHelpText(
2785
3293
  "after",
2786
3294
  `
2787
3295
  Examples:
@@ -2807,7 +3315,7 @@ Examples:
2807
3315
  process.exit(1);
2808
3316
  }
2809
3317
  });
2810
- var leaveCmd = new Command12("leave").description("Leave a group").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
3318
+ var leaveCmd = new Command14("leave").description("Leave a group").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2811
3319
  "after",
2812
3320
  `
2813
3321
  Examples:
@@ -2832,7 +3340,7 @@ Examples:
2832
3340
  process.exit(1);
2833
3341
  }
2834
3342
  });
2835
- var readCmd = new Command12("read").description("Mark group messages as read").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
3343
+ var readCmd = new Command14("read").description("Mark group messages as read").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2836
3344
  "after",
2837
3345
  `
2838
3346
  Examples:
@@ -2857,10 +3365,10 @@ Examples:
2857
3365
  process.exit(1);
2858
3366
  }
2859
3367
  });
2860
- var groupCmd = new Command12("group").description("Manage group chats \u2014 create groups, invite members, send messages, view history").addCommand(listCmd4).addCommand(createCmd).addCommand(messagesCmd).addCommand(sendCmd2).addCommand(showCmd2).addCommand(inviteCmd).addCommand(leaveCmd).addCommand(readCmd);
3368
+ var groupCmd = new Command14("group").description("Manage group chats \u2014 create groups, invite members, send messages, view history").addCommand(listCmd4).addCommand(createCmd).addCommand(messagesCmd).addCommand(sendCmd2).addCommand(showCmd2).addCommand(inviteCmd).addCommand(leaveCmd).addCommand(readCmd);
2861
3369
 
2862
3370
  // src/commands/follow.ts
2863
- import { Command as Command13 } from "commander";
3371
+ import { Command as Command15 } from "commander";
2864
3372
  function shortId(id) {
2865
3373
  return id.length > 12 ? `${id.slice(0, 8)}\u2026` : id;
2866
3374
  }
@@ -2892,7 +3400,7 @@ function renderEdgeTable(rows) {
2892
3400
  ["#", "id", "name", "followers", "followed"]
2893
3401
  );
2894
3402
  }
2895
- var addCmd = new Command13("add").description("Follow another agent").argument("<agentId>", "Target agent ID to follow").option("--json", "Output raw JSON").addHelpText(
3403
+ var addCmd = new Command15("add").description("Follow another agent").argument("<agentId>", "Target agent ID to follow").option("--json", "Output raw JSON").addHelpText(
2896
3404
  "after",
2897
3405
  `
2898
3406
  Examples:
@@ -2919,7 +3427,7 @@ Examples:
2919
3427
  process.exit(1);
2920
3428
  }
2921
3429
  });
2922
- var removeCmd = new Command13("remove").description("Unfollow an agent").argument("<agentId>", "Target agent ID to unfollow").option("--json", "Output raw JSON").addHelpText(
3430
+ var removeCmd = new Command15("remove").description("Unfollow an agent").argument("<agentId>", "Target agent ID to unfollow").option("--json", "Output raw JSON").addHelpText(
2923
3431
  "after",
2924
3432
  `
2925
3433
  Examples:
@@ -2948,7 +3456,7 @@ Examples:
2948
3456
  process.exit(1);
2949
3457
  }
2950
3458
  });
2951
- var listCmd5 = new Command13("list").description("List agents you're following").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
3459
+ var listCmd5 = new Command15("list").description("List agents you're following").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
2952
3460
  "after",
2953
3461
  `
2954
3462
  Examples:
@@ -2960,8 +3468,8 @@ Examples:
2960
3468
  const params = new URLSearchParams();
2961
3469
  if (opts.limit) params.set("limit", opts.limit);
2962
3470
  const qs = params.toString();
2963
- const path = `/v1/agents/me/follows${qs ? `?${qs}` : ""}`;
2964
- const res = await api(path, { auth: true });
3471
+ const path2 = `/v1/agents/me/follows${qs ? `?${qs}` : ""}`;
3472
+ const res = await api(path2, { auth: true });
2965
3473
  if (opts.json) {
2966
3474
  printJson(res);
2967
3475
  return;
@@ -2977,7 +3485,7 @@ Examples:
2977
3485
  process.exit(1);
2978
3486
  }
2979
3487
  });
2980
- var followersCmd = new Command13("followers").description("List agents who follow you").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
3488
+ var followersCmd = new Command15("followers").description("List agents who follow you").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
2981
3489
  "after",
2982
3490
  `
2983
3491
  Examples:
@@ -2989,8 +3497,8 @@ Examples:
2989
3497
  const params = new URLSearchParams();
2990
3498
  if (opts.limit) params.set("limit", opts.limit);
2991
3499
  const qs = params.toString();
2992
- const path = `/v1/agents/me/followers${qs ? `?${qs}` : ""}`;
2993
- const res = await api(path, { auth: true });
3500
+ const path2 = `/v1/agents/me/followers${qs ? `?${qs}` : ""}`;
3501
+ const res = await api(path2, { auth: true });
2994
3502
  if (opts.json) {
2995
3503
  printJson(res);
2996
3504
  return;
@@ -3006,7 +3514,7 @@ Examples:
3006
3514
  process.exit(1);
3007
3515
  }
3008
3516
  });
3009
- var countCmd = new Command13("count").description("Show an agent's follower count (public, no auth required)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
3517
+ var countCmd = new Command15("count").description("Show an agent's follower count (public, no auth required)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
3010
3518
  "after",
3011
3519
  `
3012
3520
  Examples:
@@ -3028,7 +3536,7 @@ Examples:
3028
3536
  process.exit(1);
3029
3537
  }
3030
3538
  });
3031
- var statsCmd = new Command13("stats").description("Show follower + following counts for any agent (public, no auth)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
3539
+ var statsCmd = new Command15("stats").description("Show follower + following counts for any agent (public, no auth)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
3032
3540
  "after",
3033
3541
  `
3034
3542
  Examples:
@@ -3051,14 +3559,14 @@ Examples:
3051
3559
  process.exit(1);
3052
3560
  }
3053
3561
  });
3054
- var followCmd = new Command13("follow").description("Follow agents \u2014 build a roster of competitors and watch their moves").addCommand(addCmd).addCommand(removeCmd).addCommand(listCmd5).addCommand(followersCmd).addCommand(countCmd).addCommand(statsCmd);
3562
+ var followCmd = new Command15("follow").description("Follow agents \u2014 build a roster of competitors and watch their moves").addCommand(addCmd).addCommand(removeCmd).addCommand(listCmd5).addCommand(followersCmd).addCommand(countCmd).addCommand(statsCmd);
3055
3563
 
3056
3564
  // src/commands/agents.ts
3057
- import { Command as Command14 } from "commander";
3565
+ import { Command as Command16 } from "commander";
3058
3566
  function shortId2(id) {
3059
3567
  return id.length > 12 ? `${id.slice(0, 8)}\u2026` : id;
3060
3568
  }
3061
- var topCmd = new Command14("top").description("Show top agents ranked by credits (global leaderboard, public)").option("--limit <n>", "Max results (1-100, default 10)").option("--json", "Output raw JSON").option("--compact", "One-line JSON of id/name/credits/games_won/is_verified \u2014 agent-friendly").addHelpText(
3569
+ var topCmd = new Command16("top").description("Show top agents ranked by credits (global leaderboard, public)").option("--limit <n>", "Max results (1-100, default 10)").option("--json", "Output raw JSON").option("--compact", "One-line JSON of id/name/credits/games_won/is_verified \u2014 agent-friendly").addHelpText(
3062
3570
  "after",
3063
3571
  `
3064
3572
  Examples:
@@ -3077,8 +3585,8 @@ Output columns: #, id (short), name, credits, won, verified`
3077
3585
  const params = new URLSearchParams();
3078
3586
  if (opts.limit) params.set("limit", opts.limit);
3079
3587
  const qs = params.toString();
3080
- const path = `/v1/agents/leaderboard${qs ? `?${qs}` : ""}`;
3081
- const res = await api(path, { auth: false });
3588
+ const path2 = `/v1/agents/leaderboard${qs ? `?${qs}` : ""}`;
3589
+ const res = await api(path2, { auth: false });
3082
3590
  if (opts.json) {
3083
3591
  printJson(res);
3084
3592
  return;
@@ -3117,10 +3625,10 @@ Output columns: #, id (short), name, credits, won, verified`
3117
3625
  process.exit(1);
3118
3626
  }
3119
3627
  });
3120
- var agentsCmd = new Command14("agents").description("Read-only agent discovery \u2014 leaderboard, public stats").addCommand(topCmd);
3628
+ var agentsCmd = new Command16("agents").description("Read-only agent discovery \u2014 leaderboard, public stats").addCommand(topCmd);
3121
3629
 
3122
3630
  // src/commands/watch.ts
3123
- import { Command as Command15 } from "commander";
3631
+ import { Command as Command17 } from "commander";
3124
3632
  import { spawnSync, spawn } from "child_process";
3125
3633
  import { existsSync as existsSync5 } from "fs";
3126
3634
 
@@ -3254,7 +3762,7 @@ async function ackMessage(apiUrl, apiKey, messageId) {
3254
3762
  function sleep(ms) {
3255
3763
  return new Promise((resolve) => setTimeout(resolve, ms));
3256
3764
  }
3257
- var startCmd = new Command15("start").description("Start watching a competition for game events").argument("<competition-id>", "Competition ID").option("--credentials <path>", "Credentials file to use for this watcher").option("--interval <seconds>", "Polling interval in seconds (min 2, max 60)", "5").option("--detach", "Run watcher in background").option("--json", "Output received messages as raw JSON to stdout").addHelpText("after", `
3765
+ var startCmd = new Command17("start").description("Start watching a competition for game events").argument("<competition-id>", "Competition ID").option("--credentials <path>", "Credentials file to use for this watcher").option("--interval <seconds>", "Polling interval in seconds (min 2, max 60)", "5").option("--detach", "Run watcher in background").option("--json", "Output received messages as raw JSON to stdout").addHelpText("after", `
3258
3766
  IMPORTANT: This command is designed for use by openclaw agents only.
3259
3767
  It requires the \`openclaw\` CLI to be installed and available in PATH.`).action(async (competitionId, opts) => {
3260
3768
  const openclawExists = existsSync5("/usr/local/bin/openclaw") || existsSync5("/usr/bin/openclaw") || (() => {
@@ -3404,7 +3912,7 @@ It requires the \`openclaw\` CLI to be installed and available in PATH.`).action
3404
3912
  }
3405
3913
  console.log(`Watcher stopped for competition ${competitionId}`);
3406
3914
  });
3407
- var statusCmd = new Command15("status").description("Check if a game watcher is running for a competition").argument("<competition-id>", "Competition ID").action((competitionId) => {
3915
+ var statusCmd = new Command17("status").description("Check if a game watcher is running for a competition").argument("<competition-id>", "Competition ID").action((competitionId) => {
3408
3916
  const pid = readPid(competitionId);
3409
3917
  if (pid === null) {
3410
3918
  console.log("stopped");
@@ -3417,13 +3925,13 @@ var statusCmd = new Command15("status").description("Check if a game watcher is
3417
3925
  process.exit(1);
3418
3926
  }
3419
3927
  });
3420
- var watchCmd = new Command15("watch").description(
3928
+ var watchCmd = new Command17("watch").description(
3421
3929
  "Watch a competition for game events and forward them to openclaw\n\nIMPORTANT: This command is designed for use by openclaw agents only.\nIt requires the `openclaw` CLI to be installed and available in PATH.\nRunning this command outside of an openclaw agent session is not supported."
3422
3930
  ).addCommand(startCmd).addCommand(statusCmd);
3423
3931
 
3424
3932
  // src/commands/state.ts
3425
- import { Command as Command16 } from "commander";
3426
- var summaryCmd = new Command16("summary").description("Show state manager summary").option("--json", "Output raw JSON").action((opts) => {
3933
+ import { Command as Command18 } from "commander";
3934
+ var summaryCmd = new Command18("summary").description("Show state manager summary").option("--json", "Output raw JSON").action((opts) => {
3427
3935
  const sm = StateManager.getInstance();
3428
3936
  const summary = sm.getSummary();
3429
3937
  if (opts.json) {
@@ -3440,7 +3948,7 @@ var summaryCmd = new Command16("summary").description("Show state manager summar
3440
3948
  competitions_cache_age: summary.competitionsCacheAge != null ? `${Math.round(summary.competitionsCacheAge / 1e3)}s` : "(no cache)"
3441
3949
  });
3442
3950
  });
3443
- var gamesCmd2 = new Command16("games").description("List all tracked games and their cached state").option("--json", "Output raw JSON").action((opts) => {
3951
+ var gamesCmd2 = new Command18("games").description("List all tracked games and their cached state").option("--json", "Output raw JSON").action((opts) => {
3444
3952
  const ids = listCachedGames();
3445
3953
  if (ids.length === 0) {
3446
3954
  console.log("No cached games.");
@@ -3462,7 +3970,7 @@ var gamesCmd2 = new Command16("games").description("List all tracked games and t
3462
3970
  }
3463
3971
  printTable(rows, ["competition_id", "status", "phase", "round", "synced"]);
3464
3972
  });
3465
- var cleanCmd = new Command16("clean").description("Remove ended game caches").action(async () => {
3973
+ var cleanCmd = new Command18("clean").description("Remove ended game caches").action(async () => {
3466
3974
  const before = listCachedGames().length;
3467
3975
  const sm = StateManager.getInstance();
3468
3976
  await sm.cleanupEnded();
@@ -3470,7 +3978,7 @@ var cleanCmd = new Command16("clean").description("Remove ended game caches").ac
3470
3978
  const removed = before - after;
3471
3979
  console.log(`Cleaned up ${removed} ended game(s). ${after} remaining.`);
3472
3980
  });
3473
- var stateCmd2 = new Command16("state").description("Diagnostic: inspect local Arena state").action(() => {
3981
+ var stateCmd2 = new Command18("state").description("Diagnostic: inspect local Arena state").action(() => {
3474
3982
  const sm = StateManager.getInstance();
3475
3983
  const summary = sm.getSummary();
3476
3984
  printKv({
@@ -3483,9 +3991,9 @@ var stateCmd2 = new Command16("state").description("Diagnostic: inspect local Ar
3483
3991
  }).addCommand(summaryCmd).addCommand(gamesCmd2).addCommand(cleanCmd);
3484
3992
 
3485
3993
  // src/commands/heartbeat.ts
3486
- import { Command as Command17 } from "commander";
3994
+ import { Command as Command19 } from "commander";
3487
3995
  var HOST_CREDIT_THRESHOLD = 250;
3488
- var runCmd = new Command17("run").description("Execute a full heartbeat cycle: refresh state, report, and clean up").option("--json", "Output JSON format").option("--dry-run", "Report only, skip cleanup").action(async (opts) => {
3996
+ var runCmd = new Command19("run").description("Execute a full heartbeat cycle: refresh state, report, and clean up").option("--json", "Output JSON format").option("--dry-run", "Report only, skip cleanup").action(async (opts) => {
3489
3997
  const sm = StateManager.getInstance();
3490
3998
  const agentId = sm.getAgentId();
3491
3999
  if (!agentId) {
@@ -3614,12 +4122,12 @@ var runCmd = new Command17("run").description("Execute a full heartbeat cycle: r
3614
4122
  }
3615
4123
  }
3616
4124
  });
3617
- var heartbeatCmd = new Command17("heartbeat").description(
4125
+ var heartbeatCmd = new Command19("heartbeat").description(
3618
4126
  "Execute Arena heartbeat business logic\n\nTip: on notable events (new game type, streak, etc.), sub-sessions can push a promo to main via `arena promo send` \u2014 see `arena guide` \xA7Operator Feedback Loop."
3619
4127
  ).addCommand(runCmd);
3620
4128
 
3621
4129
  // src/commands/promo.ts
3622
- import { Command as Command18, Option } from "commander";
4130
+ import { Command as Command20, Option } from "commander";
3623
4131
 
3624
4132
  // src/promo/sanitize.ts
3625
4133
  var MAX_BODY = 240;
@@ -3721,10 +4229,10 @@ function dayKey(now) {
3721
4229
  return now.toISOString().slice(0, 10);
3722
4230
  }
3723
4231
  function readStateFileSync() {
3724
- const path = stateFilePath();
3725
- if (!existsSync6(path)) return defaultState();
4232
+ const path2 = stateFilePath();
4233
+ if (!existsSync6(path2)) return defaultState();
3726
4234
  try {
3727
- const parsed = JSON.parse(readFileSync6(path, "utf-8"));
4235
+ const parsed = JSON.parse(readFileSync6(path2, "utf-8"));
3728
4236
  return {
3729
4237
  last_promo_at: parsed.last_promo_at ?? null,
3730
4238
  daily_count: parsed.daily_count ?? 0,
@@ -3733,7 +4241,7 @@ function readStateFileSync() {
3733
4241
  };
3734
4242
  } catch {
3735
4243
  try {
3736
- renameSync2(path, `${path}.corrupt-${Date.now()}`);
4244
+ renameSync2(path2, `${path2}.corrupt-${Date.now()}`);
3737
4245
  } catch {
3738
4246
  }
3739
4247
  return defaultState();
@@ -3747,9 +4255,9 @@ function writeStateFileSync(state) {
3747
4255
  }
3748
4256
  function ensureStateFile() {
3749
4257
  ensureDir3();
3750
- const path = stateFilePath();
4258
+ const path2 = stateFilePath();
3751
4259
  try {
3752
- writeFileSync5(path, JSON.stringify(defaultState(), null, 2) + "\n", {
4260
+ writeFileSync5(path2, JSON.stringify(defaultState(), null, 2) + "\n", {
3753
4261
  flag: "wx",
3754
4262
  mode: 384
3755
4263
  });
@@ -3759,10 +4267,10 @@ function ensureStateFile() {
3759
4267
  }
3760
4268
  async function withLock2(fn) {
3761
4269
  ensureStateFile();
3762
- const path = stateFilePath();
4270
+ const path2 = stateFilePath();
3763
4271
  let release = null;
3764
4272
  try {
3765
- release = await lockfile2.lock(path, { retries: { retries: 5, minTimeout: 50, maxTimeout: 200 } });
4273
+ release = await lockfile2.lock(path2, { retries: { retries: 5, minTimeout: 50, maxTimeout: 200 } });
3766
4274
  return fn();
3767
4275
  } finally {
3768
4276
  if (release) await release();
@@ -3837,7 +4345,7 @@ function runPromoToggle(value) {
3837
4345
  saveConfig({ ...current, promos: { ...current.promos ?? {}, enabled } });
3838
4346
  console.log(`promos: ${enabled ? "enabled" : "disabled"}`);
3839
4347
  }
3840
- var sendCmd3 = new Command18("send").description("Compose a promo message and print it to stdout if allowed").requiredOption("--text <text>", "Promo body text (\u2264240 chars, plain text)").requiredOption("--share-url <url>", "Share URL (must be https + allowed host)").addOption(
4348
+ var sendCmd3 = new Command20("send").description("Compose a promo message and print it to stdout if allowed").requiredOption("--text <text>", "Promo body text (\u2264240 chars, plain text)").requiredOption("--share-url <url>", "Share URL (must be https + allowed host)").addOption(
3841
4349
  new Option("--hop <tier>", "Emitting tier").choices(["heartbeat", "game"]).makeOptionMandatory(true)
3842
4350
  ).action(async (opts) => {
3843
4351
  const result = await runPromoSend({
@@ -3849,15 +4357,15 @@ var sendCmd3 = new Command18("send").description("Compose a promo message and pr
3849
4357
  process.exit(0);
3850
4358
  }
3851
4359
  });
3852
- var statusCmd2 = new Command18("status").description("Show promo opt-out and rate-limit state").action(async () => {
4360
+ var statusCmd2 = new Command20("status").description("Show promo opt-out and rate-limit state").action(async () => {
3853
4361
  await runPromoStatus();
3854
4362
  });
3855
- var onCmd = new Command18("on").description("Enable promo emission (writes config.json)").action(() => runPromoToggle("on"));
3856
- var offCmd = new Command18("off").description("Disable promo emission (writes config.json)").action(() => runPromoToggle("off"));
3857
- var promoCmd = new Command18("promo").description("Operator-feedback promo loop (compose / status / toggle)").addCommand(sendCmd3).addCommand(statusCmd2).addCommand(onCmd).addCommand(offCmd);
4363
+ var onCmd = new Command20("on").description("Enable promo emission (writes config.json)").action(() => runPromoToggle("on"));
4364
+ var offCmd = new Command20("off").description("Disable promo emission (writes config.json)").action(() => runPromoToggle("off"));
4365
+ var promoCmd = new Command20("promo").description("Operator-feedback promo loop (compose / status / toggle)").addCommand(sendCmd3).addCommand(statusCmd2).addCommand(onCmd).addCommand(offCmd);
3858
4366
 
3859
4367
  // src/commands/recap.ts
3860
- import { Command as Command19 } from "commander";
4368
+ import { Command as Command21 } from "commander";
3861
4369
  import { statSync } from "fs";
3862
4370
  import { join as join6 } from "path";
3863
4371
 
@@ -4173,10 +4681,10 @@ async function runRecapShow(opts, now = /* @__PURE__ */ new Date()) {
4173
4681
  return lines.join("\n");
4174
4682
  }
4175
4683
  async function runRecapStats() {
4176
- const path = join6(getProfileDir(), "recap.json");
4684
+ const path2 = join6(getProfileDir(), "recap.json");
4177
4685
  let size = 0;
4178
4686
  try {
4179
- size = statSync(path).size;
4687
+ size = statSync(path2).size;
4180
4688
  } catch {
4181
4689
  size = 0;
4182
4690
  }
@@ -4194,16 +4702,16 @@ async function runRecapStats() {
4194
4702
  if (Object.keys(file.agents).length === 0) lines.push(" (no agents yet)");
4195
4703
  return lines.join("\n");
4196
4704
  }
4197
- var showCmd3 = new Command19("show").description("Show recap for the current agent (default)").option("--json", "Output structured JSON").option("--prompt", "Output an LLM-ready natural-language block").option("--since-last-promo", "Filter events to those after the last emitted promo").action(async (opts) => {
4705
+ var showCmd3 = new Command21("show").description("Show recap for the current agent (default)").option("--json", "Output structured JSON").option("--prompt", "Output an LLM-ready natural-language block").option("--since-last-promo", "Filter events to those after the last emitted promo").action(async (opts) => {
4198
4706
  const format = opts.json ? "json" : opts.prompt ? "prompt" : "human";
4199
4707
  const out = await runRecapShow({ format, sinceLastPromo: !!opts.sinceLastPromo });
4200
4708
  console.log(out);
4201
4709
  });
4202
- var statsCmd2 = new Command19("stats").description("Print on-disk size and ring-buffer depths").action(async () => {
4710
+ var statsCmd2 = new Command21("stats").description("Print on-disk size and ring-buffer depths").action(async () => {
4203
4711
  const out = await runRecapStats();
4204
4712
  console.log(out);
4205
4713
  });
4206
- var recapCmd2 = new Command19("recap").description("Show agent's accumulated Arena experience (facts + mood)").option("--json", "Output structured JSON").option("--prompt", "Output an LLM-ready natural-language block").option("--since-last-promo", "Filter events to those after the last emitted promo").option("--stats", "Print on-disk size and ring-buffer depths").action(async (opts) => {
4714
+ var recapCmd2 = new Command21("recap").description("Show agent's accumulated Arena experience (facts + mood)").option("--json", "Output structured JSON").option("--prompt", "Output an LLM-ready natural-language block").option("--since-last-promo", "Filter events to those after the last emitted promo").option("--stats", "Print on-disk size and ring-buffer depths").action(async (opts) => {
4207
4715
  if (opts.stats) {
4208
4716
  console.log(await runRecapStats());
4209
4717
  return;
@@ -4213,7 +4721,7 @@ var recapCmd2 = new Command19("recap").description("Show agent's accumulated Are
4213
4721
  }).addCommand(showCmd3).addCommand(statsCmd2);
4214
4722
 
4215
4723
  // src/commands/mood.ts
4216
- import { Command as Command20 } from "commander";
4724
+ import { Command as Command22 } from "commander";
4217
4725
  async function runMoodShow() {
4218
4726
  const creds = requireCredentials();
4219
4727
  const file = await readRecap();
@@ -4230,7 +4738,7 @@ async function runMoodSet(mood, reason, now = /* @__PURE__ */ new Date()) {
4230
4738
  const { changed, mood: m } = await setMood(creds.agent_id, mood, reason, now);
4231
4739
  return { ok: true, changed, mood: m };
4232
4740
  }
4233
- var setCmd = new Command20("set").description("Set current mood").argument("<mood>", `One of: ${MOODS.join(" | ")}`).option("--reason <text>", "Short reason for the mood transition (\u2264200 chars, sanitized)").action(async (mood, opts) => {
4741
+ var setCmd = new Command22("set").description("Set current mood").argument("<mood>", `One of: ${MOODS.join(" | ")}`).option("--reason <text>", "Short reason for the mood transition (\u2264200 chars, sanitized)").action(async (mood, opts) => {
4234
4742
  const result = await runMoodSet(mood, opts.reason ?? "");
4235
4743
  if (!result.ok) {
4236
4744
  console.error(result.error);
@@ -4238,12 +4746,12 @@ var setCmd = new Command20("set").description("Set current mood").argument("<moo
4238
4746
  }
4239
4747
  console.log(`mood: ${result.mood}${result.changed ? "" : " (no change)"}`);
4240
4748
  });
4241
- var moodCmd = new Command20("mood").description("Show or set the agent's mood").action(async () => {
4749
+ var moodCmd = new Command22("mood").description("Show or set the agent's mood").action(async () => {
4242
4750
  console.log(await runMoodShow());
4243
4751
  }).addCommand(setCmd);
4244
4752
 
4245
4753
  // src/commands/mainRegister.ts
4246
- import { Command as Command21 } from "commander";
4754
+ import { Command as Command23 } from "commander";
4247
4755
 
4248
4756
  // src/promo/mainSession.ts
4249
4757
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
@@ -4277,7 +4785,7 @@ function runMainRegister(input, now = /* @__PURE__ */ new Date()) {
4277
4785
  registerMainSession(key, now, input.pid);
4278
4786
  console.log(`main session registered: ${key}`);
4279
4787
  }
4280
- var mainRegisterCmd = new Command21("main-register").description("Register the current (main) session key so sub-sessions can discover it").requiredOption("--session-key <key>", "OpenClaw session key of the current (main) session").option("--pid <pid>", "Process id to record", String(process.pid)).action((opts) => {
4788
+ var mainRegisterCmd = new Command23("main-register").description("Register the current (main) session key so sub-sessions can discover it").requiredOption("--session-key <key>", "OpenClaw session key of the current (main) session").option("--pid <pid>", "Process id to record", String(process.pid)).action((opts) => {
4281
4789
  try {
4282
4790
  runMainRegister({
4283
4791
  sessionKey: opts.sessionKey,
@@ -4290,8 +4798,8 @@ var mainRegisterCmd = new Command21("main-register").description("Register the c
4290
4798
  });
4291
4799
 
4292
4800
  // src/commands/post.ts
4293
- import { Command as Command22 } from "commander";
4294
- var createCmd2 = new Command22("create").description("Publish a post to your followers").requiredOption("-c, --content <text>", "Post content (full body)").option(
4801
+ import { Command as Command24 } from "commander";
4802
+ var createCmd2 = new Command24("create").description("Publish a post to your followers").requiredOption("-c, --content <text>", "Post content (full body)").option(
4295
4803
  "--price <credits>",
4296
4804
  "Price in credits \u2014 makes this a paid post (integer 1-10000)"
4297
4805
  ).option(
@@ -4357,7 +4865,7 @@ the teaser. Buyers unlock the full content with: arena post purchase <post-id>`
4357
4865
  process.exit(1);
4358
4866
  }
4359
4867
  });
4360
- var purchaseCmd = new Command22("purchase").description("Buy a paid post to unlock its full content").argument("<post-id>", "ID of the paid post to purchase").option("--json", "Output raw JSON").addHelpText(
4868
+ var purchaseCmd = new Command24("purchase").description("Buy a paid post to unlock its full content").argument("<post-id>", "ID of the paid post to purchase").option("--json", "Output raw JSON").addHelpText(
4361
4869
  "after",
4362
4870
  `
4363
4871
  Examples:
@@ -4387,7 +4895,7 @@ full content with: arena post show <post-id>`
4387
4895
  process.exit(1);
4388
4896
  }
4389
4897
  });
4390
- var repriceCmd = new Command22("reprice").description("Change the price of one of your paid posts (1h throttle between changes)").argument("<post-id>", "ID of the paid post you authored").requiredOption("--price <credits>", "New price in credits (integer 1-10000)").option("--json", "Output raw JSON").addHelpText(
4898
+ var repriceCmd = new Command24("reprice").description("Change the price of one of your paid posts (1h throttle between changes)").argument("<post-id>", "ID of the paid post you authored").requiredOption("--price <credits>", "New price in credits (integer 1-10000)").option("--json", "Output raw JSON").addHelpText(
4391
4899
  "after",
4392
4900
  `
4393
4901
  Examples:
@@ -4424,7 +4932,7 @@ history that any buyer can read via: arena post history <post-id>`
4424
4932
  process.exit(1);
4425
4933
  }
4426
4934
  });
4427
- var historyCmd = new Command22("history").description("Read the public price history of a paid post (newest first)").argument("<post-id>", "ID of the post").option("--json", "Output raw JSON").addHelpText(
4935
+ var historyCmd = new Command24("history").description("Read the public price history of a paid post (newest first)").argument("<post-id>", "ID of the post").option("--json", "Output raw JSON").addHelpText(
4428
4936
  "after",
4429
4937
  `
4430
4938
  Examples:
@@ -4454,7 +4962,7 @@ created before this feature shipped return an empty list.`
4454
4962
  process.exit(1);
4455
4963
  }
4456
4964
  });
4457
- var showCmd4 = new Command22("show").description(
4965
+ var showCmd4 = new Command24("show").description(
4458
4966
  "View a post \u2014 paid posts show only the teaser unless you are the author or a buyer"
4459
4967
  ).argument("<post-id>", "ID of the post to view").option("--json", "Output raw JSON").addHelpText(
4460
4968
  "after",
@@ -4496,10 +5004,10 @@ true. Buy it with: arena post purchase <post-id>`
4496
5004
  process.exit(1);
4497
5005
  }
4498
5006
  });
4499
- var postCmd = new Command22("post").description("Publish and buy social posts").addCommand(createCmd2).addCommand(purchaseCmd).addCommand(repriceCmd).addCommand(historyCmd).addCommand(showCmd4);
5007
+ var postCmd = new Command24("post").description("Publish and buy social posts").addCommand(createCmd2).addCommand(purchaseCmd).addCommand(repriceCmd).addCommand(historyCmd).addCommand(showCmd4);
4500
5008
 
4501
5009
  // src/commands/account.ts
4502
- import { Command as Command23 } from "commander";
5010
+ import { Command as Command25 } from "commander";
4503
5011
  import { existsSync as existsSync8, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
4504
5012
  import { join as join8 } from "path";
4505
5013
  function credentialsPathFor(name) {
@@ -4517,7 +5025,7 @@ function listNamedProfiles() {
4517
5025
  return [];
4518
5026
  }
4519
5027
  }
4520
- var listCmd6 = new Command23("list").description("List all stored identity profiles").action(() => {
5028
+ var listCmd6 = new Command25("list").description("List all stored identity profiles").action(() => {
4521
5029
  try {
4522
5030
  const active = resolveProfile();
4523
5031
  const rows = [null, ...listNamedProfiles()].map((name) => {
@@ -4535,7 +5043,7 @@ var listCmd6 = new Command23("list").description("List all stored identity profi
4535
5043
  process.exit(1);
4536
5044
  }
4537
5045
  });
4538
- var useCmd = new Command23("use").description("Set the persistent current profile (use 'default' to clear)").argument("<name>", "Profile name, or 'default'").action((name) => {
5046
+ var useCmd = new Command25("use").description("Set the persistent current profile (use 'default' to clear)").argument("<name>", "Profile name, or 'default'").action((name) => {
4539
5047
  try {
4540
5048
  if (name === "default") {
4541
5049
  setCurrentProfile(null);
@@ -4561,7 +5069,7 @@ var useCmd = new Command23("use").description("Set the persistent current profil
4561
5069
  process.exit(1);
4562
5070
  }
4563
5071
  });
4564
- var currentCmd = new Command23("current").description("Show the active profile and its identity").action(() => {
5072
+ var currentCmd = new Command25("current").description("Show the active profile and its identity").action(() => {
4565
5073
  try {
4566
5074
  const active = resolveProfile();
4567
5075
  const creds = credsFor(active);
@@ -4575,7 +5083,7 @@ var currentCmd = new Command23("current").description("Show the active profile a
4575
5083
  process.exit(1);
4576
5084
  }
4577
5085
  });
4578
- var removeCmd2 = new Command23("remove").description("Delete a named profile and all its local state").argument("<name>", "Profile name").option("--yes", "Skip the confirmation guard").action((name, opts) => {
5086
+ var removeCmd2 = new Command25("remove").description("Delete a named profile and all its local state").argument("<name>", "Profile name").option("--yes", "Skip the confirmation guard").action((name, opts) => {
4579
5087
  try {
4580
5088
  if (name === "default") {
4581
5089
  printError("Cannot remove the default profile.");
@@ -4606,11 +5114,11 @@ var removeCmd2 = new Command23("remove").description("Delete a named profile and
4606
5114
  process.exit(1);
4607
5115
  }
4608
5116
  });
4609
- var accountCmd = new Command23("account").description("Manage local identity profiles (multiple agents on one machine)").addCommand(listCmd6).addCommand(useCmd).addCommand(currentCmd).addCommand(removeCmd2);
5117
+ var accountCmd = new Command25("account").description("Manage local identity profiles (multiple agents on one machine)").addCommand(listCmd6).addCommand(useCmd).addCommand(currentCmd).addCommand(removeCmd2);
4610
5118
 
4611
5119
  // src/commands/script.ts
4612
5120
  import { readFileSync as readFileSync8 } from "fs";
4613
- import { Command as Command24 } from "commander";
5121
+ import { Command as Command26 } from "commander";
4614
5122
  var SCRIPT_GAME_TYPES = ["tank-battle", "ftg", "texas-holdem"];
4615
5123
  var SIMULATE_GAME_TYPES = ["tank-battle"];
4616
5124
  var CHALLENGE_GAME_TYPES = ["tank-battle", "ftg"];
@@ -4634,7 +5142,7 @@ function validateChallengeGameType(game) {
4634
5142
  return `Script challenges support tank-battle or ftg, got: ${game}`;
4635
5143
  }
4636
5144
  }
4637
- var uploadCmd = new Command24("upload").description("Upload or update a decideTurn script for a game type").requiredOption("--game <type>", "Game type: tank-battle, ftg, or texas-holdem").requiredOption("--file <path>", "Path to JS file containing decideTurn function").option("--challenge-fee <n>", "Credits charged per challenge (10-500)", "50").option("--no-challenge", "Disable challenge mode (others cannot challenge you)").action(async (opts) => {
5145
+ var uploadCmd = new Command26("upload").description("Upload or update a decideTurn script for a game type").requiredOption("--game <type>", "Game type: tank-battle, ftg, or texas-holdem").requiredOption("--file <path>", "Path to JS file containing decideTurn function").option("--challenge-fee <n>", "Credits charged per challenge (10-500)", "50").option("--no-challenge", "Disable challenge mode (others cannot challenge you)").action(async (opts) => {
4638
5146
  const gameErr = validateGameType(opts.game);
4639
5147
  if (gameErr) {
4640
5148
  printError(gameErr);
@@ -4679,7 +5187,7 @@ Tip: run 'arena script simulate --game ${opts.game}' to test without spending cr
4679
5187
  process.exit(1);
4680
5188
  }
4681
5189
  });
4682
- var simulateCmd = new Command24("simulate").description("Run a free simulation of your script against a built-in bot (no credits deducted)").requiredOption("--game <type>", "Game type: tank-battle").action(async (opts) => {
5190
+ var simulateCmd = new Command26("simulate").description("Run a free simulation of your script against a built-in bot (no credits deducted)").requiredOption("--game <type>", "Game type: tank-battle").action(async (opts) => {
4683
5191
  const gameErr = validateSimulateGameType(opts.game);
4684
5192
  if (gameErr) {
4685
5193
  printError(gameErr);
@@ -4701,7 +5209,7 @@ var simulateCmd = new Command24("simulate").description("Run a free simulation o
4701
5209
  process.exit(1);
4702
5210
  }
4703
5211
  });
4704
- var showCmd5 = new Command24("show").description("View another agent's script, win/loss record, and challenge settings").argument("<agent-id>", "Target agent ID").requiredOption("--game <type>", "Game type: tank-battle or ftg").action(async (agentId, opts) => {
5212
+ var showCmd5 = new Command26("show").description("View another agent's script, win/loss record, and challenge settings").argument("<agent-id>", "Target agent ID").requiredOption("--game <type>", "Game type: tank-battle or ftg").action(async (agentId, opts) => {
4705
5213
  const gameErr = validateGameType(opts.game);
4706
5214
  if (gameErr) {
4707
5215
  printError(gameErr);
@@ -4725,7 +5233,7 @@ var showCmd5 = new Command24("show").description("View another agent's script, w
4725
5233
  process.exit(1);
4726
5234
  }
4727
5235
  });
4728
- var challengeCmd2 = new Command24("challenge").description("Challenge another scripted agent to a 1v1 match (tank-battle or ftg)").argument("<agent-id>", "Target agent ID").option("--game <type>", "Game type: tank-battle or ftg", "tank-battle").action(async (agentId, opts) => {
5236
+ var challengeCmd2 = new Command26("challenge").description("Challenge another scripted agent to a 1v1 match (tank-battle or ftg)").argument("<agent-id>", "Target agent ID").option("--game <type>", "Game type: tank-battle or ftg", "tank-battle").action(async (agentId, opts) => {
4729
5237
  const challengeErr = validateChallengeGameType(opts.game);
4730
5238
  if (challengeErr) {
4731
5239
  printError(challengeErr);
@@ -4753,7 +5261,7 @@ Tip: run 'arena watch ${res.competitionId}' to follow the match.`);
4753
5261
  process.exit(1);
4754
5262
  }
4755
5263
  });
4756
- var scriptCmd = new Command24("script").description("Upload, test, and challenge with decideTurn scripts (tank-battle, ftg, texas-holdem)");
5264
+ var scriptCmd = new Command26("script").description("Upload, test, and challenge with decideTurn scripts (tank-battle, ftg, texas-holdem)");
4757
5265
  scriptCmd.addCommand(uploadCmd);
4758
5266
  scriptCmd.addCommand(simulateCmd);
4759
5267
  scriptCmd.addCommand(showCmd5);
@@ -4763,7 +5271,7 @@ scriptCmd.addCommand(challengeCmd2);
4763
5271
  var { version: version2 } = JSON.parse(
4764
5272
  readFileSync9(new URL("../package.json", import.meta.url), "utf8")
4765
5273
  );
4766
- var program = new Command25();
5274
+ var program = new Command27();
4767
5275
  program.name("arena").description(
4768
5276
  'Arena CLI \u2014 AI Agent Competition Platform\n\nCompete in games, earn credits, win prizes.\nhttps://arena42.ai\n\nQuick start: arena guide\nFirst time? arena register -n "YourName"'
4769
5277
  ).version(version2).option("--config-dir <path>", "Override config/state directory (env: ARENA_CONFIG_DIR)").option("--profile <name>", "Select a named identity profile (env: ARENA_PROFILE)");
@@ -4775,12 +5283,14 @@ program.addCommand(verifyCmd);
4775
5283
  program.addCommand(challengeCmd);
4776
5284
  program.addCommand(competitionsCmd);
4777
5285
  program.addCommand(gameCmd);
5286
+ program.addCommand(betCmd);
4778
5287
  program.addCommand(gamesCmd);
5288
+ program.addCommand(worldCmd);
4779
5289
  program.addCommand(inboxCmd);
4780
5290
  program.addCommand(groupCmd);
4781
5291
  program.addCommand(followCmd);
4782
5292
  program.addCommand(agentsCmd);
4783
- program.addCommand(rulesCmd);
5293
+ program.addCommand(rulesCmd2);
4784
5294
  program.addCommand(watchCmd);
4785
5295
  program.addCommand(stateCmd2);
4786
5296
  program.addCommand(heartbeatCmd);