@mauricode/token-derby 2.10.1 → 2.12.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 +18 -1
- package/dist/bin.js +45 -381
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,23 @@ token-derby rejoin <join-code>
|
|
|
41
41
|
token-derby end <admin-code>
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Organisations
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Join an organisation with a join token
|
|
48
|
+
token-derby organisation join <token>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Everything else — creating an organisation, managing members, and configuring
|
|
52
|
+
the schedule or webhook — is done on the web:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
token-derby web
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This opens `token-derby.mauricode.co.uk/org-manager` with a one-time login
|
|
59
|
+
link, signed in as your CLI identity.
|
|
60
|
+
|
|
44
61
|
## What's tracked
|
|
45
62
|
|
|
46
63
|
The CLI sums `message.usage.output_tokens` across every `*.jsonl` under `~/.claude/projects/`. This includes **subagents and dynamic workflows** — their transcripts nest under `<project>/<session>/subagents/…` (and `…/subagents/workflows/wf_<id>/…`), and the scanner recurses into all of them, so a Plan/Workflow that fans out across many agents counts all of that real output. Your "race tokens" are everything generated since the moment you joined. Tokens generated while disconnected are skipped — that window is your crash penalty.
|
|
@@ -76,4 +93,4 @@ at usage you didn't produce.
|
|
|
76
93
|
- `TOKEN_DERBY_API_BASE` — override the API base URL (default: `https://token-derby.mauricode.co.uk/api`)
|
|
77
94
|
- `TOKEN_DERBY_HOME` — override the data directory (default: `~/.token-derby`)
|
|
78
95
|
- `TOKEN_DERBY_CLAUDE_DIR` — override the transcripts directory (default: `~/.claude/projects`)
|
|
79
|
-
- **Top-5 conversations (primary):** a race can be created so that only each racer's **5 most-active conversations per heartbeat** count toward their **primary** model's score (secondaries unaffected). The race creator opts in at `token-derby create` (prompt) or
|
|
96
|
+
- **Top-5 conversations (primary):** a race can be created so that only each racer's **5 most-active conversations per heartbeat** count toward their **primary** model's score (secondaries unaffected). The race creator opts in at `token-derby create` (prompt) or, for organisation-scheduled races, via the "Primary top-5 cap" option on the schedule tab of `token-derby web`. Off by default (every conversation counts).
|
package/dist/bin.js
CHANGED
|
@@ -45,7 +45,6 @@ var CLI_VERSION_HEADER = "x-cli-version";
|
|
|
45
45
|
var USER_ID_HEADER = "x-user-id";
|
|
46
46
|
var USER_TOKEN_HEADER = "x-user-token";
|
|
47
47
|
var USER_NAME_MAX_LENGTH = 40;
|
|
48
|
-
var ORG_NAME_MAX_LENGTH = 12;
|
|
49
48
|
var ORG_NAME_PATTERN = /^[A-Za-z0-9]{1,12}$/;
|
|
50
49
|
|
|
51
50
|
// ../shared/dist/version-match.js
|
|
@@ -206,34 +205,8 @@ var HATS = [
|
|
|
206
205
|
{ id: "aurora_helm", name: "Aurora Helm", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...QQQQQ...", "...QQQQQ...", "..AAAAAAA..", "..AAAAAAA..", "..AQAAAQA..", "..AQAAAQA.."], colors: { A: "#00CED1", Q: "#00FF7F" }, animation: { type: "cycle", frames: ["#0000FF", "#0066FF", "#00BFFF", "#00CED1", "#00FF7F", "#7CFC00", "#00FF7F", "#00CED1"], fps: 4 } }
|
|
207
206
|
];
|
|
208
207
|
|
|
209
|
-
// ../shared/dist/
|
|
210
|
-
var
|
|
211
|
-
function parseWeekdays(spec) {
|
|
212
|
-
const tokens = spec.toLowerCase().split(",").map((s) => s.trim()).filter(Boolean);
|
|
213
|
-
if (tokens.length === 0)
|
|
214
|
-
return null;
|
|
215
|
-
const set = /* @__PURE__ */ new Set();
|
|
216
|
-
for (const tok of tokens) {
|
|
217
|
-
if (tok.includes("-")) {
|
|
218
|
-
const parts = tok.split("-");
|
|
219
|
-
if (parts.length !== 2)
|
|
220
|
-
return null;
|
|
221
|
-
const [a, b] = parts;
|
|
222
|
-
const ai = ORDER.indexOf(a);
|
|
223
|
-
const bi = ORDER.indexOf(b);
|
|
224
|
-
if (ai < 0 || bi < 0 || ai > bi)
|
|
225
|
-
return null;
|
|
226
|
-
for (let i = ai; i <= bi; i++)
|
|
227
|
-
set.add(i + 1);
|
|
228
|
-
} else {
|
|
229
|
-
const i = ORDER.indexOf(tok);
|
|
230
|
-
if (i < 0)
|
|
231
|
-
return null;
|
|
232
|
-
set.add(i + 1);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return [...set].sort((x, y) => x - y);
|
|
236
|
-
}
|
|
208
|
+
// ../shared/dist/series-transform.js
|
|
209
|
+
var PACE_WINDOW_MS = 15 * 6e4;
|
|
237
210
|
|
|
238
211
|
// src/ui/HorseSprite.tsx
|
|
239
212
|
import { Box, Text } from "ink";
|
|
@@ -740,8 +713,8 @@ var HEARTBEAT_RETRY_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
|
|
|
740
713
|
// src/version.ts
|
|
741
714
|
import { createRequire } from "module";
|
|
742
715
|
function readVersion() {
|
|
743
|
-
if ("2.
|
|
744
|
-
return "2.
|
|
716
|
+
if ("2.12.0".length > 0) {
|
|
717
|
+
return "2.12.0";
|
|
745
718
|
}
|
|
746
719
|
try {
|
|
747
720
|
const req = createRequire(import.meta.url);
|
|
@@ -897,18 +870,12 @@ function heartbeat(joinCode, horseId, token, body) {
|
|
|
897
870
|
function endRace(adminCode) {
|
|
898
871
|
return request("DELETE", `/races/admin/${encodeURIComponent(adminCode)}`, void 0, void 0);
|
|
899
872
|
}
|
|
900
|
-
function createOrganisation(body) {
|
|
901
|
-
return request("POST", "/organisations", body, void 0);
|
|
902
|
-
}
|
|
903
873
|
function joinOrganisation(body) {
|
|
904
874
|
return request("POST", "/organisations/join", body, void 0);
|
|
905
875
|
}
|
|
906
876
|
function listOrganisations() {
|
|
907
877
|
return request("GET", "/organisations", void 0, void 0);
|
|
908
878
|
}
|
|
909
|
-
function getOrganisation(name) {
|
|
910
|
-
return request("GET", `/organisations/${encodeURIComponent(name)}`, void 0, void 0);
|
|
911
|
-
}
|
|
912
879
|
function initJockey(body) {
|
|
913
880
|
return request("POST", "/jockey/init", body, void 0);
|
|
914
881
|
}
|
|
@@ -937,60 +904,15 @@ function deleteStableHorse(stableHorseId) {
|
|
|
937
904
|
void 0
|
|
938
905
|
);
|
|
939
906
|
}
|
|
940
|
-
function setOrgWebhook(orgName, body) {
|
|
941
|
-
return request(
|
|
942
|
-
"PUT",
|
|
943
|
-
`/organisations/${encodeURIComponent(orgName)}/webhook`,
|
|
944
|
-
body,
|
|
945
|
-
void 0
|
|
946
|
-
);
|
|
947
|
-
}
|
|
948
|
-
function getOrgWebhook(orgName) {
|
|
949
|
-
return request(
|
|
950
|
-
"GET",
|
|
951
|
-
`/organisations/${encodeURIComponent(orgName)}/webhook`,
|
|
952
|
-
void 0,
|
|
953
|
-
void 0
|
|
954
|
-
);
|
|
955
|
-
}
|
|
956
|
-
function deleteOrgWebhook(orgName) {
|
|
957
|
-
return request(
|
|
958
|
-
"DELETE",
|
|
959
|
-
`/organisations/${encodeURIComponent(orgName)}/webhook`,
|
|
960
|
-
void 0,
|
|
961
|
-
void 0
|
|
962
|
-
);
|
|
963
|
-
}
|
|
964
|
-
function setOrgSchedule(orgName, body) {
|
|
965
|
-
return request(
|
|
966
|
-
"PUT",
|
|
967
|
-
`/organisations/${encodeURIComponent(orgName)}/schedule`,
|
|
968
|
-
body,
|
|
969
|
-
void 0
|
|
970
|
-
);
|
|
971
|
-
}
|
|
972
|
-
function getOrgSchedule(orgName) {
|
|
973
|
-
return request(
|
|
974
|
-
"GET",
|
|
975
|
-
`/organisations/${encodeURIComponent(orgName)}/schedule`,
|
|
976
|
-
void 0,
|
|
977
|
-
void 0
|
|
978
|
-
);
|
|
979
|
-
}
|
|
980
|
-
function clearOrgSchedule(orgName) {
|
|
981
|
-
return request(
|
|
982
|
-
"DELETE",
|
|
983
|
-
`/organisations/${encodeURIComponent(orgName)}/schedule`,
|
|
984
|
-
void 0,
|
|
985
|
-
void 0
|
|
986
|
-
);
|
|
987
|
-
}
|
|
988
907
|
function rollHat(stableHorseId) {
|
|
989
908
|
return request("POST", `/jockey/me/horses/${encodeURIComponent(stableHorseId)}/roll`, void 0, void 0);
|
|
990
909
|
}
|
|
991
910
|
function equipHat(stableHorseId, body) {
|
|
992
911
|
return request("POST", `/jockey/me/horses/${encodeURIComponent(stableHorseId)}/equip`, body, void 0);
|
|
993
912
|
}
|
|
913
|
+
function createWebSession() {
|
|
914
|
+
return request("POST", "/web-sessions", void 0, void 0);
|
|
915
|
+
}
|
|
994
916
|
|
|
995
917
|
// src/commands/stable-create.ts
|
|
996
918
|
async function stableCreateCommand() {
|
|
@@ -2802,8 +2724,8 @@ function pendingFor(horse) {
|
|
|
2802
2724
|
}
|
|
2803
2725
|
async function promptYesNo(question) {
|
|
2804
2726
|
resetStdinAfterInk();
|
|
2805
|
-
const
|
|
2806
|
-
const rl =
|
|
2727
|
+
const readline6 = await import("readline/promises");
|
|
2728
|
+
const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
|
|
2807
2729
|
const a = (await rl.question(question)).trim().toLowerCase();
|
|
2808
2730
|
rl.close();
|
|
2809
2731
|
return a === "" || a === "y" || a === "yes";
|
|
@@ -2927,39 +2849,6 @@ No hat this time. +${result.xp_awarded} XP toward your next level.
|
|
|
2927
2849
|
}
|
|
2928
2850
|
}
|
|
2929
2851
|
|
|
2930
|
-
// src/commands/org-create.ts
|
|
2931
|
-
import * as readline6 from "readline/promises";
|
|
2932
|
-
import { stdin as stdin6, stdout as stdout6 } from "process";
|
|
2933
|
-
async function orgCreateCommand() {
|
|
2934
|
-
const rl = readline6.createInterface({ input: stdin6, output: stdout6 });
|
|
2935
|
-
try {
|
|
2936
|
-
const name = (await rl.question(`Organisation name (1\u2013${ORG_NAME_MAX_LENGTH} alphanumeric chars): `)).trim();
|
|
2937
|
-
if (!ORG_NAME_PATTERN.test(name)) {
|
|
2938
|
-
console.error(`Name must be 1\u2013${ORG_NAME_MAX_LENGTH} alphanumeric characters (no spaces or symbols).`);
|
|
2939
|
-
return 1;
|
|
2940
|
-
}
|
|
2941
|
-
const resp = await createOrganisation({ name });
|
|
2942
|
-
console.log("");
|
|
2943
|
-
console.log(` Organisation created: ${resp.org_name}`);
|
|
2944
|
-
console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
2945
|
-
console.log(` \u2551 JOIN TOKEN: ${resp.org_join_token.padEnd(43)}\u2551`);
|
|
2946
|
-
console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
2947
|
-
console.log(" \u26A0 Share this token to invite members. Treat it as a secret.");
|
|
2948
|
-
console.log("");
|
|
2949
|
-
console.log(` Members join with: token-derby organisation join ${resp.org_join_token}`);
|
|
2950
|
-
console.log(` Create org races: token-derby create --organisation ${resp.org_name}`);
|
|
2951
|
-
return 0;
|
|
2952
|
-
} catch (e) {
|
|
2953
|
-
if (e instanceof ApiError) {
|
|
2954
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
2955
|
-
return 1;
|
|
2956
|
-
}
|
|
2957
|
-
throw e;
|
|
2958
|
-
} finally {
|
|
2959
|
-
rl.close();
|
|
2960
|
-
}
|
|
2961
|
-
}
|
|
2962
|
-
|
|
2963
2852
|
// src/commands/org-join.ts
|
|
2964
2853
|
async function orgJoinCommand(token) {
|
|
2965
2854
|
if (!token) {
|
|
@@ -2979,209 +2868,22 @@ async function orgJoinCommand(token) {
|
|
|
2979
2868
|
}
|
|
2980
2869
|
}
|
|
2981
2870
|
|
|
2982
|
-
// src/commands/
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
if (resp.organisations.length === 0) {
|
|
2987
|
-
console.log("You are not in any organisations.");
|
|
2988
|
-
console.log("Create one with: token-derby organisation create");
|
|
2989
|
-
console.log("Or join one with: token-derby organisation join <token>");
|
|
2990
|
-
return 0;
|
|
2991
|
-
}
|
|
2992
|
-
console.log(`Your organisations (${resp.organisations.length}):`);
|
|
2993
|
-
for (const o of resp.organisations) {
|
|
2994
|
-
console.log(` \u2022 ${o.org_name}`);
|
|
2995
|
-
}
|
|
2996
|
-
return 0;
|
|
2997
|
-
} catch (e) {
|
|
2998
|
-
if (e instanceof ApiError) {
|
|
2999
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
3000
|
-
return 1;
|
|
3001
|
-
}
|
|
3002
|
-
throw e;
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
3005
|
-
|
|
3006
|
-
// src/commands/org-info.ts
|
|
3007
|
-
async function orgInfoCommand(name) {
|
|
3008
|
-
if (!name) {
|
|
3009
|
-
console.error("Usage: token-derby organisation info <name>");
|
|
3010
|
-
return 2;
|
|
3011
|
-
}
|
|
3012
|
-
if (!ORG_NAME_PATTERN.test(name)) {
|
|
3013
|
-
console.error("Organisation name must be 1\u201312 alphanumeric characters.");
|
|
3014
|
-
return 2;
|
|
3015
|
-
}
|
|
3016
|
-
try {
|
|
3017
|
-
const resp = await getOrganisation(name);
|
|
3018
|
-
console.log(`Organisation: ${resp.org_name}`);
|
|
3019
|
-
console.log(`Created: ${resp.created_at} by ${resp.creator_user_name}`);
|
|
3020
|
-
console.log("");
|
|
3021
|
-
console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
3022
|
-
console.log(` \u2551 JOIN TOKEN: ${resp.org_join_token.padEnd(43)}\u2551`);
|
|
3023
|
-
console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
3024
|
-
console.log(" \u26A0 Treat the token as a secret \u2014 anyone with it can join.");
|
|
3025
|
-
console.log("");
|
|
3026
|
-
console.log(` Members join with: token-derby organisation join ${resp.org_join_token}`);
|
|
3027
|
-
return 0;
|
|
3028
|
-
} catch (e) {
|
|
3029
|
-
if (e instanceof ApiError) {
|
|
3030
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
3031
|
-
return 1;
|
|
3032
|
-
}
|
|
3033
|
-
throw e;
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
|
|
3037
|
-
// src/commands/org-webhook-set.ts
|
|
3038
|
-
async function orgWebhookSetCommand(orgName, url) {
|
|
3039
|
-
if (!orgName || !url) {
|
|
3040
|
-
console.error("Usage: token-derby organisation webhook set <org-name> <https-url>");
|
|
3041
|
-
return 2;
|
|
3042
|
-
}
|
|
3043
|
-
try {
|
|
3044
|
-
const resp = await setOrgWebhook(orgName, { url });
|
|
3045
|
-
console.log("");
|
|
3046
|
-
console.log(` Webhook set for ${orgName}: ${resp.webhook_url}`);
|
|
3047
|
-
console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
3048
|
-
console.log(` \u2551 SECRET: ${resp.webhook_secret.padEnd(47)}\u2551`);
|
|
3049
|
-
console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
3050
|
-
console.log(" \u26A0 Save this secret now \u2014 it will not be shown again.");
|
|
3051
|
-
console.log(" Your receiver verifies requests with: X-Token-Derby-Signature: sha256=<hmac(secret, raw body)>.");
|
|
3052
|
-
return 0;
|
|
3053
|
-
} catch (e) {
|
|
3054
|
-
if (e instanceof ApiError) {
|
|
3055
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
3056
|
-
return 1;
|
|
3057
|
-
}
|
|
3058
|
-
throw e;
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
// src/commands/org-webhook-get.ts
|
|
3063
|
-
async function orgWebhookGetCommand(orgName) {
|
|
3064
|
-
if (!orgName) {
|
|
3065
|
-
console.error("Usage: token-derby organisation webhook get <org-name>");
|
|
3066
|
-
return 2;
|
|
3067
|
-
}
|
|
3068
|
-
try {
|
|
3069
|
-
const resp = await getOrgWebhook(orgName);
|
|
3070
|
-
if (resp.webhook_url) {
|
|
3071
|
-
console.log(`Webhook for ${orgName}: ${resp.webhook_url}`);
|
|
3072
|
-
} else {
|
|
3073
|
-
console.log(`No webhook configured for ${orgName}.`);
|
|
3074
|
-
}
|
|
3075
|
-
return 0;
|
|
3076
|
-
} catch (e) {
|
|
3077
|
-
if (e instanceof ApiError) {
|
|
3078
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
3079
|
-
return 1;
|
|
3080
|
-
}
|
|
3081
|
-
throw e;
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
|
-
|
|
3085
|
-
// src/commands/org-webhook-clear.ts
|
|
3086
|
-
async function orgWebhookClearCommand(orgName) {
|
|
3087
|
-
if (!orgName) {
|
|
3088
|
-
console.error("Usage: token-derby organisation webhook clear <org-name>");
|
|
3089
|
-
return 2;
|
|
3090
|
-
}
|
|
3091
|
-
try {
|
|
3092
|
-
await deleteOrgWebhook(orgName);
|
|
3093
|
-
console.log(`Webhook removed for ${orgName}.`);
|
|
3094
|
-
return 0;
|
|
3095
|
-
} catch (e) {
|
|
3096
|
-
if (e instanceof ApiError) {
|
|
3097
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
3098
|
-
return 1;
|
|
3099
|
-
}
|
|
3100
|
-
throw e;
|
|
3101
|
-
}
|
|
3102
|
-
}
|
|
3103
|
-
|
|
3104
|
-
// src/commands/org-schedule-set.ts
|
|
3105
|
-
function flag(args, name) {
|
|
3106
|
-
for (let i = 0; i < args.length; i++) {
|
|
3107
|
-
if (args[i] === name) return args[i + 1];
|
|
3108
|
-
const eq = `${name}=`;
|
|
3109
|
-
if (args[i]?.startsWith(eq)) return args[i].slice(eq.length);
|
|
3110
|
-
}
|
|
3111
|
-
return void 0;
|
|
2871
|
+
// src/commands/web.ts
|
|
2872
|
+
import { spawn as spawn2 } from "child_process";
|
|
2873
|
+
function webOrigin() {
|
|
2874
|
+
return apiBase().replace(/\/api\/?$/, "");
|
|
3112
2875
|
}
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
if (
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
}
|
|
3119
|
-
const daysSpec = flag(rest, "--days");
|
|
3120
|
-
const start = flag(rest, "--start");
|
|
3121
|
-
const end = flag(rest, "--end");
|
|
3122
|
-
const tz = flag(rest, "--tz");
|
|
3123
|
-
const name = flag(rest, "--name");
|
|
3124
|
-
const maxStr = flag(rest, "--max");
|
|
3125
|
-
const counts_input = rest.includes("--counts-input");
|
|
3126
|
-
const primary_top5 = rest.includes("--primary-top5");
|
|
3127
|
-
if (!daysSpec || !start || !end || !tz) {
|
|
3128
|
-
console.error("Required flags: --days, --start, --end, --tz");
|
|
3129
|
-
console.error(USAGE);
|
|
3130
|
-
return 2;
|
|
3131
|
-
}
|
|
3132
|
-
const weekdays = parseWeekdays(daysSpec);
|
|
3133
|
-
if (!weekdays) {
|
|
3134
|
-
console.error('Invalid --days. Use day names like "mon-fri" or "mon,wed,fri".');
|
|
3135
|
-
return 2;
|
|
3136
|
-
}
|
|
3137
|
-
let max_participants;
|
|
3138
|
-
if (maxStr !== void 0) {
|
|
3139
|
-
max_participants = Number(maxStr);
|
|
3140
|
-
if (!Number.isInteger(max_participants) || max_participants < 1) {
|
|
3141
|
-
console.error("--max must be a positive integer");
|
|
3142
|
-
return 2;
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
3145
|
-
try {
|
|
3146
|
-
const resp = await setOrgSchedule(orgName, {
|
|
3147
|
-
weekdays,
|
|
3148
|
-
start_local: start,
|
|
3149
|
-
end_local: end,
|
|
3150
|
-
tz,
|
|
3151
|
-
...name ? { race_name: name } : {},
|
|
3152
|
-
...max_participants !== void 0 ? { max_participants } : {},
|
|
3153
|
-
...counts_input ? { counts_input: true } : {},
|
|
3154
|
-
...primary_top5 ? { primary_top5: true } : {}
|
|
3155
|
-
});
|
|
3156
|
-
const s = resp.schedule;
|
|
3157
|
-
console.log(`Schedule set for ${orgName}: days [${s.weekdays.join(",")}] ${s.start_local}\u2013${s.end_local} ${s.tz}`);
|
|
3158
|
-
if (s.primary_top5) console.log(" Primary score counts only each racer's top 5 conversations per beat.");
|
|
3159
|
-
return 0;
|
|
3160
|
-
} catch (e) {
|
|
3161
|
-
if (e instanceof ApiError) {
|
|
3162
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
3163
|
-
return 1;
|
|
3164
|
-
}
|
|
3165
|
-
throw e;
|
|
3166
|
-
}
|
|
2876
|
+
function opener() {
|
|
2877
|
+
if (process.platform === "darwin") return "open";
|
|
2878
|
+
if (process.platform === "win32") return "start";
|
|
2879
|
+
if (process.platform === "linux") return "xdg-open";
|
|
2880
|
+
return null;
|
|
3167
2881
|
}
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
if (!orgName) {
|
|
3172
|
-
console.error("Usage: token-derby organisation schedule get <org-name>");
|
|
3173
|
-
return 2;
|
|
3174
|
-
}
|
|
2882
|
+
async function webCommand(deps = {}) {
|
|
2883
|
+
const spawnImpl = deps.spawnImpl ?? spawn2;
|
|
2884
|
+
let code;
|
|
3175
2885
|
try {
|
|
3176
|
-
|
|
3177
|
-
if (resp.schedule) {
|
|
3178
|
-
const s = resp.schedule;
|
|
3179
|
-
console.log(`Schedule for ${orgName}: days [${s.weekdays.join(",")}] ${s.start_local}\u2013${s.end_local} ${s.tz}`);
|
|
3180
|
-
if (s.primary_top5) console.log(" Primary top-5 conversations cap: ON.");
|
|
3181
|
-
} else {
|
|
3182
|
-
console.log(`No schedule configured for ${orgName}.`);
|
|
3183
|
-
}
|
|
3184
|
-
return 0;
|
|
2886
|
+
({ code } = await createWebSession());
|
|
3185
2887
|
} catch (e) {
|
|
3186
2888
|
if (e instanceof ApiError) {
|
|
3187
2889
|
console.error(`Error: ${e.code} ${e.message}`);
|
|
@@ -3189,25 +2891,23 @@ async function orgScheduleGetCommand(orgName) {
|
|
|
3189
2891
|
}
|
|
3190
2892
|
throw e;
|
|
3191
2893
|
}
|
|
3192
|
-
}
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
console.error(`Error: ${e.code} ${e.message}`);
|
|
3207
|
-
return 1;
|
|
2894
|
+
const url = `${webOrigin()}/org-manager#code=${code}`;
|
|
2895
|
+
console.log("");
|
|
2896
|
+
console.log(" Opening the Token Derby org manager in your browser...");
|
|
2897
|
+
console.log(` ${url}`);
|
|
2898
|
+
console.log("");
|
|
2899
|
+
console.log(" If it doesn't open, copy the link above. It expires in 60 seconds.");
|
|
2900
|
+
const cmd = opener();
|
|
2901
|
+
if (cmd) {
|
|
2902
|
+
try {
|
|
2903
|
+
const child = spawnImpl(cmd, [url], { stdio: "ignore", detached: true });
|
|
2904
|
+
child.on("error", () => {
|
|
2905
|
+
});
|
|
2906
|
+
child.unref();
|
|
2907
|
+
} catch {
|
|
3208
2908
|
}
|
|
3209
|
-
throw e;
|
|
3210
2909
|
}
|
|
2910
|
+
return 0;
|
|
3211
2911
|
}
|
|
3212
2912
|
|
|
3213
2913
|
// src/bin.ts
|
|
@@ -3229,25 +2929,9 @@ Stable management:
|
|
|
3229
2929
|
token-derby stable delete <name> Remove a horse from your stable
|
|
3230
2930
|
|
|
3231
2931
|
Organisations:
|
|
3232
|
-
token-derby organisation create Create a new organisation (interactive)
|
|
3233
2932
|
token-derby organisation join <token> Join an organisation with a join token
|
|
3234
|
-
token-derby
|
|
3235
|
-
|
|
3236
|
-
token-derby organisation webhook set <name> <url>
|
|
3237
|
-
Configure an https webhook for race events.
|
|
3238
|
-
Prints a secret used to sign each request.
|
|
3239
|
-
Only the org creator can run this.
|
|
3240
|
-
token-derby organisation webhook get <name>
|
|
3241
|
-
Show the org's configured webhook URL (or "no webhook").
|
|
3242
|
-
token-derby organisation webhook clear <name>
|
|
3243
|
-
Remove the webhook for this org.
|
|
3244
|
-
token-derby organisation schedule set <name> --days mon-fri --start 09:00 --end 17:30 --tz Europe/London
|
|
3245
|
-
Configure a repeating race schedule. Races auto-start
|
|
3246
|
-
at the daily start time. Only the org creator can run this.
|
|
3247
|
-
token-derby organisation schedule get <name>
|
|
3248
|
-
Show the org's configured schedule (or "no schedule").
|
|
3249
|
-
token-derby organisation schedule clear <name>
|
|
3250
|
-
Remove the repeating schedule for this org.
|
|
2933
|
+
token-derby web Open the web org manager (create orgs,
|
|
2934
|
+
manage schedules, webhooks, members)
|
|
3251
2935
|
|
|
3252
2936
|
Races:
|
|
3253
2937
|
token-derby create [--organisation <name>]
|
|
@@ -3298,30 +2982,9 @@ async function main() {
|
|
|
3298
2982
|
}
|
|
3299
2983
|
if (cmd === "organisation" || cmd === "org") {
|
|
3300
2984
|
const sub = argv[1];
|
|
3301
|
-
if (sub === "create") return orgCreateCommand();
|
|
3302
2985
|
if (sub === "join") return orgJoinCommand(argv[2]);
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
if (sub === "webhook") {
|
|
3306
|
-
const action = argv[2];
|
|
3307
|
-
if (action === "set") return orgWebhookSetCommand(argv[3], argv[4]);
|
|
3308
|
-
if (action === "get") return orgWebhookGetCommand(argv[3]);
|
|
3309
|
-
if (action === "clear") return orgWebhookClearCommand(argv[3]);
|
|
3310
|
-
console.error(`Unknown webhook action: ${action ?? "(none)"}`);
|
|
3311
|
-
console.error("Try: organisation webhook set <name> <url> | organisation webhook get <name> | organisation webhook clear <name>");
|
|
3312
|
-
return 2;
|
|
3313
|
-
}
|
|
3314
|
-
if (sub === "schedule") {
|
|
3315
|
-
const action = argv[2];
|
|
3316
|
-
if (action === "set") return orgScheduleSetCommand(argv[3], argv.slice(4));
|
|
3317
|
-
if (action === "get") return orgScheduleGetCommand(argv[3]);
|
|
3318
|
-
if (action === "clear") return orgScheduleClearCommand(argv[3]);
|
|
3319
|
-
console.error(`Unknown schedule action: ${action ?? "(none)"}`);
|
|
3320
|
-
console.error("Try: organisation schedule set <name> ... | organisation schedule get <name> | organisation schedule clear <name>");
|
|
3321
|
-
return 2;
|
|
3322
|
-
}
|
|
3323
|
-
console.error(`Unknown organisation subcommand: ${sub ?? "(none)"}`);
|
|
3324
|
-
console.error("Try: organisation create | organisation join <token> | organisation info <name> | organisation list | organisation webhook <set|get|clear> ... | organisation schedule <set|get|clear> ...");
|
|
2986
|
+
console.error(`Organisation management has moved to the web: token-derby web`);
|
|
2987
|
+
console.error(`The only CLI organisation command is: organisation join <token>`);
|
|
3325
2988
|
return 2;
|
|
3326
2989
|
}
|
|
3327
2990
|
if (cmd === "create") {
|
|
@@ -3331,14 +2994,15 @@ async function main() {
|
|
|
3331
2994
|
if (cmd === "join") return joinCommand(argv[1], argv.slice(2));
|
|
3332
2995
|
if (cmd === "end") return endCommand(argv[1]);
|
|
3333
2996
|
if (cmd === "roll") return rollCommand();
|
|
2997
|
+
if (cmd === "web") return webCommand();
|
|
3334
2998
|
console.error(`Unknown command: ${cmd}`);
|
|
3335
2999
|
console.error(HELP);
|
|
3336
3000
|
return 2;
|
|
3337
3001
|
}
|
|
3338
|
-
function parseFlag(args,
|
|
3002
|
+
function parseFlag(args, flag) {
|
|
3339
3003
|
for (let i = 0; i < args.length; i++) {
|
|
3340
|
-
if (args[i] ===
|
|
3341
|
-
const eq = `${
|
|
3004
|
+
if (args[i] === flag) return args[i + 1];
|
|
3005
|
+
const eq = `${flag}=`;
|
|
3342
3006
|
if (args[i]?.startsWith(eq)) return args[i].slice(eq.length);
|
|
3343
3007
|
}
|
|
3344
3008
|
return void 0;
|