@mauricode/token-derby 0.1.0 → 1.1.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
@@ -19,6 +19,9 @@ token-derby stable create
19
19
  # 2. Show your stable
20
20
  token-derby stable list
21
21
 
22
+ # 2a. Tweak an existing horse's colors
23
+ token-derby stable edit <name>
24
+
22
25
  # 3. Create a race
23
26
  token-derby create
24
27
 
package/dist/bin.js CHANGED
@@ -250,7 +250,7 @@ function defaultColors() {
250
250
 
251
251
  // src/ui/HorseCreator.tsx
252
252
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
253
- function HorseCreator({ onSubmit, onCancel, initialColors, initialName }) {
253
+ function HorseCreator({ onSubmit, onCancel, initialColors, initialName, lockName }) {
254
254
  const [colors, setColors] = useState(initialColors ?? defaultColors());
255
255
  const [slotIdx, setSlotIdx] = useState(0);
256
256
  const [namingMode, setNamingMode] = useState(false);
@@ -280,6 +280,10 @@ function HorseCreator({ onSubmit, onCancel, initialColors, initialName }) {
280
280
  return;
281
281
  }
282
282
  if (key.return) {
283
+ if (lockName) {
284
+ onSubmit(initialName ?? "", colors);
285
+ return;
286
+ }
283
287
  setNamingMode(true);
284
288
  return;
285
289
  }
@@ -316,6 +320,7 @@ function HorseCreator({ onSubmit, onCancel, initialColors, initialName }) {
316
320
 
317
321
  // src/stable/stable.ts
318
322
  import * as fs from "fs/promises";
323
+ import { randomUUID } from "crypto";
319
324
 
320
325
  // src/paths.ts
321
326
  import * as os from "os";
@@ -326,6 +331,9 @@ function homeDir() {
326
331
  function stableFile() {
327
332
  return path.join(homeDir(), "stable.json");
328
333
  }
334
+ function identityFile() {
335
+ return path.join(homeDir(), "identity.json");
336
+ }
329
337
  function activeRaceFile(joinCode) {
330
338
  return path.join(homeDir(), "active-races", `${joinCode}.json`);
331
339
  }
@@ -337,12 +345,26 @@ function claudeProjectsDir() {
337
345
  }
338
346
 
339
347
  // src/stable/stable.ts
348
+ function newStableHorseId() {
349
+ return randomUUID();
350
+ }
340
351
  async function loadStable() {
341
352
  try {
342
353
  const raw = await fs.readFile(stableFile(), "utf8");
343
354
  const parsed = JSON.parse(raw);
344
355
  if (!parsed || !Array.isArray(parsed.horses)) return { horses: [] };
345
- return parsed;
356
+ const stable = parsed;
357
+ let mutated = false;
358
+ const horses = stable.horses.map((h) => {
359
+ if (typeof h.stable_horse_id === "string" && h.stable_horse_id.length > 0) {
360
+ return h;
361
+ }
362
+ mutated = true;
363
+ return { ...h, stable_horse_id: newStableHorseId() };
364
+ });
365
+ const result = { horses };
366
+ if (mutated) await saveStable(result);
367
+ return result;
346
368
  } catch (e) {
347
369
  if (e?.code === "ENOENT") return { horses: [] };
348
370
  if (e instanceof SyntaxError) return { horses: [] };
@@ -390,7 +412,8 @@ async function stableCreateCommand() {
390
412
  return;
391
413
  }
392
414
  }
393
- await upsertHorse({ name, colors, created_at: (/* @__PURE__ */ new Date()).toISOString() });
415
+ const stable_horse_id = existing?.stable_horse_id ?? newStableHorseId();
416
+ await upsertHorse({ stable_horse_id, name, colors, created_at: (/* @__PURE__ */ new Date()).toISOString() });
394
417
  app.unmount();
395
418
  console.log(`\u2713 Saved "${name}" to your stable.`);
396
419
  },
@@ -507,6 +530,47 @@ async function stableDeleteCommand(name) {
507
530
  return 0;
508
531
  }
509
532
 
533
+ // src/commands/stable-edit.ts
534
+ import React4 from "react";
535
+ import { render as render3 } from "ink";
536
+ async function stableEditCommand(name) {
537
+ if (!name) {
538
+ console.error("Usage: token-derby stable edit <name>");
539
+ return 2;
540
+ }
541
+ const stable = await loadStable();
542
+ const existing = findHorse(stable, name);
543
+ if (!existing) {
544
+ console.error(`No horse named "${name}" in your stable.`);
545
+ return 1;
546
+ }
547
+ let exitCode = 0;
548
+ const app = render3(
549
+ React4.createElement(HorseCreator, {
550
+ initialColors: existing.colors,
551
+ initialName: existing.name,
552
+ lockName: true,
553
+ onSubmit: async (_name, colors) => {
554
+ await upsertHorse({
555
+ stable_horse_id: existing.stable_horse_id,
556
+ name: existing.name,
557
+ colors,
558
+ created_at: existing.created_at
559
+ });
560
+ app.unmount();
561
+ console.log(`\u2713 Updated "${existing.name}".`);
562
+ },
563
+ onCancel: () => {
564
+ app.unmount();
565
+ console.log("Cancelled.");
566
+ exitCode = 1;
567
+ }
568
+ })
569
+ );
570
+ await app.waitUntilExit();
571
+ return exitCode;
572
+ }
573
+
510
574
  // src/commands/create.ts
511
575
  import * as readline3 from "readline/promises";
512
576
  import { stdin as stdin3, stdout as stdout3 } from "process";
@@ -520,6 +584,61 @@ var HEARTBEAT_INTERVAL_MS = 6e4;
520
584
  var POLL_INTERVAL_MS = 3e3;
521
585
  var HEARTBEAT_RETRY_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
522
586
 
587
+ // src/version.ts
588
+ import { createRequire } from "module";
589
+ function readVersion() {
590
+ if ("1.1.0".length > 0) {
591
+ return "1.1.0";
592
+ }
593
+ try {
594
+ const req = createRequire(import.meta.url);
595
+ const pkg = req("../package.json");
596
+ if (typeof pkg.version === "string") return pkg.version;
597
+ } catch {
598
+ }
599
+ return "0.0.0-dev";
600
+ }
601
+ var CLI_VERSION = readVersion();
602
+
603
+ // ../shared/dist/constants.js
604
+ var CLI_VERSION_HEADER = "x-cli-version";
605
+ var USER_ID_HEADER = "x-user-id";
606
+ var USER_NAME_HEADER = "x-user-name";
607
+ var USER_NAME_MAX_LENGTH = 40;
608
+
609
+ // src/identity/identity.ts
610
+ import { promises as fs3 } from "fs";
611
+ import * as path3 from "path";
612
+ import * as crypto from "crypto";
613
+ async function loadIdentity() {
614
+ try {
615
+ const raw = await fs3.readFile(identityFile(), "utf8");
616
+ const parsed = JSON.parse(raw);
617
+ if (typeof parsed.user_id === "string" && typeof parsed.display_name === "string" && typeof parsed.created_at === "string") {
618
+ return parsed;
619
+ }
620
+ return null;
621
+ } catch (e) {
622
+ if (e?.code === "ENOENT") return null;
623
+ return null;
624
+ }
625
+ }
626
+ async function saveIdentity(identity) {
627
+ await fs3.mkdir(homeDir(), { recursive: true });
628
+ await fs3.writeFile(identityFile(), JSON.stringify(identity, null, 2) + "\n", "utf8");
629
+ }
630
+ function generateUserId() {
631
+ return crypto.randomUUID();
632
+ }
633
+ function validateDisplayName(name) {
634
+ const trimmed = name.trim();
635
+ if (trimmed.length < 1) return { ok: false, error: "Name cannot be empty." };
636
+ if (trimmed.length > USER_NAME_MAX_LENGTH) {
637
+ return { ok: false, error: `Name must be ${USER_NAME_MAX_LENGTH} characters or fewer.` };
638
+ }
639
+ return { ok: true, name: trimmed };
640
+ }
641
+
523
642
  // src/api/client.ts
524
643
  var ApiError = class extends Error {
525
644
  constructor(code, message, status) {
@@ -531,9 +650,20 @@ var ApiError = class extends Error {
531
650
  code;
532
651
  status;
533
652
  };
534
- async function request(method, path4, body, authToken, fetchImpl = fetch) {
535
- const url = path4.startsWith("http") ? path4 : `${apiBase()}${path4}`;
653
+ var identityCache = null;
654
+ function getIdentity() {
655
+ if (!identityCache) identityCache = loadIdentity();
656
+ return identityCache;
657
+ }
658
+ async function request(method, path5, body, authToken, fetchImpl = fetch) {
659
+ const url = path5.startsWith("http") ? path5 : `${apiBase()}${path5}`;
536
660
  const headers = {};
661
+ headers[CLI_VERSION_HEADER] = CLI_VERSION;
662
+ const identity = await getIdentity();
663
+ if (identity) {
664
+ headers[USER_ID_HEADER] = identity.user_id;
665
+ headers[USER_NAME_HEADER] = identity.display_name;
666
+ }
537
667
  if (authToken) headers["authorization"] = `Bearer ${authToken}`;
538
668
  if (body !== void 0) headers["content-type"] = "application/json";
539
669
  let res;
@@ -597,20 +727,19 @@ async function createRaceCommand() {
597
727
  console.error("Name required.");
598
728
  return 1;
599
729
  }
600
- const start = (await rl.question("Start time (ISO 8601, e.g. 2026-04-23T15:00:00Z): ")).trim();
730
+ const startRaw = (await rl.question("Start time (ISO 8601, blank = now): ")).trim();
731
+ const start = startRaw ? startRaw : (/* @__PURE__ */ new Date()).toISOString();
601
732
  if (!isIso(start)) {
602
733
  console.error("Invalid start time.");
603
734
  return 1;
604
735
  }
605
- const end = (await rl.question("End time (ISO 8601): ")).trim();
606
- if (!isIso(end)) {
607
- console.error("Invalid end time.");
608
- return 1;
609
- }
610
- if (new Date(end).getTime() <= new Date(start).getTime()) {
611
- console.error("End time must be after start time.");
736
+ const durationRaw = (await rl.question("Race duration (hours): ")).trim();
737
+ const durationHours = parseFloat(durationRaw);
738
+ if (!Number.isFinite(durationHours) || durationHours <= 0) {
739
+ console.error("Duration must be a positive number of hours.");
612
740
  return 1;
613
741
  }
742
+ const end = new Date(new Date(start).getTime() + durationHours * 36e5).toISOString();
614
743
  const tz = (await rl.question(`Time zone [${DEFAULT_TZ}]: `)).trim() || DEFAULT_TZ;
615
744
  const maxRaw = (await rl.question("Max participants [30]: ")).trim();
616
745
  const max = maxRaw ? parseInt(maxRaw, 10) : void 0;
@@ -652,8 +781,8 @@ function isIso(s) {
652
781
  }
653
782
 
654
783
  // src/commands/join.ts
655
- import React6 from "react";
656
- import { render as render3 } from "ink";
784
+ import React7 from "react";
785
+ import { render as render4 } from "ink";
657
786
 
658
787
  // src/ui/HorsePicker.tsx
659
788
  import { useState as useState2 } from "react";
@@ -705,13 +834,13 @@ function HorsePicker({ horses, onPick, onCancel }) {
705
834
 
706
835
  // src/runtime/run-race.tsx
707
836
  import { useEffect, useRef, useState as useState3 } from "react";
708
- import { useApp } from "ink";
837
+ import { Box as Box6, Text as Text6, useApp } from "ink";
709
838
 
710
839
  // src/ui/StatusScreen.tsx
711
840
  import { Box as Box5, Text as Text5 } from "ink";
712
841
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
713
842
  function StatusScreen(props) {
714
- const { race, ownHorseId, ownHorseName, ownColors, lastHeartbeatAgoSec, lastHeartbeatOk } = props;
843
+ const { race, ownHorseId, ownHorseName, ownColors, ownUserName, lastHeartbeatAgoSec, lastHeartbeatOk } = props;
715
844
  if (!race) {
716
845
  return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx5(Text5, { children: "Joining race\u2026" }) });
717
846
  }
@@ -728,9 +857,19 @@ function StatusScreen(props) {
728
857
  ] }),
729
858
  /* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "row", children: [
730
859
  /* @__PURE__ */ jsx5(HorseSprite, { sprite: MINI_SPRITE, colors: ownColors }),
731
- /* @__PURE__ */ jsxs4(Text5, { children: [
732
- " ",
733
- ownHorseName
860
+ /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
861
+ /* @__PURE__ */ jsxs4(Text5, { children: [
862
+ " ",
863
+ ownHorseName
864
+ ] }),
865
+ /* @__PURE__ */ jsxs4(Text5, { children: [
866
+ " ",
867
+ /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
868
+ "(",
869
+ ownUserName,
870
+ ")"
871
+ ] })
872
+ ] })
734
873
  ] })
735
874
  ] }),
736
875
  /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
@@ -746,7 +885,7 @@ function StatusScreen(props) {
746
885
  ] }),
747
886
  /* @__PURE__ */ jsxs4(Text5, { children: [
748
887
  "Leader: ",
749
- leader ? `${leader.name} (${leader.current_tokens})` : "\u2014"
888
+ leader ? `${leader.name}${leader.user_name ? ` (${leader.user_name})` : ""} \u2014 ${leader.current_tokens}` : "\u2014"
750
889
  ] }),
751
890
  /* @__PURE__ */ jsxs4(Text5, { children: [
752
891
  "Race elapsed: ",
@@ -855,50 +994,61 @@ function runPollLoop(opts) {
855
994
  }
856
995
 
857
996
  // src/tokens/transcripts.ts
858
- import * as fs3 from "fs/promises";
859
- import * as path3 from "path";
860
- async function sumOutputTokens() {
997
+ import * as fs4 from "fs/promises";
998
+ import * as path4 from "path";
999
+ async function sumTokens() {
861
1000
  const root = claudeProjectsDir();
862
1001
  const files = await listJsonlFiles(root);
863
- let total = 0;
1002
+ let input = 0;
1003
+ let output = 0;
864
1004
  for (const file of files) {
865
- total += await sumFile(file);
1005
+ const t = await sumFile(file);
1006
+ input += t.input;
1007
+ output += t.output;
866
1008
  }
867
- return total;
1009
+ return { input, output };
1010
+ }
1011
+ async function sumOutputTokens() {
1012
+ const { input, output } = await sumTokens();
1013
+ return input + output;
868
1014
  }
869
1015
  async function listJsonlFiles(root) {
870
1016
  let projects;
871
1017
  try {
872
- projects = await fs3.readdir(root);
1018
+ projects = await fs4.readdir(root);
873
1019
  } catch (e) {
874
1020
  if (e?.code === "ENOENT") return [];
875
1021
  throw e;
876
1022
  }
877
1023
  const out = [];
878
1024
  for (const project of projects) {
879
- const projectDir = path3.join(root, project);
1025
+ const projectDir = path4.join(root, project);
880
1026
  let stat2;
881
1027
  try {
882
- stat2 = await fs3.stat(projectDir);
1028
+ stat2 = await fs4.stat(projectDir);
883
1029
  } catch {
884
1030
  continue;
885
1031
  }
886
1032
  if (!stat2.isDirectory()) continue;
887
- const entries = await fs3.readdir(projectDir);
1033
+ const entries = await fs4.readdir(projectDir);
888
1034
  for (const entry of entries) {
889
- if (entry.endsWith(".jsonl")) out.push(path3.join(projectDir, entry));
1035
+ if (entry.endsWith(".jsonl")) out.push(path4.join(projectDir, entry));
890
1036
  }
891
1037
  }
892
1038
  return out;
893
1039
  }
1040
+ function addNum(value) {
1041
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
1042
+ }
894
1043
  async function sumFile(file) {
895
1044
  let raw;
896
1045
  try {
897
- raw = await fs3.readFile(file, "utf8");
1046
+ raw = await fs4.readFile(file, "utf8");
898
1047
  } catch {
899
- return 0;
1048
+ return { input: 0, output: 0 };
900
1049
  }
901
- let sum = 0;
1050
+ let input = 0;
1051
+ let output = 0;
902
1052
  for (const line of raw.split("\n")) {
903
1053
  if (!line.trim()) continue;
904
1054
  let parsed;
@@ -907,10 +1057,12 @@ async function sumFile(file) {
907
1057
  } catch {
908
1058
  continue;
909
1059
  }
910
- const tokens = parsed?.message?.usage?.output_tokens;
911
- if (typeof tokens === "number" && Number.isFinite(tokens)) sum += tokens;
1060
+ const usage = parsed?.message?.usage;
1061
+ if (!usage) continue;
1062
+ input += addNum(usage.input_tokens) + addNum(usage.cache_creation_input_tokens) + addNum(usage.cache_read_input_tokens);
1063
+ output += addNum(usage.output_tokens);
912
1064
  }
913
- return sum;
1065
+ return { input, output };
914
1066
  }
915
1067
 
916
1068
  // src/tokens/baseline.ts
@@ -919,13 +1071,14 @@ function initialBaseline(args) {
919
1071
  }
920
1072
 
921
1073
  // src/runtime/run-race.tsx
922
- import { jsx as jsx6 } from "react/jsx-runtime";
923
- function RunRace({ active, startingBaseline, pendingMode }) {
1074
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1075
+ function RunRace({ active, startingBaseline, pendingMode, ownUserName }) {
924
1076
  const { exit } = useApp();
925
1077
  const [race, setRace] = useState3(null);
926
1078
  const [lastHbAt, setLastHbAt] = useState3(null);
927
1079
  const [lastHbOk, setLastHbOk] = useState3(true);
928
1080
  const [tickNow, setTickNow] = useState3(/* @__PURE__ */ new Date());
1081
+ const [fatalError, setFatalError] = useState3(null);
929
1082
  const baselineRef = useRef(startingBaseline);
930
1083
  const pendingRef = useRef(pendingMode);
931
1084
  const lastTokenSampleRef = useRef(startingBaseline);
@@ -978,7 +1131,15 @@ function RunRace({ active, startingBaseline, pendingMode }) {
978
1131
  setLastHbOk(true);
979
1132
  if (resp.race_status === "finished") exit();
980
1133
  },
981
- onError: () => setLastHbOk(false),
1134
+ onError: (err) => {
1135
+ if (err instanceof ApiError && err.code === "VERSION_MISMATCH") {
1136
+ setFatalError(err.message);
1137
+ ctrl.current.abort();
1138
+ exit();
1139
+ return;
1140
+ }
1141
+ setLastHbOk(false);
1142
+ },
982
1143
  onFinished: () => exit(),
983
1144
  abortSignal: ctrl.current.signal
984
1145
  });
@@ -999,6 +1160,12 @@ function RunRace({ active, startingBaseline, pendingMode }) {
999
1160
  };
1000
1161
  }, []);
1001
1162
  const lastHeartbeatAgoSec = lastHbAt ? Math.max(0, Math.floor((tickNow.getTime() - lastHbAt.getTime()) / 1e3)) : null;
1163
+ if (fatalError) {
1164
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", padding: 1, children: [
1165
+ /* @__PURE__ */ jsx6(Text6, { color: "red", bold: true, children: "CLI version mismatch \u2014 disconnected" }),
1166
+ /* @__PURE__ */ jsx6(Text6, { children: fatalError })
1167
+ ] });
1168
+ }
1002
1169
  return /* @__PURE__ */ jsx6(
1003
1170
  StatusScreen,
1004
1171
  {
@@ -1006,6 +1173,7 @@ function RunRace({ active, startingBaseline, pendingMode }) {
1006
1173
  ownHorseId: active.horse_id,
1007
1174
  ownHorseName: active.horse_name,
1008
1175
  ownColors: active.horse_colors,
1176
+ ownUserName,
1009
1177
  lastHeartbeatAgoSec,
1010
1178
  lastHeartbeatOk: lastHbOk
1011
1179
  }
@@ -1032,56 +1200,94 @@ async function joinCommand(joinCode) {
1032
1200
  return 2;
1033
1201
  }
1034
1202
  const code = joinCode.toUpperCase();
1035
- const stable = await loadStable();
1036
- if (stable.horses.length === 0) {
1037
- console.error("Your stable is empty. Run `token-derby stable create` first.");
1203
+ const identity = await loadIdentity();
1204
+ if (!identity) {
1205
+ console.error("Run `token-derby init` to set up your identity.");
1038
1206
  return 1;
1039
1207
  }
1040
- const picked = await pickHorse(stable.horses);
1041
- if (!picked) {
1042
- console.log("Cancelled.");
1208
+ let race;
1209
+ try {
1210
+ race = await getRace(code);
1211
+ } catch (e) {
1212
+ if (e instanceof ApiError) {
1213
+ if (e.code === "RACE_NOT_FOUND") console.error(`No race with join code ${code}.`);
1214
+ else console.error(`Error: ${e.code} ${e.message}`);
1215
+ return 1;
1216
+ }
1217
+ throw e;
1218
+ }
1219
+ if (race.status === "finished") {
1220
+ console.error("This race has already ended.");
1043
1221
  return 1;
1044
1222
  }
1223
+ const ownHorse = race.horses.find((h) => h.user_id === identity.user_id) ?? null;
1224
+ let chosenStableHorseId;
1225
+ let chosenName;
1226
+ let chosenColors;
1227
+ let isResume;
1228
+ if (ownHorse) {
1229
+ chosenStableHorseId = ownHorse.stable_horse_id;
1230
+ chosenName = ownHorse.name;
1231
+ chosenColors = ownHorse.colors;
1232
+ isResume = true;
1233
+ } else {
1234
+ const stable = await loadStable();
1235
+ if (stable.horses.length === 0) {
1236
+ console.error("Your stable is empty. Run `token-derby stable create` first.");
1237
+ return 1;
1238
+ }
1239
+ const picked = await pickHorse(stable.horses);
1240
+ if (!picked) {
1241
+ console.log("Cancelled.");
1242
+ return 1;
1243
+ }
1244
+ chosenStableHorseId = picked.stable_horse_id;
1245
+ chosenName = picked.name;
1246
+ chosenColors = picked.colors;
1247
+ isResume = false;
1248
+ }
1045
1249
  let joinResp;
1046
1250
  try {
1047
- joinResp = await joinRace(code, { horse: { name: picked.name, colors: picked.colors } });
1251
+ joinResp = await joinRace(code, {
1252
+ horse: { stable_horse_id: chosenStableHorseId, name: chosenName, colors: chosenColors }
1253
+ });
1048
1254
  } catch (e) {
1049
1255
  if (e instanceof ApiError) {
1050
- if (e.code === "RACE_FULL") console.error(`This race is full.`);
1256
+ if (e.code === "RACE_FULL") console.error("This race is full.");
1051
1257
  else if (e.code === "RACE_FINISHED") console.error("This race has ended.");
1052
1258
  else if (e.code === "RACE_NOT_FOUND") console.error(`No race with join code ${code}.`);
1259
+ else if (e.code === "VERSION_MISMATCH") console.error(e.message);
1260
+ else if (e.code === "DUPLICATE_HORSE") console.error(e.message);
1261
+ else if (e.code === "IDENTITY_REQUIRED") console.error(`Error: ${e.message}`);
1053
1262
  else console.error(`Error: ${e.code} ${e.message}`);
1054
1263
  return 1;
1055
1264
  }
1056
1265
  throw e;
1057
1266
  }
1058
- const race = await getRace(code);
1059
- if (race.status === "finished") {
1060
- console.error("Race finished after join. Exiting.");
1061
- return 1;
1062
- }
1267
+ const prior = await loadActiveRace(code);
1268
+ const lastTokens = isResume && prior?.horse_id === joinResp.horse_id ? prior.last_race_tokens : ownHorse?.current_tokens ?? 0;
1063
1269
  const status = race.status;
1064
1270
  const active = {
1065
1271
  join_code: code,
1066
1272
  race_id: race.race_id,
1067
1273
  horse_id: joinResp.horse_id,
1068
1274
  heartbeat_token: joinResp.heartbeat_token,
1069
- horse_name: picked.name,
1070
- horse_colors: picked.colors,
1071
- joined_at: (/* @__PURE__ */ new Date()).toISOString(),
1072
- last_race_tokens: 0,
1275
+ horse_name: chosenName,
1276
+ horse_colors: chosenColors,
1277
+ joined_at: ownHorse?.joined_at ?? (/* @__PURE__ */ new Date()).toISOString(),
1278
+ last_race_tokens: lastTokens,
1073
1279
  last_heartbeat_at: (/* @__PURE__ */ new Date(0)).toISOString()
1074
1280
  };
1075
1281
  await saveActiveRace(active);
1076
- const initial = await buildInitialState({ active, raceStatus: status, rejoin: false });
1077
- const app = render3(React6.createElement(RunRace, { active, ...initial }));
1282
+ const initial = await buildInitialState({ active, raceStatus: status, rejoin: isResume });
1283
+ const app = render4(React7.createElement(RunRace, { active, ...initial, ownUserName: identity.display_name }));
1078
1284
  await app.waitUntilExit();
1079
1285
  return 0;
1080
1286
  }
1081
1287
  async function pickHorse(horses) {
1082
1288
  return new Promise((resolve) => {
1083
- const app = render3(
1084
- React6.createElement(HorsePicker, {
1289
+ const app = render4(
1290
+ React7.createElement(HorsePicker, {
1085
1291
  horses,
1086
1292
  onPick: (h) => {
1087
1293
  app.unmount();
@@ -1096,41 +1302,6 @@ async function pickHorse(horses) {
1096
1302
  });
1097
1303
  }
1098
1304
 
1099
- // src/commands/rejoin.ts
1100
- import React7 from "react";
1101
- import { render as render4 } from "ink";
1102
- async function rejoinCommand(joinCode) {
1103
- if (!joinCode) {
1104
- console.error("Usage: token-derby rejoin <join-code>");
1105
- return 2;
1106
- }
1107
- const code = joinCode.toUpperCase();
1108
- const active = await loadActiveRace(code);
1109
- if (!active) {
1110
- console.error(`No saved active-race state for ${code}. Use \`token-derby join ${code}\` to enter as a new horse.`);
1111
- return 1;
1112
- }
1113
- let race;
1114
- try {
1115
- race = await getRace(code);
1116
- } catch (e) {
1117
- if (e instanceof ApiError) {
1118
- console.error(`Error: ${e.code} ${e.message}`);
1119
- return 1;
1120
- }
1121
- throw e;
1122
- }
1123
- if (race.status === "finished") {
1124
- console.error("Race already finished.");
1125
- return 1;
1126
- }
1127
- const status = race.status;
1128
- const initial = await buildInitialState({ active, raceStatus: status, rejoin: true });
1129
- const app = render4(React7.createElement(RunRace, { active, ...initial }));
1130
- await app.waitUntilExit();
1131
- return 0;
1132
- }
1133
-
1134
1305
  // src/commands/end.ts
1135
1306
  import * as readline4 from "readline/promises";
1136
1307
  import { stdin as stdin4, stdout as stdout4 } from "process";
@@ -1160,23 +1331,71 @@ async function endCommand(adminCode) {
1160
1331
  }
1161
1332
  }
1162
1333
 
1334
+ // src/commands/init.ts
1335
+ import * as readline5 from "readline/promises";
1336
+ import { stdin as stdin5, stdout as stdout5 } from "process";
1337
+ async function initCommand() {
1338
+ const existing = await loadIdentity();
1339
+ const rl = readline5.createInterface({ input: stdin5, output: stdout5 });
1340
+ try {
1341
+ if (existing) {
1342
+ console.log(`Current jockey name: ${existing.display_name}`);
1343
+ const raw2 = (await rl.question("New jockey name (use your real name please) [keep]: ")).trim();
1344
+ if (!raw2) {
1345
+ console.log("Kept existing name.");
1346
+ return 0;
1347
+ }
1348
+ const v2 = validateDisplayName(raw2);
1349
+ if (!v2.ok) {
1350
+ console.error(v2.error);
1351
+ return 1;
1352
+ }
1353
+ const updated = { ...existing, display_name: v2.name };
1354
+ await saveIdentity(updated);
1355
+ console.log(`Updated jockey name to: ${updated.display_name}`);
1356
+ return 0;
1357
+ }
1358
+ const raw = (await rl.question("Jockey Name (use your real name please): ")).trim();
1359
+ const v = validateDisplayName(raw);
1360
+ if (!v.ok) {
1361
+ console.error(v.error);
1362
+ return 1;
1363
+ }
1364
+ const identity = {
1365
+ user_id: generateUserId(),
1366
+ display_name: v.name,
1367
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
1368
+ };
1369
+ await saveIdentity(identity);
1370
+ console.log("");
1371
+ console.log(`Welcome, ${identity.display_name}!`);
1372
+ console.log(`Your identity is saved. You can now create a stable and join races.`);
1373
+ return 0;
1374
+ } finally {
1375
+ rl.close();
1376
+ }
1377
+ }
1378
+
1163
1379
  // src/bin.ts
1164
- var VERSION = "0.1.0";
1165
- var HELP = `token-derby v${VERSION}
1380
+ var HELP = `token-derby v${CLI_VERSION}
1381
+
1382
+ Identity:
1383
+ token-derby init Set up your jockey identity (run this first)
1166
1384
 
1167
1385
  Stable management:
1168
1386
  token-derby stable create Make a new horse (interactive)
1169
1387
  token-derby stable list Show your saved horses
1388
+ token-derby stable edit <name> Edit an existing horse's colors
1170
1389
  token-derby stable delete <name> Remove a horse from your stable
1171
1390
 
1172
1391
  Races:
1173
1392
  token-derby create Create a new race (interactive)
1174
- token-derby join <join-code> Pick a horse and join a race
1175
- token-derby rejoin <join-code> Resume a race after a disconnect
1393
+ token-derby join <join-code> Join (or resume) a race
1176
1394
  token-derby end <admin-code> End a race early
1177
1395
 
1178
1396
  Environment:
1179
1397
  TOKEN_DERBY_API_BASE Override API base URL (default: production)
1398
+ TOKEN_DERBY_HOME Override identity/stable directory
1180
1399
  `;
1181
1400
  async function main() {
1182
1401
  const argv = process.argv.slice(2);
@@ -1186,21 +1405,27 @@ async function main() {
1186
1405
  return 0;
1187
1406
  }
1188
1407
  if (cmd === "--version" || cmd === "-v") {
1189
- console.log(VERSION);
1408
+ console.log(CLI_VERSION);
1190
1409
  return 0;
1191
1410
  }
1411
+ if (cmd === "init") return initCommand();
1412
+ const identity = await loadIdentity();
1413
+ if (!identity) {
1414
+ console.error("Run `token-derby init` to set up your identity before using any other command.");
1415
+ return 1;
1416
+ }
1192
1417
  if (cmd === "stable") {
1193
1418
  const sub = argv[1];
1194
1419
  if (sub === "create") return stableCreateCommand();
1195
1420
  if (sub === "list") return stableListCommand();
1421
+ if (sub === "edit") return stableEditCommand(argv[2]);
1196
1422
  if (sub === "delete") return stableDeleteCommand(argv[2]);
1197
1423
  console.error(`Unknown stable subcommand: ${sub ?? "(none)"}`);
1198
- console.error("Try: stable create | stable list | stable delete <name>");
1424
+ console.error("Try: stable create | stable list | stable edit <name> | stable delete <name>");
1199
1425
  return 2;
1200
1426
  }
1201
1427
  if (cmd === "create") return createRaceCommand();
1202
1428
  if (cmd === "join") return joinCommand(argv[1]);
1203
- if (cmd === "rejoin") return rejoinCommand(argv[1]);
1204
1429
  if (cmd === "end") return endCommand(argv[1]);
1205
1430
  console.error(`Unknown command: ${cmd}`);
1206
1431
  console.error(HELP);
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/stable-create.ts","../src/ui/HorseCreator.tsx","../src/ui/HorseSprite.tsx","../src/ui/sprite.ts","../src/ui/sprite-render.ts","../src/ui/palette.ts","../src/stable/stable.ts","../src/paths.ts","../src/commands/stable-list.tsx","../src/commands/stable-delete.ts","../src/stable/active-race.ts","../src/commands/create.ts","../src/config.ts","../src/api/client.ts","../src/api/endpoints.ts","../src/commands/join.ts","../src/ui/HorsePicker.tsx","../src/runtime/run-race.tsx","../src/ui/StatusScreen.tsx","../src/runtime/heartbeat-loop.ts","../src/runtime/poll-loop.ts","../src/tokens/transcripts.ts","../src/tokens/baseline.ts","../src/commands/rejoin.ts","../src/commands/end.ts","../src/bin.ts"],"sourcesContent":["import React from 'react';\nimport { render } from 'ink';\nimport { HorseCreator } from '../ui/HorseCreator.js';\nimport { upsertHorse, loadStable, findHorse } from '../stable/stable.js';\nimport * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\n\nexport async function stableCreateCommand(): Promise<number> {\n let exitCode = 0;\n const app = render(\n React.createElement(HorseCreator, {\n onSubmit: async (name, colors) => {\n const stable = await loadStable();\n const existing = findHorse(stable, name);\n if (existing) {\n app.unmount();\n const rl = readline.createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(`Horse \"${name}\" already exists. Overwrite? [y/N] `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n console.log('Cancelled.');\n exitCode = 1;\n return;\n }\n }\n await upsertHorse({ name, colors, created_at: new Date().toISOString() });\n app.unmount();\n console.log(`✓ Saved \"${name}\" to your stable.`);\n },\n onCancel: () => {\n app.unmount();\n console.log('Cancelled.');\n exitCode = 1;\n },\n }),\n );\n await app.waitUntilExit();\n return exitCode;\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport TextInput from 'ink-text-input';\nimport type { HorseColors } from '@token-derby/shared';\nimport { HorseSprite } from './HorseSprite.js';\nimport { MAIN_SPRITE } from './sprite.js';\nimport { SLOTS, PALETTES, nextColor, prevColor, defaultColors, type Slot } from './palette.js';\n\ntype Props = {\n onSubmit: (name: string, colors: HorseColors) => void;\n onCancel: () => void;\n initialColors?: HorseColors;\n initialName?: string;\n};\n\nexport function HorseCreator({ onSubmit, onCancel, initialColors, initialName }: Props) {\n const [colors, setColors] = useState<HorseColors>(initialColors ?? defaultColors());\n const [slotIdx, setSlotIdx] = useState(0);\n const [namingMode, setNamingMode] = useState(false);\n const [name, setName] = useState(initialName ?? '');\n const [error, setError] = useState<string | null>(null);\n\n const slot: Slot = SLOTS[slotIdx]!;\n\n useInput((input, key) => {\n if (namingMode) return;\n if (key.escape) { onCancel(); return; }\n if (key.upArrow) { setSlotIdx((slotIdx - 1 + SLOTS.length) % SLOTS.length); return; }\n if (key.downArrow) { setSlotIdx((slotIdx + 1) % SLOTS.length); return; }\n if (key.leftArrow) { setColors({ ...colors, [slot]: prevColor(slot, colors[slot]) }); return; }\n if (key.rightArrow) { setColors({ ...colors, [slot]: nextColor(slot, colors[slot]) }); return; }\n if (key.return) { setNamingMode(true); return; }\n });\n\n const handleNameSubmit = (value: string) => {\n if (!value.trim()) {\n setError('Name required');\n return;\n }\n onSubmit(value.trim(), colors);\n };\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={1}>\n <HorseSprite sprite={MAIN_SPRITE} colors={colors} />\n </Box>\n\n <Box flexDirection=\"column\">\n {SLOTS.map((s, i) => (\n <Text key={s}>\n {i === slotIdx ? '►' : ' '} {s.padEnd(7)} <Text color={colors[s]}>██</Text> {colors[s]}\n </Text>\n ))}\n </Box>\n\n {!namingMode && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text dimColor>↑/↓ select slot · ←/→ cycle color · Enter accept · Esc cancel</Text>\n </Box>\n )}\n\n {namingMode && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text>Name your horse: </Text>\n <TextInput value={name} onChange={(v) => { setName(v); setError(null); }} onSubmit={handleNameSubmit} />\n {error && <Text color=\"red\">{error}</Text>}\n </Box>\n )}\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { HorseColors } from '@token-derby/shared';\nimport { renderSprite, type Cell } from './sprite-render.js';\nimport type { SlotTag } from './sprite.js';\n\ntype Props = {\n sprite: readonly (readonly SlotTag[])[];\n colors: HorseColors;\n};\n\nexport function HorseSprite({ sprite, colors }: Props) {\n const grid = renderSprite(sprite, colors);\n return (\n <Box flexDirection=\"column\">\n {grid.map((row, y) => (\n <Text key={y}>{rowToAnsi(row)}</Text>\n ))}\n </Box>\n );\n}\n\nfunction rowToAnsi(row: Cell[]): string {\n let out = '';\n for (const cell of row) {\n if (cell.top === null && cell.bottom === null) {\n out += ' ';\n } else if (cell.top !== null && cell.bottom !== null) {\n out += ansiFg(cell.top) + ansiBg(cell.bottom) + '▀' + RESET;\n } else if (cell.top !== null) {\n out += ansiFg(cell.top) + '▀' + RESET;\n } else {\n out += ansiFg(cell.bottom!) + '▄' + RESET;\n }\n }\n return out;\n}\n\nconst RESET = '\\x1b[0m';\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const h = hex.replace('#', '');\n return [\n parseInt(h.slice(0, 2), 16),\n parseInt(h.slice(2, 4), 16),\n parseInt(h.slice(4, 6), 16),\n ];\n}\n\nfunction ansiFg(hex: string): string {\n const [r, g, b] = hexToRgb(hex);\n return `\\x1b[38;2;${r};${g};${b}m`;\n}\n\nfunction ansiBg(hex: string): string {\n const [r, g, b] = hexToRgb(hex);\n return `\\x1b[48;2;${r};${g};${b}m`;\n}\n","// Pixel slot tags. `null` = transparent.\n// B = body, M = mane, T = tail, S = saddle, E = eye (fixed black), H = hoof (fixed dark)\nexport type SlotTag = 'B' | 'M' | 'T' | 'S' | 'E' | 'H' | null;\n\nexport const FIXED_COLORS = {\n E: '#000000',\n H: '#1F1108',\n} as const;\n\n// Each pair of adjacent rows is intentionally identical so every half-block\n// cell renders as a solid color. The only intentional split is the last row\n// (legs/hooves transition).\nconst MAIN_ROWS: readonly string[] = [\n '................................',\n '................................',\n '..........................MMM...',\n '..........................MMM...',\n '.........................MBBEBB.',\n '.........................MBBEBB.',\n '........................MBBBBBBB',\n '........................MBBBBBBB',\n '..................MMMMMMMBBB....',\n '..................MMMMMMMBBB....',\n '....BBBBBBBBSSSSSSMMBBBBBB......',\n '...BBBBBBBBBSSSSSSMMBBBBBB......',\n '.TTBBBBBBBBBSSSSSSBBBBBBBB......',\n '.TTBBBBBBBBBSSSSSSBBBBBBBB......',\n 'TTTBBBBBBBBBBBBBBBBBBBBBBB......',\n 'TTTBBBBBBBBBBBBBBBBBBBBB........',\n '...BBB.BBB.....BBB.BBB..........',\n '...BBB.BBB.....BBB.BBB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '...HHH.HHH.....HHH.HHH..........',\n];\n\nconst MINI_ROWS: readonly string[] = [\n '.BBSSMBB',\n '.BBSSMBB',\n 'TBBBBBB.',\n 'THH..HH.',\n];\n\nexport const MAIN_SPRITE: readonly (readonly SlotTag[])[] = parse(MAIN_ROWS, 32, 24);\nexport const MINI_SPRITE: readonly (readonly SlotTag[])[] = parse(MINI_ROWS, 8, 4);\n\nfunction parse(rows: readonly string[], width: number, height: number): SlotTag[][] {\n if (rows.length !== height) {\n throw new Error(`sprite has ${rows.length} rows, expected ${height}`);\n }\n return rows.map((row, y) => {\n if (row.length !== width) {\n throw new Error(`sprite row ${y} has length ${row.length}, expected ${width}`);\n }\n return [...row].map(c => toTag(c));\n });\n}\n\nfunction toTag(c: string): SlotTag {\n switch (c) {\n case 'B': return 'B';\n case 'M': return 'M';\n case 'T': return 'T';\n case 'S': return 'S';\n case 'E': return 'E';\n case 'H': return 'H';\n case '.': return null;\n default: throw new Error(`unknown sprite char: ${c}`);\n }\n}\n","import type { HorseColors } from '@token-derby/shared';\nimport { FIXED_COLORS, type SlotTag } from './sprite.js';\n\nexport type Cell = {\n top: string | null;\n bottom: string | null;\n};\n\nexport function renderSprite(\n sprite: readonly (readonly SlotTag[])[],\n colors: HorseColors,\n): Cell[][] {\n const out: Cell[][] = [];\n for (let y = 0; y + 1 < sprite.length || y < sprite.length; y += 2) {\n const topRow = sprite[y];\n const bottomRow = sprite[y + 1];\n if (!topRow) break;\n const row: Cell[] = [];\n for (let x = 0; x < topRow.length; x++) {\n row.push({\n top: tagColor(topRow[x] ?? null, colors),\n bottom: tagColor(bottomRow?.[x] ?? null, colors),\n });\n }\n out.push(row);\n if (!bottomRow) break;\n }\n return out;\n}\n\nfunction tagColor(tag: SlotTag, colors: HorseColors): string | null {\n if (tag === null) return null;\n if (tag === 'E') return FIXED_COLORS.E;\n if (tag === 'H') return FIXED_COLORS.H;\n if (tag === 'B') return colors.body;\n if (tag === 'M') return colors.mane;\n if (tag === 'T') return colors.tail;\n if (tag === 'S') return colors.saddle;\n return null;\n}\n","import type { HorseColors } from '@token-derby/shared';\n\nexport type Slot = keyof HorseColors;\n\nexport const SLOTS: readonly Slot[] = ['body', 'mane', 'tail', 'saddle'] as const;\n\nexport const PALETTES: Record<Slot, readonly string[]> = {\n body: [\n '#8B4513', '#A0522D', '#D2691E', '#CD853F', '#DEB887', '#F5DEB3',\n '#FFFFFF', '#000000', '#4A2C2A', '#5D3A1A', '#704214', '#9C5919',\n '#B87333', '#E5B783', '#F0E1C9', '#2F1B0C',\n ],\n mane: [\n '#000000', '#1C1C1C', '#2F1B0C', '#4A2C2A', '#5D3A1A', '#8B4513',\n '#FFFFFF', '#F5F5DC', '#DEB887', '#CD853F', '#FF4500', '#B22222',\n '#191970', '#4B0082', '#2E8B57', '#FFD700',\n ],\n tail: [\n '#000000', '#1C1C1C', '#2F1B0C', '#4A2C2A', '#5D3A1A', '#8B4513',\n '#FFFFFF', '#F5F5DC', '#DEB887', '#CD853F', '#FF4500', '#B22222',\n '#191970', '#4B0082', '#2E8B57', '#FFD700',\n ],\n saddle: [\n '#C0392B', '#922B21', '#7B241C', '#641E16', '#1F618D', '#21618C',\n '#1B4F72', '#0E6655', '#117A65', '#196F3D', '#7D6608', '#9A7D0A',\n '#6E2C00', '#4D5656', '#212F3D', '#000000',\n ],\n};\n\nexport function nextColor(slot: Slot, current: string): string {\n const palette = PALETTES[slot];\n const idx = palette.indexOf(current);\n return palette[(idx + 1 + palette.length) % palette.length] ?? palette[0]!;\n}\n\nexport function prevColor(slot: Slot, current: string): string {\n const palette = PALETTES[slot];\n const idx = palette.indexOf(current);\n if (idx < 0) return palette[0]!;\n return palette[(idx - 1 + palette.length) % palette.length]!;\n}\n\nexport function defaultColors(): HorseColors {\n return {\n body: PALETTES.body[0]!,\n mane: PALETTES.mane[0]!,\n tail: PALETTES.tail[0]!,\n saddle: PALETTES.saddle[0]!,\n };\n}\n","import * as fs from 'node:fs/promises';\nimport type { HorseColors } from '@token-derby/shared';\nimport { homeDir, stableFile } from '../paths.js';\n\nexport type StableHorse = {\n name: string;\n colors: HorseColors;\n created_at: string;\n};\n\nexport type Stable = {\n horses: StableHorse[];\n};\n\nexport async function loadStable(): Promise<Stable> {\n try {\n const raw = await fs.readFile(stableFile(), 'utf8');\n const parsed = JSON.parse(raw);\n if (!parsed || !Array.isArray(parsed.horses)) return { horses: [] };\n return parsed as Stable;\n } catch (e: any) {\n if (e?.code === 'ENOENT') return { horses: [] };\n if (e instanceof SyntaxError) return { horses: [] };\n throw e;\n }\n}\n\nexport async function saveStable(stable: Stable): Promise<void> {\n await fs.mkdir(homeDir(), { recursive: true });\n await fs.writeFile(stableFile(), JSON.stringify(stable, null, 2) + '\\n', 'utf8');\n}\n\nexport async function upsertHorse(horse: StableHorse): Promise<void> {\n const stable = await loadStable();\n const idx = stable.horses.findIndex(h => h.name === horse.name);\n if (idx >= 0) stable.horses[idx] = horse;\n else stable.horses.push(horse);\n await saveStable(stable);\n}\n\nexport async function removeHorse(name: string): Promise<void> {\n const stable = await loadStable();\n stable.horses = stable.horses.filter(h => h.name !== name);\n await saveStable(stable);\n}\n\nexport function findHorse(stable: Stable, name: string): StableHorse | undefined {\n return stable.horses.find(h => h.name === name);\n}\n","import * as os from 'node:os';\nimport * as path from 'node:path';\n\nexport function homeDir(): string {\n return process.env.TOKEN_DERBY_HOME ?? path.join(os.homedir(), '.token-derby');\n}\n\nexport function stableFile(): string {\n return path.join(homeDir(), 'stable.json');\n}\n\nexport function activeRaceFile(joinCode: string): string {\n return path.join(homeDir(), 'active-races', `${joinCode}.json`);\n}\n\nexport function activeRacesDir(): string {\n return path.join(homeDir(), 'active-races');\n}\n\nexport function claudeProjectsDir(): string {\n return process.env.TOKEN_DERBY_CLAUDE_DIR ?? path.join(os.homedir(), '.claude', 'projects');\n}\n","import React from 'react';\nimport { render, Box, Text } from 'ink';\nimport { loadStable } from '../stable/stable.js';\nimport { HorseSprite } from '../ui/HorseSprite.js';\nimport { MINI_SPRITE } from '../ui/sprite.js';\n\nexport async function stableListCommand(): Promise<number> {\n const stable = await loadStable();\n if (stable.horses.length === 0) {\n console.log('Your stable is empty. Run `token-derby stable create` to add a horse.');\n return 0;\n }\n const app = render(\n React.createElement(StableList, { horses: stable.horses }),\n );\n await app.waitUntilExit();\n return 0;\n}\n\nfunction StableList({ horses }: { horses: { name: string; colors: any; created_at: string }[] }) {\n React.useEffect(() => {\n setImmediate(() => process.exit(0));\n }, []);\n return (\n <Box flexDirection=\"column\">\n <Text bold>Your stable ({horses.length}):</Text>\n {horses.map(h => (\n <Box key={h.name} flexDirection=\"row\" marginTop={1}>\n <HorseSprite sprite={MINI_SPRITE} colors={h.colors} />\n <Text> {h.name}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n","import * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport { loadStable, findHorse, removeHorse } from '../stable/stable.js';\nimport { listActiveRaces, loadActiveRace } from '../stable/active-race.js';\n\nexport async function stableDeleteCommand(name: string | undefined): Promise<number> {\n if (!name) {\n console.error('Usage: token-derby stable delete <name>');\n return 2;\n }\n const stable = await loadStable();\n const horse = findHorse(stable, name);\n if (!horse) {\n console.error(`No horse named \"${name}\" in your stable.`);\n return 1;\n }\n\n const codes = await listActiveRaces();\n for (const code of codes) {\n const active = await loadActiveRace(code);\n if (active?.horse_name === name) {\n console.error(`\"${name}\" is currently running in race ${code}. Close that terminal first.`);\n return 1;\n }\n }\n\n const rl = readline.createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(`Delete \"${name}\" from your stable? [y/N] `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n console.log('Cancelled.');\n return 1;\n }\n await removeHorse(name);\n console.log(`✓ Deleted \"${name}\".`);\n return 0;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { HorseColors } from '@token-derby/shared';\nimport { activeRaceFile, activeRacesDir } from '../paths.js';\n\nexport type ActiveRace = {\n join_code: string;\n race_id: string;\n horse_id: string;\n heartbeat_token: string;\n horse_name: string;\n horse_colors: HorseColors;\n joined_at: string;\n last_race_tokens: number;\n last_heartbeat_at: string;\n};\n\nexport async function loadActiveRace(joinCode: string): Promise<ActiveRace | null> {\n try {\n const raw = await fs.readFile(activeRaceFile(joinCode), 'utf8');\n return JSON.parse(raw) as ActiveRace;\n } catch (e: any) {\n if (e?.code === 'ENOENT') return null;\n throw e;\n }\n}\n\nexport async function saveActiveRace(active: ActiveRace): Promise<void> {\n await fs.mkdir(activeRacesDir(), { recursive: true });\n await fs.writeFile(\n activeRaceFile(active.join_code),\n JSON.stringify(active, null, 2) + '\\n',\n 'utf8',\n );\n}\n\nexport async function deleteActiveRace(joinCode: string): Promise<void> {\n try {\n await fs.unlink(activeRaceFile(joinCode));\n } catch (e: any) {\n if (e?.code !== 'ENOENT') throw e;\n }\n}\n\nexport async function listActiveRaces(): Promise<string[]> {\n try {\n const entries = await fs.readdir(activeRacesDir());\n return entries\n .filter(f => f.endsWith('.json'))\n .map(f => path.basename(f, '.json'));\n } catch (e: any) {\n if (e?.code === 'ENOENT') return [];\n throw e;\n }\n}\n","import * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport { createRace } from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\n\nconst DEFAULT_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n\nexport async function createRaceCommand(): Promise<number> {\n const rl = readline.createInterface({ input: stdin, output: stdout });\n try {\n const name = (await rl.question('Race name: ')).trim();\n if (!name) { console.error('Name required.'); return 1; }\n\n const start = (await rl.question('Start time (ISO 8601, e.g. 2026-04-23T15:00:00Z): ')).trim();\n if (!isIso(start)) { console.error('Invalid start time.'); return 1; }\n\n const end = (await rl.question('End time (ISO 8601): ')).trim();\n if (!isIso(end)) { console.error('Invalid end time.'); return 1; }\n if (new Date(end).getTime() <= new Date(start).getTime()) {\n console.error('End time must be after start time.'); return 1;\n }\n\n const tz = (await rl.question(`Time zone [${DEFAULT_TZ}]: `)).trim() || DEFAULT_TZ;\n const maxRaw = (await rl.question('Max participants [30]: ')).trim();\n const max = maxRaw ? parseInt(maxRaw, 10) : undefined;\n if (max !== undefined && (!Number.isFinite(max) || max < 1)) {\n console.error('Max participants must be a positive number.'); return 1;\n }\n\n const resp = await createRace({\n name, start_time: start, end_time: end, tz,\n ...(max !== undefined ? { max_participants: max } : {}),\n });\n\n console.log('');\n console.log(' ╔══════════════════════════════════════╗');\n console.log(` ║ JOIN CODE: ${resp.join_code.padEnd(23)}║`);\n console.log(' ╚══════════════════════════════════════╝');\n console.log('');\n console.log(` Admin code: ${resp.admin_code}`);\n console.log(' ⚠ Save the admin code — you need it to end the race early.');\n console.log('');\n console.log(` Share with participants: token-derby join ${resp.join_code}`);\n return 0;\n } catch (e) {\n if (e instanceof ApiError) {\n console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n } finally {\n rl.close();\n }\n}\n\nfunction isIso(s: string): boolean {\n if (!s) return false;\n const d = new Date(s);\n return !Number.isNaN(d.getTime());\n}\n","export const DEFAULT_API_BASE = 'https://token-derby.mauricode.co.uk/api';\n\nexport function apiBase(): string {\n return process.env.TOKEN_DERBY_API_BASE ?? DEFAULT_API_BASE;\n}\n\nexport const HEARTBEAT_INTERVAL_MS = 60_000;\nexport const POLL_INTERVAL_MS = 3_000;\nexport const HEARTBEAT_RETRY_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 15_000];\n","import { apiBase } from '../config.js';\n\nexport type ApiErrorCode =\n | 'RACE_NOT_FOUND'\n | 'RACE_FULL'\n | 'RACE_FINISHED'\n | 'INVALID_TOKEN'\n | 'RATE_LIMITED'\n | 'BAD_REQUEST'\n | 'NETWORK_ERROR';\n\nexport class ApiError extends Error {\n constructor(\n readonly code: ApiErrorCode,\n message: string,\n readonly status: number,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\ntype FetchFn = typeof fetch;\n\nexport async function request<T>(\n method: string,\n path: string,\n body: unknown,\n authToken: string | undefined,\n fetchImpl: FetchFn = fetch,\n): Promise<T> {\n const url = path.startsWith('http') ? path : `${apiBase()}${path}`;\n const headers: Record<string, string> = {};\n if (authToken) headers['authorization'] = `Bearer ${authToken}`;\n if (body !== undefined) headers['content-type'] = 'application/json';\n\n let res: Awaited<ReturnType<FetchFn>>;\n try {\n res = await fetchImpl(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n } catch (e: any) {\n throw new ApiError('NETWORK_ERROR', e?.message ?? 'fetch failed', 0);\n }\n\n const text = await res.text();\n const contentType = res.headers.get('content-type') ?? '';\n let parsed: any = null;\n if (contentType.includes('application/json') && text.length > 0) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = null;\n }\n }\n\n if (!res.ok) {\n if (parsed && typeof parsed.code === 'string') {\n throw new ApiError(parsed.code as ApiErrorCode, parsed.message ?? 'API error', res.status);\n }\n throw new ApiError('NETWORK_ERROR', `HTTP ${res.status}`, res.status);\n }\n\n return parsed as T;\n}\n","import type {\n CreateRaceRequest, CreateRaceResponse,\n GetRaceResponse, JoinRaceRequest, JoinRaceResponse,\n HeartbeatRequest, HeartbeatResponse, EndRaceResponse,\n} from '@token-derby/shared';\nimport { request } from './client.js';\n\nexport function createRace(body: CreateRaceRequest) {\n return request<CreateRaceResponse>('POST', '/races', body, undefined);\n}\n\nexport function getRace(joinCode: string) {\n return request<GetRaceResponse>('GET', `/races/${encodeURIComponent(joinCode)}`, undefined, undefined);\n}\n\nexport function joinRace(joinCode: string, body: JoinRaceRequest) {\n return request<JoinRaceResponse>('POST', `/races/${encodeURIComponent(joinCode)}/join`, body, undefined);\n}\n\nexport function heartbeat(joinCode: string, horseId: string, token: string, body: HeartbeatRequest) {\n return request<HeartbeatResponse>(\n 'POST',\n `/races/${encodeURIComponent(joinCode)}/horses/${encodeURIComponent(horseId)}/heartbeat`,\n body,\n token,\n );\n}\n\nexport function endRace(adminCode: string) {\n return request<EndRaceResponse>('DELETE', `/races/admin/${encodeURIComponent(adminCode)}`, undefined, undefined);\n}\n","import React from 'react';\nimport { render } from 'ink';\nimport { loadStable } from '../stable/stable.js';\nimport { HorsePicker } from '../ui/HorsePicker.js';\nimport { joinRace, getRace } from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\nimport { saveActiveRace, type ActiveRace } from '../stable/active-race.js';\nimport { RunRace, buildInitialState } from '../runtime/run-race.js';\nimport type { StableHorse } from '../stable/stable.js';\n\nexport async function joinCommand(joinCode: string | undefined): Promise<number> {\n if (!joinCode) {\n console.error('Usage: token-derby join <join-code>');\n return 2;\n }\n const code = joinCode.toUpperCase();\n\n const stable = await loadStable();\n if (stable.horses.length === 0) {\n console.error('Your stable is empty. Run `token-derby stable create` first.');\n return 1;\n }\n\n const picked = await pickHorse(stable.horses);\n if (!picked) { console.log('Cancelled.'); return 1; }\n\n let joinResp;\n try {\n joinResp = await joinRace(code, { horse: { name: picked.name, colors: picked.colors } });\n } catch (e) {\n if (e instanceof ApiError) {\n if (e.code === 'RACE_FULL') console.error(`This race is full.`);\n else if (e.code === 'RACE_FINISHED') console.error('This race has ended.');\n else if (e.code === 'RACE_NOT_FOUND') console.error(`No race with join code ${code}.`);\n else console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n }\n\n const race = await getRace(code);\n if (race.status === 'finished') {\n console.error('Race finished after join. Exiting.');\n return 1;\n }\n const status: 'pending' | 'live' = race.status;\n\n const active: ActiveRace = {\n join_code: code,\n race_id: race.race_id,\n horse_id: joinResp.horse_id,\n heartbeat_token: joinResp.heartbeat_token,\n horse_name: picked.name,\n horse_colors: picked.colors,\n joined_at: new Date().toISOString(),\n last_race_tokens: 0,\n last_heartbeat_at: new Date(0).toISOString(),\n };\n await saveActiveRace(active);\n\n const initial = await buildInitialState({ active, raceStatus: status, rejoin: false });\n const app = render(React.createElement(RunRace, { active, ...initial }));\n await app.waitUntilExit();\n return 0;\n}\n\nasync function pickHorse(horses: StableHorse[]): Promise<StableHorse | null> {\n return new Promise(resolve => {\n const app = render(\n React.createElement(HorsePicker, {\n horses,\n onPick: (h: StableHorse) => { app.unmount(); resolve(h); },\n onCancel: () => { app.unmount(); resolve(null); },\n }),\n );\n });\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { HorseSprite } from './HorseSprite.js';\nimport { MINI_SPRITE } from './sprite.js';\nimport type { StableHorse } from '../stable/stable.js';\n\ntype Props = {\n horses: StableHorse[];\n onPick: (horse: StableHorse) => void;\n onCancel: () => void;\n};\n\nexport function HorsePicker({ horses, onPick, onCancel }: Props) {\n const [idx, setIdx] = useState(0);\n\n useInput((input, key) => {\n if (key.escape) { onCancel(); return; }\n if (horses.length === 0) return;\n if (key.upArrow) { setIdx((idx - 1 + horses.length) % horses.length); return; }\n if (key.downArrow) { setIdx((idx + 1) % horses.length); return; }\n if (key.return) { onPick(horses[idx]!); return; }\n });\n\n if (horses.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text>No horses in your stable.</Text>\n <Text dimColor>Run `token-derby stable create` to make one.</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n <Text>Pick a horse to race:</Text>\n {horses.map((h, i) => (\n <Box key={h.name} flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text>{i === idx ? '►' : ' '} {h.name}</Text>\n </Box>\n <Box flexDirection=\"row\">\n <Text> </Text>\n <HorseSprite sprite={MINI_SPRITE} colors={h.colors} />\n </Box>\n </Box>\n ))}\n <Box marginTop={1}>\n <Text dimColor>↑/↓ choose · Enter pick · Esc cancel</Text>\n </Box>\n </Box>\n );\n}\n","import React, { useEffect, useRef, useState } from 'react';\nimport { useApp } from 'ink';\nimport type { GetRaceResponse, HeartbeatResponse } from '@token-derby/shared';\nimport { StatusScreen } from '../ui/StatusScreen.js';\nimport { runHeartbeatLoop } from './heartbeat-loop.js';\nimport { runPollLoop } from './poll-loop.js';\nimport { sumOutputTokens } from '../tokens/transcripts.js';\nimport { initialBaseline } from '../tokens/baseline.js';\nimport * as endpoints from '../api/endpoints.js';\nimport { saveActiveRace, type ActiveRace } from '../stable/active-race.js';\nimport { HEARTBEAT_INTERVAL_MS, POLL_INTERVAL_MS, HEARTBEAT_RETRY_DELAYS_MS } from '../config.js';\n\nexport type RunRaceProps = {\n active: ActiveRace;\n startingBaseline: number;\n pendingMode: boolean;\n};\n\nexport function RunRace({ active, startingBaseline, pendingMode }: RunRaceProps) {\n const { exit } = useApp();\n const [race, setRace] = useState<GetRaceResponse | null>(null);\n const [lastHbAt, setLastHbAt] = useState<Date | null>(null);\n const [lastHbOk, setLastHbOk] = useState<boolean>(true);\n const [tickNow, setTickNow] = useState<Date>(new Date());\n\n const baselineRef = useRef(startingBaseline);\n const pendingRef = useRef(pendingMode);\n const lastTokenSampleRef = useRef<number>(startingBaseline);\n const ctrl = useRef(new AbortController());\n\n // Re-render every second so the \"Ns ago\" counter updates.\n useEffect(() => {\n const t = setInterval(() => setTickNow(new Date()), 1_000);\n return () => clearInterval(t);\n }, []);\n\n // Re-snapshot baseline when race transitions pending → live.\n useEffect(() => {\n if (pendingRef.current && race?.status === 'live') {\n sumOutputTokens().then(total => {\n baselineRef.current = total;\n pendingRef.current = false;\n });\n }\n }, [race?.status]);\n\n useEffect(() => {\n runPollLoop({\n fetchRace: () => endpoints.getRace(active.join_code),\n intervalMs: POLL_INTERVAL_MS,\n onSnapshot: (r) => setRace(r),\n onError: () => {/* silently keep last-known state */},\n abortSignal: ctrl.current.signal,\n });\n\n runHeartbeatLoop({\n sendHeartbeat: async (currentTokens) => {\n const resp = await endpoints.heartbeat(\n active.join_code, active.horse_id, active.heartbeat_token, { current_tokens: currentTokens },\n );\n const updated: ActiveRace = {\n ...active,\n last_race_tokens: currentTokens,\n last_heartbeat_at: new Date().toISOString(),\n };\n await saveActiveRace(updated);\n return resp;\n },\n getCurrentTokens: () => {\n if (pendingRef.current) return 0;\n return Math.max(0, lastTokenSampleRef.current - baselineRef.current);\n },\n intervalMs: HEARTBEAT_INTERVAL_MS,\n retryDelaysMs: HEARTBEAT_RETRY_DELAYS_MS,\n onSuccess: (resp: HeartbeatResponse) => {\n setLastHbAt(new Date());\n setLastHbOk(true);\n if (resp.race_status === 'finished') exit();\n },\n onError: () => setLastHbOk(false),\n onFinished: () => exit(),\n abortSignal: ctrl.current.signal,\n });\n\n // Token sampler — refresh the running token total every 5s so the heartbeat sees fresh data.\n const sampler = setInterval(async () => {\n try {\n lastTokenSampleRef.current = await sumOutputTokens();\n } catch {/* keep last sample */}\n }, 5_000);\n // Prime it once at startup.\n sumOutputTokens().then(t => { lastTokenSampleRef.current = t; }).catch(() => {});\n\n const controller = ctrl.current;\n return () => {\n clearInterval(sampler);\n controller.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const lastHeartbeatAgoSec = lastHbAt\n ? Math.max(0, Math.floor((tickNow.getTime() - lastHbAt.getTime()) / 1000))\n : null;\n\n return (\n <StatusScreen\n race={race}\n ownHorseId={active.horse_id}\n ownHorseName={active.horse_name}\n ownColors={active.horse_colors}\n lastHeartbeatAgoSec={lastHeartbeatAgoSec}\n lastHeartbeatOk={lastHbOk}\n />\n );\n}\n\nexport async function buildInitialState(args: {\n active: ActiveRace;\n raceStatus: 'pending' | 'live';\n rejoin: boolean;\n}): Promise<{ startingBaseline: number; pendingMode: boolean }> {\n const runningTotal = await sumOutputTokens();\n if (args.rejoin) {\n return {\n startingBaseline: Math.max(0, runningTotal - args.active.last_race_tokens),\n pendingMode: args.raceStatus === 'pending',\n };\n }\n return {\n startingBaseline: initialBaseline({ runningTotal, status: args.raceStatus }),\n pendingMode: args.raceStatus === 'pending',\n };\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { GetRaceResponse, HorseColors, HorseView } from '@token-derby/shared';\nimport { HorseSprite } from './HorseSprite.js';\nimport { MINI_SPRITE } from './sprite.js';\n\ntype Props = {\n race: GetRaceResponse | null;\n ownHorseId: string;\n ownHorseName: string;\n ownColors: HorseColors;\n lastHeartbeatAgoSec: number | null;\n lastHeartbeatOk: boolean;\n};\n\nexport function StatusScreen(props: Props) {\n const { race, ownHorseId, ownHorseName, ownColors, lastHeartbeatAgoSec, lastHeartbeatOk } = props;\n\n if (!race) {\n return (\n <Box flexDirection=\"column\">\n <Text>Joining race…</Text>\n </Box>\n );\n }\n\n const own: HorseView | undefined = race.horses.find(h => h.horse_id === ownHorseId);\n const leader: HorseView | undefined = race.horses[0];\n const elapsedPct = elapsed(race);\n const timeLeft = formatDuration(race.time_left_seconds);\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1}>\n <Text>\n 🏇 TOKEN DERBY ─── <Text bold>{race.name}</Text> ─── status: <Text color={statusColor(race.status)}>{race.status}</Text>\n </Text>\n\n <Box marginTop={1} flexDirection=\"row\">\n <HorseSprite sprite={MINI_SPRITE} colors={ownColors} />\n <Text> {ownHorseName}</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text>Tokens (race): {own?.current_tokens ?? 0}</Text>\n <Text>Position: {own?.rank ?? '—'} of {race.horses.length}</Text>\n <Text>\n Leader: {leader ? `${leader.name} (${leader.current_tokens})` : '—'}\n </Text>\n <Text>Race elapsed: {(elapsedPct * 100).toFixed(0)}% {bar(elapsedPct, 20)}</Text>\n <Text>Time left: {timeLeft}</Text>\n <Text>\n Last heartbeat: {lastHeartbeatAgoSec === null ? '—' : `${lastHeartbeatAgoSec}s ago`}\n {' '}\n <Text color={lastHeartbeatOk ? 'green' : 'yellow'}>\n {lastHeartbeatOk ? '✓' : '⚠'}\n </Text>\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>Press Ctrl+C to crash out of the race.</Text>\n </Box>\n </Box>\n );\n}\n\nfunction elapsed(race: GetRaceResponse): number {\n const start = new Date(race.start_time).getTime();\n const end = new Date(race.end_time).getTime();\n const now = new Date(race.server_time).getTime();\n if (end <= start) return 0;\n const v = (now - start) / (end - start);\n return Math.max(0, Math.min(1, v));\n}\n\nfunction bar(pct: number, width: number): string {\n const filled = Math.round(pct * width);\n return '▓'.repeat(filled) + '░'.repeat(width - filled);\n}\n\nfunction statusColor(status: GetRaceResponse['status']): string {\n if (status === 'live') return 'green';\n if (status === 'pending') return 'yellow';\n return 'gray';\n}\n\nfunction formatDuration(seconds: number): string {\n const s = Math.max(0, Math.floor(seconds));\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const ss = s % 60;\n return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${ss.toString().padStart(2, '0')}`;\n}\n","import type { HeartbeatResponse } from '@token-derby/shared';\n\nexport type HeartbeatLoopOptions = {\n sendHeartbeat: (currentTokens: number) => Promise<HeartbeatResponse>;\n getCurrentTokens: () => number;\n intervalMs: number;\n retryDelaysMs: readonly number[];\n onSuccess: (resp: HeartbeatResponse) => void;\n onError: (err: unknown) => void;\n onFinished: () => void;\n abortSignal: AbortSignal;\n};\n\nexport function runHeartbeatLoop(opts: HeartbeatLoopOptions): void {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let retryIndex = 0;\n let stopped = false;\n\n const stop = () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n timer = null;\n };\n\n opts.abortSignal.addEventListener('abort', stop, { once: true });\n\n const schedule = (delay: number) => {\n if (stopped) return;\n timer = setTimeout(tick, delay);\n };\n\n const tick = async () => {\n if (stopped) return;\n try {\n const tokens = opts.getCurrentTokens();\n const resp = await opts.sendHeartbeat(tokens);\n retryIndex = 0;\n opts.onSuccess(resp);\n if (resp.race_status === 'finished') {\n opts.onFinished();\n stop();\n return;\n }\n schedule(opts.intervalMs);\n } catch (err) {\n opts.onError(err);\n const delay = opts.retryDelaysMs[Math.min(retryIndex, opts.retryDelaysMs.length - 1)] ?? 1_000;\n retryIndex += 1;\n schedule(delay);\n }\n };\n\n schedule(0);\n}\n","import type { GetRaceResponse } from '@token-derby/shared';\n\nexport type PollLoopOptions = {\n fetchRace: () => Promise<GetRaceResponse>;\n intervalMs: number;\n onSnapshot: (race: GetRaceResponse) => void;\n onError: (err: unknown) => void;\n abortSignal: AbortSignal;\n};\n\nexport function runPollLoop(opts: PollLoopOptions): void {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let stopped = false;\n\n const stop = () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n timer = null;\n };\n\n opts.abortSignal.addEventListener('abort', stop, { once: true });\n\n const tick = async () => {\n if (stopped) return;\n try {\n const race = await opts.fetchRace();\n if (!stopped) opts.onSnapshot(race);\n } catch (err) {\n if (!stopped) opts.onError(err);\n }\n if (!stopped) timer = setTimeout(tick, opts.intervalMs);\n };\n\n timer = setTimeout(tick, 0);\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { claudeProjectsDir } from '../paths.js';\n\nexport async function sumOutputTokens(): Promise<number> {\n const root = claudeProjectsDir();\n const files = await listJsonlFiles(root);\n let total = 0;\n for (const file of files) {\n total += await sumFile(file);\n }\n return total;\n}\n\nasync function listJsonlFiles(root: string): Promise<string[]> {\n let projects: string[];\n try {\n projects = await fs.readdir(root);\n } catch (e: any) {\n if (e?.code === 'ENOENT') return [];\n throw e;\n }\n const out: string[] = [];\n for (const project of projects) {\n const projectDir = path.join(root, project);\n let stat;\n try {\n stat = await fs.stat(projectDir);\n } catch {\n continue;\n }\n if (!stat.isDirectory()) continue;\n const entries = await fs.readdir(projectDir);\n for (const entry of entries) {\n if (entry.endsWith('.jsonl')) out.push(path.join(projectDir, entry));\n }\n }\n return out;\n}\n\nasync function sumFile(file: string): Promise<number> {\n let raw: string;\n try {\n raw = await fs.readFile(file, 'utf8');\n } catch {\n return 0;\n }\n let sum = 0;\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n let parsed: any;\n try {\n parsed = JSON.parse(line);\n } catch {\n continue;\n }\n const tokens = parsed?.message?.usage?.output_tokens;\n if (typeof tokens === 'number' && Number.isFinite(tokens)) sum += tokens;\n }\n return sum;\n}\n","import type { RaceStatus } from '@token-derby/shared';\n\nexport function initialBaseline(args: { runningTotal: number; status: RaceStatus }): number {\n return args.runningTotal;\n}\n\nexport function rejoinBaseline(args: { runningTotal: number; lastRaceTokens: number }): number {\n return Math.max(0, args.runningTotal - args.lastRaceTokens);\n}\n\nexport function currentRaceTokens(runningTotal: number, baseline: number): number {\n return Math.max(0, runningTotal - baseline);\n}\n","import React from 'react';\nimport { render } from 'ink';\nimport { loadActiveRace } from '../stable/active-race.js';\nimport { getRace } from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\nimport { RunRace, buildInitialState } from '../runtime/run-race.js';\n\nexport async function rejoinCommand(joinCode: string | undefined): Promise<number> {\n if (!joinCode) {\n console.error('Usage: token-derby rejoin <join-code>');\n return 2;\n }\n const code = joinCode.toUpperCase();\n\n const active = await loadActiveRace(code);\n if (!active) {\n console.error(`No saved active-race state for ${code}. Use \\`token-derby join ${code}\\` to enter as a new horse.`);\n return 1;\n }\n\n let race;\n try {\n race = await getRace(code);\n } catch (e) {\n if (e instanceof ApiError) {\n console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n }\n if (race.status === 'finished') {\n console.error('Race already finished.');\n return 1;\n }\n const status: 'pending' | 'live' = race.status;\n\n const initial = await buildInitialState({ active, raceStatus: status, rejoin: true });\n const app = render(React.createElement(RunRace, { active, ...initial }));\n await app.waitUntilExit();\n return 0;\n}\n","import * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport { endRace } from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\n\nexport async function endCommand(adminCode: string | undefined): Promise<number> {\n if (!adminCode) {\n console.error('Usage: token-derby end <admin-code>');\n return 2;\n }\n const rl = readline.createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question('End the race now and freeze final tokens? [y/N] ')).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n console.log('Cancelled.');\n return 1;\n }\n try {\n await endRace(adminCode);\n console.log('✓ Race ended.');\n return 0;\n } catch (e) {\n if (e instanceof ApiError) {\n if (e.code === 'RACE_NOT_FOUND') console.error('No race with that admin code.');\n else console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n }\n}\n","import { stableCreateCommand } from './commands/stable-create.js';\nimport { stableListCommand } from './commands/stable-list.js';\nimport { stableDeleteCommand } from './commands/stable-delete.js';\nimport { createRaceCommand } from './commands/create.js';\nimport { joinCommand } from './commands/join.js';\nimport { rejoinCommand } from './commands/rejoin.js';\nimport { endCommand } from './commands/end.js';\n\nconst VERSION = '0.1.0';\n\nconst HELP = `token-derby v${VERSION}\n\nStable management:\n token-derby stable create Make a new horse (interactive)\n token-derby stable list Show your saved horses\n token-derby stable delete <name> Remove a horse from your stable\n\nRaces:\n token-derby create Create a new race (interactive)\n token-derby join <join-code> Pick a horse and join a race\n token-derby rejoin <join-code> Resume a race after a disconnect\n token-derby end <admin-code> End a race early\n\nEnvironment:\n TOKEN_DERBY_API_BASE Override API base URL (default: production)\n`;\n\nasync function main(): Promise<number> {\n const argv = process.argv.slice(2);\n const cmd = argv[0];\n\n if (!cmd || cmd === '--help' || cmd === '-h') { console.log(HELP); return 0; }\n if (cmd === '--version' || cmd === '-v') { console.log(VERSION); return 0; }\n\n if (cmd === 'stable') {\n const sub = argv[1];\n if (sub === 'create') return stableCreateCommand();\n if (sub === 'list') return stableListCommand();\n if (sub === 'delete') return stableDeleteCommand(argv[2]);\n console.error(`Unknown stable subcommand: ${sub ?? '(none)'}`);\n console.error('Try: stable create | stable list | stable delete <name>');\n return 2;\n }\n\n if (cmd === 'create') return createRaceCommand();\n if (cmd === 'join') return joinCommand(argv[1]);\n if (cmd === 'rejoin') return rejoinCommand(argv[1]);\n if (cmd === 'end') return endCommand(argv[1]);\n\n console.error(`Unknown command: ${cmd}`);\n console.error(HELP);\n return 2;\n}\n\nmain().then(\n code => process.exit(code),\n err => {\n console.error(err?.stack ?? err);\n process.exit(1);\n },\n);\n"],"mappings":"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","names":["React","Box","Text","jsx","Box","Text","React","React","render","Box","Text","jsx","jsxs","render","React","Box","Text","readline","stdin","stdout","fs","path","stdin","stdout","readline","stdin","stdout","path","stdin","stdout","React","render","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","useState","Box","Text","jsx","jsxs","Box","Text","fs","path","stat","jsx","useState","render","React","React","render","render","React","readline","stdin","stdout","stdin","stdout"]}
1
+ {"version":3,"sources":["../src/commands/stable-create.ts","../src/ui/HorseCreator.tsx","../src/ui/HorseSprite.tsx","../src/ui/sprite.ts","../src/ui/sprite-render.ts","../src/ui/palette.ts","../src/stable/stable.ts","../src/paths.ts","../src/commands/stable-list.tsx","../src/commands/stable-delete.ts","../src/stable/active-race.ts","../src/commands/stable-edit.ts","../src/commands/create.ts","../src/config.ts","../src/version.ts","../../shared/src/constants.ts","../src/identity/identity.ts","../src/api/client.ts","../src/api/endpoints.ts","../src/commands/join.ts","../src/ui/HorsePicker.tsx","../src/runtime/run-race.tsx","../src/ui/StatusScreen.tsx","../src/runtime/heartbeat-loop.ts","../src/runtime/poll-loop.ts","../src/tokens/transcripts.ts","../src/tokens/baseline.ts","../src/commands/end.ts","../src/commands/init.ts","../src/bin.ts"],"sourcesContent":["import React from 'react';\nimport { render } from 'ink';\nimport { HorseCreator } from '../ui/HorseCreator.js';\nimport { upsertHorse, loadStable, findHorse, newStableHorseId } from '../stable/stable.js';\nimport * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\n\nexport async function stableCreateCommand(): Promise<number> {\n let exitCode = 0;\n const app = render(\n React.createElement(HorseCreator, {\n onSubmit: async (name, colors) => {\n const stable = await loadStable();\n const existing = findHorse(stable, name);\n if (existing) {\n app.unmount();\n const rl = readline.createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(`Horse \"${name}\" already exists. Overwrite? [y/N] `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n console.log('Cancelled.');\n exitCode = 1;\n return;\n }\n }\n const stable_horse_id = existing?.stable_horse_id ?? newStableHorseId();\n await upsertHorse({ stable_horse_id, name, colors, created_at: new Date().toISOString() });\n app.unmount();\n console.log(`✓ Saved \"${name}\" to your stable.`);\n },\n onCancel: () => {\n app.unmount();\n console.log('Cancelled.');\n exitCode = 1;\n },\n }),\n );\n await app.waitUntilExit();\n return exitCode;\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport TextInput from 'ink-text-input';\nimport type { HorseColors } from '@token-derby/shared';\nimport { HorseSprite } from './HorseSprite.js';\nimport { MAIN_SPRITE } from './sprite.js';\nimport { SLOTS, PALETTES, nextColor, prevColor, defaultColors, type Slot } from './palette.js';\n\ntype Props = {\n onSubmit: (name: string, colors: HorseColors) => void;\n onCancel: () => void;\n initialColors?: HorseColors;\n initialName?: string;\n lockName?: boolean;\n};\n\nexport function HorseCreator({ onSubmit, onCancel, initialColors, initialName, lockName }: Props) {\n const [colors, setColors] = useState<HorseColors>(initialColors ?? defaultColors());\n const [slotIdx, setSlotIdx] = useState(0);\n const [namingMode, setNamingMode] = useState(false);\n const [name, setName] = useState(initialName ?? '');\n const [error, setError] = useState<string | null>(null);\n\n const slot: Slot = SLOTS[slotIdx]!;\n\n useInput((input, key) => {\n if (namingMode) return;\n if (key.escape) { onCancel(); return; }\n if (key.upArrow) { setSlotIdx((slotIdx - 1 + SLOTS.length) % SLOTS.length); return; }\n if (key.downArrow) { setSlotIdx((slotIdx + 1) % SLOTS.length); return; }\n if (key.leftArrow) { setColors({ ...colors, [slot]: prevColor(slot, colors[slot]) }); return; }\n if (key.rightArrow) { setColors({ ...colors, [slot]: nextColor(slot, colors[slot]) }); return; }\n if (key.return) {\n if (lockName) { onSubmit(initialName ?? '', colors); return; }\n setNamingMode(true);\n return;\n }\n });\n\n const handleNameSubmit = (value: string) => {\n if (!value.trim()) {\n setError('Name required');\n return;\n }\n onSubmit(value.trim(), colors);\n };\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={1}>\n <HorseSprite sprite={MAIN_SPRITE} colors={colors} />\n </Box>\n\n <Box flexDirection=\"column\">\n {SLOTS.map((s, i) => (\n <Text key={s}>\n {i === slotIdx ? '►' : ' '} {s.padEnd(7)} <Text color={colors[s]}>██</Text> {colors[s]}\n </Text>\n ))}\n </Box>\n\n {!namingMode && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text dimColor>↑/↓ select slot · ←/→ cycle color · Enter accept · Esc cancel</Text>\n </Box>\n )}\n\n {namingMode && (\n <Box marginTop={1} flexDirection=\"column\">\n <Text>Name your horse: </Text>\n <TextInput value={name} onChange={(v) => { setName(v); setError(null); }} onSubmit={handleNameSubmit} />\n {error && <Text color=\"red\">{error}</Text>}\n </Box>\n )}\n </Box>\n );\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { HorseColors } from '@token-derby/shared';\nimport { renderSprite, type Cell } from './sprite-render.js';\nimport type { SlotTag } from './sprite.js';\n\ntype Props = {\n sprite: readonly (readonly SlotTag[])[];\n colors: HorseColors;\n};\n\nexport function HorseSprite({ sprite, colors }: Props) {\n const grid = renderSprite(sprite, colors);\n return (\n <Box flexDirection=\"column\">\n {grid.map((row, y) => (\n <Text key={y}>{rowToAnsi(row)}</Text>\n ))}\n </Box>\n );\n}\n\nfunction rowToAnsi(row: Cell[]): string {\n let out = '';\n for (const cell of row) {\n if (cell.top === null && cell.bottom === null) {\n out += ' ';\n } else if (cell.top !== null && cell.bottom !== null) {\n out += ansiFg(cell.top) + ansiBg(cell.bottom) + '▀' + RESET;\n } else if (cell.top !== null) {\n out += ansiFg(cell.top) + '▀' + RESET;\n } else {\n out += ansiFg(cell.bottom!) + '▄' + RESET;\n }\n }\n return out;\n}\n\nconst RESET = '\\x1b[0m';\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const h = hex.replace('#', '');\n return [\n parseInt(h.slice(0, 2), 16),\n parseInt(h.slice(2, 4), 16),\n parseInt(h.slice(4, 6), 16),\n ];\n}\n\nfunction ansiFg(hex: string): string {\n const [r, g, b] = hexToRgb(hex);\n return `\\x1b[38;2;${r};${g};${b}m`;\n}\n\nfunction ansiBg(hex: string): string {\n const [r, g, b] = hexToRgb(hex);\n return `\\x1b[48;2;${r};${g};${b}m`;\n}\n","// Pixel slot tags. `null` = transparent.\n// B = body, M = mane, T = tail, S = saddle, E = eye (fixed black), H = hoof (fixed dark)\nexport type SlotTag = 'B' | 'M' | 'T' | 'S' | 'E' | 'H' | null;\n\nexport const FIXED_COLORS = {\n E: '#000000',\n H: '#1F1108',\n} as const;\n\n// Each pair of adjacent rows is intentionally identical so every half-block\n// cell renders as a solid color. The only intentional split is the last row\n// (legs/hooves transition).\nconst MAIN_ROWS: readonly string[] = [\n '................................',\n '................................',\n '..........................MMM...',\n '..........................MMM...',\n '.........................MBBEBB.',\n '.........................MBBEBB.',\n '........................MBBBBBBB',\n '........................MBBBBBBB',\n '..................MMMMMMMBBB....',\n '..................MMMMMMMBBB....',\n '....BBBBBBBBSSSSSSMMBBBBBB......',\n '...BBBBBBBBBSSSSSSMMBBBBBB......',\n '.TTBBBBBBBBBSSSSSSBBBBBBBB......',\n '.TTBBBBBBBBBSSSSSSBBBBBBBB......',\n 'TTTBBBBBBBBBBBBBBBBBBBBBBB......',\n 'TTTBBBBBBBBBBBBBBBBBBBBB........',\n '...BBB.BBB.....BBB.BBB..........',\n '...BBB.BBB.....BBB.BBB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '....BB..BB......BB..BB..........',\n '...HHH.HHH.....HHH.HHH..........',\n];\n\nconst MINI_ROWS: readonly string[] = [\n '.BBSSMBB',\n '.BBSSMBB',\n 'TBBBBBB.',\n 'THH..HH.',\n];\n\nexport const MAIN_SPRITE: readonly (readonly SlotTag[])[] = parse(MAIN_ROWS, 32, 24);\nexport const MINI_SPRITE: readonly (readonly SlotTag[])[] = parse(MINI_ROWS, 8, 4);\n\nfunction parse(rows: readonly string[], width: number, height: number): SlotTag[][] {\n if (rows.length !== height) {\n throw new Error(`sprite has ${rows.length} rows, expected ${height}`);\n }\n return rows.map((row, y) => {\n if (row.length !== width) {\n throw new Error(`sprite row ${y} has length ${row.length}, expected ${width}`);\n }\n return [...row].map(c => toTag(c));\n });\n}\n\nfunction toTag(c: string): SlotTag {\n switch (c) {\n case 'B': return 'B';\n case 'M': return 'M';\n case 'T': return 'T';\n case 'S': return 'S';\n case 'E': return 'E';\n case 'H': return 'H';\n case '.': return null;\n default: throw new Error(`unknown sprite char: ${c}`);\n }\n}\n","import type { HorseColors } from '@token-derby/shared';\nimport { FIXED_COLORS, type SlotTag } from './sprite.js';\n\nexport type Cell = {\n top: string | null;\n bottom: string | null;\n};\n\nexport function renderSprite(\n sprite: readonly (readonly SlotTag[])[],\n colors: HorseColors,\n): Cell[][] {\n const out: Cell[][] = [];\n for (let y = 0; y + 1 < sprite.length || y < sprite.length; y += 2) {\n const topRow = sprite[y];\n const bottomRow = sprite[y + 1];\n if (!topRow) break;\n const row: Cell[] = [];\n for (let x = 0; x < topRow.length; x++) {\n row.push({\n top: tagColor(topRow[x] ?? null, colors),\n bottom: tagColor(bottomRow?.[x] ?? null, colors),\n });\n }\n out.push(row);\n if (!bottomRow) break;\n }\n return out;\n}\n\nfunction tagColor(tag: SlotTag, colors: HorseColors): string | null {\n if (tag === null) return null;\n if (tag === 'E') return FIXED_COLORS.E;\n if (tag === 'H') return FIXED_COLORS.H;\n if (tag === 'B') return colors.body;\n if (tag === 'M') return colors.mane;\n if (tag === 'T') return colors.tail;\n if (tag === 'S') return colors.saddle;\n return null;\n}\n","import type { HorseColors } from '@token-derby/shared';\n\nexport type Slot = keyof HorseColors;\n\nexport const SLOTS: readonly Slot[] = ['body', 'mane', 'tail', 'saddle'] as const;\n\nexport const PALETTES: Record<Slot, readonly string[]> = {\n body: [\n '#8B4513', '#A0522D', '#D2691E', '#CD853F', '#DEB887', '#F5DEB3',\n '#FFFFFF', '#000000', '#4A2C2A', '#5D3A1A', '#704214', '#9C5919',\n '#B87333', '#E5B783', '#F0E1C9', '#2F1B0C',\n ],\n mane: [\n '#000000', '#1C1C1C', '#2F1B0C', '#4A2C2A', '#5D3A1A', '#8B4513',\n '#FFFFFF', '#F5F5DC', '#DEB887', '#CD853F', '#FF4500', '#B22222',\n '#191970', '#4B0082', '#2E8B57', '#FFD700',\n ],\n tail: [\n '#000000', '#1C1C1C', '#2F1B0C', '#4A2C2A', '#5D3A1A', '#8B4513',\n '#FFFFFF', '#F5F5DC', '#DEB887', '#CD853F', '#FF4500', '#B22222',\n '#191970', '#4B0082', '#2E8B57', '#FFD700',\n ],\n saddle: [\n '#C0392B', '#922B21', '#7B241C', '#641E16', '#1F618D', '#21618C',\n '#1B4F72', '#0E6655', '#117A65', '#196F3D', '#7D6608', '#9A7D0A',\n '#6E2C00', '#4D5656', '#212F3D', '#000000',\n ],\n};\n\nexport function nextColor(slot: Slot, current: string): string {\n const palette = PALETTES[slot];\n const idx = palette.indexOf(current);\n return palette[(idx + 1 + palette.length) % palette.length] ?? palette[0]!;\n}\n\nexport function prevColor(slot: Slot, current: string): string {\n const palette = PALETTES[slot];\n const idx = palette.indexOf(current);\n if (idx < 0) return palette[0]!;\n return palette[(idx - 1 + palette.length) % palette.length]!;\n}\n\nexport function defaultColors(): HorseColors {\n return {\n body: PALETTES.body[0]!,\n mane: PALETTES.mane[0]!,\n tail: PALETTES.tail[0]!,\n saddle: PALETTES.saddle[0]!,\n };\n}\n","import * as fs from 'node:fs/promises';\nimport { randomUUID } from 'node:crypto';\nimport type { HorseColors } from '@token-derby/shared';\nimport { homeDir, stableFile } from '../paths.js';\n\nexport type StableHorse = {\n stable_horse_id: string;\n name: string;\n colors: HorseColors;\n created_at: string;\n};\n\nexport type Stable = {\n horses: StableHorse[];\n};\n\nexport function newStableHorseId(): string {\n return randomUUID();\n}\n\nexport async function loadStable(): Promise<Stable> {\n try {\n const raw = await fs.readFile(stableFile(), 'utf8');\n const parsed = JSON.parse(raw);\n if (!parsed || !Array.isArray(parsed.horses)) return { horses: [] };\n const stable = parsed as { horses: Array<Partial<StableHorse> & { name: string; colors: HorseColors; created_at: string }> };\n let mutated = false;\n const horses: StableHorse[] = stable.horses.map(h => {\n if (typeof h.stable_horse_id === 'string' && h.stable_horse_id.length > 0) {\n return h as StableHorse;\n }\n mutated = true;\n return { ...h, stable_horse_id: newStableHorseId() } as StableHorse;\n });\n const result: Stable = { horses };\n if (mutated) await saveStable(result);\n return result;\n } catch (e: any) {\n if (e?.code === 'ENOENT') return { horses: [] };\n if (e instanceof SyntaxError) return { horses: [] };\n throw e;\n }\n}\n\nexport async function saveStable(stable: Stable): Promise<void> {\n await fs.mkdir(homeDir(), { recursive: true });\n await fs.writeFile(stableFile(), JSON.stringify(stable, null, 2) + '\\n', 'utf8');\n}\n\nexport async function upsertHorse(horse: StableHorse): Promise<void> {\n const stable = await loadStable();\n const idx = stable.horses.findIndex(h => h.name === horse.name);\n if (idx >= 0) stable.horses[idx] = horse;\n else stable.horses.push(horse);\n await saveStable(stable);\n}\n\nexport async function removeHorse(name: string): Promise<void> {\n const stable = await loadStable();\n stable.horses = stable.horses.filter(h => h.name !== name);\n await saveStable(stable);\n}\n\nexport function findHorse(stable: Stable, name: string): StableHorse | undefined {\n return stable.horses.find(h => h.name === name);\n}\n","import * as os from 'node:os';\nimport * as path from 'node:path';\n\nexport function homeDir(): string {\n return process.env.TOKEN_DERBY_HOME ?? path.join(os.homedir(), '.token-derby');\n}\n\nexport function stableFile(): string {\n return path.join(homeDir(), 'stable.json');\n}\n\nexport function identityFile(): string {\n return path.join(homeDir(), 'identity.json');\n}\n\nexport function activeRaceFile(joinCode: string): string {\n return path.join(homeDir(), 'active-races', `${joinCode}.json`);\n}\n\nexport function activeRacesDir(): string {\n return path.join(homeDir(), 'active-races');\n}\n\nexport function claudeProjectsDir(): string {\n return process.env.TOKEN_DERBY_CLAUDE_DIR ?? path.join(os.homedir(), '.claude', 'projects');\n}\n","import React from 'react';\nimport { render, Box, Text } from 'ink';\nimport { loadStable } from '../stable/stable.js';\nimport { HorseSprite } from '../ui/HorseSprite.js';\nimport { MINI_SPRITE } from '../ui/sprite.js';\n\nexport async function stableListCommand(): Promise<number> {\n const stable = await loadStable();\n if (stable.horses.length === 0) {\n console.log('Your stable is empty. Run `token-derby stable create` to add a horse.');\n return 0;\n }\n const app = render(\n React.createElement(StableList, { horses: stable.horses }),\n );\n await app.waitUntilExit();\n return 0;\n}\n\nfunction StableList({ horses }: { horses: { name: string; colors: any; created_at: string }[] }) {\n React.useEffect(() => {\n setImmediate(() => process.exit(0));\n }, []);\n return (\n <Box flexDirection=\"column\">\n <Text bold>Your stable ({horses.length}):</Text>\n {horses.map(h => (\n <Box key={h.name} flexDirection=\"row\" marginTop={1}>\n <HorseSprite sprite={MINI_SPRITE} colors={h.colors} />\n <Text> {h.name}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n","import * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport { loadStable, findHorse, removeHorse } from '../stable/stable.js';\nimport { listActiveRaces, loadActiveRace } from '../stable/active-race.js';\n\nexport async function stableDeleteCommand(name: string | undefined): Promise<number> {\n if (!name) {\n console.error('Usage: token-derby stable delete <name>');\n return 2;\n }\n const stable = await loadStable();\n const horse = findHorse(stable, name);\n if (!horse) {\n console.error(`No horse named \"${name}\" in your stable.`);\n return 1;\n }\n\n const codes = await listActiveRaces();\n for (const code of codes) {\n const active = await loadActiveRace(code);\n if (active?.horse_name === name) {\n console.error(`\"${name}\" is currently running in race ${code}. Close that terminal first.`);\n return 1;\n }\n }\n\n const rl = readline.createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question(`Delete \"${name}\" from your stable? [y/N] `)).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n console.log('Cancelled.');\n return 1;\n }\n await removeHorse(name);\n console.log(`✓ Deleted \"${name}\".`);\n return 0;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { HorseColors } from '@token-derby/shared';\nimport { activeRaceFile, activeRacesDir } from '../paths.js';\n\nexport type ActiveRace = {\n join_code: string;\n race_id: string;\n horse_id: string;\n heartbeat_token: string;\n horse_name: string;\n horse_colors: HorseColors;\n joined_at: string;\n last_race_tokens: number;\n last_heartbeat_at: string;\n};\n\nexport async function loadActiveRace(joinCode: string): Promise<ActiveRace | null> {\n try {\n const raw = await fs.readFile(activeRaceFile(joinCode), 'utf8');\n return JSON.parse(raw) as ActiveRace;\n } catch (e: any) {\n if (e?.code === 'ENOENT') return null;\n throw e;\n }\n}\n\nexport async function saveActiveRace(active: ActiveRace): Promise<void> {\n await fs.mkdir(activeRacesDir(), { recursive: true });\n await fs.writeFile(\n activeRaceFile(active.join_code),\n JSON.stringify(active, null, 2) + '\\n',\n 'utf8',\n );\n}\n\nexport async function deleteActiveRace(joinCode: string): Promise<void> {\n try {\n await fs.unlink(activeRaceFile(joinCode));\n } catch (e: any) {\n if (e?.code !== 'ENOENT') throw e;\n }\n}\n\nexport async function listActiveRaces(): Promise<string[]> {\n try {\n const entries = await fs.readdir(activeRacesDir());\n return entries\n .filter(f => f.endsWith('.json'))\n .map(f => path.basename(f, '.json'));\n } catch (e: any) {\n if (e?.code === 'ENOENT') return [];\n throw e;\n }\n}\n","import React from 'react';\nimport { render } from 'ink';\nimport { HorseCreator } from '../ui/HorseCreator.js';\nimport { upsertHorse, loadStable, findHorse } from '../stable/stable.js';\n\nexport async function stableEditCommand(name: string | undefined): Promise<number> {\n if (!name) {\n console.error('Usage: token-derby stable edit <name>');\n return 2;\n }\n const stable = await loadStable();\n const existing = findHorse(stable, name);\n if (!existing) {\n console.error(`No horse named \"${name}\" in your stable.`);\n return 1;\n }\n\n let exitCode = 0;\n const app = render(\n React.createElement(HorseCreator, {\n initialColors: existing.colors,\n initialName: existing.name,\n lockName: true,\n onSubmit: async (_name, colors) => {\n await upsertHorse({\n stable_horse_id: existing.stable_horse_id,\n name: existing.name,\n colors,\n created_at: existing.created_at,\n });\n app.unmount();\n console.log(`✓ Updated \"${existing.name}\".`);\n },\n onCancel: () => {\n app.unmount();\n console.log('Cancelled.');\n exitCode = 1;\n },\n }),\n );\n await app.waitUntilExit();\n return exitCode;\n}\n","import * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport { createRace } from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\n\nconst DEFAULT_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n\nexport async function createRaceCommand(): Promise<number> {\n const rl = readline.createInterface({ input: stdin, output: stdout });\n try {\n const name = (await rl.question('Race name: ')).trim();\n if (!name) { console.error('Name required.'); return 1; }\n\n const startRaw = (await rl.question('Start time (ISO 8601, blank = now): ')).trim();\n const start = startRaw ? startRaw : new Date().toISOString();\n if (!isIso(start)) { console.error('Invalid start time.'); return 1; }\n\n const durationRaw = (await rl.question('Race duration (hours): ')).trim();\n const durationHours = parseFloat(durationRaw);\n if (!Number.isFinite(durationHours) || durationHours <= 0) {\n console.error('Duration must be a positive number of hours.'); return 1;\n }\n const end = new Date(new Date(start).getTime() + durationHours * 3600_000).toISOString();\n\n const tz = (await rl.question(`Time zone [${DEFAULT_TZ}]: `)).trim() || DEFAULT_TZ;\n const maxRaw = (await rl.question('Max participants [30]: ')).trim();\n const max = maxRaw ? parseInt(maxRaw, 10) : undefined;\n if (max !== undefined && (!Number.isFinite(max) || max < 1)) {\n console.error('Max participants must be a positive number.'); return 1;\n }\n\n const resp = await createRace({\n name, start_time: start, end_time: end, tz,\n ...(max !== undefined ? { max_participants: max } : {}),\n });\n\n console.log('');\n console.log(' ╔══════════════════════════════════════╗');\n console.log(` ║ JOIN CODE: ${resp.join_code.padEnd(23)}║`);\n console.log(' ╚══════════════════════════════════════╝');\n console.log('');\n console.log(` Admin code: ${resp.admin_code}`);\n console.log(' ⚠ Save the admin code — you need it to end the race early.');\n console.log('');\n console.log(` Share with participants: token-derby join ${resp.join_code}`);\n return 0;\n } catch (e) {\n if (e instanceof ApiError) {\n console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n } finally {\n rl.close();\n }\n}\n\nfunction isIso(s: string): boolean {\n if (!s) return false;\n const d = new Date(s);\n return !Number.isNaN(d.getTime());\n}\n","export const DEFAULT_API_BASE = 'https://token-derby.mauricode.co.uk/api';\n\nexport function apiBase(): string {\n return process.env.TOKEN_DERBY_API_BASE ?? DEFAULT_API_BASE;\n}\n\nexport const HEARTBEAT_INTERVAL_MS = 60_000;\nexport const POLL_INTERVAL_MS = 3_000;\nexport const HEARTBEAT_RETRY_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 15_000];\n","import { createRequire } from 'node:module';\n\ndeclare const __CLI_VERSION__: string | undefined;\n\nfunction readVersion(): string {\n if (typeof __CLI_VERSION__ === 'string' && __CLI_VERSION__.length > 0) {\n return __CLI_VERSION__;\n }\n try {\n const req = createRequire(import.meta.url);\n const pkg = req('../package.json') as { version?: string };\n if (typeof pkg.version === 'string') return pkg.version;\n } catch {\n // fall through\n }\n return '0.0.0-dev';\n}\n\nexport const CLI_VERSION: string = readVersion();\n","export const DEFAULT_MAX_PARTICIPANTS = 30;\nexport const JOIN_CODE_LENGTH = 6;\nexport const JOIN_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\nexport const CLI_VERSION_HEADER = 'x-cli-version';\nexport const USER_ID_HEADER = 'x-user-id';\nexport const USER_NAME_HEADER = 'x-user-name';\nexport const USER_NAME_MAX_LENGTH = 40;\n","import { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport * as crypto from 'node:crypto';\nimport { USER_NAME_MAX_LENGTH } from '@token-derby/shared';\nimport { identityFile, homeDir } from '../paths.js';\n\nexport type Identity = {\n user_id: string;\n display_name: string;\n created_at: string;\n};\n\nexport async function loadIdentity(): Promise<Identity | null> {\n try {\n const raw = await fs.readFile(identityFile(), 'utf8');\n const parsed = JSON.parse(raw) as Partial<Identity>;\n if (\n typeof parsed.user_id === 'string' &&\n typeof parsed.display_name === 'string' &&\n typeof parsed.created_at === 'string'\n ) {\n return parsed as Identity;\n }\n return null;\n } catch (e: any) {\n if (e?.code === 'ENOENT') return null;\n return null;\n }\n}\n\nexport async function saveIdentity(identity: Identity): Promise<void> {\n await fs.mkdir(homeDir(), { recursive: true });\n await fs.writeFile(identityFile(), JSON.stringify(identity, null, 2) + '\\n', 'utf8');\n}\n\nexport function generateUserId(): string {\n return crypto.randomUUID();\n}\n\nexport function validateDisplayName(name: string): { ok: true; name: string } | { ok: false; error: string } {\n const trimmed = name.trim();\n if (trimmed.length < 1) return { ok: false, error: 'Name cannot be empty.' };\n if (trimmed.length > USER_NAME_MAX_LENGTH) {\n return { ok: false, error: `Name must be ${USER_NAME_MAX_LENGTH} characters or fewer.` };\n }\n return { ok: true, name: trimmed };\n}\n\nexport function identityFilePath(): string {\n return identityFile();\n}\n\nexport function identityFileDir(): string {\n return path.dirname(identityFile());\n}\n","import { apiBase } from '../config.js';\nimport { CLI_VERSION } from '../version.js';\nimport { CLI_VERSION_HEADER, USER_ID_HEADER, USER_NAME_HEADER } from '@token-derby/shared';\nimport { loadIdentity, type Identity } from '../identity/identity.js';\n\nexport type ApiErrorCode =\n | 'RACE_NOT_FOUND'\n | 'RACE_FULL'\n | 'RACE_FINISHED'\n | 'INVALID_TOKEN'\n | 'RATE_LIMITED'\n | 'BAD_REQUEST'\n | 'VERSION_MISMATCH'\n | 'IDENTITY_REQUIRED'\n | 'DUPLICATE_HORSE'\n | 'NETWORK_ERROR';\n\nexport class ApiError extends Error {\n constructor(\n readonly code: ApiErrorCode,\n message: string,\n readonly status: number,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\ntype FetchFn = typeof fetch;\n\nlet identityCache: Promise<Identity | null> | null = null;\nfunction getIdentity(): Promise<Identity | null> {\n if (!identityCache) identityCache = loadIdentity();\n return identityCache;\n}\n\n// Tests can reset the cached identity.\nexport function _resetIdentityCacheForTests(): void {\n identityCache = null;\n}\n\nexport async function request<T>(\n method: string,\n path: string,\n body: unknown,\n authToken: string | undefined,\n fetchImpl: FetchFn = fetch,\n): Promise<T> {\n const url = path.startsWith('http') ? path : `${apiBase()}${path}`;\n const headers: Record<string, string> = {};\n headers[CLI_VERSION_HEADER] = CLI_VERSION;\n const identity = await getIdentity();\n if (identity) {\n headers[USER_ID_HEADER] = identity.user_id;\n headers[USER_NAME_HEADER] = identity.display_name;\n }\n if (authToken) headers['authorization'] = `Bearer ${authToken}`;\n if (body !== undefined) headers['content-type'] = 'application/json';\n\n let res: Awaited<ReturnType<FetchFn>>;\n try {\n res = await fetchImpl(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n } catch (e: any) {\n throw new ApiError('NETWORK_ERROR', e?.message ?? 'fetch failed', 0);\n }\n\n const text = await res.text();\n const contentType = res.headers.get('content-type') ?? '';\n let parsed: any = null;\n if (contentType.includes('application/json') && text.length > 0) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = null;\n }\n }\n\n if (!res.ok) {\n if (parsed && typeof parsed.code === 'string') {\n throw new ApiError(parsed.code as ApiErrorCode, parsed.message ?? 'API error', res.status);\n }\n throw new ApiError('NETWORK_ERROR', `HTTP ${res.status}`, res.status);\n }\n\n return parsed as T;\n}\n","import type {\n CreateRaceRequest, CreateRaceResponse,\n GetRaceResponse, JoinRaceRequest, JoinRaceResponse,\n HeartbeatRequest, HeartbeatResponse, EndRaceResponse,\n} from '@token-derby/shared';\nimport { request } from './client.js';\n\nexport function createRace(body: CreateRaceRequest) {\n return request<CreateRaceResponse>('POST', '/races', body, undefined);\n}\n\nexport function getRace(joinCode: string) {\n return request<GetRaceResponse>('GET', `/races/${encodeURIComponent(joinCode)}`, undefined, undefined);\n}\n\nexport function joinRace(joinCode: string, body: JoinRaceRequest) {\n return request<JoinRaceResponse>('POST', `/races/${encodeURIComponent(joinCode)}/join`, body, undefined);\n}\n\nexport function heartbeat(joinCode: string, horseId: string, token: string, body: HeartbeatRequest) {\n return request<HeartbeatResponse>(\n 'POST',\n `/races/${encodeURIComponent(joinCode)}/horses/${encodeURIComponent(horseId)}/heartbeat`,\n body,\n token,\n );\n}\n\nexport function endRace(adminCode: string) {\n return request<EndRaceResponse>('DELETE', `/races/admin/${encodeURIComponent(adminCode)}`, undefined, undefined);\n}\n","import React from 'react';\nimport { render } from 'ink';\nimport type { HorseColors, HorseView } from '@token-derby/shared';\nimport { loadStable } from '../stable/stable.js';\nimport { HorsePicker } from '../ui/HorsePicker.js';\nimport { joinRace, getRace } from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\nimport { saveActiveRace, loadActiveRace, type ActiveRace } from '../stable/active-race.js';\nimport { RunRace, buildInitialState } from '../runtime/run-race.js';\nimport { loadIdentity } from '../identity/identity.js';\nimport type { StableHorse } from '../stable/stable.js';\n\nexport async function joinCommand(joinCode: string | undefined): Promise<number> {\n if (!joinCode) {\n console.error('Usage: token-derby join <join-code>');\n return 2;\n }\n const code = joinCode.toUpperCase();\n\n const identity = await loadIdentity();\n if (!identity) {\n // Defensive — bin.ts already checks. Kept so this command is self-contained.\n console.error('Run `token-derby init` to set up your identity.');\n return 1;\n }\n\n // Pre-flight: fetch the race view to detect whether this user is already in it.\n let race;\n try {\n race = await getRace(code);\n } catch (e) {\n if (e instanceof ApiError) {\n if (e.code === 'RACE_NOT_FOUND') console.error(`No race with join code ${code}.`);\n else console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n }\n if (race.status === 'finished') {\n console.error('This race has already ended.');\n return 1;\n }\n\n const ownHorse = race.horses.find(h => h.user_id === identity.user_id) ?? null;\n\n let chosenStableHorseId: string;\n let chosenName: string;\n let chosenColors: HorseColors;\n let isResume: boolean;\n\n if (ownHorse) {\n // Auto-resume: use server's snapshot of the horse, no picker.\n chosenStableHorseId = ownHorse.stable_horse_id;\n chosenName = ownHorse.name;\n chosenColors = ownHorse.colors;\n isResume = true;\n } else {\n const stable = await loadStable();\n if (stable.horses.length === 0) {\n console.error('Your stable is empty. Run `token-derby stable create` first.');\n return 1;\n }\n const picked = await pickHorse(stable.horses);\n if (!picked) { console.log('Cancelled.'); return 1; }\n chosenStableHorseId = picked.stable_horse_id;\n chosenName = picked.name;\n chosenColors = picked.colors;\n isResume = false;\n }\n\n let joinResp;\n try {\n joinResp = await joinRace(code, {\n horse: { stable_horse_id: chosenStableHorseId, name: chosenName, colors: chosenColors },\n });\n } catch (e) {\n if (e instanceof ApiError) {\n if (e.code === 'RACE_FULL') console.error('This race is full.');\n else if (e.code === 'RACE_FINISHED') console.error('This race has ended.');\n else if (e.code === 'RACE_NOT_FOUND') console.error(`No race with join code ${code}.`);\n else if (e.code === 'VERSION_MISMATCH') console.error(e.message);\n else if (e.code === 'DUPLICATE_HORSE') console.error(e.message);\n else if (e.code === 'IDENTITY_REQUIRED') console.error(`Error: ${e.message}`);\n else console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n }\n\n // For resume, try to carry forward our last token total from active-races state.\n const prior = await loadActiveRace(code);\n const lastTokens = isResume && prior?.horse_id === joinResp.horse_id ? prior.last_race_tokens : (ownHorse?.current_tokens ?? 0);\n\n const status: 'pending' | 'live' = race.status;\n const active: ActiveRace = {\n join_code: code,\n race_id: race.race_id,\n horse_id: joinResp.horse_id,\n heartbeat_token: joinResp.heartbeat_token,\n horse_name: chosenName,\n horse_colors: chosenColors,\n joined_at: ownHorse?.joined_at ?? new Date().toISOString(),\n last_race_tokens: lastTokens,\n last_heartbeat_at: new Date(0).toISOString(),\n };\n await saveActiveRace(active);\n\n const initial = await buildInitialState({ active, raceStatus: status, rejoin: isResume });\n const app = render(React.createElement(RunRace, { active, ...initial, ownUserName: identity.display_name }));\n await app.waitUntilExit();\n return 0;\n}\n\nasync function pickHorse(horses: StableHorse[]): Promise<StableHorse | null> {\n return new Promise(resolve => {\n const app = render(\n React.createElement(HorsePicker, {\n horses,\n onPick: (h: StableHorse) => { app.unmount(); resolve(h); },\n onCancel: () => { app.unmount(); resolve(null); },\n }),\n );\n });\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { HorseSprite } from './HorseSprite.js';\nimport { MINI_SPRITE } from './sprite.js';\nimport type { StableHorse } from '../stable/stable.js';\n\ntype Props = {\n horses: StableHorse[];\n onPick: (horse: StableHorse) => void;\n onCancel: () => void;\n};\n\nexport function HorsePicker({ horses, onPick, onCancel }: Props) {\n const [idx, setIdx] = useState(0);\n\n useInput((input, key) => {\n if (key.escape) { onCancel(); return; }\n if (horses.length === 0) return;\n if (key.upArrow) { setIdx((idx - 1 + horses.length) % horses.length); return; }\n if (key.downArrow) { setIdx((idx + 1) % horses.length); return; }\n if (key.return) { onPick(horses[idx]!); return; }\n });\n\n if (horses.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text>No horses in your stable.</Text>\n <Text dimColor>Run `token-derby stable create` to make one.</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n <Text>Pick a horse to race:</Text>\n {horses.map((h, i) => (\n <Box key={h.name} flexDirection=\"column\">\n <Box flexDirection=\"row\">\n <Text>{i === idx ? '►' : ' '} {h.name}</Text>\n </Box>\n <Box flexDirection=\"row\">\n <Text> </Text>\n <HorseSprite sprite={MINI_SPRITE} colors={h.colors} />\n </Box>\n </Box>\n ))}\n <Box marginTop={1}>\n <Text dimColor>↑/↓ choose · Enter pick · Esc cancel</Text>\n </Box>\n </Box>\n );\n}\n","import React, { useEffect, useRef, useState } from 'react';\nimport { Box, Text, useApp } from 'ink';\nimport type { GetRaceResponse, HeartbeatResponse } from '@token-derby/shared';\nimport { StatusScreen } from '../ui/StatusScreen.js';\nimport { runHeartbeatLoop } from './heartbeat-loop.js';\nimport { runPollLoop } from './poll-loop.js';\nimport { sumOutputTokens } from '../tokens/transcripts.js';\nimport { initialBaseline } from '../tokens/baseline.js';\nimport * as endpoints from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\nimport { saveActiveRace, type ActiveRace } from '../stable/active-race.js';\nimport { HEARTBEAT_INTERVAL_MS, POLL_INTERVAL_MS, HEARTBEAT_RETRY_DELAYS_MS } from '../config.js';\n\nexport type RunRaceProps = {\n active: ActiveRace;\n startingBaseline: number;\n pendingMode: boolean;\n ownUserName: string;\n};\n\nexport function RunRace({ active, startingBaseline, pendingMode, ownUserName }: RunRaceProps) {\n const { exit } = useApp();\n const [race, setRace] = useState<GetRaceResponse | null>(null);\n const [lastHbAt, setLastHbAt] = useState<Date | null>(null);\n const [lastHbOk, setLastHbOk] = useState<boolean>(true);\n const [tickNow, setTickNow] = useState<Date>(new Date());\n const [fatalError, setFatalError] = useState<string | null>(null);\n\n const baselineRef = useRef(startingBaseline);\n const pendingRef = useRef(pendingMode);\n const lastTokenSampleRef = useRef<number>(startingBaseline);\n const ctrl = useRef(new AbortController());\n\n // Re-render every second so the \"Ns ago\" counter updates.\n useEffect(() => {\n const t = setInterval(() => setTickNow(new Date()), 1_000);\n return () => clearInterval(t);\n }, []);\n\n // Re-snapshot baseline when race transitions pending → live.\n useEffect(() => {\n if (pendingRef.current && race?.status === 'live') {\n sumOutputTokens().then(total => {\n baselineRef.current = total;\n pendingRef.current = false;\n });\n }\n }, [race?.status]);\n\n useEffect(() => {\n runPollLoop({\n fetchRace: () => endpoints.getRace(active.join_code),\n intervalMs: POLL_INTERVAL_MS,\n onSnapshot: (r) => setRace(r),\n onError: () => {/* silently keep last-known state */},\n abortSignal: ctrl.current.signal,\n });\n\n runHeartbeatLoop({\n sendHeartbeat: async (currentTokens) => {\n const resp = await endpoints.heartbeat(\n active.join_code, active.horse_id, active.heartbeat_token, { current_tokens: currentTokens },\n );\n const updated: ActiveRace = {\n ...active,\n last_race_tokens: currentTokens,\n last_heartbeat_at: new Date().toISOString(),\n };\n await saveActiveRace(updated);\n return resp;\n },\n getCurrentTokens: () => {\n if (pendingRef.current) return 0;\n return Math.max(0, lastTokenSampleRef.current - baselineRef.current);\n },\n intervalMs: HEARTBEAT_INTERVAL_MS,\n retryDelaysMs: HEARTBEAT_RETRY_DELAYS_MS,\n onSuccess: (resp: HeartbeatResponse) => {\n setLastHbAt(new Date());\n setLastHbOk(true);\n if (resp.race_status === 'finished') exit();\n },\n onError: (err) => {\n if (err instanceof ApiError && err.code === 'VERSION_MISMATCH') {\n setFatalError(err.message);\n ctrl.current.abort();\n exit();\n return;\n }\n setLastHbOk(false);\n },\n onFinished: () => exit(),\n abortSignal: ctrl.current.signal,\n });\n\n // Token sampler — refresh the running token total every 5s so the heartbeat sees fresh data.\n const sampler = setInterval(async () => {\n try {\n lastTokenSampleRef.current = await sumOutputTokens();\n } catch {/* keep last sample */}\n }, 5_000);\n // Prime it once at startup.\n sumOutputTokens().then(t => { lastTokenSampleRef.current = t; }).catch(() => {});\n\n const controller = ctrl.current;\n return () => {\n clearInterval(sampler);\n controller.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const lastHeartbeatAgoSec = lastHbAt\n ? Math.max(0, Math.floor((tickNow.getTime() - lastHbAt.getTime()) / 1000))\n : null;\n\n if (fatalError) {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text color=\"red\" bold>CLI version mismatch — disconnected</Text>\n <Text>{fatalError}</Text>\n </Box>\n );\n }\n\n return (\n <StatusScreen\n race={race}\n ownHorseId={active.horse_id}\n ownHorseName={active.horse_name}\n ownColors={active.horse_colors}\n ownUserName={ownUserName}\n lastHeartbeatAgoSec={lastHeartbeatAgoSec}\n lastHeartbeatOk={lastHbOk}\n />\n );\n}\n\nexport async function buildInitialState(args: {\n active: ActiveRace;\n raceStatus: 'pending' | 'live';\n rejoin: boolean;\n}): Promise<{ startingBaseline: number; pendingMode: boolean }> {\n const runningTotal = await sumOutputTokens();\n if (args.rejoin) {\n return {\n startingBaseline: Math.max(0, runningTotal - args.active.last_race_tokens),\n pendingMode: args.raceStatus === 'pending',\n };\n }\n return {\n startingBaseline: initialBaseline({ runningTotal, status: args.raceStatus }),\n pendingMode: args.raceStatus === 'pending',\n };\n}\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { GetRaceResponse, HorseColors, HorseView } from '@token-derby/shared';\nimport { HorseSprite } from './HorseSprite.js';\nimport { MINI_SPRITE } from './sprite.js';\n\ntype Props = {\n race: GetRaceResponse | null;\n ownHorseId: string;\n ownHorseName: string;\n ownColors: HorseColors;\n ownUserName: string;\n lastHeartbeatAgoSec: number | null;\n lastHeartbeatOk: boolean;\n};\n\nexport function StatusScreen(props: Props) {\n const { race, ownHorseId, ownHorseName, ownColors, ownUserName, lastHeartbeatAgoSec, lastHeartbeatOk } = props;\n\n if (!race) {\n return (\n <Box flexDirection=\"column\">\n <Text>Joining race…</Text>\n </Box>\n );\n }\n\n const own: HorseView | undefined = race.horses.find(h => h.horse_id === ownHorseId);\n const leader: HorseView | undefined = race.horses[0];\n const elapsedPct = elapsed(race);\n const timeLeft = formatDuration(race.time_left_seconds);\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" paddingX={1}>\n <Text>\n 🏇 TOKEN DERBY ─── <Text bold>{race.name}</Text> ─── status: <Text color={statusColor(race.status)}>{race.status}</Text>\n </Text>\n\n <Box marginTop={1} flexDirection=\"row\">\n <HorseSprite sprite={MINI_SPRITE} colors={ownColors} />\n <Box flexDirection=\"column\">\n <Text> {ownHorseName}</Text>\n <Text> <Text dimColor>({ownUserName})</Text></Text>\n </Box>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text>Tokens (race): {own?.current_tokens ?? 0}</Text>\n <Text>Position: {own?.rank ?? '—'} of {race.horses.length}</Text>\n <Text>\n Leader: {leader ? `${leader.name}${leader.user_name ? ` (${leader.user_name})` : ''} — ${leader.current_tokens}` : '—'}\n </Text>\n <Text>Race elapsed: {(elapsedPct * 100).toFixed(0)}% {bar(elapsedPct, 20)}</Text>\n <Text>Time left: {timeLeft}</Text>\n <Text>\n Last heartbeat: {lastHeartbeatAgoSec === null ? '—' : `${lastHeartbeatAgoSec}s ago`}\n {' '}\n <Text color={lastHeartbeatOk ? 'green' : 'yellow'}>\n {lastHeartbeatOk ? '✓' : '⚠'}\n </Text>\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>Press Ctrl+C to crash out of the race.</Text>\n </Box>\n </Box>\n );\n}\n\nfunction elapsed(race: GetRaceResponse): number {\n const start = new Date(race.start_time).getTime();\n const end = new Date(race.end_time).getTime();\n const now = new Date(race.server_time).getTime();\n if (end <= start) return 0;\n const v = (now - start) / (end - start);\n return Math.max(0, Math.min(1, v));\n}\n\nfunction bar(pct: number, width: number): string {\n const filled = Math.round(pct * width);\n return '▓'.repeat(filled) + '░'.repeat(width - filled);\n}\n\nfunction statusColor(status: GetRaceResponse['status']): string {\n if (status === 'live') return 'green';\n if (status === 'pending') return 'yellow';\n return 'gray';\n}\n\nfunction formatDuration(seconds: number): string {\n const s = Math.max(0, Math.floor(seconds));\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const ss = s % 60;\n return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${ss.toString().padStart(2, '0')}`;\n}\n","import type { HeartbeatResponse } from '@token-derby/shared';\n\nexport type HeartbeatLoopOptions = {\n sendHeartbeat: (currentTokens: number) => Promise<HeartbeatResponse>;\n getCurrentTokens: () => number;\n intervalMs: number;\n retryDelaysMs: readonly number[];\n onSuccess: (resp: HeartbeatResponse) => void;\n onError: (err: unknown) => void;\n onFinished: () => void;\n abortSignal: AbortSignal;\n};\n\nexport function runHeartbeatLoop(opts: HeartbeatLoopOptions): void {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let retryIndex = 0;\n let stopped = false;\n\n const stop = () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n timer = null;\n };\n\n opts.abortSignal.addEventListener('abort', stop, { once: true });\n\n const schedule = (delay: number) => {\n if (stopped) return;\n timer = setTimeout(tick, delay);\n };\n\n const tick = async () => {\n if (stopped) return;\n try {\n const tokens = opts.getCurrentTokens();\n const resp = await opts.sendHeartbeat(tokens);\n retryIndex = 0;\n opts.onSuccess(resp);\n if (resp.race_status === 'finished') {\n opts.onFinished();\n stop();\n return;\n }\n schedule(opts.intervalMs);\n } catch (err) {\n opts.onError(err);\n const delay = opts.retryDelaysMs[Math.min(retryIndex, opts.retryDelaysMs.length - 1)] ?? 1_000;\n retryIndex += 1;\n schedule(delay);\n }\n };\n\n schedule(0);\n}\n","import type { GetRaceResponse } from '@token-derby/shared';\n\nexport type PollLoopOptions = {\n fetchRace: () => Promise<GetRaceResponse>;\n intervalMs: number;\n onSnapshot: (race: GetRaceResponse) => void;\n onError: (err: unknown) => void;\n abortSignal: AbortSignal;\n};\n\nexport function runPollLoop(opts: PollLoopOptions): void {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let stopped = false;\n\n const stop = () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n timer = null;\n };\n\n opts.abortSignal.addEventListener('abort', stop, { once: true });\n\n const tick = async () => {\n if (stopped) return;\n try {\n const race = await opts.fetchRace();\n if (!stopped) opts.onSnapshot(race);\n } catch (err) {\n if (!stopped) opts.onError(err);\n }\n if (!stopped) timer = setTimeout(tick, opts.intervalMs);\n };\n\n timer = setTimeout(tick, 0);\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { claudeProjectsDir } from '../paths.js';\n\nexport type TokenTotals = { input: number; output: number };\n\nexport async function sumTokens(): Promise<TokenTotals> {\n const root = claudeProjectsDir();\n const files = await listJsonlFiles(root);\n let input = 0;\n let output = 0;\n for (const file of files) {\n const t = await sumFile(file);\n input += t.input;\n output += t.output;\n }\n return { input, output };\n}\n\n/** @deprecated Use sumTokens() instead */\nexport async function sumOutputTokens(): Promise<number> {\n const { input, output } = await sumTokens();\n return input + output;\n}\n\nasync function listJsonlFiles(root: string): Promise<string[]> {\n let projects: string[];\n try {\n projects = await fs.readdir(root);\n } catch (e: any) {\n if (e?.code === 'ENOENT') return [];\n throw e;\n }\n const out: string[] = [];\n for (const project of projects) {\n const projectDir = path.join(root, project);\n let stat;\n try {\n stat = await fs.stat(projectDir);\n } catch {\n continue;\n }\n if (!stat.isDirectory()) continue;\n const entries = await fs.readdir(projectDir);\n for (const entry of entries) {\n if (entry.endsWith('.jsonl')) out.push(path.join(projectDir, entry));\n }\n }\n return out;\n}\n\nfunction addNum(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n\nasync function sumFile(file: string): Promise<TokenTotals> {\n let raw: string;\n try {\n raw = await fs.readFile(file, 'utf8');\n } catch {\n return { input: 0, output: 0 };\n }\n let input = 0;\n let output = 0;\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n let parsed: any;\n try {\n parsed = JSON.parse(line);\n } catch {\n continue;\n }\n const usage = parsed?.message?.usage;\n if (!usage) continue;\n input += addNum(usage.input_tokens)\n + addNum(usage.cache_creation_input_tokens)\n + addNum(usage.cache_read_input_tokens);\n output += addNum(usage.output_tokens);\n }\n return { input, output };\n}\n","import type { RaceStatus } from '@token-derby/shared';\n\nexport function initialBaseline(args: { runningTotal: number; status: RaceStatus }): number {\n return args.runningTotal;\n}\n\nexport function rejoinBaseline(args: { runningTotal: number; lastRaceTokens: number }): number {\n return Math.max(0, args.runningTotal - args.lastRaceTokens);\n}\n\nexport function currentRaceTokens(runningTotal: number, baseline: number): number {\n return Math.max(0, runningTotal - baseline);\n}\n","import * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport { endRace } from '../api/endpoints.js';\nimport { ApiError } from '../api/client.js';\n\nexport async function endCommand(adminCode: string | undefined): Promise<number> {\n if (!adminCode) {\n console.error('Usage: token-derby end <admin-code>');\n return 2;\n }\n const rl = readline.createInterface({ input: stdin, output: stdout });\n const answer = (await rl.question('End the race now and freeze final tokens? [y/N] ')).trim().toLowerCase();\n rl.close();\n if (answer !== 'y' && answer !== 'yes') {\n console.log('Cancelled.');\n return 1;\n }\n try {\n await endRace(adminCode);\n console.log('✓ Race ended.');\n return 0;\n } catch (e) {\n if (e instanceof ApiError) {\n if (e.code === 'RACE_NOT_FOUND') console.error('No race with that admin code.');\n else console.error(`Error: ${e.code} ${e.message}`);\n return 1;\n }\n throw e;\n }\n}\n","import * as readline from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport {\n loadIdentity,\n saveIdentity,\n generateUserId,\n validateDisplayName,\n type Identity,\n} from '../identity/identity.js';\n\nexport async function initCommand(): Promise<number> {\n const existing = await loadIdentity();\n const rl = readline.createInterface({ input: stdin, output: stdout });\n try {\n if (existing) {\n console.log(`Current jockey name: ${existing.display_name}`);\n const raw = (await rl.question('New jockey name (use your real name please) [keep]: ')).trim();\n if (!raw) {\n console.log('Kept existing name.');\n return 0;\n }\n const v = validateDisplayName(raw);\n if (!v.ok) { console.error(v.error); return 1; }\n const updated: Identity = { ...existing, display_name: v.name };\n await saveIdentity(updated);\n console.log(`Updated jockey name to: ${updated.display_name}`);\n return 0;\n }\n\n const raw = (await rl.question('Jockey Name (use your real name please): ')).trim();\n const v = validateDisplayName(raw);\n if (!v.ok) { console.error(v.error); return 1; }\n\n const identity: Identity = {\n user_id: generateUserId(),\n display_name: v.name,\n created_at: new Date().toISOString(),\n };\n await saveIdentity(identity);\n console.log('');\n console.log(`Welcome, ${identity.display_name}!`);\n console.log(`Your identity is saved. You can now create a stable and join races.`);\n return 0;\n } finally {\n rl.close();\n }\n}\n","import { stableCreateCommand } from './commands/stable-create.js';\nimport { stableListCommand } from './commands/stable-list.js';\nimport { stableDeleteCommand } from './commands/stable-delete.js';\nimport { stableEditCommand } from './commands/stable-edit.js';\nimport { createRaceCommand } from './commands/create.js';\nimport { joinCommand } from './commands/join.js';\nimport { endCommand } from './commands/end.js';\nimport { initCommand } from './commands/init.js';\nimport { CLI_VERSION } from './version.js';\nimport { loadIdentity } from './identity/identity.js';\n\nconst HELP = `token-derby v${CLI_VERSION}\n\nIdentity:\n token-derby init Set up your jockey identity (run this first)\n\nStable management:\n token-derby stable create Make a new horse (interactive)\n token-derby stable list Show your saved horses\n token-derby stable edit <name> Edit an existing horse's colors\n token-derby stable delete <name> Remove a horse from your stable\n\nRaces:\n token-derby create Create a new race (interactive)\n token-derby join <join-code> Join (or resume) a race\n token-derby end <admin-code> End a race early\n\nEnvironment:\n TOKEN_DERBY_API_BASE Override API base URL (default: production)\n TOKEN_DERBY_HOME Override identity/stable directory\n`;\n\nasync function main(): Promise<number> {\n const argv = process.argv.slice(2);\n const cmd = argv[0];\n\n if (!cmd || cmd === '--help' || cmd === '-h') { console.log(HELP); return 0; }\n if (cmd === '--version' || cmd === '-v') { console.log(CLI_VERSION); return 0; }\n\n if (cmd === 'init') return initCommand();\n\n // Every other command requires an identity. `init` is the only escape hatch.\n const identity = await loadIdentity();\n if (!identity) {\n console.error('Run `token-derby init` to set up your identity before using any other command.');\n return 1;\n }\n\n if (cmd === 'stable') {\n const sub = argv[1];\n if (sub === 'create') return stableCreateCommand();\n if (sub === 'list') return stableListCommand();\n if (sub === 'edit') return stableEditCommand(argv[2]);\n if (sub === 'delete') return stableDeleteCommand(argv[2]);\n console.error(`Unknown stable subcommand: ${sub ?? '(none)'}`);\n console.error('Try: stable create | stable list | stable edit <name> | stable delete <name>');\n return 2;\n }\n\n if (cmd === 'create') return createRaceCommand();\n if (cmd === 'join') return joinCommand(argv[1]);\n if (cmd === 'end') return endCommand(argv[1]);\n\n console.error(`Unknown command: ${cmd}`);\n console.error(HELP);\n return 2;\n}\n\nmain().then(\n code => process.exit(code),\n err => {\n console.error(err?.stack ?? err);\n process.exit(1);\n },\n);\n"],"mappings":"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","names":["React","Box","Text","jsx","Box","Text","React","React","render","Box","Text","jsx","jsxs","render","React","Box","Text","readline","stdin","stdout","fs","path","stdin","stdout","React","render","render","React","readline","stdin","stdout","fs","path","fs","path","stdin","stdout","React","render","useState","Box","Text","useInput","jsx","jsxs","useState","useInput","Box","Text","useState","Box","Text","Box","Text","jsx","jsxs","Box","Text","fs","path","stat","jsx","jsxs","useState","Box","Text","render","React","readline","stdin","stdout","stdin","stdout","readline","stdin","stdout","stdin","stdout","raw","v"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mauricode/token-derby",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "description": "Token Derby CLI — manage your stable, run horses in token races driven by Claude Code output.",
5
5
  "type": "module",
6
6
  "bin": {