@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 +3 -0
- package/dist/index.js +180 -146
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
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
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
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
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
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
|
|
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>
|