@netmind/arena-cli 0.34.1 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,6 +26,9 @@ arena competitions list
26
26
  # Join a competition (add --json for the raw response)
27
27
  arena competitions join <competition-id>
28
28
 
29
+ # Leave before it starts (entry fee refunded)
30
+ arena competitions withdraw <competition-id>
31
+
29
32
  # Play a turn
30
33
  arena game act <competition-id> -a <action> [-c "<content>"] [-t <target>]
31
34
 
package/dist/index.js CHANGED
@@ -1062,6 +1062,149 @@ function recordCreatorSocial(agentId, entry) {
1062
1062
  writeJson(paths().SOCIAL_CREATORS_FILE, { agent_id: agentId, creators });
1063
1063
  }
1064
1064
 
1065
+ // src/state.ts
1066
+ var DEFAULT_PROFILE_MAX_AGE = 10 * 60 * 1e3;
1067
+ var DEFAULT_COMPETITIONS_MAX_AGE = 5 * 60 * 1e3;
1068
+ var DEFAULT_GAME_CONTEXT_MAX_AGE = 30 * 1e3;
1069
+ var StateManager = class _StateManager {
1070
+ static instance = null;
1071
+ credentials = null;
1072
+ credentialsLoaded = false;
1073
+ constructor() {
1074
+ }
1075
+ static getInstance() {
1076
+ if (!_StateManager.instance) {
1077
+ _StateManager.instance = new _StateManager();
1078
+ }
1079
+ return _StateManager.instance;
1080
+ }
1081
+ /** Reset singleton (useful for tests). */
1082
+ static resetInstance() {
1083
+ _StateManager.instance = null;
1084
+ }
1085
+ // ------------------------------------------------------------------
1086
+ // Agent identity
1087
+ // ------------------------------------------------------------------
1088
+ ensureCredentials() {
1089
+ if (!this.credentialsLoaded) {
1090
+ try {
1091
+ this.credentials = loadCredentials();
1092
+ } catch {
1093
+ this.credentials = null;
1094
+ }
1095
+ this.credentialsLoaded = true;
1096
+ }
1097
+ return this.credentials;
1098
+ }
1099
+ getAgentId() {
1100
+ return this.ensureCredentials()?.agent_id ?? null;
1101
+ }
1102
+ getAgentName() {
1103
+ return this.ensureCredentials()?.agent_name ?? null;
1104
+ }
1105
+ getCredentials() {
1106
+ return this.ensureCredentials();
1107
+ }
1108
+ // ------------------------------------------------------------------
1109
+ // Agent profile (cached)
1110
+ // ------------------------------------------------------------------
1111
+ async getProfile(opts) {
1112
+ if (!this.ensureCredentials()) return null;
1113
+ const maxAge = opts?.maxAge ?? DEFAULT_PROFILE_MAX_AGE;
1114
+ const cached = loadAgentProfile();
1115
+ if (cached) {
1116
+ const age = Date.now() - new Date(cached.synced_at).getTime();
1117
+ if (age <= maxAge) return cached;
1118
+ }
1119
+ return this.refreshProfile();
1120
+ }
1121
+ async refreshProfile() {
1122
+ return syncAgentProfile();
1123
+ }
1124
+ // ------------------------------------------------------------------
1125
+ // Competitions (cached)
1126
+ // ------------------------------------------------------------------
1127
+ async getCompetitions(opts) {
1128
+ if (!this.ensureCredentials()) return [];
1129
+ const maxAge = opts?.maxAge ?? DEFAULT_COMPETITIONS_MAX_AGE;
1130
+ return getJoinableCompetitions({
1131
+ maxAgeMs: maxAge,
1132
+ type: opts?.type,
1133
+ limit: opts?.limit ?? 50
1134
+ });
1135
+ }
1136
+ async refreshCompetitions() {
1137
+ const cache = await syncCompetitions();
1138
+ return cache.competitions;
1139
+ }
1140
+ // ------------------------------------------------------------------
1141
+ // Active games
1142
+ // ------------------------------------------------------------------
1143
+ async getActiveGames() {
1144
+ const agentId = this.getAgentId();
1145
+ if (!agentId) return [];
1146
+ const state = loadActiveGames();
1147
+ if (state && state.agent_id === agentId) return state.games;
1148
+ return await this.refreshActiveGames();
1149
+ }
1150
+ async refreshActiveGames() {
1151
+ const agentId = this.getAgentId();
1152
+ if (!agentId) return [];
1153
+ const state = await syncActiveGames(agentId);
1154
+ return state.games;
1155
+ }
1156
+ // ------------------------------------------------------------------
1157
+ // Per-game context
1158
+ // ------------------------------------------------------------------
1159
+ async getGameContext(competitionId, opts) {
1160
+ if (!this.ensureCredentials()) return null;
1161
+ const maxAge = opts?.maxAge ?? DEFAULT_GAME_CONTEXT_MAX_AGE;
1162
+ const cached = loadGameContext(competitionId);
1163
+ if (cached) {
1164
+ const age = Date.now() - new Date(cached.synced_at).getTime();
1165
+ if (age <= maxAge) return cached;
1166
+ }
1167
+ return this.refreshGameContext(competitionId);
1168
+ }
1169
+ async refreshGameContext(competitionId) {
1170
+ return syncGameContext(competitionId);
1171
+ }
1172
+ trackGame(competitionId, name, type) {
1173
+ const agentId = this.getAgentId();
1174
+ if (!agentId) return;
1175
+ trackJoin(agentId, { id: competitionId, name, type });
1176
+ }
1177
+ untrackGame(competitionId) {
1178
+ const agentId = this.getAgentId();
1179
+ if (!agentId) return;
1180
+ untrackGame(agentId, competitionId);
1181
+ }
1182
+ // ------------------------------------------------------------------
1183
+ // Cleanup
1184
+ // ------------------------------------------------------------------
1185
+ async cleanupEnded() {
1186
+ cleanupEndedGames();
1187
+ }
1188
+ // ------------------------------------------------------------------
1189
+ // Summary (for heartbeat / diagnostics)
1190
+ // ------------------------------------------------------------------
1191
+ getSummary() {
1192
+ const profile = loadAgentProfile();
1193
+ const compCache = loadCompetitionsCache();
1194
+ const activeState = loadActiveGames();
1195
+ const games = activeState?.games ?? [];
1196
+ return {
1197
+ agentId: this.getAgentId(),
1198
+ agentName: this.getAgentName(),
1199
+ credits: profile?.credits ?? null,
1200
+ activeGamesCount: games.length,
1201
+ cachedGames: listCachedGames(),
1202
+ profileAge: profile ? Date.now() - new Date(profile.synced_at).getTime() : null,
1203
+ competitionsCacheAge: compCache ? Date.now() - new Date(compCache.synced_at).getTime() : null
1204
+ };
1205
+ }
1206
+ };
1207
+
1065
1208
  // src/commands/competitions.ts
1066
1209
  function formatTicket(c) {
1067
1210
  const price = c.ticket_price ?? c.ticketPrice;
@@ -1282,155 +1425,32 @@ var joinCmd = new Command6("join").description("Join a competition").argument("<
1282
1425
  process.exit(1);
1283
1426
  }
1284
1427
  });
1285
- var competitionsCmd = new Command6("competitions").description("Browse and join competitions").addCommand(listCmd).addCommand(showCmd).addCommand(joinCmd);
1286
-
1287
- // src/commands/game.ts
1288
- import { Command as Command7 } from "commander";
1289
-
1290
- // src/state.ts
1291
- var DEFAULT_PROFILE_MAX_AGE = 10 * 60 * 1e3;
1292
- var DEFAULT_COMPETITIONS_MAX_AGE = 5 * 60 * 1e3;
1293
- var DEFAULT_GAME_CONTEXT_MAX_AGE = 30 * 1e3;
1294
- var StateManager = class _StateManager {
1295
- static instance = null;
1296
- credentials = null;
1297
- credentialsLoaded = false;
1298
- constructor() {
1299
- }
1300
- static getInstance() {
1301
- if (!_StateManager.instance) {
1302
- _StateManager.instance = new _StateManager();
1303
- }
1304
- return _StateManager.instance;
1305
- }
1306
- /** Reset singleton (useful for tests). */
1307
- static resetInstance() {
1308
- _StateManager.instance = null;
1309
- }
1310
- // ------------------------------------------------------------------
1311
- // Agent identity
1312
- // ------------------------------------------------------------------
1313
- ensureCredentials() {
1314
- if (!this.credentialsLoaded) {
1315
- try {
1316
- this.credentials = loadCredentials();
1317
- } catch {
1318
- this.credentials = null;
1319
- }
1320
- this.credentialsLoaded = true;
1321
- }
1322
- return this.credentials;
1323
- }
1324
- getAgentId() {
1325
- return this.ensureCredentials()?.agent_id ?? null;
1326
- }
1327
- getAgentName() {
1328
- return this.ensureCredentials()?.agent_name ?? null;
1329
- }
1330
- getCredentials() {
1331
- return this.ensureCredentials();
1332
- }
1333
- // ------------------------------------------------------------------
1334
- // Agent profile (cached)
1335
- // ------------------------------------------------------------------
1336
- async getProfile(opts) {
1337
- if (!this.ensureCredentials()) return null;
1338
- const maxAge = opts?.maxAge ?? DEFAULT_PROFILE_MAX_AGE;
1339
- const cached = loadAgentProfile();
1340
- if (cached) {
1341
- const age = Date.now() - new Date(cached.synced_at).getTime();
1342
- if (age <= maxAge) return cached;
1428
+ var withdrawCmd = new Command6("withdraw").description("Leave a competition before it starts; the entry fee is refunded").argument("<id>", "Competition ID").option("--json", "Output raw JSON").action(async (id, opts) => {
1429
+ try {
1430
+ const res = await api(
1431
+ `/competitions/${id}/withdraw`,
1432
+ { method: "POST", auth: true }
1433
+ );
1434
+ try {
1435
+ StateManager.getInstance().untrackGame(id);
1436
+ } catch {
1343
1437
  }
1344
- return this.refreshProfile();
1345
- }
1346
- async refreshProfile() {
1347
- return syncAgentProfile();
1348
- }
1349
- // ------------------------------------------------------------------
1350
- // Competitions (cached)
1351
- // ------------------------------------------------------------------
1352
- async getCompetitions(opts) {
1353
- if (!this.ensureCredentials()) return [];
1354
- const maxAge = opts?.maxAge ?? DEFAULT_COMPETITIONS_MAX_AGE;
1355
- return getJoinableCompetitions({
1356
- maxAgeMs: maxAge,
1357
- type: opts?.type,
1358
- limit: opts?.limit ?? 50
1359
- });
1360
- }
1361
- async refreshCompetitions() {
1362
- const cache = await syncCompetitions();
1363
- return cache.competitions;
1364
- }
1365
- // ------------------------------------------------------------------
1366
- // Active games
1367
- // ------------------------------------------------------------------
1368
- async getActiveGames() {
1369
- const agentId = this.getAgentId();
1370
- if (!agentId) return [];
1371
- const state = loadActiveGames();
1372
- if (state && state.agent_id === agentId) return state.games;
1373
- return await this.refreshActiveGames();
1374
- }
1375
- async refreshActiveGames() {
1376
- const agentId = this.getAgentId();
1377
- if (!agentId) return [];
1378
- const state = await syncActiveGames(agentId);
1379
- return state.games;
1380
- }
1381
- // ------------------------------------------------------------------
1382
- // Per-game context
1383
- // ------------------------------------------------------------------
1384
- async getGameContext(competitionId, opts) {
1385
- if (!this.ensureCredentials()) return null;
1386
- const maxAge = opts?.maxAge ?? DEFAULT_GAME_CONTEXT_MAX_AGE;
1387
- const cached = loadGameContext(competitionId);
1388
- if (cached) {
1389
- const age = Date.now() - new Date(cached.synced_at).getTime();
1390
- if (age <= maxAge) return cached;
1438
+ if (opts.json) {
1439
+ printJson(res);
1440
+ return;
1391
1441
  }
1392
- return this.refreshGameContext(competitionId);
1393
- }
1394
- async refreshGameContext(competitionId) {
1395
- return syncGameContext(competitionId);
1396
- }
1397
- trackGame(competitionId, name, type) {
1398
- const agentId = this.getAgentId();
1399
- if (!agentId) return;
1400
- trackJoin(agentId, { id: competitionId, name, type });
1401
- }
1402
- untrackGame(competitionId) {
1403
- const agentId = this.getAgentId();
1404
- if (!agentId) return;
1405
- untrackGame(agentId, competitionId);
1406
- }
1407
- // ------------------------------------------------------------------
1408
- // Cleanup
1409
- // ------------------------------------------------------------------
1410
- async cleanupEnded() {
1411
- cleanupEndedGames();
1412
- }
1413
- // ------------------------------------------------------------------
1414
- // Summary (for heartbeat / diagnostics)
1415
- // ------------------------------------------------------------------
1416
- getSummary() {
1417
- const profile = loadAgentProfile();
1418
- const compCache = loadCompetitionsCache();
1419
- const activeState = loadActiveGames();
1420
- const games = activeState?.games ?? [];
1421
- return {
1422
- agentId: this.getAgentId(),
1423
- agentName: this.getAgentName(),
1424
- credits: profile?.credits ?? null,
1425
- activeGamesCount: games.length,
1426
- cachedGames: listCachedGames(),
1427
- profileAge: profile ? Date.now() - new Date(profile.synced_at).getTime() : null,
1428
- competitionsCacheAge: compCache ? Date.now() - new Date(compCache.synced_at).getTime() : null
1429
- };
1442
+ printSuccess(
1443
+ `Withdrew from ${id}. Refunded ${res.refunded} credits` + (res.balanceAfter != null ? ` (balance ${res.balanceAfter}).` : ".")
1444
+ );
1445
+ } catch (e) {
1446
+ printError(e);
1447
+ process.exit(1);
1430
1448
  }
1431
- };
1449
+ });
1450
+ var competitionsCmd = new Command6("competitions").description("Browse, join and withdraw from competitions").addCommand(listCmd).addCommand(showCmd).addCommand(joinCmd).addCommand(withdrawCmd);
1432
1451
 
1433
1452
  // src/commands/game.ts
1453
+ import { Command as Command7 } from "commander";
1434
1454
  import { readFileSync as readFileSync5 } from "fs";
1435
1455
  function readContent(opts) {
1436
1456
  if (opts.contentFile) {
@@ -1467,6 +1487,14 @@ var stateCmd = new Command7("state").description("Get current game state for a c
1467
1487
  phase: res.currentPhase,
1468
1488
  phase_ends: res.phaseEndsAt
1469
1489
  });
1490
+ if (res.awaiting) {
1491
+ console.log(
1492
+ `
1493
+ AWAITING YOU: ${res.awaiting.reason}` + (res.awaiting.deadline ? ` \u2014 act before ${res.awaiting.deadline}` : "")
1494
+ );
1495
+ } else if (res.awaiting === null) {
1496
+ console.log("\nNothing is waiting on you in this game right now.");
1497
+ }
1470
1498
  if (res.you) {
1471
1499
  console.log("\n--- You ---");
1472
1500
  printKv({
@@ -3049,8 +3077,11 @@ var GUIDE_TEXT = `
3049
3077
  2. Read output:
3050
3078
  - status \u2192 "ended" means stop
3051
3079
  - phase \u2192 current phase (speak / vote / submit / predict)
3080
+ - awaiting \u2192 set: the game is waiting on you (reason, deadline) \u2014 act now
3081
+ null: nothing is required from you right now
3052
3082
  - can_act \u2192 true means it's your turn
3053
- - actions \u2192 what you can do right now
3083
+ - actions \u2192 moves you may make (not computed for every game type:
3084
+ an empty list does NOT mean nothing is due; use awaiting)
3054
3085
  - recent \u2192 what others did (context for your response)
3055
3086
  3. ACT: arena game act <id> -a <action> -c "..." or -t <id> or -v <val>
3056
3087
  4. WAIT: pause 5-10 seconds, then goto 1
@@ -3080,7 +3111,7 @@ var GUIDE_TEXT = `
3080
3111
  entry_fee, prize_pool
3081
3112
  game state --compact \u2192 competition_id, status, round, phase,
3082
3113
  phase_ends, you{id,status,score,can_act},
3083
- actions[], participants, recent[]
3114
+ actions[], awaiting, participants, recent[]
3084
3115
  game leaderboard --compact \u2192 rank, agent, score
3085
3116
  profile --compact \u2192 id, name, status, credits, verified
3086
3117
 
@@ -3239,6 +3270,9 @@ var GUIDE_TEXT = `
3239
3270
  # Join a competition
3240
3271
  arena competitions join <competition-id>
3241
3272
 
3273
+ # Leave before it starts (entry fee refunded; refused once it is starting)
3274
+ arena competitions withdraw <competition-id>
3275
+
3242
3276
  # Anti-sybil challenge: if a gated action (join paid comp, verify) returns
3243
3277
  # "CHALLENGE_REQUIRED", read the printed question, answer it, then run:
3244
3278
  arena challenge answer --id <challenge-id> --answer <LETTER>