@mauricode/token-derby 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/bin.js +301 -102
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
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
|
}
|
|
@@ -326,6 +330,9 @@ function homeDir() {
|
|
|
326
330
|
function stableFile() {
|
|
327
331
|
return path.join(homeDir(), "stable.json");
|
|
328
332
|
}
|
|
333
|
+
function identityFile() {
|
|
334
|
+
return path.join(homeDir(), "identity.json");
|
|
335
|
+
}
|
|
329
336
|
function activeRaceFile(joinCode) {
|
|
330
337
|
return path.join(homeDir(), "active-races", `${joinCode}.json`);
|
|
331
338
|
}
|
|
@@ -507,6 +514,42 @@ async function stableDeleteCommand(name) {
|
|
|
507
514
|
return 0;
|
|
508
515
|
}
|
|
509
516
|
|
|
517
|
+
// src/commands/stable-edit.ts
|
|
518
|
+
import React4 from "react";
|
|
519
|
+
import { render as render3 } from "ink";
|
|
520
|
+
async function stableEditCommand(name) {
|
|
521
|
+
if (!name) {
|
|
522
|
+
console.error("Usage: token-derby stable edit <name>");
|
|
523
|
+
return 2;
|
|
524
|
+
}
|
|
525
|
+
const stable = await loadStable();
|
|
526
|
+
const existing = findHorse(stable, name);
|
|
527
|
+
if (!existing) {
|
|
528
|
+
console.error(`No horse named "${name}" in your stable.`);
|
|
529
|
+
return 1;
|
|
530
|
+
}
|
|
531
|
+
let exitCode = 0;
|
|
532
|
+
const app = render3(
|
|
533
|
+
React4.createElement(HorseCreator, {
|
|
534
|
+
initialColors: existing.colors,
|
|
535
|
+
initialName: existing.name,
|
|
536
|
+
lockName: true,
|
|
537
|
+
onSubmit: async (_name, colors) => {
|
|
538
|
+
await upsertHorse({ name: existing.name, colors, created_at: existing.created_at });
|
|
539
|
+
app.unmount();
|
|
540
|
+
console.log(`\u2713 Updated "${existing.name}".`);
|
|
541
|
+
},
|
|
542
|
+
onCancel: () => {
|
|
543
|
+
app.unmount();
|
|
544
|
+
console.log("Cancelled.");
|
|
545
|
+
exitCode = 1;
|
|
546
|
+
}
|
|
547
|
+
})
|
|
548
|
+
);
|
|
549
|
+
await app.waitUntilExit();
|
|
550
|
+
return exitCode;
|
|
551
|
+
}
|
|
552
|
+
|
|
510
553
|
// src/commands/create.ts
|
|
511
554
|
import * as readline3 from "readline/promises";
|
|
512
555
|
import { stdin as stdin3, stdout as stdout3 } from "process";
|
|
@@ -520,6 +563,61 @@ var HEARTBEAT_INTERVAL_MS = 6e4;
|
|
|
520
563
|
var POLL_INTERVAL_MS = 3e3;
|
|
521
564
|
var HEARTBEAT_RETRY_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
|
|
522
565
|
|
|
566
|
+
// src/version.ts
|
|
567
|
+
import { createRequire } from "module";
|
|
568
|
+
function readVersion() {
|
|
569
|
+
if ("1.0.0".length > 0) {
|
|
570
|
+
return "1.0.0";
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
const req = createRequire(import.meta.url);
|
|
574
|
+
const pkg = req("../package.json");
|
|
575
|
+
if (typeof pkg.version === "string") return pkg.version;
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
578
|
+
return "0.0.0-dev";
|
|
579
|
+
}
|
|
580
|
+
var CLI_VERSION = readVersion();
|
|
581
|
+
|
|
582
|
+
// ../shared/dist/constants.js
|
|
583
|
+
var CLI_VERSION_HEADER = "x-cli-version";
|
|
584
|
+
var USER_ID_HEADER = "x-user-id";
|
|
585
|
+
var USER_NAME_HEADER = "x-user-name";
|
|
586
|
+
var USER_NAME_MAX_LENGTH = 40;
|
|
587
|
+
|
|
588
|
+
// src/identity/identity.ts
|
|
589
|
+
import { promises as fs3 } from "fs";
|
|
590
|
+
import * as path3 from "path";
|
|
591
|
+
import * as crypto from "crypto";
|
|
592
|
+
async function loadIdentity() {
|
|
593
|
+
try {
|
|
594
|
+
const raw = await fs3.readFile(identityFile(), "utf8");
|
|
595
|
+
const parsed = JSON.parse(raw);
|
|
596
|
+
if (typeof parsed.user_id === "string" && typeof parsed.display_name === "string" && typeof parsed.created_at === "string") {
|
|
597
|
+
return parsed;
|
|
598
|
+
}
|
|
599
|
+
return null;
|
|
600
|
+
} catch (e) {
|
|
601
|
+
if (e?.code === "ENOENT") return null;
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async function saveIdentity(identity) {
|
|
606
|
+
await fs3.mkdir(homeDir(), { recursive: true });
|
|
607
|
+
await fs3.writeFile(identityFile(), JSON.stringify(identity, null, 2) + "\n", "utf8");
|
|
608
|
+
}
|
|
609
|
+
function generateUserId() {
|
|
610
|
+
return crypto.randomUUID();
|
|
611
|
+
}
|
|
612
|
+
function validateDisplayName(name) {
|
|
613
|
+
const trimmed = name.trim();
|
|
614
|
+
if (trimmed.length < 1) return { ok: false, error: "Name cannot be empty." };
|
|
615
|
+
if (trimmed.length > USER_NAME_MAX_LENGTH) {
|
|
616
|
+
return { ok: false, error: `Name must be ${USER_NAME_MAX_LENGTH} characters or fewer.` };
|
|
617
|
+
}
|
|
618
|
+
return { ok: true, name: trimmed };
|
|
619
|
+
}
|
|
620
|
+
|
|
523
621
|
// src/api/client.ts
|
|
524
622
|
var ApiError = class extends Error {
|
|
525
623
|
constructor(code, message, status) {
|
|
@@ -531,9 +629,20 @@ var ApiError = class extends Error {
|
|
|
531
629
|
code;
|
|
532
630
|
status;
|
|
533
631
|
};
|
|
534
|
-
|
|
535
|
-
|
|
632
|
+
var identityCache = null;
|
|
633
|
+
function getIdentity() {
|
|
634
|
+
if (!identityCache) identityCache = loadIdentity();
|
|
635
|
+
return identityCache;
|
|
636
|
+
}
|
|
637
|
+
async function request(method, path5, body, authToken, fetchImpl = fetch) {
|
|
638
|
+
const url = path5.startsWith("http") ? path5 : `${apiBase()}${path5}`;
|
|
536
639
|
const headers = {};
|
|
640
|
+
headers[CLI_VERSION_HEADER] = CLI_VERSION;
|
|
641
|
+
const identity = await getIdentity();
|
|
642
|
+
if (identity) {
|
|
643
|
+
headers[USER_ID_HEADER] = identity.user_id;
|
|
644
|
+
headers[USER_NAME_HEADER] = identity.display_name;
|
|
645
|
+
}
|
|
537
646
|
if (authToken) headers["authorization"] = `Bearer ${authToken}`;
|
|
538
647
|
if (body !== void 0) headers["content-type"] = "application/json";
|
|
539
648
|
let res;
|
|
@@ -597,20 +706,19 @@ async function createRaceCommand() {
|
|
|
597
706
|
console.error("Name required.");
|
|
598
707
|
return 1;
|
|
599
708
|
}
|
|
600
|
-
const
|
|
709
|
+
const startRaw = (await rl.question("Start time (ISO 8601, blank = now): ")).trim();
|
|
710
|
+
const start = startRaw ? startRaw : (/* @__PURE__ */ new Date()).toISOString();
|
|
601
711
|
if (!isIso(start)) {
|
|
602
712
|
console.error("Invalid start time.");
|
|
603
713
|
return 1;
|
|
604
714
|
}
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
610
|
-
if (new Date(end).getTime() <= new Date(start).getTime()) {
|
|
611
|
-
console.error("End time must be after start time.");
|
|
715
|
+
const durationRaw = (await rl.question("Race duration (hours): ")).trim();
|
|
716
|
+
const durationHours = parseFloat(durationRaw);
|
|
717
|
+
if (!Number.isFinite(durationHours) || durationHours <= 0) {
|
|
718
|
+
console.error("Duration must be a positive number of hours.");
|
|
612
719
|
return 1;
|
|
613
720
|
}
|
|
721
|
+
const end = new Date(new Date(start).getTime() + durationHours * 36e5).toISOString();
|
|
614
722
|
const tz = (await rl.question(`Time zone [${DEFAULT_TZ}]: `)).trim() || DEFAULT_TZ;
|
|
615
723
|
const maxRaw = (await rl.question("Max participants [30]: ")).trim();
|
|
616
724
|
const max = maxRaw ? parseInt(maxRaw, 10) : void 0;
|
|
@@ -652,8 +760,8 @@ function isIso(s) {
|
|
|
652
760
|
}
|
|
653
761
|
|
|
654
762
|
// src/commands/join.ts
|
|
655
|
-
import
|
|
656
|
-
import { render as
|
|
763
|
+
import React7 from "react";
|
|
764
|
+
import { render as render4 } from "ink";
|
|
657
765
|
|
|
658
766
|
// src/ui/HorsePicker.tsx
|
|
659
767
|
import { useState as useState2 } from "react";
|
|
@@ -705,13 +813,13 @@ function HorsePicker({ horses, onPick, onCancel }) {
|
|
|
705
813
|
|
|
706
814
|
// src/runtime/run-race.tsx
|
|
707
815
|
import { useEffect, useRef, useState as useState3 } from "react";
|
|
708
|
-
import { useApp } from "ink";
|
|
816
|
+
import { Box as Box6, Text as Text6, useApp } from "ink";
|
|
709
817
|
|
|
710
818
|
// src/ui/StatusScreen.tsx
|
|
711
819
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
712
820
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
713
821
|
function StatusScreen(props) {
|
|
714
|
-
const { race, ownHorseId, ownHorseName, ownColors, lastHeartbeatAgoSec, lastHeartbeatOk } = props;
|
|
822
|
+
const { race, ownHorseId, ownHorseName, ownColors, ownUserName, lastHeartbeatAgoSec, lastHeartbeatOk } = props;
|
|
715
823
|
if (!race) {
|
|
716
824
|
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsx5(Text5, { children: "Joining race\u2026" }) });
|
|
717
825
|
}
|
|
@@ -728,9 +836,19 @@ function StatusScreen(props) {
|
|
|
728
836
|
] }),
|
|
729
837
|
/* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "row", children: [
|
|
730
838
|
/* @__PURE__ */ jsx5(HorseSprite, { sprite: MINI_SPRITE, colors: ownColors }),
|
|
731
|
-
/* @__PURE__ */ jsxs4(
|
|
732
|
-
|
|
733
|
-
|
|
839
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
840
|
+
/* @__PURE__ */ jsxs4(Text5, { children: [
|
|
841
|
+
" ",
|
|
842
|
+
ownHorseName
|
|
843
|
+
] }),
|
|
844
|
+
/* @__PURE__ */ jsxs4(Text5, { children: [
|
|
845
|
+
" ",
|
|
846
|
+
/* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
|
|
847
|
+
"(",
|
|
848
|
+
ownUserName,
|
|
849
|
+
")"
|
|
850
|
+
] })
|
|
851
|
+
] })
|
|
734
852
|
] })
|
|
735
853
|
] }),
|
|
736
854
|
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
@@ -746,7 +864,7 @@ function StatusScreen(props) {
|
|
|
746
864
|
] }),
|
|
747
865
|
/* @__PURE__ */ jsxs4(Text5, { children: [
|
|
748
866
|
"Leader: ",
|
|
749
|
-
leader ? `${leader.name} (${leader.
|
|
867
|
+
leader ? `${leader.name}${leader.user_name ? ` (${leader.user_name})` : ""} \u2014 ${leader.current_tokens}` : "\u2014"
|
|
750
868
|
] }),
|
|
751
869
|
/* @__PURE__ */ jsxs4(Text5, { children: [
|
|
752
870
|
"Race elapsed: ",
|
|
@@ -855,50 +973,61 @@ function runPollLoop(opts) {
|
|
|
855
973
|
}
|
|
856
974
|
|
|
857
975
|
// src/tokens/transcripts.ts
|
|
858
|
-
import * as
|
|
859
|
-
import * as
|
|
860
|
-
async function
|
|
976
|
+
import * as fs4 from "fs/promises";
|
|
977
|
+
import * as path4 from "path";
|
|
978
|
+
async function sumTokens() {
|
|
861
979
|
const root = claudeProjectsDir();
|
|
862
980
|
const files = await listJsonlFiles(root);
|
|
863
|
-
let
|
|
981
|
+
let input = 0;
|
|
982
|
+
let output = 0;
|
|
864
983
|
for (const file of files) {
|
|
865
|
-
|
|
984
|
+
const t = await sumFile(file);
|
|
985
|
+
input += t.input;
|
|
986
|
+
output += t.output;
|
|
866
987
|
}
|
|
867
|
-
return
|
|
988
|
+
return { input, output };
|
|
989
|
+
}
|
|
990
|
+
async function sumOutputTokens() {
|
|
991
|
+
const { input, output } = await sumTokens();
|
|
992
|
+
return input + output;
|
|
868
993
|
}
|
|
869
994
|
async function listJsonlFiles(root) {
|
|
870
995
|
let projects;
|
|
871
996
|
try {
|
|
872
|
-
projects = await
|
|
997
|
+
projects = await fs4.readdir(root);
|
|
873
998
|
} catch (e) {
|
|
874
999
|
if (e?.code === "ENOENT") return [];
|
|
875
1000
|
throw e;
|
|
876
1001
|
}
|
|
877
1002
|
const out = [];
|
|
878
1003
|
for (const project of projects) {
|
|
879
|
-
const projectDir =
|
|
1004
|
+
const projectDir = path4.join(root, project);
|
|
880
1005
|
let stat2;
|
|
881
1006
|
try {
|
|
882
|
-
stat2 = await
|
|
1007
|
+
stat2 = await fs4.stat(projectDir);
|
|
883
1008
|
} catch {
|
|
884
1009
|
continue;
|
|
885
1010
|
}
|
|
886
1011
|
if (!stat2.isDirectory()) continue;
|
|
887
|
-
const entries = await
|
|
1012
|
+
const entries = await fs4.readdir(projectDir);
|
|
888
1013
|
for (const entry of entries) {
|
|
889
|
-
if (entry.endsWith(".jsonl")) out.push(
|
|
1014
|
+
if (entry.endsWith(".jsonl")) out.push(path4.join(projectDir, entry));
|
|
890
1015
|
}
|
|
891
1016
|
}
|
|
892
1017
|
return out;
|
|
893
1018
|
}
|
|
1019
|
+
function addNum(value) {
|
|
1020
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1021
|
+
}
|
|
894
1022
|
async function sumFile(file) {
|
|
895
1023
|
let raw;
|
|
896
1024
|
try {
|
|
897
|
-
raw = await
|
|
1025
|
+
raw = await fs4.readFile(file, "utf8");
|
|
898
1026
|
} catch {
|
|
899
|
-
return 0;
|
|
1027
|
+
return { input: 0, output: 0 };
|
|
900
1028
|
}
|
|
901
|
-
let
|
|
1029
|
+
let input = 0;
|
|
1030
|
+
let output = 0;
|
|
902
1031
|
for (const line of raw.split("\n")) {
|
|
903
1032
|
if (!line.trim()) continue;
|
|
904
1033
|
let parsed;
|
|
@@ -907,10 +1036,12 @@ async function sumFile(file) {
|
|
|
907
1036
|
} catch {
|
|
908
1037
|
continue;
|
|
909
1038
|
}
|
|
910
|
-
const
|
|
911
|
-
if (
|
|
1039
|
+
const usage = parsed?.message?.usage;
|
|
1040
|
+
if (!usage) continue;
|
|
1041
|
+
input += addNum(usage.input_tokens) + addNum(usage.cache_creation_input_tokens) + addNum(usage.cache_read_input_tokens);
|
|
1042
|
+
output += addNum(usage.output_tokens);
|
|
912
1043
|
}
|
|
913
|
-
return
|
|
1044
|
+
return { input, output };
|
|
914
1045
|
}
|
|
915
1046
|
|
|
916
1047
|
// src/tokens/baseline.ts
|
|
@@ -919,13 +1050,14 @@ function initialBaseline(args) {
|
|
|
919
1050
|
}
|
|
920
1051
|
|
|
921
1052
|
// src/runtime/run-race.tsx
|
|
922
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
923
|
-
function RunRace({ active, startingBaseline, pendingMode }) {
|
|
1053
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1054
|
+
function RunRace({ active, startingBaseline, pendingMode, ownUserName }) {
|
|
924
1055
|
const { exit } = useApp();
|
|
925
1056
|
const [race, setRace] = useState3(null);
|
|
926
1057
|
const [lastHbAt, setLastHbAt] = useState3(null);
|
|
927
1058
|
const [lastHbOk, setLastHbOk] = useState3(true);
|
|
928
1059
|
const [tickNow, setTickNow] = useState3(/* @__PURE__ */ new Date());
|
|
1060
|
+
const [fatalError, setFatalError] = useState3(null);
|
|
929
1061
|
const baselineRef = useRef(startingBaseline);
|
|
930
1062
|
const pendingRef = useRef(pendingMode);
|
|
931
1063
|
const lastTokenSampleRef = useRef(startingBaseline);
|
|
@@ -978,7 +1110,15 @@ function RunRace({ active, startingBaseline, pendingMode }) {
|
|
|
978
1110
|
setLastHbOk(true);
|
|
979
1111
|
if (resp.race_status === "finished") exit();
|
|
980
1112
|
},
|
|
981
|
-
onError: () =>
|
|
1113
|
+
onError: (err) => {
|
|
1114
|
+
if (err instanceof ApiError && err.code === "VERSION_MISMATCH") {
|
|
1115
|
+
setFatalError(err.message);
|
|
1116
|
+
ctrl.current.abort();
|
|
1117
|
+
exit();
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
setLastHbOk(false);
|
|
1121
|
+
},
|
|
982
1122
|
onFinished: () => exit(),
|
|
983
1123
|
abortSignal: ctrl.current.signal
|
|
984
1124
|
});
|
|
@@ -999,6 +1139,12 @@ function RunRace({ active, startingBaseline, pendingMode }) {
|
|
|
999
1139
|
};
|
|
1000
1140
|
}, []);
|
|
1001
1141
|
const lastHeartbeatAgoSec = lastHbAt ? Math.max(0, Math.floor((tickNow.getTime() - lastHbAt.getTime()) / 1e3)) : null;
|
|
1142
|
+
if (fatalError) {
|
|
1143
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", padding: 1, children: [
|
|
1144
|
+
/* @__PURE__ */ jsx6(Text6, { color: "red", bold: true, children: "CLI version mismatch \u2014 disconnected" }),
|
|
1145
|
+
/* @__PURE__ */ jsx6(Text6, { children: fatalError })
|
|
1146
|
+
] });
|
|
1147
|
+
}
|
|
1002
1148
|
return /* @__PURE__ */ jsx6(
|
|
1003
1149
|
StatusScreen,
|
|
1004
1150
|
{
|
|
@@ -1006,6 +1152,7 @@ function RunRace({ active, startingBaseline, pendingMode }) {
|
|
|
1006
1152
|
ownHorseId: active.horse_id,
|
|
1007
1153
|
ownHorseName: active.horse_name,
|
|
1008
1154
|
ownColors: active.horse_colors,
|
|
1155
|
+
ownUserName,
|
|
1009
1156
|
lastHeartbeatAgoSec,
|
|
1010
1157
|
lastHeartbeatOk: lastHbOk
|
|
1011
1158
|
}
|
|
@@ -1032,56 +1179,89 @@ async function joinCommand(joinCode) {
|
|
|
1032
1179
|
return 2;
|
|
1033
1180
|
}
|
|
1034
1181
|
const code = joinCode.toUpperCase();
|
|
1035
|
-
const
|
|
1036
|
-
if (
|
|
1037
|
-
console.error("
|
|
1182
|
+
const identity = await loadIdentity();
|
|
1183
|
+
if (!identity) {
|
|
1184
|
+
console.error("Run `token-derby init` to set up your identity.");
|
|
1038
1185
|
return 1;
|
|
1039
1186
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1187
|
+
let race;
|
|
1188
|
+
try {
|
|
1189
|
+
race = await getRace(code);
|
|
1190
|
+
} catch (e) {
|
|
1191
|
+
if (e instanceof ApiError) {
|
|
1192
|
+
if (e.code === "RACE_NOT_FOUND") console.error(`No race with join code ${code}.`);
|
|
1193
|
+
else console.error(`Error: ${e.code} ${e.message}`);
|
|
1194
|
+
return 1;
|
|
1195
|
+
}
|
|
1196
|
+
throw e;
|
|
1197
|
+
}
|
|
1198
|
+
if (race.status === "finished") {
|
|
1199
|
+
console.error("This race has already ended.");
|
|
1043
1200
|
return 1;
|
|
1044
1201
|
}
|
|
1202
|
+
const ownHorse = race.horses.find((h) => h.user_id === identity.user_id) ?? null;
|
|
1203
|
+
let chosenName;
|
|
1204
|
+
let chosenColors;
|
|
1205
|
+
let isResume;
|
|
1206
|
+
if (ownHorse) {
|
|
1207
|
+
chosenName = ownHorse.name;
|
|
1208
|
+
chosenColors = ownHorse.colors;
|
|
1209
|
+
isResume = true;
|
|
1210
|
+
} else {
|
|
1211
|
+
const stable = await loadStable();
|
|
1212
|
+
if (stable.horses.length === 0) {
|
|
1213
|
+
console.error("Your stable is empty. Run `token-derby stable create` first.");
|
|
1214
|
+
return 1;
|
|
1215
|
+
}
|
|
1216
|
+
const picked = await pickHorse(stable.horses);
|
|
1217
|
+
if (!picked) {
|
|
1218
|
+
console.log("Cancelled.");
|
|
1219
|
+
return 1;
|
|
1220
|
+
}
|
|
1221
|
+
chosenName = picked.name;
|
|
1222
|
+
chosenColors = picked.colors;
|
|
1223
|
+
isResume = false;
|
|
1224
|
+
}
|
|
1045
1225
|
let joinResp;
|
|
1046
1226
|
try {
|
|
1047
|
-
joinResp = await joinRace(code, { horse: { name:
|
|
1227
|
+
joinResp = await joinRace(code, { horse: { name: chosenName, colors: chosenColors } });
|
|
1048
1228
|
} catch (e) {
|
|
1049
1229
|
if (e instanceof ApiError) {
|
|
1050
|
-
if (e.code === "RACE_FULL") console.error(
|
|
1230
|
+
if (e.code === "RACE_FULL") console.error("This race is full.");
|
|
1051
1231
|
else if (e.code === "RACE_FINISHED") console.error("This race has ended.");
|
|
1052
1232
|
else if (e.code === "RACE_NOT_FOUND") console.error(`No race with join code ${code}.`);
|
|
1233
|
+
else if (e.code === "VERSION_MISMATCH") console.error(e.message);
|
|
1234
|
+
else if (e.code === "DUPLICATE_HORSE") console.error(e.message);
|
|
1235
|
+
else if (e.code === "IDENTITY_REQUIRED") console.error(`Error: ${e.message}`);
|
|
1053
1236
|
else console.error(`Error: ${e.code} ${e.message}`);
|
|
1054
1237
|
return 1;
|
|
1055
1238
|
}
|
|
1056
1239
|
throw e;
|
|
1057
1240
|
}
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
console.error("Race finished after join. Exiting.");
|
|
1061
|
-
return 1;
|
|
1062
|
-
}
|
|
1241
|
+
const prior = await loadActiveRace(code);
|
|
1242
|
+
const lastTokens = isResume && prior?.horse_id === joinResp.horse_id ? prior.last_race_tokens : ownHorse?.current_tokens ?? 0;
|
|
1063
1243
|
const status = race.status;
|
|
1064
1244
|
const active = {
|
|
1065
1245
|
join_code: code,
|
|
1066
1246
|
race_id: race.race_id,
|
|
1067
1247
|
horse_id: joinResp.horse_id,
|
|
1068
1248
|
heartbeat_token: joinResp.heartbeat_token,
|
|
1069
|
-
horse_name:
|
|
1070
|
-
horse_colors:
|
|
1071
|
-
joined_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1072
|
-
last_race_tokens:
|
|
1249
|
+
horse_name: chosenName,
|
|
1250
|
+
horse_colors: chosenColors,
|
|
1251
|
+
joined_at: ownHorse?.joined_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1252
|
+
last_race_tokens: lastTokens,
|
|
1073
1253
|
last_heartbeat_at: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
1074
1254
|
};
|
|
1075
1255
|
await saveActiveRace(active);
|
|
1076
|
-
const initial = await buildInitialState({ active, raceStatus: status, rejoin:
|
|
1077
|
-
const app =
|
|
1256
|
+
const initial = await buildInitialState({ active, raceStatus: status, rejoin: isResume });
|
|
1257
|
+
const app = render4(React7.createElement(RunRace, { active, ...initial, ownUserName: identity.display_name }));
|
|
1078
1258
|
await app.waitUntilExit();
|
|
1079
1259
|
return 0;
|
|
1080
1260
|
}
|
|
1081
1261
|
async function pickHorse(horses) {
|
|
1082
1262
|
return new Promise((resolve) => {
|
|
1083
|
-
const app =
|
|
1084
|
-
|
|
1263
|
+
const app = render4(
|
|
1264
|
+
React7.createElement(HorsePicker, {
|
|
1085
1265
|
horses,
|
|
1086
1266
|
onPick: (h) => {
|
|
1087
1267
|
app.unmount();
|
|
@@ -1096,41 +1276,6 @@ async function pickHorse(horses) {
|
|
|
1096
1276
|
});
|
|
1097
1277
|
}
|
|
1098
1278
|
|
|
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
1279
|
// src/commands/end.ts
|
|
1135
1280
|
import * as readline4 from "readline/promises";
|
|
1136
1281
|
import { stdin as stdin4, stdout as stdout4 } from "process";
|
|
@@ -1160,23 +1305,71 @@ async function endCommand(adminCode) {
|
|
|
1160
1305
|
}
|
|
1161
1306
|
}
|
|
1162
1307
|
|
|
1308
|
+
// src/commands/init.ts
|
|
1309
|
+
import * as readline5 from "readline/promises";
|
|
1310
|
+
import { stdin as stdin5, stdout as stdout5 } from "process";
|
|
1311
|
+
async function initCommand() {
|
|
1312
|
+
const existing = await loadIdentity();
|
|
1313
|
+
const rl = readline5.createInterface({ input: stdin5, output: stdout5 });
|
|
1314
|
+
try {
|
|
1315
|
+
if (existing) {
|
|
1316
|
+
console.log(`Current jockey name: ${existing.display_name}`);
|
|
1317
|
+
const raw2 = (await rl.question("New jockey name (use your real name please) [keep]: ")).trim();
|
|
1318
|
+
if (!raw2) {
|
|
1319
|
+
console.log("Kept existing name.");
|
|
1320
|
+
return 0;
|
|
1321
|
+
}
|
|
1322
|
+
const v2 = validateDisplayName(raw2);
|
|
1323
|
+
if (!v2.ok) {
|
|
1324
|
+
console.error(v2.error);
|
|
1325
|
+
return 1;
|
|
1326
|
+
}
|
|
1327
|
+
const updated = { ...existing, display_name: v2.name };
|
|
1328
|
+
await saveIdentity(updated);
|
|
1329
|
+
console.log(`Updated jockey name to: ${updated.display_name}`);
|
|
1330
|
+
return 0;
|
|
1331
|
+
}
|
|
1332
|
+
const raw = (await rl.question("Jockey Name (use your real name please): ")).trim();
|
|
1333
|
+
const v = validateDisplayName(raw);
|
|
1334
|
+
if (!v.ok) {
|
|
1335
|
+
console.error(v.error);
|
|
1336
|
+
return 1;
|
|
1337
|
+
}
|
|
1338
|
+
const identity = {
|
|
1339
|
+
user_id: generateUserId(),
|
|
1340
|
+
display_name: v.name,
|
|
1341
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1342
|
+
};
|
|
1343
|
+
await saveIdentity(identity);
|
|
1344
|
+
console.log("");
|
|
1345
|
+
console.log(`Welcome, ${identity.display_name}!`);
|
|
1346
|
+
console.log(`Your identity is saved. You can now create a stable and join races.`);
|
|
1347
|
+
return 0;
|
|
1348
|
+
} finally {
|
|
1349
|
+
rl.close();
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1163
1353
|
// src/bin.ts
|
|
1164
|
-
var
|
|
1165
|
-
|
|
1354
|
+
var HELP = `token-derby v${CLI_VERSION}
|
|
1355
|
+
|
|
1356
|
+
Identity:
|
|
1357
|
+
token-derby init Set up your jockey identity (run this first)
|
|
1166
1358
|
|
|
1167
1359
|
Stable management:
|
|
1168
1360
|
token-derby stable create Make a new horse (interactive)
|
|
1169
1361
|
token-derby stable list Show your saved horses
|
|
1362
|
+
token-derby stable edit <name> Edit an existing horse's colors
|
|
1170
1363
|
token-derby stable delete <name> Remove a horse from your stable
|
|
1171
1364
|
|
|
1172
1365
|
Races:
|
|
1173
1366
|
token-derby create Create a new race (interactive)
|
|
1174
|
-
token-derby join <join-code>
|
|
1175
|
-
token-derby rejoin <join-code> Resume a race after a disconnect
|
|
1367
|
+
token-derby join <join-code> Join (or resume) a race
|
|
1176
1368
|
token-derby end <admin-code> End a race early
|
|
1177
1369
|
|
|
1178
1370
|
Environment:
|
|
1179
1371
|
TOKEN_DERBY_API_BASE Override API base URL (default: production)
|
|
1372
|
+
TOKEN_DERBY_HOME Override identity/stable directory
|
|
1180
1373
|
`;
|
|
1181
1374
|
async function main() {
|
|
1182
1375
|
const argv = process.argv.slice(2);
|
|
@@ -1186,21 +1379,27 @@ async function main() {
|
|
|
1186
1379
|
return 0;
|
|
1187
1380
|
}
|
|
1188
1381
|
if (cmd === "--version" || cmd === "-v") {
|
|
1189
|
-
console.log(
|
|
1382
|
+
console.log(CLI_VERSION);
|
|
1190
1383
|
return 0;
|
|
1191
1384
|
}
|
|
1385
|
+
if (cmd === "init") return initCommand();
|
|
1386
|
+
const identity = await loadIdentity();
|
|
1387
|
+
if (!identity) {
|
|
1388
|
+
console.error("Run `token-derby init` to set up your identity before using any other command.");
|
|
1389
|
+
return 1;
|
|
1390
|
+
}
|
|
1192
1391
|
if (cmd === "stable") {
|
|
1193
1392
|
const sub = argv[1];
|
|
1194
1393
|
if (sub === "create") return stableCreateCommand();
|
|
1195
1394
|
if (sub === "list") return stableListCommand();
|
|
1395
|
+
if (sub === "edit") return stableEditCommand(argv[2]);
|
|
1196
1396
|
if (sub === "delete") return stableDeleteCommand(argv[2]);
|
|
1197
1397
|
console.error(`Unknown stable subcommand: ${sub ?? "(none)"}`);
|
|
1198
|
-
console.error("Try: stable create | stable list | stable delete <name>");
|
|
1398
|
+
console.error("Try: stable create | stable list | stable edit <name> | stable delete <name>");
|
|
1199
1399
|
return 2;
|
|
1200
1400
|
}
|
|
1201
1401
|
if (cmd === "create") return createRaceCommand();
|
|
1202
1402
|
if (cmd === "join") return joinCommand(argv[1]);
|
|
1203
|
-
if (cmd === "rejoin") return rejoinCommand(argv[1]);
|
|
1204
1403
|
if (cmd === "end") return endCommand(argv[1]);
|
|
1205
1404
|
console.error(`Unknown command: ${cmd}`);
|
|
1206
1405
|
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 } 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 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 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 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({ name: existing.name, colors, created_at: existing.created_at });\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 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 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 chosenName = picked.name;\n chosenColors = picked.colors;\n isResume = false;\n }\n\n let joinResp;\n try {\n joinResp = await joinRace(code, { horse: { name: chosenName, colors: chosenColors } });\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":";;;AAAA,OAAOA,YAAW;AAClB,SAAS,cAAc;;;ACDvB,SAAgB,gBAAgB;AAChC,SAAS,OAAAC,MAAK,QAAAC,OAAM,gBAAgB;AACpC,OAAO,eAAe;;;ACDtB,SAAS,KAAK,YAAY;;;ACGnB,IAAM,eAAe;AAAA,EAC1B,GAAG;AAAA,EACH,GAAG;AACL;AAKA,IAAM,YAA+B;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,YAA+B;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,cAA+C,MAAM,WAAW,IAAI,EAAE;AAC5E,IAAM,cAA+C,MAAM,WAAW,GAAG,CAAC;AAEjF,SAAS,MAAM,MAAyB,OAAe,QAA6B;AAClF,MAAI,KAAK,WAAW,QAAQ;AAC1B,UAAM,IAAI,MAAM,cAAc,KAAK,MAAM,mBAAmB,MAAM,EAAE;AAAA,EACtE;AACA,SAAO,KAAK,IAAI,CAAC,KAAK,MAAM;AAC1B,QAAI,IAAI,WAAW,OAAO;AACxB,YAAM,IAAI,MAAM,cAAc,CAAC,eAAe,IAAI,MAAM,cAAc,KAAK,EAAE;AAAA,IAC/E;AACA,WAAO,CAAC,GAAG,GAAG,EAAE,IAAI,OAAK,MAAM,CAAC,CAAC;AAAA,EACnC,CAAC;AACH;AAEA,SAAS,MAAM,GAAoB;AACjC,UAAQ,GAAG;AAAA,IACT,KAAK;AAAK,aAAO;AAAA,IACjB,KAAK;AAAK,aAAO;AAAA,IACjB,KAAK;AAAK,aAAO;AAAA,IACjB,KAAK;AAAK,aAAO;AAAA,IACjB,KAAK;AAAK,aAAO;AAAA,IACjB,KAAK;AAAK,aAAO;AAAA,IACjB,KAAK;AAAK,aAAO;AAAA,IACjB;AAAS,YAAM,IAAI,MAAM,wBAAwB,CAAC,EAAE;AAAA,EACtD;AACF;;;AChEO,SAAS,aACd,QACA,QACU;AACV,QAAM,MAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,IAAI,OAAO,UAAU,IAAI,OAAO,QAAQ,KAAK,GAAG;AAClE,UAAM,SAAS,OAAO,CAAC;AACvB,UAAM,YAAY,OAAO,IAAI,CAAC;AAC9B,QAAI,CAAC,OAAQ;AACb,UAAM,MAAc,CAAC;AACrB,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAI,KAAK;AAAA,QACP,KAAK,SAAS,OAAO,CAAC,KAAK,MAAM,MAAM;AAAA,QACvC,QAAQ,SAAS,YAAY,CAAC,KAAK,MAAM,MAAM;AAAA,MACjD,CAAC;AAAA,IACH;AACA,QAAI,KAAK,GAAG;AACZ,QAAI,CAAC,UAAW;AAAA,EAClB;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAc,QAAoC;AAClE,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,QAAQ,IAAK,QAAO,aAAa;AACrC,MAAI,QAAQ,IAAK,QAAO,aAAa;AACrC,MAAI,QAAQ,IAAK,QAAO,OAAO;AAC/B,MAAI,QAAQ,IAAK,QAAO,OAAO;AAC/B,MAAI,QAAQ,IAAK,QAAO,OAAO;AAC/B,MAAI,QAAQ,IAAK,QAAO,OAAO;AAC/B,SAAO;AACT;;;AFvBQ;AALD,SAAS,YAAY,EAAE,QAAQ,OAAO,GAAU;AACrD,QAAM,OAAO,aAAa,QAAQ,MAAM;AACxC,SACE,oBAAC,OAAI,eAAc,UAChB,eAAK,IAAI,CAAC,KAAK,MACd,oBAAC,QAAc,oBAAU,GAAG,KAAjB,CAAmB,CAC/B,GACH;AAEJ;AAEA,SAAS,UAAU,KAAqB;AACtC,MAAI,MAAM;AACV,aAAW,QAAQ,KAAK;AACtB,QAAI,KAAK,QAAQ,QAAQ,KAAK,WAAW,MAAM;AAC7C,aAAO;AAAA,IACT,WAAW,KAAK,QAAQ,QAAQ,KAAK,WAAW,MAAM;AACpD,aAAO,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,MAAM,IAAI,WAAM;AAAA,IACxD,WAAW,KAAK,QAAQ,MAAM;AAC5B,aAAO,OAAO,KAAK,GAAG,IAAI,WAAM;AAAA,IAClC,OAAO;AACL,aAAO,OAAO,KAAK,MAAO,IAAI,WAAM;AAAA,IACtC;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,QAAQ;AAEd,SAAS,SAAS,KAAuC;AACvD,QAAM,IAAI,IAAI,QAAQ,KAAK,EAAE;AAC7B,SAAO;AAAA,IACL,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAC1B,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAC1B,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,EAC5B;AACF;AAEA,SAAS,OAAO,KAAqB;AACnC,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG;AAC9B,SAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC;AAEA,SAAS,OAAO,KAAqB;AACnC,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,GAAG;AAC9B,SAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC;;;AGrDO,IAAM,QAAyB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ;AAEhE,IAAM,WAA4C;AAAA,EACvD,MAAM;AAAA,IACJ;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,EACnC;AAAA,EACA,MAAM;AAAA,IACJ;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,EACnC;AAAA,EACA,MAAM;AAAA,IACJ;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,EACnC;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,IACvD;AAAA,IAAW;AAAA,IAAW;AAAA,IAAW;AAAA,EACnC;AACF;AAEO,SAAS,UAAU,MAAY,SAAyB;AAC7D,QAAM,UAAU,SAAS,IAAI;AAC7B,QAAM,MAAM,QAAQ,QAAQ,OAAO;AACnC,SAAO,SAAS,MAAM,IAAI,QAAQ,UAAU,QAAQ,MAAM,KAAK,QAAQ,CAAC;AAC1E;AAEO,SAAS,UAAU,MAAY,SAAyB;AAC7D,QAAM,UAAU,SAAS,IAAI;AAC7B,QAAM,MAAM,QAAQ,QAAQ,OAAO;AACnC,MAAI,MAAM,EAAG,QAAO,QAAQ,CAAC;AAC7B,SAAO,SAAS,MAAM,IAAI,QAAQ,UAAU,QAAQ,MAAM;AAC5D;AAEO,SAAS,gBAA6B;AAC3C,SAAO;AAAA,IACL,MAAM,SAAS,KAAK,CAAC;AAAA,IACrB,MAAM,SAAS,KAAK,CAAC;AAAA,IACrB,MAAM,SAAS,KAAK,CAAC;AAAA,IACrB,QAAQ,SAAS,OAAO,CAAC;AAAA,EAC3B;AACF;;;AJCQ,gBAAAC,MAKE,YALF;AAlCD,SAAS,aAAa,EAAE,UAAU,UAAU,eAAe,aAAa,SAAS,GAAU;AAChG,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAsB,iBAAiB,cAAc,CAAC;AAClF,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,CAAC;AACxC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,eAAe,EAAE;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,QAAM,OAAa,MAAM,OAAO;AAEhC,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,WAAY;AAChB,QAAI,IAAI,QAAQ;AAAE,eAAS;AAAG;AAAA,IAAQ;AACtC,QAAI,IAAI,SAAS;AAAE,kBAAY,UAAU,IAAI,MAAM,UAAU,MAAM,MAAM;AAAG;AAAA,IAAQ;AACpF,QAAI,IAAI,WAAW;AAAE,kBAAY,UAAU,KAAK,MAAM,MAAM;AAAG;AAAA,IAAQ;AACvE,QAAI,IAAI,WAAW;AAAE,gBAAU,EAAE,GAAG,QAAQ,CAAC,IAAI,GAAG,UAAU,MAAM,OAAO,IAAI,CAAC,EAAE,CAAC;AAAG;AAAA,IAAQ;AAC9F,QAAI,IAAI,YAAY;AAAE,gBAAU,EAAE,GAAG,QAAQ,CAAC,IAAI,GAAG,UAAU,MAAM,OAAO,IAAI,CAAC,EAAE,CAAC;AAAG;AAAA,IAAQ;AAC/F,QAAI,IAAI,QAAQ;AACd,UAAI,UAAU;AAAE,iBAAS,eAAe,IAAI,MAAM;AAAG;AAAA,MAAQ;AAC7D,oBAAc,IAAI;AAClB;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,mBAAmB,CAAC,UAAkB;AAC1C,QAAI,CAAC,MAAM,KAAK,GAAG;AACjB,eAAS,eAAe;AACxB;AAAA,IACF;AACA,aAAS,MAAM,KAAK,GAAG,MAAM;AAAA,EAC/B;AAEA,SACE,qBAACC,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAD,KAACC,MAAA,EAAI,cAAc,GACjB,0BAAAD,KAAC,eAAY,QAAQ,aAAa,QAAgB,GACpD;AAAA,IAEA,gBAAAA,KAACC,MAAA,EAAI,eAAc,UAChB,gBAAM,IAAI,CAAC,GAAG,MACb,qBAACC,OAAA,EACE;AAAA,YAAM,UAAU,WAAM;AAAA,MAAI;AAAA,MAAE,EAAE,OAAO,CAAC;AAAA,MAAE;AAAA,MAAC,gBAAAF,KAACE,OAAA,EAAK,OAAO,OAAO,CAAC,GAAG,0BAAE;AAAA,MAAO;AAAA,MAAE,OAAO,CAAC;AAAA,SAD5E,CAEX,CACD,GACH;AAAA,IAEC,CAAC,cACA,gBAAAF,KAACC,MAAA,EAAI,WAAW,GAAG,eAAc,UAC/B,0BAAAD,KAACE,OAAA,EAAK,UAAQ,MAAC,wGAA6D,GAC9E;AAAA,IAGD,cACC,qBAACD,MAAA,EAAI,WAAW,GAAG,eAAc,UAC/B;AAAA,sBAAAD,KAACE,OAAA,EAAK,+BAAiB;AAAA,MACvB,gBAAAF,KAAC,aAAU,OAAO,MAAM,UAAU,CAAC,MAAM;AAAE,gBAAQ,CAAC;AAAG,iBAAS,IAAI;AAAA,MAAG,GAAG,UAAU,kBAAkB;AAAA,MACrG,SAAS,gBAAAA,KAACE,OAAA,EAAK,OAAM,OAAO,iBAAM;AAAA,OACrC;AAAA,KAEJ;AAEJ;;;AK5EA,YAAY,QAAQ;;;ACApB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEf,SAAS,UAAkB;AAChC,SAAO,QAAQ,IAAI,oBAAyB,UAAQ,WAAQ,GAAG,cAAc;AAC/E;AAEO,SAAS,aAAqB;AACnC,SAAY,UAAK,QAAQ,GAAG,aAAa;AAC3C;AAEO,SAAS,eAAuB;AACrC,SAAY,UAAK,QAAQ,GAAG,eAAe;AAC7C;AAEO,SAAS,eAAe,UAA0B;AACvD,SAAY,UAAK,QAAQ,GAAG,gBAAgB,GAAG,QAAQ,OAAO;AAChE;AAEO,SAAS,iBAAyB;AACvC,SAAY,UAAK,QAAQ,GAAG,cAAc;AAC5C;AAEO,SAAS,oBAA4B;AAC1C,SAAO,QAAQ,IAAI,0BAA+B,UAAQ,WAAQ,GAAG,WAAW,UAAU;AAC5F;;;ADXA,eAAsB,aAA8B;AAClD,MAAI;AACF,UAAM,MAAM,MAAS,YAAS,WAAW,GAAG,MAAM;AAClD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,UAAU,CAAC,MAAM,QAAQ,OAAO,MAAM,EAAG,QAAO,EAAE,QAAQ,CAAC,EAAE;AAClE,WAAO;AAAA,EACT,SAAS,GAAQ;AACf,QAAI,GAAG,SAAS,SAAU,QAAO,EAAE,QAAQ,CAAC,EAAE;AAC9C,QAAI,aAAa,YAAa,QAAO,EAAE,QAAQ,CAAC,EAAE;AAClD,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,WAAW,QAA+B;AAC9D,QAAS,SAAM,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC7C,QAAS,aAAU,WAAW,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,MAAM;AACjF;AAEA,eAAsB,YAAY,OAAmC;AACnE,QAAM,SAAS,MAAM,WAAW;AAChC,QAAM,MAAM,OAAO,OAAO,UAAU,OAAK,EAAE,SAAS,MAAM,IAAI;AAC9D,MAAI,OAAO,EAAG,QAAO,OAAO,GAAG,IAAI;AAAA,MAC9B,QAAO,OAAO,KAAK,KAAK;AAC7B,QAAM,WAAW,MAAM;AACzB;AAEA,eAAsB,YAAY,MAA6B;AAC7D,QAAM,SAAS,MAAM,WAAW;AAChC,SAAO,SAAS,OAAO,OAAO,OAAO,OAAK,EAAE,SAAS,IAAI;AACzD,QAAM,WAAW,MAAM;AACzB;AAEO,SAAS,UAAU,QAAgB,MAAuC;AAC/E,SAAO,OAAO,OAAO,KAAK,OAAK,EAAE,SAAS,IAAI;AAChD;;;AN5CA,YAAY,cAAc;AAC1B,SAAS,OAAO,cAAc;AAE9B,eAAsB,sBAAuC;AAC3D,MAAI,WAAW;AACf,QAAM,MAAM;AAAA,IACVC,OAAM,cAAc,cAAc;AAAA,MAChC,UAAU,OAAO,MAAM,WAAW;AAChC,cAAM,SAAS,MAAM,WAAW;AAChC,cAAM,WAAW,UAAU,QAAQ,IAAI;AACvC,YAAI,UAAU;AACZ,cAAI,QAAQ;AACZ,gBAAM,KAAc,yBAAgB,EAAE,OAAO,OAAO,QAAQ,OAAO,CAAC;AACpE,gBAAM,UAAU,MAAM,GAAG,SAAS,UAAU,IAAI,qCAAqC,GAAG,KAAK,EAAE,YAAY;AAC3G,aAAG,MAAM;AACT,cAAI,WAAW,OAAO,WAAW,OAAO;AACtC,oBAAQ,IAAI,YAAY;AACxB,uBAAW;AACX;AAAA,UACF;AAAA,QACF;AACA,cAAM,YAAY,EAAE,MAAM,QAAQ,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AACxE,YAAI,QAAQ;AACZ,gBAAQ,IAAI,iBAAY,IAAI,mBAAmB;AAAA,MACjD;AAAA,MACA,UAAU,MAAM;AACd,YAAI,QAAQ;AACZ,gBAAQ,IAAI,YAAY;AACxB,mBAAW;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AACA,QAAM,IAAI,cAAc;AACxB,SAAO;AACT;;;AQtCA,OAAOC,YAAW;AAClB,SAAS,UAAAC,SAAQ,OAAAC,MAAK,QAAAC,aAAY;AAwB5B,SAGI,OAAAC,MAHJ,QAAAC,aAAA;AAnBN,eAAsB,oBAAqC;AACzD,QAAM,SAAS,MAAM,WAAW;AAChC,MAAI,OAAO,OAAO,WAAW,GAAG;AAC9B,YAAQ,IAAI,uEAAuE;AACnF,WAAO;AAAA,EACT;AACA,QAAM,MAAMC;AAAA,IACVC,OAAM,cAAc,YAAY,EAAE,QAAQ,OAAO,OAAO,CAAC;AAAA,EAC3D;AACA,QAAM,IAAI,cAAc;AACxB,SAAO;AACT;AAEA,SAAS,WAAW,EAAE,OAAO,GAAoE;AAC/F,EAAAA,OAAM,UAAU,MAAM;AACpB,iBAAa,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,EACpC,GAAG,CAAC,CAAC;AACL,SACE,gBAAAF,MAACG,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAH,MAACI,OAAA,EAAK,MAAI,MAAC;AAAA;AAAA,MAAc,OAAO;AAAA,MAAO;AAAA,OAAE;AAAA,IACxC,OAAO,IAAI,OACV,gBAAAJ,MAACG,MAAA,EAAiB,eAAc,OAAM,WAAW,GAC/C;AAAA,sBAAAJ,KAAC,eAAY,QAAQ,aAAa,QAAQ,EAAE,QAAQ;AAAA,MACpD,gBAAAC,MAACI,OAAA,EAAK;AAAA;AAAA,QAAG,EAAE;AAAA,SAAK;AAAA,SAFR,EAAE,IAGZ,CACD;AAAA,KACH;AAEJ;;;AClCA,YAAYC,eAAc;AAC1B,SAAS,SAAAC,QAAO,UAAAC,eAAc;;;ACD9B,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAgBtB,eAAsB,eAAe,UAA8C;AACjF,MAAI;AACF,UAAM,MAAM,MAAS,aAAS,eAAe,QAAQ,GAAG,MAAM;AAC9D,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,GAAQ;AACf,QAAI,GAAG,SAAS,SAAU,QAAO;AACjC,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eAAe,QAAmC;AACtE,QAAS,UAAM,eAAe,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,QAAS;AAAA,IACP,eAAe,OAAO,SAAS;AAAA,IAC/B,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI;AAAA,IAClC;AAAA,EACF;AACF;AAUA,eAAsB,kBAAqC;AACzD,MAAI;AACF,UAAM,UAAU,MAAS,YAAQ,eAAe,CAAC;AACjD,WAAO,QACJ,OAAO,OAAK,EAAE,SAAS,OAAO,CAAC,EAC/B,IAAI,OAAU,eAAS,GAAG,OAAO,CAAC;AAAA,EACvC,SAAS,GAAQ;AACf,QAAI,GAAG,SAAS,SAAU,QAAO,CAAC;AAClC,UAAM;AAAA,EACR;AACF;;;ADjDA,eAAsB,oBAAoB,MAA2C;AACnF,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,yCAAyC;AACvD,WAAO;AAAA,EACT;AACA,QAAM,SAAS,MAAM,WAAW;AAChC,QAAM,QAAQ,UAAU,QAAQ,IAAI;AACpC,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,mBAAmB,IAAI,mBAAmB;AACxD,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,gBAAgB;AACpC,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,MAAM,eAAe,IAAI;AACxC,QAAI,QAAQ,eAAe,MAAM;AAC/B,cAAQ,MAAM,IAAI,IAAI,kCAAkC,IAAI,8BAA8B;AAC1F,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,KAAc,0BAAgB,EAAE,OAAOC,QAAO,QAAQC,QAAO,CAAC;AACpE,QAAM,UAAU,MAAM,GAAG,SAAS,WAAW,IAAI,4BAA4B,GAAG,KAAK,EAAE,YAAY;AACnG,KAAG,MAAM;AACT,MAAI,WAAW,OAAO,WAAW,OAAO;AACtC,YAAQ,IAAI,YAAY;AACxB,WAAO;AAAA,EACT;AACA,QAAM,YAAY,IAAI;AACtB,UAAQ,IAAI,mBAAc,IAAI,IAAI;AAClC,SAAO;AACT;;;AEpCA,OAAOC,YAAW;AAClB,SAAS,UAAAC,eAAc;AAIvB,eAAsB,kBAAkB,MAA2C;AACjF,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,uCAAuC;AACrD,WAAO;AAAA,EACT;AACA,QAAM,SAAS,MAAM,WAAW;AAChC,QAAM,WAAW,UAAU,QAAQ,IAAI;AACvC,MAAI,CAAC,UAAU;AACb,YAAQ,MAAM,mBAAmB,IAAI,mBAAmB;AACxD,WAAO;AAAA,EACT;AAEA,MAAI,WAAW;AACf,QAAM,MAAMC;AAAA,IACVC,OAAM,cAAc,cAAc;AAAA,MAChC,eAAe,SAAS;AAAA,MACxB,aAAa,SAAS;AAAA,MACtB,UAAU;AAAA,MACV,UAAU,OAAO,OAAO,WAAW;AACjC,cAAM,YAAY,EAAE,MAAM,SAAS,MAAM,QAAQ,YAAY,SAAS,WAAW,CAAC;AAClF,YAAI,QAAQ;AACZ,gBAAQ,IAAI,mBAAc,SAAS,IAAI,IAAI;AAAA,MAC7C;AAAA,MACA,UAAU,MAAM;AACd,YAAI,QAAQ;AACZ,gBAAQ,IAAI,YAAY;AACxB,mBAAW;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AACA,QAAM,IAAI,cAAc;AACxB,SAAO;AACT;;;ACrCA,YAAYC,eAAc;AAC1B,SAAS,SAAAC,QAAO,UAAAC,eAAc;;;ACDvB,IAAM,mBAAmB;AAEzB,SAAS,UAAkB;AAChC,SAAO,QAAQ,IAAI,wBAAwB;AAC7C;AAEO,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB;AACzB,IAAM,4BAA4B,CAAC,KAAO,KAAO,KAAO,KAAO,IAAM;;;ACR5E,SAAS,qBAAqB;AAI9B,SAAS,cAAsB;AAC7B,MAA2C,QAAgB,SAAS,GAAG;AACrE,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,MAAM,cAAc,YAAY,GAAG;AACzC,UAAM,MAAM,IAAI,iBAAiB;AACjC,QAAI,OAAO,IAAI,YAAY,SAAU,QAAO,IAAI;AAAA,EAClD,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,IAAM,cAAsB,YAAY;;;ACfxC,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AACzB,IAAM,uBAAuB;;;ACNpC,SAAS,YAAYC,WAAU;AAC/B,YAAYC,WAAU;AACtB,YAAY,YAAY;AAUxB,eAAsB,eAAyC;AAC7D,MAAI;AACF,UAAM,MAAM,MAAMC,IAAG,SAAS,aAAa,GAAG,MAAM;AACpD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QACE,OAAO,OAAO,YAAY,YAC1B,OAAO,OAAO,iBAAiB,YAC/B,OAAO,OAAO,eAAe,UAC7B;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,SAAS,GAAQ;AACf,QAAI,GAAG,SAAS,SAAU,QAAO;AACjC,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,aAAa,UAAmC;AACpE,QAAMA,IAAG,MAAM,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC7C,QAAMA,IAAG,UAAU,aAAa,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,MAAM;AACrF;AAEO,SAAS,iBAAyB;AACvC,SAAc,kBAAW;AAC3B;AAEO,SAAS,oBAAoB,MAAyE;AAC3G,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,QAAQ,SAAS,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,wBAAwB;AAC3E,MAAI,QAAQ,SAAS,sBAAsB;AACzC,WAAO,EAAE,IAAI,OAAO,OAAO,gBAAgB,oBAAoB,wBAAwB;AAAA,EACzF;AACA,SAAO,EAAE,IAAI,MAAM,MAAM,QAAQ;AACnC;;;AC7BO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YACW,MACT,SACS,QACT;AACA,UAAM,OAAO;AAJJ;AAEA;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EANW;AAAA,EAEA;AAKb;AAIA,IAAI,gBAAiD;AACrD,SAAS,cAAwC;AAC/C,MAAI,CAAC,cAAe,iBAAgB,aAAa;AACjD,SAAO;AACT;AAOA,eAAsB,QACpB,QACAC,OACA,MACA,WACA,YAAqB,OACT;AACZ,QAAM,MAAMA,MAAK,WAAW,MAAM,IAAIA,QAAO,GAAG,QAAQ,CAAC,GAAGA,KAAI;AAChE,QAAM,UAAkC,CAAC;AACzC,UAAQ,kBAAkB,IAAI;AAC9B,QAAM,WAAW,MAAM,YAAY;AACnC,MAAI,UAAU;AACZ,YAAQ,cAAc,IAAI,SAAS;AACnC,YAAQ,gBAAgB,IAAI,SAAS;AAAA,EACvC;AACA,MAAI,UAAW,SAAQ,eAAe,IAAI,UAAU,SAAS;AAC7D,MAAI,SAAS,OAAW,SAAQ,cAAc,IAAI;AAElD,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,UAAU,KAAK;AAAA,MACzB;AAAA,MACA;AAAA,MACA,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,IACpD,CAAC;AAAA,EACH,SAAS,GAAQ;AACf,UAAM,IAAI,SAAS,iBAAiB,GAAG,WAAW,gBAAgB,CAAC;AAAA,EACrE;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAM,cAAc,IAAI,QAAQ,IAAI,cAAc,KAAK;AACvD,MAAI,SAAc;AAClB,MAAI,YAAY,SAAS,kBAAkB,KAAK,KAAK,SAAS,GAAG;AAC/D,QAAI;AACF,eAAS,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACN,eAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,UAAU,OAAO,OAAO,SAAS,UAAU;AAC7C,YAAM,IAAI,SAAS,OAAO,MAAsB,OAAO,WAAW,aAAa,IAAI,MAAM;AAAA,IAC3F;AACA,UAAM,IAAI,SAAS,iBAAiB,QAAQ,IAAI,MAAM,IAAI,IAAI,MAAM;AAAA,EACtE;AAEA,SAAO;AACT;;;AClFO,SAAS,WAAW,MAAyB;AAClD,SAAO,QAA4B,QAAQ,UAAU,MAAM,MAAS;AACtE;AAEO,SAAS,QAAQ,UAAkB;AACxC,SAAO,QAAyB,OAAO,UAAU,mBAAmB,QAAQ,CAAC,IAAI,QAAW,MAAS;AACvG;AAEO,SAAS,SAAS,UAAkB,MAAuB;AAChE,SAAO,QAA0B,QAAQ,UAAU,mBAAmB,QAAQ,CAAC,SAAS,MAAM,MAAS;AACzG;AAEO,SAAS,UAAU,UAAkB,SAAiB,OAAe,MAAwB;AAClG,SAAO;AAAA,IACL;AAAA,IACA,UAAU,mBAAmB,QAAQ,CAAC,WAAW,mBAAmB,OAAO,CAAC;AAAA,IAC5E;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,QAAQ,WAAmB;AACzC,SAAO,QAAyB,UAAU,gBAAgB,mBAAmB,SAAS,CAAC,IAAI,QAAW,MAAS;AACjH;;;ANzBA,IAAM,aAAa,KAAK,eAAe,EAAE,gBAAgB,EAAE,YAAY;AAEvE,eAAsB,oBAAqC;AACzD,QAAM,KAAc,0BAAgB,EAAE,OAAOC,QAAO,QAAQC,QAAO,CAAC;AACpE,MAAI;AACF,UAAM,QAAQ,MAAM,GAAG,SAAS,aAAa,GAAG,KAAK;AACrD,QAAI,CAAC,MAAM;AAAE,cAAQ,MAAM,gBAAgB;AAAG,aAAO;AAAA,IAAG;AAExD,UAAM,YAAY,MAAM,GAAG,SAAS,sCAAsC,GAAG,KAAK;AAClF,UAAM,QAAQ,WAAW,YAAW,oBAAI,KAAK,GAAE,YAAY;AAC3D,QAAI,CAAC,MAAM,KAAK,GAAG;AAAE,cAAQ,MAAM,qBAAqB;AAAG,aAAO;AAAA,IAAG;AAErE,UAAM,eAAe,MAAM,GAAG,SAAS,yBAAyB,GAAG,KAAK;AACxE,UAAM,gBAAgB,WAAW,WAAW;AAC5C,QAAI,CAAC,OAAO,SAAS,aAAa,KAAK,iBAAiB,GAAG;AACzD,cAAQ,MAAM,8CAA8C;AAAG,aAAO;AAAA,IACxE;AACA,UAAM,MAAM,IAAI,KAAK,IAAI,KAAK,KAAK,EAAE,QAAQ,IAAI,gBAAgB,IAAQ,EAAE,YAAY;AAEvF,UAAM,MAAM,MAAM,GAAG,SAAS,cAAc,UAAU,KAAK,GAAG,KAAK,KAAK;AACxE,UAAM,UAAU,MAAM,GAAG,SAAS,yBAAyB,GAAG,KAAK;AACnE,UAAM,MAAM,SAAS,SAAS,QAAQ,EAAE,IAAI;AAC5C,QAAI,QAAQ,WAAc,CAAC,OAAO,SAAS,GAAG,KAAK,MAAM,IAAI;AAC3D,cAAQ,MAAM,6CAA6C;AAAG,aAAO;AAAA,IACvE;AAEA,UAAM,OAAO,MAAM,WAAW;AAAA,MAC5B;AAAA,MAAM,YAAY;AAAA,MAAO,UAAU;AAAA,MAAK;AAAA,MACxC,GAAI,QAAQ,SAAY,EAAE,kBAAkB,IAAI,IAAI,CAAC;AAAA,IACvD,CAAC;AAED,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,oPAA4C;AACxD,YAAQ,IAAI,0BAAqB,KAAK,UAAU,OAAO,EAAE,CAAC,QAAG;AAC7D,YAAQ,IAAI,oPAA4C;AACxD,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,kBAAkB,KAAK,UAAU,EAAE;AAC/C,YAAQ,IAAI,yEAA+D;AAC3E,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,gDAAgD,KAAK,SAAS,EAAE;AAC5E,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAI,aAAa,UAAU;AACzB,cAAQ,MAAM,UAAU,EAAE,IAAI,IAAI,EAAE,OAAO,EAAE;AAC7C,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,SAAS,MAAM,GAAoB;AACjC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,IAAI,KAAK,CAAC;AACpB,SAAO,CAAC,OAAO,MAAM,EAAE,QAAQ,CAAC;AAClC;;;AO7DA,OAAOC,YAAW;AAClB,SAAS,UAAAC,eAAc;;;ACDvB,SAAgB,YAAAC,iBAAgB;AAChC,SAAS,OAAAC,MAAK,QAAAC,OAAM,YAAAC,iBAAgB;AAwB9B,SACE,OAAAC,MADF,QAAAC,aAAA;AAbC,SAAS,YAAY,EAAE,QAAQ,QAAQ,SAAS,GAAU;AAC/D,QAAM,CAAC,KAAK,MAAM,IAAIC,UAAS,CAAC;AAEhC,EAAAC,UAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,IAAI,QAAQ;AAAE,eAAS;AAAG;AAAA,IAAQ;AACtC,QAAI,OAAO,WAAW,EAAG;AACzB,QAAI,IAAI,SAAS;AAAE,cAAQ,MAAM,IAAI,OAAO,UAAU,OAAO,MAAM;AAAG;AAAA,IAAQ;AAC9E,QAAI,IAAI,WAAW;AAAE,cAAQ,MAAM,KAAK,OAAO,MAAM;AAAG;AAAA,IAAQ;AAChE,QAAI,IAAI,QAAQ;AAAE,aAAO,OAAO,GAAG,CAAE;AAAG;AAAA,IAAQ;AAAA,EAClD,CAAC;AAED,MAAI,OAAO,WAAW,GAAG;AACvB,WACE,gBAAAF,MAACG,MAAA,EAAI,eAAc,UACjB;AAAA,sBAAAJ,KAACK,OAAA,EAAK,uCAAyB;AAAA,MAC/B,gBAAAL,KAACK,OAAA,EAAK,UAAQ,MAAC,0DAA4C;AAAA,OAC7D;AAAA,EAEJ;AAEA,SACE,gBAAAJ,MAACG,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAJ,KAACK,OAAA,EAAK,mCAAqB;AAAA,IAC1B,OAAO,IAAI,CAAC,GAAG,MACd,gBAAAJ,MAACG,MAAA,EAAiB,eAAc,UAC9B;AAAA,sBAAAJ,KAACI,MAAA,EAAI,eAAc,OACjB,0BAAAH,MAACI,OAAA,EAAM;AAAA,cAAM,MAAM,WAAM;AAAA,QAAI;AAAA,QAAE,EAAE;AAAA,SAAK,GACxC;AAAA,MACA,gBAAAJ,MAACG,MAAA,EAAI,eAAc,OACjB;AAAA,wBAAAJ,KAACK,OAAA,EAAK,gBAAE;AAAA,QACR,gBAAAL,KAAC,eAAY,QAAQ,aAAa,QAAQ,EAAE,QAAQ;AAAA,SACtD;AAAA,SAPQ,EAAE,IAQZ,CACD;AAAA,IACD,gBAAAA,KAACI,MAAA,EAAI,WAAW,GACd,0BAAAJ,KAACK,OAAA,EAAK,UAAQ,MAAC,kEAAoC,GACrD;AAAA,KACF;AAEJ;;;ACnDA,SAAgB,WAAW,QAAQ,YAAAC,iBAAgB;AACnD,SAAS,OAAAC,MAAK,QAAAC,OAAM,cAAc;;;ACAlC,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAqBlB,gBAAAC,MAYF,QAAAC,aAZE;AAND,SAAS,aAAa,OAAc;AACzC,QAAM,EAAE,MAAM,YAAY,cAAc,WAAW,aAAa,qBAAqB,gBAAgB,IAAI;AAEzG,MAAI,CAAC,MAAM;AACT,WACE,gBAAAD,KAACE,MAAA,EAAI,eAAc,UACjB,0BAAAF,KAACG,OAAA,EAAK,gCAAa,GACrB;AAAA,EAEJ;AAEA,QAAM,MAA6B,KAAK,OAAO,KAAK,OAAK,EAAE,aAAa,UAAU;AAClF,QAAM,SAAgC,KAAK,OAAO,CAAC;AACnD,QAAM,aAAa,QAAQ,IAAI;AAC/B,QAAM,WAAW,eAAe,KAAK,iBAAiB;AAEtD,SACE,gBAAAF,MAACC,MAAA,EAAI,eAAc,UAAS,aAAY,SAAQ,UAAU,GACxD;AAAA,oBAAAD,MAACE,OAAA,EAAK;AAAA;AAAA,MACe,gBAAAH,KAACG,OAAA,EAAK,MAAI,MAAE,eAAK,MAAK;AAAA,MAAO;AAAA,MAAa,gBAAAH,KAACG,OAAA,EAAK,OAAO,YAAY,KAAK,MAAM,GAAI,eAAK,QAAO;AAAA,OACnH;AAAA,IAEA,gBAAAF,MAACC,MAAA,EAAI,WAAW,GAAG,eAAc,OAC/B;AAAA,sBAAAF,KAAC,eAAY,QAAQ,aAAa,QAAQ,WAAW;AAAA,MACrD,gBAAAC,MAACC,MAAA,EAAI,eAAc,UACjB;AAAA,wBAAAD,MAACE,OAAA,EAAK;AAAA;AAAA,UAAG;AAAA,WAAa;AAAA,QACtB,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,UAAE,gBAAAF,MAACE,OAAA,EAAK,UAAQ,MAAC;AAAA;AAAA,YAAE;AAAA,YAAY;AAAA,aAAC;AAAA,WAAO;AAAA,SAC/C;AAAA,OACF;AAAA,IAEA,gBAAAF,MAACC,MAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,sBAAAD,MAACE,OAAA,EAAK;AAAA;AAAA,QAAiB,KAAK,kBAAkB;AAAA,SAAE;AAAA,MAChD,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QAAiB,KAAK,QAAQ;AAAA,QAAI;AAAA,QAAK,KAAK,OAAO;AAAA,SAAO;AAAA,MAChE,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QACa,SAAS,GAAG,OAAO,IAAI,GAAG,OAAO,YAAY,KAAK,OAAO,SAAS,MAAM,EAAE,WAAM,OAAO,cAAc,KAAK;AAAA,SAC7H;AAAA,MACA,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,SAAkB,aAAa,KAAK,QAAQ,CAAC;AAAA,QAAE;AAAA,QAAI,IAAI,YAAY,EAAE;AAAA,SAAE;AAAA,MAC7E,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QAAiB;AAAA,SAAS;AAAA,MAChC,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QACa,wBAAwB,OAAO,WAAM,GAAG,mBAAmB;AAAA,QAC3E;AAAA,QACD,gBAAAH,KAACG,OAAA,EAAK,OAAO,kBAAkB,UAAU,UACtC,4BAAkB,WAAM,UAC3B;AAAA,SACF;AAAA,OACF;AAAA,IAEA,gBAAAH,KAACE,MAAA,EAAI,WAAW,GACd,0BAAAF,KAACG,OAAA,EAAK,UAAQ,MAAC,oDAAsC,GACvD;AAAA,KACF;AAEJ;AAEA,SAAS,QAAQ,MAA+B;AAC9C,QAAM,QAAQ,IAAI,KAAK,KAAK,UAAU,EAAE,QAAQ;AAChD,QAAM,MAAM,IAAI,KAAK,KAAK,QAAQ,EAAE,QAAQ;AAC5C,QAAM,MAAM,IAAI,KAAK,KAAK,WAAW,EAAE,QAAQ;AAC/C,MAAI,OAAO,MAAO,QAAO;AACzB,QAAM,KAAK,MAAM,UAAU,MAAM;AACjC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AACnC;AAEA,SAAS,IAAI,KAAa,OAAuB;AAC/C,QAAM,SAAS,KAAK,MAAM,MAAM,KAAK;AACrC,SAAO,SAAI,OAAO,MAAM,IAAI,SAAI,OAAO,QAAQ,MAAM;AACvD;AAEA,SAAS,YAAY,QAA2C;AAC9D,MAAI,WAAW,OAAQ,QAAO;AAC9B,MAAI,WAAW,UAAW,QAAO;AACjC,SAAO;AACT;AAEA,SAAS,eAAe,SAAyB;AAC/C,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AACzC,QAAM,IAAI,KAAK,MAAM,IAAI,IAAI;AAC7B,QAAM,IAAI,KAAK,MAAO,IAAI,OAAQ,EAAE;AACpC,QAAM,KAAK,IAAI;AACf,SAAO,GAAG,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,GAAG,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAC5G;;;ACnFO,SAAS,iBAAiB,MAAkC;AACjE,MAAI,QAA8C;AAClD,MAAI,aAAa;AACjB,MAAI,UAAU;AAEd,QAAM,OAAO,MAAM;AACjB,cAAU;AACV,QAAI,MAAO,cAAa,KAAK;AAC7B,YAAQ;AAAA,EACV;AAEA,OAAK,YAAY,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AAE/D,QAAM,WAAW,CAAC,UAAkB;AAClC,QAAI,QAAS;AACb,YAAQ,WAAW,MAAM,KAAK;AAAA,EAChC;AAEA,QAAM,OAAO,YAAY;AACvB,QAAI,QAAS;AACb,QAAI;AACF,YAAM,SAAS,KAAK,iBAAiB;AACrC,YAAM,OAAO,MAAM,KAAK,cAAc,MAAM;AAC5C,mBAAa;AACb,WAAK,UAAU,IAAI;AACnB,UAAI,KAAK,gBAAgB,YAAY;AACnC,aAAK,WAAW;AAChB,aAAK;AACL;AAAA,MACF;AACA,eAAS,KAAK,UAAU;AAAA,IAC1B,SAAS,KAAK;AACZ,WAAK,QAAQ,GAAG;AAChB,YAAM,QAAQ,KAAK,cAAc,KAAK,IAAI,YAAY,KAAK,cAAc,SAAS,CAAC,CAAC,KAAK;AACzF,oBAAc;AACd,eAAS,KAAK;AAAA,IAChB;AAAA,EACF;AAEA,WAAS,CAAC;AACZ;;;AC3CO,SAAS,YAAY,MAA6B;AACvD,MAAI,QAA8C;AAClD,MAAI,UAAU;AAEd,QAAM,OAAO,MAAM;AACjB,cAAU;AACV,QAAI,MAAO,cAAa,KAAK;AAC7B,YAAQ;AAAA,EACV;AAEA,OAAK,YAAY,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AAE/D,QAAM,OAAO,YAAY;AACvB,QAAI,QAAS;AACb,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,UAAU;AAClC,UAAI,CAAC,QAAS,MAAK,WAAW,IAAI;AAAA,IACpC,SAAS,KAAK;AACZ,UAAI,CAAC,QAAS,MAAK,QAAQ,GAAG;AAAA,IAChC;AACA,QAAI,CAAC,QAAS,SAAQ,WAAW,MAAM,KAAK,UAAU;AAAA,EACxD;AAEA,UAAQ,WAAW,MAAM,CAAC;AAC5B;;;AClCA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAKtB,eAAsB,YAAkC;AACtD,QAAM,OAAO,kBAAkB;AAC/B,QAAM,QAAQ,MAAM,eAAe,IAAI;AACvC,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,MAAM,QAAQ,IAAI;AAC5B,aAAS,EAAE;AACX,cAAU,EAAE;AAAA,EACd;AACA,SAAO,EAAE,OAAO,OAAO;AACzB;AAGA,eAAsB,kBAAmC;AACvD,QAAM,EAAE,OAAO,OAAO,IAAI,MAAM,UAAU;AAC1C,SAAO,QAAQ;AACjB;AAEA,eAAe,eAAe,MAAiC;AAC7D,MAAI;AACJ,MAAI;AACF,eAAW,MAAS,YAAQ,IAAI;AAAA,EAClC,SAAS,GAAQ;AACf,QAAI,GAAG,SAAS,SAAU,QAAO,CAAC;AAClC,UAAM;AAAA,EACR;AACA,QAAM,MAAgB,CAAC;AACvB,aAAW,WAAW,UAAU;AAC9B,UAAM,aAAkB,WAAK,MAAM,OAAO;AAC1C,QAAIC;AACJ,QAAI;AACF,MAAAA,QAAO,MAAS,SAAK,UAAU;AAAA,IACjC,QAAQ;AACN;AAAA,IACF;AACA,QAAI,CAACA,MAAK,YAAY,EAAG;AACzB,UAAM,UAAU,MAAS,YAAQ,UAAU;AAC3C,eAAW,SAAS,SAAS;AAC3B,UAAI,MAAM,SAAS,QAAQ,EAAG,KAAI,KAAU,WAAK,YAAY,KAAK,CAAC;AAAA,IACrE;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,OAAO,OAAwB;AACtC,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAEA,eAAe,QAAQ,MAAoC;AACzD,MAAI;AACJ,MAAI;AACF,UAAM,MAAS,aAAS,MAAM,MAAM;AAAA,EACtC,QAAQ;AACN,WAAO,EAAE,OAAO,GAAG,QAAQ,EAAE;AAAA,EAC/B;AACA,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,aAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,QAAI,CAAC,KAAK,KAAK,EAAG;AAClB,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACN;AAAA,IACF;AACA,UAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAI,CAAC,MAAO;AACZ,aAAS,OAAO,MAAM,YAAY,IACzB,OAAO,MAAM,2BAA2B,IACxC,OAAO,MAAM,uBAAuB;AAC7C,cAAU,OAAO,MAAM,aAAa;AAAA,EACtC;AACA,SAAO,EAAE,OAAO,OAAO;AACzB;;;AC9EO,SAAS,gBAAgB,MAA4D;AAC1F,SAAO,KAAK;AACd;;;ALkHM,SACE,OAAAC,MADF,QAAAC,aAAA;AAlGC,SAAS,QAAQ,EAAE,QAAQ,kBAAkB,aAAa,YAAY,GAAiB;AAC5F,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAiC,IAAI;AAC7D,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAsB,IAAI;AAC1D,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAkB,IAAI;AACtD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAe,oBAAI,KAAK,CAAC;AACvD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAwB,IAAI;AAEhE,QAAM,cAAc,OAAO,gBAAgB;AAC3C,QAAM,aAAa,OAAO,WAAW;AACrC,QAAM,qBAAqB,OAAe,gBAAgB;AAC1D,QAAM,OAAO,OAAO,IAAI,gBAAgB,CAAC;AAGzC,YAAU,MAAM;AACd,UAAM,IAAI,YAAY,MAAM,WAAW,oBAAI,KAAK,CAAC,GAAG,GAAK;AACzD,WAAO,MAAM,cAAc,CAAC;AAAA,EAC9B,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,QAAI,WAAW,WAAW,MAAM,WAAW,QAAQ;AACjD,sBAAgB,EAAE,KAAK,WAAS;AAC9B,oBAAY,UAAU;AACtB,mBAAW,UAAU;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,MAAM,MAAM,CAAC;AAEjB,YAAU,MAAM;AACd,gBAAY;AAAA,MACV,WAAW,MAAgB,QAAQ,OAAO,SAAS;AAAA,MACnD,YAAY;AAAA,MACZ,YAAY,CAAC,MAAM,QAAQ,CAAC;AAAA,MAC5B,SAAS,MAAM;AAAA,MAAqC;AAAA,MACpD,aAAa,KAAK,QAAQ;AAAA,IAC5B,CAAC;AAED,qBAAiB;AAAA,MACf,eAAe,OAAO,kBAAkB;AACtC,cAAM,OAAO,MAAgB;AAAA,UAC3B,OAAO;AAAA,UAAW,OAAO;AAAA,UAAU,OAAO;AAAA,UAAiB,EAAE,gBAAgB,cAAc;AAAA,QAC7F;AACA,cAAM,UAAsB;AAAA,UAC1B,GAAG;AAAA,UACH,kBAAkB;AAAA,UAClB,oBAAmB,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC5C;AACA,cAAM,eAAe,OAAO;AAC5B,eAAO;AAAA,MACT;AAAA,MACA,kBAAkB,MAAM;AACtB,YAAI,WAAW,QAAS,QAAO;AAC/B,eAAO,KAAK,IAAI,GAAG,mBAAmB,UAAU,YAAY,OAAO;AAAA,MACrE;AAAA,MACA,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW,CAAC,SAA4B;AACtC,oBAAY,oBAAI,KAAK,CAAC;AACtB,oBAAY,IAAI;AAChB,YAAI,KAAK,gBAAgB,WAAY,MAAK;AAAA,MAC5C;AAAA,MACA,SAAS,CAAC,QAAQ;AAChB,YAAI,eAAe,YAAY,IAAI,SAAS,oBAAoB;AAC9D,wBAAc,IAAI,OAAO;AACzB,eAAK,QAAQ,MAAM;AACnB,eAAK;AACL;AAAA,QACF;AACA,oBAAY,KAAK;AAAA,MACnB;AAAA,MACA,YAAY,MAAM,KAAK;AAAA,MACvB,aAAa,KAAK,QAAQ;AAAA,IAC5B,CAAC;AAGD,UAAM,UAAU,YAAY,YAAY;AACtC,UAAI;AACF,2BAAmB,UAAU,MAAM,gBAAgB;AAAA,MACrD,QAAQ;AAAA,MAAuB;AAAA,IACjC,GAAG,GAAK;AAER,oBAAgB,EAAE,KAAK,OAAK;AAAE,yBAAmB,UAAU;AAAA,IAAG,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAE/E,UAAM,aAAa,KAAK;AACxB,WAAO,MAAM;AACX,oBAAc,OAAO;AACrB,iBAAW,MAAM;AAAA,IACnB;AAAA,EAEF,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsB,WACxB,KAAK,IAAI,GAAG,KAAK,OAAO,QAAQ,QAAQ,IAAI,SAAS,QAAQ,KAAK,GAAI,CAAC,IACvE;AAEJ,MAAI,YAAY;AACd,WACE,gBAAAD,MAACE,MAAA,EAAI,eAAc,UAAS,SAAS,GACnC;AAAA,sBAAAH,KAACI,OAAA,EAAK,OAAM,OAAM,MAAI,MAAC,sDAAmC;AAAA,MAC1D,gBAAAJ,KAACI,OAAA,EAAM,sBAAW;AAAA,OACpB;AAAA,EAEJ;AAEA,SACE,gBAAAJ;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,YAAY,OAAO;AAAA,MACnB,cAAc,OAAO;AAAA,MACrB,WAAW,OAAO;AAAA,MAClB;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA;AAAA,EACnB;AAEJ;AAEA,eAAsB,kBAAkB,MAIwB;AAC9D,QAAM,eAAe,MAAM,gBAAgB;AAC3C,MAAI,KAAK,QAAQ;AACf,WAAO;AAAA,MACL,kBAAkB,KAAK,IAAI,GAAG,eAAe,KAAK,OAAO,gBAAgB;AAAA,MACzE,aAAa,KAAK,eAAe;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AAAA,IACL,kBAAkB,gBAAgB,EAAE,cAAc,QAAQ,KAAK,WAAW,CAAC;AAAA,IAC3E,aAAa,KAAK,eAAe;AAAA,EACnC;AACF;;;AF9IA,eAAsB,YAAY,UAA+C;AAC/E,MAAI,CAAC,UAAU;AACb,YAAQ,MAAM,qCAAqC;AACnD,WAAO;AAAA,EACT;AACA,QAAM,OAAO,SAAS,YAAY;AAElC,QAAM,WAAW,MAAM,aAAa;AACpC,MAAI,CAAC,UAAU;AAEb,YAAQ,MAAM,iDAAiD;AAC/D,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,IAAI;AAAA,EAC3B,SAAS,GAAG;AACV,QAAI,aAAa,UAAU;AACzB,UAAI,EAAE,SAAS,iBAAkB,SAAQ,MAAM,0BAA0B,IAAI,GAAG;AAAA,UAC3E,SAAQ,MAAM,UAAU,EAAE,IAAI,IAAI,EAAE,OAAO,EAAE;AAClD,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACA,MAAI,KAAK,WAAW,YAAY;AAC9B,YAAQ,MAAM,8BAA8B;AAC5C,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,KAAK,OAAO,KAAK,OAAK,EAAE,YAAY,SAAS,OAAO,KAAK;AAE1E,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,MAAI,UAAU;AAEZ,iBAAa,SAAS;AACtB,mBAAe,SAAS;AACxB,eAAW;AAAA,EACb,OAAO;AACL,UAAM,SAAS,MAAM,WAAW;AAChC,QAAI,OAAO,OAAO,WAAW,GAAG;AAC9B,cAAQ,MAAM,8DAA8D;AAC5E,aAAO;AAAA,IACT;AACA,UAAM,SAAS,MAAM,UAAU,OAAO,MAAM;AAC5C,QAAI,CAAC,QAAQ;AAAE,cAAQ,IAAI,YAAY;AAAG,aAAO;AAAA,IAAG;AACpD,iBAAa,OAAO;AACpB,mBAAe,OAAO;AACtB,eAAW;AAAA,EACb;AAEA,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,SAAS,MAAM,EAAE,OAAO,EAAE,MAAM,YAAY,QAAQ,aAAa,EAAE,CAAC;AAAA,EACvF,SAAS,GAAG;AACV,QAAI,aAAa,UAAU;AACzB,UAAI,EAAE,SAAS,YAAa,SAAQ,MAAM,oBAAoB;AAAA,eACrD,EAAE,SAAS,gBAAiB,SAAQ,MAAM,sBAAsB;AAAA,eAChE,EAAE,SAAS,iBAAkB,SAAQ,MAAM,0BAA0B,IAAI,GAAG;AAAA,eAC5E,EAAE,SAAS,mBAAoB,SAAQ,MAAM,EAAE,OAAO;AAAA,eACtD,EAAE,SAAS,kBAAmB,SAAQ,MAAM,EAAE,OAAO;AAAA,eACrD,EAAE,SAAS,oBAAqB,SAAQ,MAAM,UAAU,EAAE,OAAO,EAAE;AAAA,UACvE,SAAQ,MAAM,UAAU,EAAE,IAAI,IAAI,EAAE,OAAO,EAAE;AAClD,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AAGA,QAAM,QAAQ,MAAM,eAAe,IAAI;AACvC,QAAM,aAAa,YAAY,OAAO,aAAa,SAAS,WAAW,MAAM,mBAAoB,UAAU,kBAAkB;AAE7H,QAAM,SAA6B,KAAK;AACxC,QAAM,SAAqB;AAAA,IACzB,WAAW;AAAA,IACX,SAAS,KAAK;AAAA,IACd,UAAU,SAAS;AAAA,IACnB,iBAAiB,SAAS;AAAA,IAC1B,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,WAAW,UAAU,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACzD,kBAAkB;AAAA,IAClB,oBAAmB,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,EAC7C;AACA,QAAM,eAAe,MAAM;AAE3B,QAAM,UAAU,MAAM,kBAAkB,EAAE,QAAQ,YAAY,QAAQ,QAAQ,SAAS,CAAC;AACxF,QAAM,MAAMK,QAAOC,OAAM,cAAc,SAAS,EAAE,QAAQ,GAAG,SAAS,aAAa,SAAS,aAAa,CAAC,CAAC;AAC3G,QAAM,IAAI,cAAc;AACxB,SAAO;AACT;AAEA,eAAe,UAAU,QAAoD;AAC3E,SAAO,IAAI,QAAQ,aAAW;AAC5B,UAAM,MAAMD;AAAA,MACVC,OAAM,cAAc,aAAa;AAAA,QAC/B;AAAA,QACA,QAAQ,CAAC,MAAmB;AAAE,cAAI,QAAQ;AAAG,kBAAQ,CAAC;AAAA,QAAG;AAAA,QACzD,UAAU,MAAM;AAAE,cAAI,QAAQ;AAAG,kBAAQ,IAAI;AAAA,QAAG;AAAA,MAClD,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;;;AQtHA,YAAYC,eAAc;AAC1B,SAAS,SAAAC,QAAO,UAAAC,eAAc;AAI9B,eAAsB,WAAW,WAAgD;AAC/E,MAAI,CAAC,WAAW;AACd,YAAQ,MAAM,qCAAqC;AACnD,WAAO;AAAA,EACT;AACA,QAAM,KAAc,0BAAgB,EAAE,OAAOC,QAAO,QAAQC,QAAO,CAAC;AACpE,QAAM,UAAU,MAAM,GAAG,SAAS,kDAAkD,GAAG,KAAK,EAAE,YAAY;AAC1G,KAAG,MAAM;AACT,MAAI,WAAW,OAAO,WAAW,OAAO;AACtC,YAAQ,IAAI,YAAY;AACxB,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,QAAQ,SAAS;AACvB,YAAQ,IAAI,oBAAe;AAC3B,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAI,aAAa,UAAU;AACzB,UAAI,EAAE,SAAS,iBAAkB,SAAQ,MAAM,+BAA+B;AAAA,UACzE,SAAQ,MAAM,UAAU,EAAE,IAAI,IAAI,EAAE,OAAO,EAAE;AAClD,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;;;AC7BA,YAAYC,eAAc;AAC1B,SAAS,SAAAC,QAAO,UAAAC,eAAc;AAS9B,eAAsB,cAA+B;AACnD,QAAM,WAAW,MAAM,aAAa;AACpC,QAAM,KAAc,0BAAgB,EAAE,OAAOC,QAAO,QAAQC,QAAO,CAAC;AACpE,MAAI;AACF,QAAI,UAAU;AACZ,cAAQ,IAAI,wBAAwB,SAAS,YAAY,EAAE;AAC3D,YAAMC,QAAO,MAAM,GAAG,SAAS,sDAAsD,GAAG,KAAK;AAC7F,UAAI,CAACA,MAAK;AACR,gBAAQ,IAAI,qBAAqB;AACjC,eAAO;AAAA,MACT;AACA,YAAMC,KAAI,oBAAoBD,IAAG;AACjC,UAAI,CAACC,GAAE,IAAI;AAAE,gBAAQ,MAAMA,GAAE,KAAK;AAAG,eAAO;AAAA,MAAG;AAC/C,YAAM,UAAoB,EAAE,GAAG,UAAU,cAAcA,GAAE,KAAK;AAC9D,YAAM,aAAa,OAAO;AAC1B,cAAQ,IAAI,2BAA2B,QAAQ,YAAY,EAAE;AAC7D,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,MAAM,GAAG,SAAS,2CAA2C,GAAG,KAAK;AAClF,UAAM,IAAI,oBAAoB,GAAG;AACjC,QAAI,CAAC,EAAE,IAAI;AAAE,cAAQ,MAAM,EAAE,KAAK;AAAG,aAAO;AAAA,IAAG;AAE/C,UAAM,WAAqB;AAAA,MACzB,SAAS,eAAe;AAAA,MACxB,cAAc,EAAE;AAAA,MAChB,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC;AACA,UAAM,aAAa,QAAQ;AAC3B,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,YAAY,SAAS,YAAY,GAAG;AAChD,YAAQ,IAAI,qEAAqE;AACjF,WAAO;AAAA,EACT,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;;;ACnCA,IAAM,OAAO,gBAAgB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBxC,eAAe,OAAwB;AACrC,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,MAAM,KAAK,CAAC;AAElB,MAAI,CAAC,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAAE,YAAQ,IAAI,IAAI;AAAG,WAAO;AAAA,EAAG;AAC7E,MAAI,QAAQ,eAAe,QAAQ,MAAM;AAAE,YAAQ,IAAI,WAAW;AAAG,WAAO;AAAA,EAAG;AAE/E,MAAI,QAAQ,OAAQ,QAAO,YAAY;AAGvC,QAAM,WAAW,MAAM,aAAa;AACpC,MAAI,CAAC,UAAU;AACb,YAAQ,MAAM,gFAAgF;AAC9F,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,UAAU;AACpB,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,SAAU,QAAO,oBAAoB;AACjD,QAAI,QAAQ,OAAQ,QAAO,kBAAkB;AAC7C,QAAI,QAAQ,OAAQ,QAAO,kBAAkB,KAAK,CAAC,CAAC;AACpD,QAAI,QAAQ,SAAU,QAAO,oBAAoB,KAAK,CAAC,CAAC;AACxD,YAAQ,MAAM,8BAA8B,OAAO,QAAQ,EAAE;AAC7D,YAAQ,MAAM,8EAA8E;AAC5F,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,SAAU,QAAO,kBAAkB;AAC/C,MAAI,QAAQ,OAAU,QAAO,YAAY,KAAK,CAAC,CAAC;AAChD,MAAI,QAAQ,MAAU,QAAO,WAAW,KAAK,CAAC,CAAC;AAE/C,UAAQ,MAAM,oBAAoB,GAAG,EAAE;AACvC,UAAQ,MAAM,IAAI;AAClB,SAAO;AACT;AAEA,KAAK,EAAE;AAAA,EACL,UAAQ,QAAQ,KAAK,IAAI;AAAA,EACzB,SAAO;AACL,YAAQ,MAAM,KAAK,SAAS,GAAG;AAC/B,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","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"]}
|