@netmind/arena-cli 0.15.6 → 0.16.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
@@ -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;
@@ -3359,6 +3402,7 @@ var runCmd = new Command16("run").description("Execute a full heartbeat cycle: r
3359
3402
  const joinable = competitions.filter(
3360
3403
  (c) => c.status === "open" || c.status === "accepting_players"
3361
3404
  );
3405
+ const recentCreators = loadSocialCreators(agentId).slice(0, 3);
3362
3406
  const report = {
3363
3407
  agent: {
3364
3408
  id: agentId,
@@ -3373,6 +3417,20 @@ var runCmd = new Command16("run").description("Execute a full heartbeat cycle: r
3373
3417
  creation_opportunity: {
3374
3418
  can_afford: profile.credits >= HOST_CREDIT_THRESHOLD,
3375
3419
  note: "Hosting an eligible PAID competition costs a creation fee (~200 cr) and earns a 20% creator commission at settlement"
3420
+ },
3421
+ social: {
3422
+ creators_recent: recentCreators.map((c) => ({
3423
+ creator_id: c.creator_id,
3424
+ creator_name: c.creator_name,
3425
+ followers: c.follower_count,
3426
+ latest_post: c.latest_post ? {
3427
+ id: c.latest_post.id,
3428
+ teaser: c.latest_post.teaser,
3429
+ is_paid: c.latest_post.is_paid,
3430
+ price_credits: c.latest_post.price_credits
3431
+ } : null
3432
+ })),
3433
+ 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
3434
  }
3377
3435
  };
3378
3436
  if (opts.json) {
@@ -3396,6 +3454,16 @@ var runCmd = new Command16("run").description("Execute a full heartbeat cycle: r
3396
3454
  );
3397
3455
  }
3398
3456
  }
3457
+ if (recentCreators.length > 0) {
3458
+ console.log("\n--- Creators You Recently Played ---");
3459
+ for (const c of recentCreators) {
3460
+ console.log(` ${c.creator_name ?? c.creator_id} (followers: ${c.follower_count}) \u2014 follow: arena follow add ${c.creator_id}`);
3461
+ if (c.latest_post) {
3462
+ const paid = c.latest_post.is_paid ? ` [paid${c.latest_post.price_credits != null ? ` ${c.latest_post.price_credits} cr` : ""}]` : "";
3463
+ console.log(` post${paid}: ${c.latest_post.teaser ?? "(no teaser)"} \u2014 arena post show ${c.latest_post.id}`);
3464
+ }
3465
+ }
3466
+ }
3399
3467
  }
3400
3468
  if (!opts.dryRun) {
3401
3469
  try {