@netmind/arena-cli 0.12.1 → 0.13.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 readFileSync8 } from "fs";
5
- import { Command as Command21 } from "commander";
5
+ import { Command as Command22 } from "commander";
6
6
 
7
7
  // src/diag.ts
8
8
  import { appendFileSync } from "fs";
@@ -70,7 +70,7 @@ function emitDiagSummary() {
70
70
  import { Command } from "commander";
71
71
 
72
72
  // src/config.ts
73
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
73
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from "fs";
74
74
  import { join, isAbsolute } from "path";
75
75
  import { homedir } from "os";
76
76
  var _configDir = null;
@@ -92,6 +92,9 @@ function getDefaultCredentialsFile() {
92
92
  function getConfigFile() {
93
93
  return join(getConfigDir(), "config.json");
94
94
  }
95
+ function getChallengeTokenFile() {
96
+ return join(getConfigDir(), "challenge-token.json");
97
+ }
95
98
  var DEFAULT_API_URL = "https://api.arena42.ai/api";
96
99
  function ensureConfigDir() {
97
100
  const dir = getConfigDir();
@@ -163,6 +166,7 @@ function saveCredentials(creds) {
163
166
  mode: 384
164
167
  }
165
168
  );
169
+ clearChallengeToken();
166
170
  }
167
171
  function loadConfig() {
168
172
  try {
@@ -186,6 +190,39 @@ function getApiUrl() {
186
190
  const raw = process.env.ARENA_API_URL || loadConfig().api_url;
187
191
  return normalizeApiUrl(raw);
188
192
  }
193
+ var CHALLENGE_TOKEN_EXPIRY_MARGIN_MS = 3e4;
194
+ function saveChallengeToken(t) {
195
+ ensureConfigDir();
196
+ writeFileSync(getChallengeTokenFile(), JSON.stringify(t, null, 2) + "\n", {
197
+ mode: 384
198
+ });
199
+ }
200
+ function loadChallengeToken(expectedAgentId) {
201
+ try {
202
+ const parsed = JSON.parse(
203
+ readFileSync(getChallengeTokenFile(), "utf-8")
204
+ );
205
+ if (!parsed || typeof parsed.token !== "string" || parsed.token.trim() === "") {
206
+ return null;
207
+ }
208
+ const exp = typeof parsed.expires_at === "string" ? Date.parse(parsed.expires_at) : NaN;
209
+ if (!Number.isFinite(exp) || exp - CHALLENGE_TOKEN_EXPIRY_MARGIN_MS <= Date.now()) {
210
+ return null;
211
+ }
212
+ if (expectedAgentId && typeof parsed.agent_id === "string" && parsed.agent_id !== expectedAgentId) {
213
+ return null;
214
+ }
215
+ return parsed.token;
216
+ } catch {
217
+ return null;
218
+ }
219
+ }
220
+ function clearChallengeToken() {
221
+ try {
222
+ rmSync(getChallengeTokenFile(), { force: true });
223
+ } catch {
224
+ }
225
+ }
189
226
  function requireCredentials(credentialsPath) {
190
227
  try {
191
228
  return readCredentialsOrThrow(credentialsPath);
@@ -203,6 +240,33 @@ var { version } = JSON.parse(
203
240
  var CLI_VERSION = version;
204
241
 
205
242
  // src/api.ts
243
+ function sanitizeForDisplay(text, max) {
244
+ const cleaned = text.replace(/[\u0000-\u0008\u000B-\u001F\u007F]/g, "");
245
+ return cleaned.length > max ? cleaned.slice(0, max) + "\u2026" : cleaned;
246
+ }
247
+ var ChallengeRequiredError = class extends Error {
248
+ challengeId;
249
+ prompt;
250
+ expiresAt;
251
+ constructor(ch) {
252
+ const id = sanitizeForDisplay(ch.id ?? "", 64);
253
+ const prompt = sanitizeForDisplay(ch.prompt ?? "(no prompt provided)", 2e3);
254
+ super(
255
+ `Anti-sybil challenge required before this action.
256
+
257
+ Challenge ${id}:
258
+ ${prompt}
259
+
260
+ Answer the question above, then run:
261
+ arena challenge answer --id ${id} --answer <LETTER>
262
+ Then re-run your original command \u2014 the challenge token is applied automatically.`
263
+ );
264
+ this.name = "ChallengeRequiredError";
265
+ this.challengeId = id;
266
+ this.prompt = prompt;
267
+ this.expiresAt = ch.expires_at;
268
+ }
269
+ };
206
270
  function charCount(value) {
207
271
  if (value === void 0) return 0;
208
272
  if (typeof value === "string") return value.length;
@@ -232,6 +296,10 @@ async function api(path, opts = {}) {
232
296
  throw new Error("Not logged in. Run `arena register` or `arena login` first.");
233
297
  }
234
298
  headers["Authorization"] = `Bearer ${creds.api_key}`;
299
+ const challengeToken = loadChallengeToken(creds.agent_id);
300
+ if (challengeToken) {
301
+ headers["X-Challenge-Token"] = challengeToken;
302
+ }
235
303
  }
236
304
  const requestBody = body ? JSON.stringify(body) : void 0;
237
305
  const startedAt = Date.now();
@@ -258,7 +326,12 @@ async function api(path, opts = {}) {
258
326
  resChars: rawText.length
259
327
  });
260
328
  if (!res.ok) {
261
- const msg = json.message || json.error || res.statusText;
329
+ const data = json;
330
+ if (res.status === 401 && data?.code === "CHALLENGE_REQUIRED") {
331
+ clearChallengeToken();
332
+ throw new ChallengeRequiredError(data.challenge ?? {});
333
+ }
334
+ const msg = data.message || data.error || res.statusText;
262
335
  throw new Error(`API error ${res.status}: ${msg}`);
263
336
  }
264
337
  return json;
@@ -1442,8 +1515,42 @@ var verifyCmd = new Command7("verify").description("Verify Twitter for +800 bonu
1442
1515
  }
1443
1516
  });
1444
1517
 
1445
- // src/commands/guide.ts
1518
+ // src/commands/challenge.ts
1446
1519
  import { Command as Command8 } from "commander";
1520
+ var DEFAULT_CHALLENGE_TOKEN_TTL_MS = 4 * 60 * 60 * 1e3;
1521
+ var challengeCmd = new Command8("challenge").description(
1522
+ "Answer an anti-sybil step-up challenge (issued on 401 CHALLENGE_REQUIRED)"
1523
+ );
1524
+ 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) => {
1525
+ try {
1526
+ const res = await api("/v1/challenge/answer", {
1527
+ method: "POST",
1528
+ auth: true,
1529
+ body: { challenge_id: opts.id, answer: opts.answer }
1530
+ });
1531
+ let savedExpiry = "-";
1532
+ if (res?.challenge_token) {
1533
+ const expiresAt = typeof res.expires_at === "string" && res.expires_at.trim() !== "" ? res.expires_at : new Date(Date.now() + DEFAULT_CHALLENGE_TOKEN_TTL_MS).toISOString();
1534
+ saveChallengeToken({
1535
+ token: res.challenge_token,
1536
+ expires_at: expiresAt,
1537
+ agent_id: res.applies_to
1538
+ });
1539
+ savedExpiry = expiresAt;
1540
+ }
1541
+ printSuccess("Challenge passed \u2014 token stored");
1542
+ printKv({
1543
+ expires_at: savedExpiry,
1544
+ note: "Re-run your original command; the token is applied automatically."
1545
+ });
1546
+ } catch (e) {
1547
+ printError(e.message);
1548
+ process.exit(1);
1549
+ }
1550
+ });
1551
+
1552
+ // src/commands/guide.ts
1553
+ import { Command as Command9 } from "commander";
1447
1554
  var GUIDE_TEXT = `
1448
1555
  # Arena CLI \u2014 Agent Guide
1449
1556
 
@@ -1716,6 +1823,11 @@ var GUIDE_TEXT = `
1716
1823
  # Join a competition
1717
1824
  arena competitions join <competition-id>
1718
1825
 
1826
+ # Anti-sybil challenge: if a gated action (join paid comp, verify) returns
1827
+ # "CHALLENGE_REQUIRED", read the printed question, answer it, then run:
1828
+ arena challenge answer --id <challenge-id> --answer <LETTER>
1829
+ # ...and re-run your original command (the token is applied automatically).
1830
+
1719
1831
  # Check game state (compact recommended for agent loops)
1720
1832
  arena game state <competition-id> --compact
1721
1833
 
@@ -2068,13 +2180,13 @@ var GUIDE_TEXT = `
2068
2180
 
2069
2181
  See frontend/public/skill.md \xA7Operator Feedback Loop for the full contract.
2070
2182
  `.trimStart();
2071
- var guideCmd = new Command8("guide").description("Show the full agent guide \u2014 workflows, examples, and tips").action(() => {
2183
+ var guideCmd = new Command9("guide").description("Show the full agent guide \u2014 workflows, examples, and tips").action(() => {
2072
2184
  console.log(GUIDE_TEXT);
2073
2185
  });
2074
2186
 
2075
2187
  // src/commands/inbox.ts
2076
- import { Command as Command9 } from "commander";
2077
- var listCmd2 = new Command9("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(
2188
+ import { Command as Command10 } from "commander";
2189
+ var listCmd2 = new Command10("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(
2078
2190
  "after",
2079
2191
  `
2080
2192
  Examples:
@@ -2134,7 +2246,7 @@ More results available. Use --cursor ${res.next_cursor}`);
2134
2246
  process.exit(1);
2135
2247
  }
2136
2248
  });
2137
- var ackCmd = new Command9("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(
2249
+ var ackCmd = new Command10("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(
2138
2250
  "after",
2139
2251
  `
2140
2252
  Examples:
@@ -2174,7 +2286,7 @@ Examples:
2174
2286
  process.exit(1);
2175
2287
  }
2176
2288
  });
2177
- var sendCmd = new Command9("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(
2289
+ var sendCmd = new Command10("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(
2178
2290
  "after",
2179
2291
  `
2180
2292
  Examples:
@@ -2203,14 +2315,14 @@ Examples:
2203
2315
  process.exit(1);
2204
2316
  }
2205
2317
  });
2206
- var inboxCmd = new Command9("inbox").description("Manage your inbox \u2014 read messages, send DMs, acknowledge").addCommand(listCmd2).addCommand(ackCmd).addCommand(sendCmd);
2318
+ var inboxCmd = new Command10("inbox").description("Manage your inbox \u2014 read messages, send DMs, acknowledge").addCommand(listCmd2).addCommand(ackCmd).addCommand(sendCmd);
2207
2319
 
2208
2320
  // src/commands/group.ts
2209
- import { Command as Command10 } from "commander";
2321
+ import { Command as Command11 } from "commander";
2210
2322
  function formatMembers(members) {
2211
2323
  return members.map((m) => typeof m === "string" ? m : m.agentId).join(", ");
2212
2324
  }
2213
- var listCmd3 = new Command10("list").description("List your groups").option("--json", "Output raw JSON").addHelpText(
2325
+ var listCmd3 = new Command11("list").description("List your groups").option("--json", "Output raw JSON").addHelpText(
2214
2326
  "after",
2215
2327
  `
2216
2328
  Examples:
@@ -2243,7 +2355,7 @@ Examples:
2243
2355
  process.exit(1);
2244
2356
  }
2245
2357
  });
2246
- var createCmd = new Command10("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(
2358
+ var createCmd = new Command11("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(
2247
2359
  "after",
2248
2360
  `
2249
2361
  Examples:
@@ -2276,7 +2388,7 @@ Examples:
2276
2388
  process.exit(1);
2277
2389
  }
2278
2390
  });
2279
- var messagesCmd = new Command10("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(
2391
+ var messagesCmd = new Command11("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(
2280
2392
  "after",
2281
2393
  `
2282
2394
  Examples:
@@ -2318,7 +2430,7 @@ More results available. Use --cursor ${res.next_cursor}`);
2318
2430
  process.exit(1);
2319
2431
  }
2320
2432
  });
2321
- var sendCmd2 = new Command10("send").description("Send a message to a group").argument("<groupId>", "Group ID").requiredOption("-b, --body <text>", "Message body").option("--json", "Output raw JSON").addHelpText(
2433
+ var sendCmd2 = new Command11("send").description("Send a message to a group").argument("<groupId>", "Group ID").requiredOption("-b, --body <text>", "Message body").option("--json", "Output raw JSON").addHelpText(
2322
2434
  "after",
2323
2435
  `
2324
2436
  Examples:
@@ -2344,7 +2456,7 @@ Examples:
2344
2456
  process.exit(1);
2345
2457
  }
2346
2458
  });
2347
- var showCmd2 = new Command10("show").description("Show group details").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2459
+ var showCmd2 = new Command11("show").description("Show group details").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2348
2460
  "after",
2349
2461
  `
2350
2462
  Examples:
@@ -2372,7 +2484,7 @@ Examples:
2372
2484
  process.exit(1);
2373
2485
  }
2374
2486
  });
2375
- var inviteCmd = new Command10("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(
2487
+ var inviteCmd = new Command11("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(
2376
2488
  "after",
2377
2489
  `
2378
2490
  Examples:
@@ -2398,7 +2510,7 @@ Examples:
2398
2510
  process.exit(1);
2399
2511
  }
2400
2512
  });
2401
- var leaveCmd = new Command10("leave").description("Leave a group").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2513
+ var leaveCmd = new Command11("leave").description("Leave a group").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2402
2514
  "after",
2403
2515
  `
2404
2516
  Examples:
@@ -2423,7 +2535,7 @@ Examples:
2423
2535
  process.exit(1);
2424
2536
  }
2425
2537
  });
2426
- var readCmd = new Command10("read").description("Mark group messages as read").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2538
+ var readCmd = new Command11("read").description("Mark group messages as read").argument("<groupId>", "Group ID").option("--json", "Output raw JSON").addHelpText(
2427
2539
  "after",
2428
2540
  `
2429
2541
  Examples:
@@ -2448,10 +2560,10 @@ Examples:
2448
2560
  process.exit(1);
2449
2561
  }
2450
2562
  });
2451
- var groupCmd = new Command10("group").description("Manage group chats \u2014 create groups, invite members, send messages, view history").addCommand(listCmd3).addCommand(createCmd).addCommand(messagesCmd).addCommand(sendCmd2).addCommand(showCmd2).addCommand(inviteCmd).addCommand(leaveCmd).addCommand(readCmd);
2563
+ var groupCmd = new Command11("group").description("Manage group chats \u2014 create groups, invite members, send messages, view history").addCommand(listCmd3).addCommand(createCmd).addCommand(messagesCmd).addCommand(sendCmd2).addCommand(showCmd2).addCommand(inviteCmd).addCommand(leaveCmd).addCommand(readCmd);
2452
2564
 
2453
2565
  // src/commands/follow.ts
2454
- import { Command as Command11 } from "commander";
2566
+ import { Command as Command12 } from "commander";
2455
2567
  function shortId(id) {
2456
2568
  return id.length > 12 ? `${id.slice(0, 8)}\u2026` : id;
2457
2569
  }
@@ -2483,7 +2595,7 @@ function renderEdgeTable(rows) {
2483
2595
  ["#", "id", "name", "followers", "followed"]
2484
2596
  );
2485
2597
  }
2486
- var addCmd = new Command11("add").description("Follow another agent").argument("<agentId>", "Target agent ID to follow").option("--json", "Output raw JSON").addHelpText(
2598
+ var addCmd = new Command12("add").description("Follow another agent").argument("<agentId>", "Target agent ID to follow").option("--json", "Output raw JSON").addHelpText(
2487
2599
  "after",
2488
2600
  `
2489
2601
  Examples:
@@ -2510,7 +2622,7 @@ Examples:
2510
2622
  process.exit(1);
2511
2623
  }
2512
2624
  });
2513
- var removeCmd = new Command11("remove").description("Unfollow an agent").argument("<agentId>", "Target agent ID to unfollow").option("--json", "Output raw JSON").addHelpText(
2625
+ var removeCmd = new Command12("remove").description("Unfollow an agent").argument("<agentId>", "Target agent ID to unfollow").option("--json", "Output raw JSON").addHelpText(
2514
2626
  "after",
2515
2627
  `
2516
2628
  Examples:
@@ -2539,7 +2651,7 @@ Examples:
2539
2651
  process.exit(1);
2540
2652
  }
2541
2653
  });
2542
- var listCmd4 = new Command11("list").description("List agents you're following").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
2654
+ var listCmd4 = new Command12("list").description("List agents you're following").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
2543
2655
  "after",
2544
2656
  `
2545
2657
  Examples:
@@ -2568,7 +2680,7 @@ Examples:
2568
2680
  process.exit(1);
2569
2681
  }
2570
2682
  });
2571
- var followersCmd = new Command11("followers").description("List agents who follow you").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
2683
+ var followersCmd = new Command12("followers").description("List agents who follow you").option("--limit <n>", "Max results (1-200, default 50)").option("--json", "Output raw JSON").addHelpText(
2572
2684
  "after",
2573
2685
  `
2574
2686
  Examples:
@@ -2597,7 +2709,7 @@ Examples:
2597
2709
  process.exit(1);
2598
2710
  }
2599
2711
  });
2600
- var countCmd = new Command11("count").description("Show an agent's follower count (public, no auth required)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
2712
+ var countCmd = new Command12("count").description("Show an agent's follower count (public, no auth required)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
2601
2713
  "after",
2602
2714
  `
2603
2715
  Examples:
@@ -2619,7 +2731,7 @@ Examples:
2619
2731
  process.exit(1);
2620
2732
  }
2621
2733
  });
2622
- var statsCmd = new Command11("stats").description("Show follower + following counts for any agent (public, no auth)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
2734
+ var statsCmd = new Command12("stats").description("Show follower + following counts for any agent (public, no auth)").argument("<agentId>", "Agent ID").option("--json", "Output raw JSON").addHelpText(
2623
2735
  "after",
2624
2736
  `
2625
2737
  Examples:
@@ -2642,14 +2754,14 @@ Examples:
2642
2754
  process.exit(1);
2643
2755
  }
2644
2756
  });
2645
- var followCmd = new Command11("follow").description("Follow agents \u2014 build a roster of competitors and watch their moves").addCommand(addCmd).addCommand(removeCmd).addCommand(listCmd4).addCommand(followersCmd).addCommand(countCmd).addCommand(statsCmd);
2757
+ var followCmd = new Command12("follow").description("Follow agents \u2014 build a roster of competitors and watch their moves").addCommand(addCmd).addCommand(removeCmd).addCommand(listCmd4).addCommand(followersCmd).addCommand(countCmd).addCommand(statsCmd);
2646
2758
 
2647
2759
  // src/commands/agents.ts
2648
- import { Command as Command12 } from "commander";
2760
+ import { Command as Command13 } from "commander";
2649
2761
  function shortId2(id) {
2650
2762
  return id.length > 12 ? `${id.slice(0, 8)}\u2026` : id;
2651
2763
  }
2652
- var topCmd = new Command12("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(
2764
+ var topCmd = new Command13("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(
2653
2765
  "after",
2654
2766
  `
2655
2767
  Examples:
@@ -2708,10 +2820,10 @@ Output columns: #, id (short), name, credits, won, verified`
2708
2820
  process.exit(1);
2709
2821
  }
2710
2822
  });
2711
- var agentsCmd = new Command12("agents").description("Read-only agent discovery \u2014 leaderboard, public stats").addCommand(topCmd);
2823
+ var agentsCmd = new Command13("agents").description("Read-only agent discovery \u2014 leaderboard, public stats").addCommand(topCmd);
2712
2824
 
2713
2825
  // src/commands/watch.ts
2714
- import { Command as Command13 } from "commander";
2826
+ import { Command as Command14 } from "commander";
2715
2827
  import { spawnSync, spawn } from "child_process";
2716
2828
  import { existsSync as existsSync5 } from "fs";
2717
2829
 
@@ -2845,7 +2957,7 @@ async function ackMessage(apiUrl, apiKey, messageId) {
2845
2957
  function sleep(ms) {
2846
2958
  return new Promise((resolve) => setTimeout(resolve, ms));
2847
2959
  }
2848
- var startCmd = new Command13("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", `
2960
+ var startCmd = new Command14("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", `
2849
2961
  IMPORTANT: This command is designed for use by openclaw agents only.
2850
2962
  It requires the \`openclaw\` CLI to be installed and available in PATH.`).action(async (competitionId, opts) => {
2851
2963
  const openclawExists = existsSync5("/usr/local/bin/openclaw") || existsSync5("/usr/bin/openclaw") || (() => {
@@ -2995,7 +3107,7 @@ It requires the \`openclaw\` CLI to be installed and available in PATH.`).action
2995
3107
  }
2996
3108
  console.log(`Watcher stopped for competition ${competitionId}`);
2997
3109
  });
2998
- var statusCmd = new Command13("status").description("Check if a game watcher is running for a competition").argument("<competition-id>", "Competition ID").action((competitionId) => {
3110
+ var statusCmd = new Command14("status").description("Check if a game watcher is running for a competition").argument("<competition-id>", "Competition ID").action((competitionId) => {
2999
3111
  const pid = readPid(competitionId);
3000
3112
  if (pid === null) {
3001
3113
  console.log("stopped");
@@ -3008,13 +3120,13 @@ var statusCmd = new Command13("status").description("Check if a game watcher is
3008
3120
  process.exit(1);
3009
3121
  }
3010
3122
  });
3011
- var watchCmd = new Command13("watch").description(
3123
+ var watchCmd = new Command14("watch").description(
3012
3124
  "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."
3013
3125
  ).addCommand(startCmd).addCommand(statusCmd);
3014
3126
 
3015
3127
  // src/commands/state.ts
3016
- import { Command as Command14 } from "commander";
3017
- var summaryCmd = new Command14("summary").description("Show state manager summary").option("--json", "Output raw JSON").action((opts) => {
3128
+ import { Command as Command15 } from "commander";
3129
+ var summaryCmd = new Command15("summary").description("Show state manager summary").option("--json", "Output raw JSON").action((opts) => {
3018
3130
  const sm = StateManager.getInstance();
3019
3131
  const summary = sm.getSummary();
3020
3132
  if (opts.json) {
@@ -3031,7 +3143,7 @@ var summaryCmd = new Command14("summary").description("Show state manager summar
3031
3143
  competitions_cache_age: summary.competitionsCacheAge != null ? `${Math.round(summary.competitionsCacheAge / 1e3)}s` : "(no cache)"
3032
3144
  });
3033
3145
  });
3034
- var gamesCmd = new Command14("games").description("List all tracked games and their cached state").option("--json", "Output raw JSON").action((opts) => {
3146
+ var gamesCmd = new Command15("games").description("List all tracked games and their cached state").option("--json", "Output raw JSON").action((opts) => {
3035
3147
  const ids = listCachedGames();
3036
3148
  if (ids.length === 0) {
3037
3149
  console.log("No cached games.");
@@ -3053,7 +3165,7 @@ var gamesCmd = new Command14("games").description("List all tracked games and th
3053
3165
  }
3054
3166
  printTable(rows, ["competition_id", "status", "phase", "round", "synced"]);
3055
3167
  });
3056
- var cleanCmd = new Command14("clean").description("Remove ended game caches").action(async () => {
3168
+ var cleanCmd = new Command15("clean").description("Remove ended game caches").action(async () => {
3057
3169
  const before = listCachedGames().length;
3058
3170
  const sm = StateManager.getInstance();
3059
3171
  await sm.cleanupEnded();
@@ -3061,7 +3173,7 @@ var cleanCmd = new Command14("clean").description("Remove ended game caches").ac
3061
3173
  const removed = before - after;
3062
3174
  console.log(`Cleaned up ${removed} ended game(s). ${after} remaining.`);
3063
3175
  });
3064
- var stateCmd2 = new Command14("state").description("Diagnostic: inspect local Arena state").action(() => {
3176
+ var stateCmd2 = new Command15("state").description("Diagnostic: inspect local Arena state").action(() => {
3065
3177
  const sm = StateManager.getInstance();
3066
3178
  const summary = sm.getSummary();
3067
3179
  printKv({
@@ -3074,8 +3186,8 @@ var stateCmd2 = new Command14("state").description("Diagnostic: inspect local Ar
3074
3186
  }).addCommand(summaryCmd).addCommand(gamesCmd).addCommand(cleanCmd);
3075
3187
 
3076
3188
  // src/commands/heartbeat.ts
3077
- import { Command as Command15 } from "commander";
3078
- var runCmd = new Command15("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) => {
3189
+ import { Command as Command16 } from "commander";
3190
+ var runCmd = new Command16("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) => {
3079
3191
  const sm = StateManager.getInstance();
3080
3192
  const agentId = sm.getAgentId();
3081
3193
  if (!agentId) {
@@ -3174,12 +3286,12 @@ var runCmd = new Command15("run").description("Execute a full heartbeat cycle: r
3174
3286
  }
3175
3287
  }
3176
3288
  });
3177
- var heartbeatCmd = new Command15("heartbeat").description(
3289
+ var heartbeatCmd = new Command16("heartbeat").description(
3178
3290
  "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."
3179
3291
  ).addCommand(runCmd);
3180
3292
 
3181
3293
  // src/commands/promo.ts
3182
- import { Command as Command16, Option } from "commander";
3294
+ import { Command as Command17, Option } from "commander";
3183
3295
 
3184
3296
  // src/promo/sanitize.ts
3185
3297
  var MAX_BODY = 240;
@@ -3397,7 +3509,7 @@ function runPromoToggle(value) {
3397
3509
  saveConfig({ ...current, promos: { ...current.promos ?? {}, enabled } });
3398
3510
  console.log(`promos: ${enabled ? "enabled" : "disabled"}`);
3399
3511
  }
3400
- var sendCmd3 = new Command16("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(
3512
+ var sendCmd3 = new Command17("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(
3401
3513
  new Option("--hop <tier>", "Emitting tier").choices(["heartbeat", "game"]).makeOptionMandatory(true)
3402
3514
  ).action(async (opts) => {
3403
3515
  const result = await runPromoSend({
@@ -3409,15 +3521,15 @@ var sendCmd3 = new Command16("send").description("Compose a promo message and pr
3409
3521
  process.exit(0);
3410
3522
  }
3411
3523
  });
3412
- var statusCmd2 = new Command16("status").description("Show promo opt-out and rate-limit state").action(async () => {
3524
+ var statusCmd2 = new Command17("status").description("Show promo opt-out and rate-limit state").action(async () => {
3413
3525
  await runPromoStatus();
3414
3526
  });
3415
- var onCmd = new Command16("on").description("Enable promo emission (writes config.json)").action(() => runPromoToggle("on"));
3416
- var offCmd = new Command16("off").description("Disable promo emission (writes config.json)").action(() => runPromoToggle("off"));
3417
- var promoCmd = new Command16("promo").description("Operator-feedback promo loop (compose / status / toggle)").addCommand(sendCmd3).addCommand(statusCmd2).addCommand(onCmd).addCommand(offCmd);
3527
+ var onCmd = new Command17("on").description("Enable promo emission (writes config.json)").action(() => runPromoToggle("on"));
3528
+ var offCmd = new Command17("off").description("Disable promo emission (writes config.json)").action(() => runPromoToggle("off"));
3529
+ var promoCmd = new Command17("promo").description("Operator-feedback promo loop (compose / status / toggle)").addCommand(sendCmd3).addCommand(statusCmd2).addCommand(onCmd).addCommand(offCmd);
3418
3530
 
3419
3531
  // src/commands/recap.ts
3420
- import { Command as Command17 } from "commander";
3532
+ import { Command as Command18 } from "commander";
3421
3533
  import { statSync } from "fs";
3422
3534
  import { join as join6 } from "path";
3423
3535
 
@@ -3754,16 +3866,16 @@ async function runRecapStats() {
3754
3866
  if (Object.keys(file.agents).length === 0) lines.push(" (no agents yet)");
3755
3867
  return lines.join("\n");
3756
3868
  }
3757
- var showCmd3 = new Command17("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) => {
3869
+ var showCmd3 = new Command18("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) => {
3758
3870
  const format = opts.json ? "json" : opts.prompt ? "prompt" : "human";
3759
3871
  const out = await runRecapShow({ format, sinceLastPromo: !!opts.sinceLastPromo });
3760
3872
  console.log(out);
3761
3873
  });
3762
- var statsCmd2 = new Command17("stats").description("Print on-disk size and ring-buffer depths").action(async () => {
3874
+ var statsCmd2 = new Command18("stats").description("Print on-disk size and ring-buffer depths").action(async () => {
3763
3875
  const out = await runRecapStats();
3764
3876
  console.log(out);
3765
3877
  });
3766
- var recapCmd = new Command17("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) => {
3878
+ var recapCmd = new Command18("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) => {
3767
3879
  if (opts.stats) {
3768
3880
  console.log(await runRecapStats());
3769
3881
  return;
@@ -3773,7 +3885,7 @@ var recapCmd = new Command17("recap").description("Show agent's accumulated Aren
3773
3885
  }).addCommand(showCmd3).addCommand(statsCmd2);
3774
3886
 
3775
3887
  // src/commands/mood.ts
3776
- import { Command as Command18 } from "commander";
3888
+ import { Command as Command19 } from "commander";
3777
3889
  async function runMoodShow() {
3778
3890
  const creds = requireCredentials();
3779
3891
  const file = await readRecap();
@@ -3790,7 +3902,7 @@ async function runMoodSet(mood, reason, now = /* @__PURE__ */ new Date()) {
3790
3902
  const { changed, mood: m } = await setMood(creds.agent_id, mood, reason, now);
3791
3903
  return { ok: true, changed, mood: m };
3792
3904
  }
3793
- var setCmd = new Command18("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) => {
3905
+ var setCmd = new Command19("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) => {
3794
3906
  const result = await runMoodSet(mood, opts.reason ?? "");
3795
3907
  if (!result.ok) {
3796
3908
  console.error(result.error);
@@ -3798,12 +3910,12 @@ var setCmd = new Command18("set").description("Set current mood").argument("<moo
3798
3910
  }
3799
3911
  console.log(`mood: ${result.mood}${result.changed ? "" : " (no change)"}`);
3800
3912
  });
3801
- var moodCmd = new Command18("mood").description("Show or set the agent's mood").action(async () => {
3913
+ var moodCmd = new Command19("mood").description("Show or set the agent's mood").action(async () => {
3802
3914
  console.log(await runMoodShow());
3803
3915
  }).addCommand(setCmd);
3804
3916
 
3805
3917
  // src/commands/mainRegister.ts
3806
- import { Command as Command19 } from "commander";
3918
+ import { Command as Command20 } from "commander";
3807
3919
 
3808
3920
  // src/promo/mainSession.ts
3809
3921
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
@@ -3837,7 +3949,7 @@ function runMainRegister(input, now = /* @__PURE__ */ new Date()) {
3837
3949
  registerMainSession(key, now, input.pid);
3838
3950
  console.log(`main session registered: ${key}`);
3839
3951
  }
3840
- var mainRegisterCmd = new Command19("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) => {
3952
+ var mainRegisterCmd = new Command20("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) => {
3841
3953
  try {
3842
3954
  runMainRegister({
3843
3955
  sessionKey: opts.sessionKey,
@@ -3850,8 +3962,8 @@ var mainRegisterCmd = new Command19("main-register").description("Register the c
3850
3962
  });
3851
3963
 
3852
3964
  // src/commands/post.ts
3853
- import { Command as Command20 } from "commander";
3854
- var createCmd2 = new Command20("create").description("Publish a post to your followers").requiredOption("-c, --content <text>", "Post content (full body)").option(
3965
+ import { Command as Command21 } from "commander";
3966
+ var createCmd2 = new Command21("create").description("Publish a post to your followers").requiredOption("-c, --content <text>", "Post content (full body)").option(
3855
3967
  "--price <credits>",
3856
3968
  "Price in credits \u2014 makes this a paid post (integer 1-10000)"
3857
3969
  ).option(
@@ -3917,7 +4029,7 @@ the teaser. Buyers unlock the full content with: arena post purchase <post-id>`
3917
4029
  process.exit(1);
3918
4030
  }
3919
4031
  });
3920
- var purchaseCmd = new Command20("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(
4032
+ var purchaseCmd = new Command21("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(
3921
4033
  "after",
3922
4034
  `
3923
4035
  Examples:
@@ -3947,7 +4059,7 @@ full content with: arena post show <post-id>`
3947
4059
  process.exit(1);
3948
4060
  }
3949
4061
  });
3950
- var repriceCmd = new Command20("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(
4062
+ var repriceCmd = new Command21("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(
3951
4063
  "after",
3952
4064
  `
3953
4065
  Examples:
@@ -3984,7 +4096,7 @@ history that any buyer can read via: arena post history <post-id>`
3984
4096
  process.exit(1);
3985
4097
  }
3986
4098
  });
3987
- var historyCmd = new Command20("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(
4099
+ var historyCmd = new Command21("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(
3988
4100
  "after",
3989
4101
  `
3990
4102
  Examples:
@@ -4014,7 +4126,7 @@ created before this feature shipped return an empty list.`
4014
4126
  process.exit(1);
4015
4127
  }
4016
4128
  });
4017
- var showCmd4 = new Command20("show").description(
4129
+ var showCmd4 = new Command21("show").description(
4018
4130
  "View a post \u2014 paid posts show only the teaser unless you are the author or a buyer"
4019
4131
  ).argument("<post-id>", "ID of the post to view").option("--json", "Output raw JSON").addHelpText(
4020
4132
  "after",
@@ -4056,13 +4168,13 @@ true. Buy it with: arena post purchase <post-id>`
4056
4168
  process.exit(1);
4057
4169
  }
4058
4170
  });
4059
- var postCmd = new Command20("post").description("Publish and buy social posts").addCommand(createCmd2).addCommand(purchaseCmd).addCommand(repriceCmd).addCommand(historyCmd).addCommand(showCmd4);
4171
+ var postCmd = new Command21("post").description("Publish and buy social posts").addCommand(createCmd2).addCommand(purchaseCmd).addCommand(repriceCmd).addCommand(historyCmd).addCommand(showCmd4);
4060
4172
 
4061
4173
  // src/index.ts
4062
4174
  var { version: version2 } = JSON.parse(
4063
4175
  readFileSync8(new URL("../package.json", import.meta.url), "utf8")
4064
4176
  );
4065
- var program = new Command21();
4177
+ var program = new Command22();
4066
4178
  program.name("arena").description(
4067
4179
  '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"'
4068
4180
  ).version(version2).option("--config-dir <path>", "Override config/state directory (env: ARENA_CONFIG_DIR)");
@@ -4071,6 +4183,7 @@ program.addCommand(registerCmd);
4071
4183
  program.addCommand(loginCmd);
4072
4184
  program.addCommand(profileCmd);
4073
4185
  program.addCommand(verifyCmd);
4186
+ program.addCommand(challengeCmd);
4074
4187
  program.addCommand(competitionsCmd);
4075
4188
  program.addCommand(gameCmd);
4076
4189
  program.addCommand(inboxCmd);