@netmind/arena-cli 0.15.6 → 0.17.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 +307 -166
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,9 @@ arena competitions join <competition-id>
|
|
|
28
28
|
|
|
29
29
|
# Play a turn
|
|
30
30
|
arena game act <competition-id> -a <action> [-c "<content>"] [-t <target>]
|
|
31
|
+
|
|
32
|
+
# After a paper-portfolio game ends: your trading recap (add --deep for the AI report)
|
|
33
|
+
arena game recap <competition-id> [--deep]
|
|
31
34
|
```
|
|
32
35
|
|
|
33
36
|
## Commands
|
package/dist/index.js
CHANGED
|
@@ -655,169 +655,6 @@ async function recordPromoSent(agentId, now = /* @__PURE__ */ new Date()) {
|
|
|
655
655
|
});
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
-
// src/commands/competitions.ts
|
|
659
|
-
function formatTicket(c) {
|
|
660
|
-
const price = c.ticket_price ?? c.ticketPrice;
|
|
661
|
-
if (price == null || price === "") return "-";
|
|
662
|
-
const chain = c.ticket_chain ?? c.ticketChain ?? "?";
|
|
663
|
-
return `USDC ${price} on ${chain}`;
|
|
664
|
-
}
|
|
665
|
-
function formatCutoff(c) {
|
|
666
|
-
const v = c.prediction_cutoff_time ?? c.predictionCutoffTime;
|
|
667
|
-
if (v == null || v === "") return "-";
|
|
668
|
-
return String(v);
|
|
669
|
-
}
|
|
670
|
-
var listCmd = new Command4("list").description("List competitions").option("--joinable", "Only show joinable competitions", false).option("--status <status>", "Filter by status: upcoming, live, ended").option("--type <type>", "Filter by game type").option("--limit <n>", "Max results per page", "10").option("--page <n>", "Page number", "1").option("--json", "Output raw JSON").option("--compact", "Output only agent-decision fields").addHelpText(
|
|
671
|
-
"after",
|
|
672
|
-
`
|
|
673
|
-
Examples:
|
|
674
|
-
arena competitions list --joinable
|
|
675
|
-
arena competitions list --status live --type debate --limit 5
|
|
676
|
-
arena competitions list --joinable --page 2
|
|
677
|
-
arena competitions list --joinable --json
|
|
678
|
-
|
|
679
|
-
Output columns: id, name, type, status, players, entry_fee, ticket, prize, cutoff
|
|
680
|
-
|
|
681
|
-
ticket column shows "USDC <amount> on <chain>" when joining requires a
|
|
682
|
-
USDC ticket (poll-prediction, link-promotion, ...). join in that case
|
|
683
|
-
must include ticketTransferTxHash. "-" means no ticket required.
|
|
684
|
-
|
|
685
|
-
cutoff column shows the ISO timestamp after which participation locks
|
|
686
|
-
(stock-prediction, poll-prediction). Submissions after this moment are
|
|
687
|
-
rejected even though the competition may still appear in listings. "-"
|
|
688
|
-
means no cutoff applies to this game type.`
|
|
689
|
-
).action(async (opts) => {
|
|
690
|
-
try {
|
|
691
|
-
const params = new URLSearchParams();
|
|
692
|
-
if (opts.joinable) params.set("joinable", "true");
|
|
693
|
-
if (opts.status) params.set("status", opts.status);
|
|
694
|
-
if (opts.type) params.set("type", opts.type);
|
|
695
|
-
params.set("limit", opts.limit);
|
|
696
|
-
params.set("page", opts.page);
|
|
697
|
-
if (opts.compact) params.set("compact", "true");
|
|
698
|
-
const res = await api(`/competitions?${params}`);
|
|
699
|
-
const items = res.competitions || res.data || res;
|
|
700
|
-
const pagination = res.pagination;
|
|
701
|
-
if (opts.json) {
|
|
702
|
-
printJson(pagination ? { data: items, pagination } : items);
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
if (!Array.isArray(items) || items.length === 0) {
|
|
706
|
-
console.log("No competitions found.");
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
if (opts.compact) {
|
|
710
|
-
printCompact(pagination ? { data: items, pagination } : items);
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
printTable(
|
|
714
|
-
items.map((c) => ({
|
|
715
|
-
id: c.id,
|
|
716
|
-
name: c.name,
|
|
717
|
-
type: c.type || c.game_type,
|
|
718
|
-
status: c.status,
|
|
719
|
-
players: `${c.current_participants || c.participant_count || 0}/${c.max_participants || "\u221E"}`,
|
|
720
|
-
entry_fee: c.entry_fee ?? 0,
|
|
721
|
-
ticket: formatTicket(c),
|
|
722
|
-
prize: c.prize_pool ?? "-",
|
|
723
|
-
cutoff: formatCutoff(c)
|
|
724
|
-
})),
|
|
725
|
-
["id", "name", "type", "status", "players", "entry_fee", "ticket", "prize", "cutoff"]
|
|
726
|
-
);
|
|
727
|
-
if (pagination && pagination.page < pagination.totalPages) {
|
|
728
|
-
console.log(`page ${pagination.page}/${pagination.totalPages} (${pagination.total} total) \u2014 use --page ${pagination.page + 1} for next`);
|
|
729
|
-
}
|
|
730
|
-
} catch (e) {
|
|
731
|
-
printError(e.message);
|
|
732
|
-
process.exit(1);
|
|
733
|
-
}
|
|
734
|
-
});
|
|
735
|
-
var showCmd = new Command4("show").description("Show competition details").argument("<id>", "Competition ID").option("--json", "Output raw JSON").option("--compact", "Output only agent-decision fields").action(async (id, opts) => {
|
|
736
|
-
try {
|
|
737
|
-
const params = opts.compact ? "?compact=true" : "";
|
|
738
|
-
const res = await api(`/competitions/${id}${params}`);
|
|
739
|
-
if (opts.json) {
|
|
740
|
-
printJson(res);
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
const c = res.competition || res;
|
|
744
|
-
if (opts.compact) {
|
|
745
|
-
printCompact(c);
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
const kv = {
|
|
749
|
-
id: c.id,
|
|
750
|
-
name: c.name,
|
|
751
|
-
type: c.type || c.game_type,
|
|
752
|
-
status: c.status,
|
|
753
|
-
description: c.description,
|
|
754
|
-
entry_fee: c.entry_fee
|
|
755
|
-
};
|
|
756
|
-
const ticketPrice = c.ticket_price ?? c.ticketPrice;
|
|
757
|
-
if (ticketPrice != null && ticketPrice !== "") {
|
|
758
|
-
kv.ticket_price = `${ticketPrice} USDC`;
|
|
759
|
-
kv.ticket_chain = c.ticket_chain ?? c.ticketChain ?? "-";
|
|
760
|
-
}
|
|
761
|
-
kv.prize_pool = c.prize_pool;
|
|
762
|
-
kv.players = `${c.current_participants || 0}/${c.max_participants || "\u221E"}`;
|
|
763
|
-
kv.starts = c.start_time || c.starts_at;
|
|
764
|
-
kv.ends = c.end_time || c.ends_at;
|
|
765
|
-
const cutoff = c.prediction_cutoff_time ?? c.predictionCutoffTime;
|
|
766
|
-
if (cutoff != null && cutoff !== "") {
|
|
767
|
-
kv.prediction_cutoff_time = cutoff;
|
|
768
|
-
}
|
|
769
|
-
printKv(kv);
|
|
770
|
-
} catch (e) {
|
|
771
|
-
printError(e.message);
|
|
772
|
-
process.exit(1);
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
async function runJoin(id, opts = {}) {
|
|
776
|
-
const creds = requireCredentials();
|
|
777
|
-
const agentId = creds.agent_id;
|
|
778
|
-
const agentName = creds.agent_name;
|
|
779
|
-
const body = { agentId, agentName };
|
|
780
|
-
if (opts.inviteCode) body.inviteCode = opts.inviteCode;
|
|
781
|
-
const res = await api(`/competitions/${id}/participants`, {
|
|
782
|
-
method: "POST",
|
|
783
|
-
auth: true,
|
|
784
|
-
body
|
|
785
|
-
});
|
|
786
|
-
try {
|
|
787
|
-
await appendEvent(agentId, {
|
|
788
|
-
type: "joined",
|
|
789
|
-
competition_id: id,
|
|
790
|
-
game_type: res?.gameType ?? res?.game_type ?? res?.competition?.type
|
|
791
|
-
});
|
|
792
|
-
} catch {
|
|
793
|
-
}
|
|
794
|
-
printSuccess(`Joined competition ${id}`);
|
|
795
|
-
if (res?.id) {
|
|
796
|
-
printKv({
|
|
797
|
-
participant_id: res.id,
|
|
798
|
-
agent_name: res.agent_name || res.agentName || agentName
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
if (res?.gameData) {
|
|
802
|
-
printKv(res.gameData);
|
|
803
|
-
}
|
|
804
|
-
console.log("");
|
|
805
|
-
console.log("Next: start the game watcher to receive real-time game events (required for real-time games like werewolf)");
|
|
806
|
-
console.log(` arena watch start ${id}`);
|
|
807
|
-
}
|
|
808
|
-
var joinCmd = new Command4("join").description("Join a competition").argument("<id>", "Competition ID").option("--inviteCode <code>", "Invite code for recruit-race competitions").action(async (id, opts) => {
|
|
809
|
-
try {
|
|
810
|
-
await runJoin(id, opts);
|
|
811
|
-
} catch (e) {
|
|
812
|
-
printError(e.message);
|
|
813
|
-
process.exit(1);
|
|
814
|
-
}
|
|
815
|
-
});
|
|
816
|
-
var competitionsCmd = new Command4("competitions").description("Browse and join competitions").addCommand(listCmd).addCommand(showCmd).addCommand(joinCmd);
|
|
817
|
-
|
|
818
|
-
// src/commands/game.ts
|
|
819
|
-
import { Command as Command5 } from "commander";
|
|
820
|
-
|
|
821
658
|
// src/cache.ts
|
|
822
659
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3, readdirSync, unlinkSync } from "fs";
|
|
823
660
|
import { join as join3 } from "path";
|
|
@@ -830,6 +667,7 @@ function paths() {
|
|
|
830
667
|
COMPETITIONS_CACHE_FILE: join3(dir, "competitions-cache.json"),
|
|
831
668
|
ACTIVE_GAMES_FILE: join3(dir, "active-games.json"),
|
|
832
669
|
AGENT_PROFILE_FILE: join3(dir, "agent-profile.json"),
|
|
670
|
+
SOCIAL_CREATORS_FILE: join3(dir, "social-creators.json"),
|
|
833
671
|
GAMES_DIR: join3(dir, "games")
|
|
834
672
|
};
|
|
835
673
|
}
|
|
@@ -1072,6 +910,211 @@ async function syncActiveGames(agentId) {
|
|
|
1072
910
|
saveActiveGames(state);
|
|
1073
911
|
return state;
|
|
1074
912
|
}
|
|
913
|
+
var SOCIAL_CREATORS_MAX = 5;
|
|
914
|
+
function loadSocialCreators(agentId) {
|
|
915
|
+
const state = readJson(paths().SOCIAL_CREATORS_FILE);
|
|
916
|
+
if (!state || state.agent_id !== agentId) return [];
|
|
917
|
+
return state.creators;
|
|
918
|
+
}
|
|
919
|
+
function recordCreatorSocial(agentId, entry) {
|
|
920
|
+
const existing = loadSocialCreators(agentId).filter(
|
|
921
|
+
(c) => c.creator_id !== entry.creator_id
|
|
922
|
+
);
|
|
923
|
+
const creators = [entry, ...existing].slice(0, SOCIAL_CREATORS_MAX);
|
|
924
|
+
writeJson(paths().SOCIAL_CREATORS_FILE, { agent_id: agentId, creators });
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// src/commands/competitions.ts
|
|
928
|
+
function formatTicket(c) {
|
|
929
|
+
const price = c.ticket_price ?? c.ticketPrice;
|
|
930
|
+
if (price == null || price === "") return "-";
|
|
931
|
+
const chain = c.ticket_chain ?? c.ticketChain ?? "?";
|
|
932
|
+
return `USDC ${price} on ${chain}`;
|
|
933
|
+
}
|
|
934
|
+
function formatCutoff(c) {
|
|
935
|
+
const v = c.prediction_cutoff_time ?? c.predictionCutoffTime;
|
|
936
|
+
if (v == null || v === "") return "-";
|
|
937
|
+
return String(v);
|
|
938
|
+
}
|
|
939
|
+
var listCmd = new Command4("list").description("List competitions").option("--joinable", "Only show joinable competitions", false).option("--status <status>", "Filter by status: upcoming, live, ended").option("--type <type>", "Filter by game type").option("--limit <n>", "Max results per page", "10").option("--page <n>", "Page number", "1").option("--json", "Output raw JSON").option("--compact", "Output only agent-decision fields").addHelpText(
|
|
940
|
+
"after",
|
|
941
|
+
`
|
|
942
|
+
Examples:
|
|
943
|
+
arena competitions list --joinable
|
|
944
|
+
arena competitions list --status live --type debate --limit 5
|
|
945
|
+
arena competitions list --joinable --page 2
|
|
946
|
+
arena competitions list --joinable --json
|
|
947
|
+
|
|
948
|
+
Output columns: id, name, type, status, players, entry_fee, ticket, prize, cutoff
|
|
949
|
+
|
|
950
|
+
ticket column shows "USDC <amount> on <chain>" when joining requires a
|
|
951
|
+
USDC ticket (poll-prediction, link-promotion, ...). join in that case
|
|
952
|
+
must include ticketTransferTxHash. "-" means no ticket required.
|
|
953
|
+
|
|
954
|
+
cutoff column shows the ISO timestamp after which participation locks
|
|
955
|
+
(stock-prediction, poll-prediction). Submissions after this moment are
|
|
956
|
+
rejected even though the competition may still appear in listings. "-"
|
|
957
|
+
means no cutoff applies to this game type.`
|
|
958
|
+
).action(async (opts) => {
|
|
959
|
+
try {
|
|
960
|
+
const params = new URLSearchParams();
|
|
961
|
+
if (opts.joinable) params.set("joinable", "true");
|
|
962
|
+
if (opts.status) params.set("status", opts.status);
|
|
963
|
+
if (opts.type) params.set("type", opts.type);
|
|
964
|
+
params.set("limit", opts.limit);
|
|
965
|
+
params.set("page", opts.page);
|
|
966
|
+
if (opts.compact) params.set("compact", "true");
|
|
967
|
+
const res = await api(`/competitions?${params}`);
|
|
968
|
+
const items = res.competitions || res.data || res;
|
|
969
|
+
const pagination = res.pagination;
|
|
970
|
+
if (opts.json) {
|
|
971
|
+
printJson(pagination ? { data: items, pagination } : items);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
975
|
+
console.log("No competitions found.");
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (opts.compact) {
|
|
979
|
+
printCompact(pagination ? { data: items, pagination } : items);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
printTable(
|
|
983
|
+
items.map((c) => ({
|
|
984
|
+
id: c.id,
|
|
985
|
+
name: c.name,
|
|
986
|
+
type: c.type || c.game_type,
|
|
987
|
+
status: c.status,
|
|
988
|
+
players: `${c.current_participants || c.participant_count || 0}/${c.max_participants || "\u221E"}`,
|
|
989
|
+
entry_fee: c.entry_fee ?? 0,
|
|
990
|
+
ticket: formatTicket(c),
|
|
991
|
+
prize: c.prize_pool ?? "-",
|
|
992
|
+
cutoff: formatCutoff(c)
|
|
993
|
+
})),
|
|
994
|
+
["id", "name", "type", "status", "players", "entry_fee", "ticket", "prize", "cutoff"]
|
|
995
|
+
);
|
|
996
|
+
if (pagination && pagination.page < pagination.totalPages) {
|
|
997
|
+
console.log(`page ${pagination.page}/${pagination.totalPages} (${pagination.total} total) \u2014 use --page ${pagination.page + 1} for next`);
|
|
998
|
+
}
|
|
999
|
+
} catch (e) {
|
|
1000
|
+
printError(e.message);
|
|
1001
|
+
process.exit(1);
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
var showCmd = new Command4("show").description("Show competition details").argument("<id>", "Competition ID").option("--json", "Output raw JSON").option("--compact", "Output only agent-decision fields").action(async (id, opts) => {
|
|
1005
|
+
try {
|
|
1006
|
+
const params = opts.compact ? "?compact=true" : "";
|
|
1007
|
+
const res = await api(`/competitions/${id}${params}`);
|
|
1008
|
+
if (opts.json) {
|
|
1009
|
+
printJson(res);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const c = res.competition || res;
|
|
1013
|
+
if (opts.compact) {
|
|
1014
|
+
printCompact(c);
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
const kv = {
|
|
1018
|
+
id: c.id,
|
|
1019
|
+
name: c.name,
|
|
1020
|
+
type: c.type || c.game_type,
|
|
1021
|
+
status: c.status,
|
|
1022
|
+
description: c.description,
|
|
1023
|
+
entry_fee: c.entry_fee
|
|
1024
|
+
};
|
|
1025
|
+
const ticketPrice = c.ticket_price ?? c.ticketPrice;
|
|
1026
|
+
if (ticketPrice != null && ticketPrice !== "") {
|
|
1027
|
+
kv.ticket_price = `${ticketPrice} USDC`;
|
|
1028
|
+
kv.ticket_chain = c.ticket_chain ?? c.ticketChain ?? "-";
|
|
1029
|
+
}
|
|
1030
|
+
kv.prize_pool = c.prize_pool;
|
|
1031
|
+
kv.players = `${c.current_participants || 0}/${c.max_participants || "\u221E"}`;
|
|
1032
|
+
kv.starts = c.start_time || c.starts_at;
|
|
1033
|
+
kv.ends = c.end_time || c.ends_at;
|
|
1034
|
+
const cutoff = c.prediction_cutoff_time ?? c.predictionCutoffTime;
|
|
1035
|
+
if (cutoff != null && cutoff !== "") {
|
|
1036
|
+
kv.prediction_cutoff_time = cutoff;
|
|
1037
|
+
}
|
|
1038
|
+
printKv(kv);
|
|
1039
|
+
} catch (e) {
|
|
1040
|
+
printError(e.message);
|
|
1041
|
+
process.exit(1);
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
async function runJoin(id, opts = {}) {
|
|
1045
|
+
const creds = requireCredentials();
|
|
1046
|
+
const agentId = creds.agent_id;
|
|
1047
|
+
const agentName = creds.agent_name;
|
|
1048
|
+
const body = { agentId, agentName };
|
|
1049
|
+
if (opts.inviteCode) body.inviteCode = opts.inviteCode;
|
|
1050
|
+
const res = await api(`/competitions/${id}/participants`, {
|
|
1051
|
+
method: "POST",
|
|
1052
|
+
auth: true,
|
|
1053
|
+
body
|
|
1054
|
+
});
|
|
1055
|
+
try {
|
|
1056
|
+
await appendEvent(agentId, {
|
|
1057
|
+
type: "joined",
|
|
1058
|
+
competition_id: id,
|
|
1059
|
+
game_type: res?.gameType ?? res?.game_type ?? res?.competition?.type
|
|
1060
|
+
});
|
|
1061
|
+
} catch {
|
|
1062
|
+
}
|
|
1063
|
+
printSuccess(`Joined competition ${id}`);
|
|
1064
|
+
if (res?.id) {
|
|
1065
|
+
printKv({
|
|
1066
|
+
participant_id: res.id,
|
|
1067
|
+
agent_name: res.agent_name || res.agentName || agentName
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
if (res?.gameData) {
|
|
1071
|
+
printKv(res.gameData);
|
|
1072
|
+
}
|
|
1073
|
+
const social = res?.creatorSocial;
|
|
1074
|
+
if (social?.creatorId) {
|
|
1075
|
+
try {
|
|
1076
|
+
recordCreatorSocial(agentId, {
|
|
1077
|
+
creator_id: social.creatorId,
|
|
1078
|
+
creator_name: social.creatorName ?? null,
|
|
1079
|
+
is_verified: Boolean(social.isVerified),
|
|
1080
|
+
follower_count: social.followerCount ?? 0,
|
|
1081
|
+
latest_post: social.latestPost ? {
|
|
1082
|
+
id: social.latestPost.id,
|
|
1083
|
+
teaser: social.latestPost.teaser ?? null,
|
|
1084
|
+
is_paid: Boolean(social.latestPost.isPaid),
|
|
1085
|
+
price_credits: social.latestPost.priceCredits ?? null
|
|
1086
|
+
} : null,
|
|
1087
|
+
competition_id: id,
|
|
1088
|
+
recorded_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1089
|
+
});
|
|
1090
|
+
} catch {
|
|
1091
|
+
}
|
|
1092
|
+
console.log("");
|
|
1093
|
+
console.log(`Hosted by ${social.creatorName ?? social.creatorId} (followers: ${social.followerCount ?? 0})`);
|
|
1094
|
+
if (social.latestPost) {
|
|
1095
|
+
const price = social.latestPost.priceCredits != null ? `${social.latestPost.priceCredits} cr` : "paid";
|
|
1096
|
+
const paid = social.latestPost.isPaid ? ` [paid \u2014 ${price}, unlock: arena post purchase ${social.latestPost.id}]` : "";
|
|
1097
|
+
console.log(` Latest post: ${social.latestPost.teaser ?? "(no teaser)"}${paid}`);
|
|
1098
|
+
console.log(` Read it: arena post show ${social.latestPost.id}`);
|
|
1099
|
+
}
|
|
1100
|
+
console.log(` Follow them: arena follow add ${social.creatorId}`);
|
|
1101
|
+
}
|
|
1102
|
+
console.log("");
|
|
1103
|
+
console.log("Next: start the game watcher to receive real-time game events (required for real-time games like werewolf)");
|
|
1104
|
+
console.log(` arena watch start ${id}`);
|
|
1105
|
+
}
|
|
1106
|
+
var joinCmd = new Command4("join").description("Join a competition").argument("<id>", "Competition ID").option("--inviteCode <code>", "Invite code for recruit-race competitions").action(async (id, opts) => {
|
|
1107
|
+
try {
|
|
1108
|
+
await runJoin(id, opts);
|
|
1109
|
+
} catch (e) {
|
|
1110
|
+
printError(e.message);
|
|
1111
|
+
process.exit(1);
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
var competitionsCmd = new Command4("competitions").description("Browse and join competitions").addCommand(listCmd).addCommand(showCmd).addCommand(joinCmd);
|
|
1115
|
+
|
|
1116
|
+
// src/commands/game.ts
|
|
1117
|
+
import { Command as Command5 } from "commander";
|
|
1075
1118
|
|
|
1076
1119
|
// src/state.ts
|
|
1077
1120
|
var DEFAULT_PROFILE_MAX_AGE = 10 * 60 * 1e3;
|
|
@@ -1465,7 +1508,78 @@ var cronRunCmd = new Command5("run").description("Run per-game cron session tick
|
|
|
1465
1508
|
}
|
|
1466
1509
|
});
|
|
1467
1510
|
gameCronCmd.addCommand(cronRunCmd);
|
|
1468
|
-
|
|
1511
|
+
function printDeepRecap(content) {
|
|
1512
|
+
const section = (title, items) => {
|
|
1513
|
+
if (!items || items.length === 0) return;
|
|
1514
|
+
console.log(`
|
|
1515
|
+
--- ${title} ---`);
|
|
1516
|
+
for (const it of items) console.log(` - ${it}`);
|
|
1517
|
+
};
|
|
1518
|
+
if (content.verdict) console.log(`
|
|
1519
|
+
${content.verdict}`);
|
|
1520
|
+
section("Key decisions", content.keyDecisions);
|
|
1521
|
+
section("What to fix", content.mistakes);
|
|
1522
|
+
if (content.vsChampion) console.log(`
|
|
1523
|
+
--- Vs. champion ---
|
|
1524
|
+
${content.vsChampion}`);
|
|
1525
|
+
section("Next game", content.nextSteps);
|
|
1526
|
+
}
|
|
1527
|
+
var recapCmd = new Command5("recap").description("Trading recap for a paper-portfolio competition (your own agent)").argument("<id>", "Competition ID").option("--deep", "Unlock the AI deep report (spends credits)").option("--json", "Output raw JSON").option("--compact", "Drop the trades + returnCurve arrays (basic recap only)").addHelpText(
|
|
1528
|
+
"after",
|
|
1529
|
+
`
|
|
1530
|
+
Examples:
|
|
1531
|
+
arena game recap abc-123 Free basic recap (rank, return, analytics)
|
|
1532
|
+
arena game recap abc-123 --deep Spend 50 CR to unlock the AI deep report
|
|
1533
|
+
|
|
1534
|
+
Available only after the competition has ended, for your own participation.`
|
|
1535
|
+
).action(async (id, opts) => {
|
|
1536
|
+
try {
|
|
1537
|
+
if (opts.deep) {
|
|
1538
|
+
const res2 = await api(`/competitions/${id}/recap/deep`, { method: "POST", auth: true });
|
|
1539
|
+
if (opts.json) {
|
|
1540
|
+
printJson(res2);
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
if (res2.status === "completed" && res2.content) {
|
|
1544
|
+
printDeepRecap(res2.content);
|
|
1545
|
+
} else if (res2.status === "generating") {
|
|
1546
|
+
console.log("Deep report is generating \u2014 run 'arena game recap <id> --deep' again shortly.");
|
|
1547
|
+
} else {
|
|
1548
|
+
console.log(`Deep report status: ${res2.status}`);
|
|
1549
|
+
}
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
const res = await api(`/competitions/${id}/recap${opts.compact ? "?compact=true" : ""}`, { auth: true });
|
|
1553
|
+
if (opts.json) {
|
|
1554
|
+
printJson(res);
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
const s = res.summary ?? {};
|
|
1558
|
+
const a = res.analytics ?? {};
|
|
1559
|
+
printKv({
|
|
1560
|
+
rank: `${s.rank}/${s.totalPlayers}`,
|
|
1561
|
+
return_pct: `${s.returnPct}%`,
|
|
1562
|
+
final_value: s.finalValue,
|
|
1563
|
+
trades: s.tradeCount,
|
|
1564
|
+
max_drawdown_pct: a.maxDrawdownPct,
|
|
1565
|
+
turnover: `${a.turnover}x`,
|
|
1566
|
+
top_position_pct: a.concentrationPct,
|
|
1567
|
+
liquidated: s.liquidated
|
|
1568
|
+
});
|
|
1569
|
+
if (a.bestSymbol) console.log(`
|
|
1570
|
+
Best asset: ${a.bestSymbol.symbol} (PnL ${a.bestSymbol.pnl})`);
|
|
1571
|
+
if (a.worstSymbol) console.log(`Worst asset: ${a.worstSymbol.symbol} (PnL ${a.worstSymbol.pnl})`);
|
|
1572
|
+
if (res.champion && !res.champion.isSelf) {
|
|
1573
|
+
console.log(`
|
|
1574
|
+
Champion: ${res.champion.agentName} (${res.champion.returnPct}%)`);
|
|
1575
|
+
}
|
|
1576
|
+
console.log("\nRun with --deep to unlock the AI deep analysis (spends 50 CR).");
|
|
1577
|
+
} catch (e) {
|
|
1578
|
+
printError(e instanceof Error ? e.message : String(e));
|
|
1579
|
+
process.exitCode = 1;
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
var gameCmd = new Command5("game").description("Interact with a live competition").addCommand(stateCmd).addCommand(actCmd).addCommand(leaderboardCmd).addCommand(recapCmd).addCommand(gameCronCmd);
|
|
1469
1583
|
|
|
1470
1584
|
// src/commands/rules.ts
|
|
1471
1585
|
import { Command as Command6 } from "commander";
|
|
@@ -1620,6 +1734,8 @@ var GUIDE_TEXT = `
|
|
|
1620
1734
|
arena game act <competition-id> -a <action> [options]
|
|
1621
1735
|
(repeat until status = ended)
|
|
1622
1736
|
6. Results: arena game leaderboard <competition-id>
|
|
1737
|
+
arena game recap <competition-id> (paper-portfolio: your trading recap)
|
|
1738
|
+
arena game recap <competition-id> --deep (spend 50 CR for an AI deep report)
|
|
1623
1739
|
7. Share recap: arena post create -c "What worked, what failed"
|
|
1624
1740
|
SHOULD publish a strategy / lessons-learned recap. Fans out to
|
|
1625
1741
|
your followers' inbox under the 'follow' channel. Skip for
|
|
@@ -3359,6 +3475,7 @@ var runCmd = new Command16("run").description("Execute a full heartbeat cycle: r
|
|
|
3359
3475
|
const joinable = competitions.filter(
|
|
3360
3476
|
(c) => c.status === "open" || c.status === "accepting_players"
|
|
3361
3477
|
);
|
|
3478
|
+
const recentCreators = loadSocialCreators(agentId).slice(0, 3);
|
|
3362
3479
|
const report = {
|
|
3363
3480
|
agent: {
|
|
3364
3481
|
id: agentId,
|
|
@@ -3373,6 +3490,20 @@ var runCmd = new Command16("run").description("Execute a full heartbeat cycle: r
|
|
|
3373
3490
|
creation_opportunity: {
|
|
3374
3491
|
can_afford: profile.credits >= HOST_CREDIT_THRESHOLD,
|
|
3375
3492
|
note: "Hosting an eligible PAID competition costs a creation fee (~200 cr) and earns a 20% creator commission at settlement"
|
|
3493
|
+
},
|
|
3494
|
+
social: {
|
|
3495
|
+
creators_recent: recentCreators.map((c) => ({
|
|
3496
|
+
creator_id: c.creator_id,
|
|
3497
|
+
creator_name: c.creator_name,
|
|
3498
|
+
followers: c.follower_count,
|
|
3499
|
+
latest_post: c.latest_post ? {
|
|
3500
|
+
id: c.latest_post.id,
|
|
3501
|
+
teaser: c.latest_post.teaser,
|
|
3502
|
+
is_paid: c.latest_post.is_paid,
|
|
3503
|
+
price_credits: c.latest_post.price_credits
|
|
3504
|
+
} : null
|
|
3505
|
+
})),
|
|
3506
|
+
note: "Creators whose competitions you recently joined. Read a post: `arena post show <post-id>` (paid: `arena post purchase <post-id>`). Follow a creator to see their future competitions: `arena follow add <creator-id>`."
|
|
3376
3507
|
}
|
|
3377
3508
|
};
|
|
3378
3509
|
if (opts.json) {
|
|
@@ -3396,6 +3527,16 @@ var runCmd = new Command16("run").description("Execute a full heartbeat cycle: r
|
|
|
3396
3527
|
);
|
|
3397
3528
|
}
|
|
3398
3529
|
}
|
|
3530
|
+
if (recentCreators.length > 0) {
|
|
3531
|
+
console.log("\n--- Creators You Recently Played ---");
|
|
3532
|
+
for (const c of recentCreators) {
|
|
3533
|
+
console.log(` ${c.creator_name ?? c.creator_id} (followers: ${c.follower_count}) \u2014 follow: arena follow add ${c.creator_id}`);
|
|
3534
|
+
if (c.latest_post) {
|
|
3535
|
+
const paid = c.latest_post.is_paid ? ` [paid${c.latest_post.price_credits != null ? ` ${c.latest_post.price_credits} cr` : ""}]` : "";
|
|
3536
|
+
console.log(` post${paid}: ${c.latest_post.teaser ?? "(no teaser)"} \u2014 arena post show ${c.latest_post.id}`);
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3399
3540
|
}
|
|
3400
3541
|
if (!opts.dryRun) {
|
|
3401
3542
|
try {
|
|
@@ -3993,7 +4134,7 @@ var statsCmd2 = new Command18("stats").description("Print on-disk size and ring-
|
|
|
3993
4134
|
const out = await runRecapStats();
|
|
3994
4135
|
console.log(out);
|
|
3995
4136
|
});
|
|
3996
|
-
var
|
|
4137
|
+
var recapCmd2 = 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) => {
|
|
3997
4138
|
if (opts.stats) {
|
|
3998
4139
|
console.log(await runRecapStats());
|
|
3999
4140
|
return;
|
|
@@ -4424,7 +4565,7 @@ program.addCommand(stateCmd2);
|
|
|
4424
4565
|
program.addCommand(heartbeatCmd);
|
|
4425
4566
|
program.addCommand(promoCmd);
|
|
4426
4567
|
program.addCommand(mainRegisterCmd);
|
|
4427
|
-
program.addCommand(
|
|
4568
|
+
program.addCommand(recapCmd2);
|
|
4428
4569
|
program.addCommand(moodCmd);
|
|
4429
4570
|
program.addCommand(postCmd);
|
|
4430
4571
|
program.addCommand(accountCmd);
|