@irtio/cli 0.5.2 → 0.7.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.
Files changed (39) hide show
  1. package/dist/api-keys-UTLYMZYN.js +222 -0
  2. package/dist/api.d.ts +52 -0
  3. package/dist/api.js +15 -0
  4. package/dist/bundle.js +1 -1
  5. package/dist/{chunk-GBNHBWES.js → chunk-BQBOBFBO.js} +10 -6
  6. package/dist/chunk-IDF46P7R.js +98 -0
  7. package/dist/{chunk-32QTPKVT.js → chunk-JL235KIE.js} +1 -1
  8. package/dist/chunk-OCVALOGK.js +31 -0
  9. package/dist/{chunk-KRQUAEN2.js → chunk-OTSFRVJN.js} +12 -6
  10. package/dist/chunk-UPHQM6NZ.js +72 -0
  11. package/dist/chunk-WFMRNGO5.js +481 -0
  12. package/dist/chunk-ZK5JLUD4.js +94 -0
  13. package/dist/credentials.d.ts +61 -0
  14. package/dist/credentials.js +20 -0
  15. package/dist/delete-project-MXUYNGAO.js +118 -0
  16. package/dist/deploy.d.ts +151 -0
  17. package/dist/{deploy-3SABPL3T.js → deploy.js} +324 -44
  18. package/dist/{dev-QJOGXLKM.js → dev-AUZ4OLA3.js} +3066 -211
  19. package/dist/index.js +121 -25
  20. package/dist/init.d.ts +1 -1
  21. package/dist/init.js +20 -4
  22. package/dist/{keys-XBORZAPI.js → keys-NXRIBJZP.js} +8 -4
  23. package/dist/leaderboard-ZBJBKETM.js +406 -0
  24. package/dist/{login-3EXB4CGX.js → login-3RVN5PPN.js} +12 -5
  25. package/dist/{logs-2EOXLNWF.js → logs-AT7G6YRH.js} +9 -5
  26. package/dist/{migrate-WUO2GBMX.js → migrate-FMXTRVUV.js} +12 -8
  27. package/dist/ratings-XBLX2MUW.js +297 -0
  28. package/dist/{rollback-GA6UY772.js → rollback-LI4TDLQA.js} +9 -5
  29. package/dist/rooms-52Q5KBUS.js +411 -0
  30. package/dist/simulate.d.ts +254 -6
  31. package/dist/simulate.js +925 -64
  32. package/dist/{static-deploy-5TBH4VNA.js → static-deploy-7UCYINJB.js} +6 -4
  33. package/dist/status-JZGKH2P6.js +219 -0
  34. package/dist/usage-7S447INI.js +213 -0
  35. package/dist/{whoami-CI5D5RCC.js → whoami-UFSWPWK6.js} +8 -4
  36. package/package.json +23 -7
  37. package/dist/chunk-BPE452KF.js +0 -180
  38. package/dist/chunk-TV66QHFP.js +0 -167
  39. package/dist/rooms-B66LQIIF.js +0 -226
@@ -0,0 +1,297 @@
1
+ import {
2
+ readProjectConfig
3
+ } from "./chunk-JL235KIE.js";
4
+ import {
5
+ HelpRequested,
6
+ helpFor,
7
+ helpRequested
8
+ } from "./chunk-OCVALOGK.js";
9
+ import {
10
+ createApiClient,
11
+ isLoginRequired
12
+ } from "./chunk-IDF46P7R.js";
13
+ import {
14
+ resolveControlUrlForUser
15
+ } from "./chunk-UPHQM6NZ.js";
16
+
17
+ // src/ratings.ts
18
+ import pc from "picocolors";
19
+ var USAGE = `usage: irtio ratings <queue> [options]
20
+ irtio ratings list [options]
21
+ irtio ratings delete <queue> --player <id> --yes [options]
22
+
23
+ Prints a project's skill ratings for one queue, best first. Needs your login: a rating is a
24
+ number the matchmaker uses, not a scoreboard for players to read.
25
+
26
+ There is no submit command. A rating moves only from room code (room.ratings.report), which
27
+ is what makes it mean anything.
28
+
29
+ list every queue with ratings on the project, with its row count
30
+ delete remove one player's rating in one queue. Needs --player, your login and --yes;
31
+ without --yes it prints what would go and changes nothing.
32
+
33
+ options:
34
+ --limit <n> how many rows (default 20, max 100)
35
+ --cursor <c> continue from a previous page's nextCursor
36
+ --all page through the whole queue, following cursors
37
+ --player <id> with delete: whose rating to remove
38
+ --yes with delete: actually delete
39
+ --project <id> project id (default: the project file)
40
+ -c, --config <f> the project file to read (default irtio.json)
41
+ --url <control> control plane (default: your stored login)
42
+ -h, --help print this
43
+ `;
44
+ function parseRatingsArgs(args) {
45
+ if (helpRequested(args)) throw helpFor(USAGE);
46
+ const parsed = {};
47
+ if (args[0] === "list" || args[0] === "delete") parsed.action = args[0];
48
+ for (let i = parsed.action === void 0 ? 0 : 1; i < args.length; i++) {
49
+ const arg = args[i];
50
+ switch (arg) {
51
+ case "--cursor": {
52
+ const value = args[++i];
53
+ if (value === void 0 || value === "") {
54
+ throw new Error("irtio ratings: --cursor needs a value");
55
+ }
56
+ parsed.cursor = value;
57
+ break;
58
+ }
59
+ case "--all":
60
+ parsed.all = true;
61
+ break;
62
+ case "--yes":
63
+ parsed.yes = true;
64
+ break;
65
+ case "--player": {
66
+ const value = args[++i];
67
+ if (value === void 0 || value === "") {
68
+ throw new Error("irtio ratings: --player needs a player id");
69
+ }
70
+ parsed.player = value;
71
+ break;
72
+ }
73
+ case "--limit": {
74
+ const value = Number(args[++i]);
75
+ if (!Number.isInteger(value) || value < 1) {
76
+ throw new Error("irtio ratings: --limit needs a whole number of at least 1");
77
+ }
78
+ parsed.limit = value;
79
+ break;
80
+ }
81
+ case "--project": {
82
+ const value = args[++i];
83
+ if (value === void 0) throw new Error("irtio ratings: --project needs a value");
84
+ parsed.project = value;
85
+ break;
86
+ }
87
+ case "-c":
88
+ case "--config": {
89
+ const value = args[++i];
90
+ if (value === void 0 || value === "") {
91
+ throw new Error("irtio ratings: --config needs a value");
92
+ }
93
+ parsed.config = value;
94
+ break;
95
+ }
96
+ case "--url": {
97
+ const value = args[++i];
98
+ if (value === void 0) throw new Error("irtio ratings: --url needs a value");
99
+ parsed.url = value;
100
+ break;
101
+ }
102
+ default:
103
+ if (arg.startsWith("-")) {
104
+ throw new Error(`irtio ratings: unknown option ${JSON.stringify(arg)}`);
105
+ }
106
+ if (parsed.queue !== void 0) {
107
+ throw new Error("irtio ratings: give exactly one queue name");
108
+ }
109
+ parsed.queue = arg;
110
+ }
111
+ }
112
+ return parsed;
113
+ }
114
+ function formatRatings(body) {
115
+ const out = [pc.dim(`queue ${body.queue} \xB7 Glicko-2, higher is stronger`), ""];
116
+ if (body.entries.length === 0) {
117
+ out.push(pc.dim("no ratings in this queue yet"));
118
+ out.push(pc.dim("a rating appears when room code calls room.ratings.report"));
119
+ return out;
120
+ }
121
+ const rankWidth = Math.max(4, ...body.entries.map((e) => String(e.rank).length));
122
+ const idWidth = Math.max(6, ...body.entries.map((e) => e.playerId.length));
123
+ out.push(pc.dim(`${"RANK".padEnd(rankWidth)} ${"PLAYER".padEnd(idWidth)} RATING \xB1DEV GAMES`));
124
+ for (const e of body.entries) {
125
+ out.push(
126
+ `${String(e.rank).padEnd(rankWidth)} ${e.playerId.padEnd(idWidth)} ${e.rating.toFixed(0).padStart(6)} ${e.deviation.toFixed(0).padStart(4)} ${String(e.games).padStart(5)}`
127
+ );
128
+ }
129
+ out.push("");
130
+ out.push(
131
+ pc.dim("\xB1DEV is how sure the rating is. 350 means a new player; under 80 means settled.")
132
+ );
133
+ return out;
134
+ }
135
+ async function runRatings(options) {
136
+ const log = options.log ?? ((line) => console.log(line));
137
+ const controlUrl = await resolveControlUrlForUser(options.controlUrl);
138
+ const client = options.client ?? await createApiClient(controlUrl);
139
+ const base = `/v1/projects/${encodeURIComponent(options.project)}/ratings/${encodeURIComponent(
140
+ options.queue
141
+ )}`;
142
+ const pageUrl = (cursor) => {
143
+ const query = new URLSearchParams();
144
+ if (options.limit !== void 0) query.set("limit", String(options.limit));
145
+ if (cursor !== void 0) query.set("cursor", cursor);
146
+ const q = query.toString();
147
+ return `${base}${q === "" ? "" : `?${q}`}`;
148
+ };
149
+ let body = await client.get(pageUrl(options.cursor));
150
+ const entries = [...body.entries];
151
+ if (options.all === true) {
152
+ let next = body.nextCursor;
153
+ for (let page = 1; next !== void 0 && page < 100; page++) {
154
+ body = await client.get(pageUrl(next));
155
+ for (const entry of body.entries) {
156
+ entries.push({ ...entry, rank: entries.length + 1 });
157
+ }
158
+ next = body.nextCursor;
159
+ }
160
+ if (next !== void 0) {
161
+ throw new Error(
162
+ "irtio ratings: --all stopped after a hundred pages. Read it in pieces with --cursor"
163
+ );
164
+ }
165
+ }
166
+ const combined = { ...body, entries };
167
+ for (const line of formatRatings(combined)) log(line);
168
+ if (options.all !== true && body.nextCursor !== void 0) {
169
+ log("");
170
+ log(pc.dim(`more rows: --cursor ${body.nextCursor}`));
171
+ }
172
+ return combined;
173
+ }
174
+ function formatQueueList(queues) {
175
+ if (queues.length === 0) {
176
+ return [
177
+ pc.dim("no ratings on this project yet"),
178
+ pc.dim("a rating reaches a queue from room code: room.ratings.report")
179
+ ];
180
+ }
181
+ const nameWidth = Math.max(5, ...queues.map((q) => q.queue.length));
182
+ const out = [pc.dim(`${"QUEUE".padEnd(nameWidth)} ${"ROWS".padStart(7)} MATCHING`)];
183
+ for (const q of queues) {
184
+ const matching = q.skill ? "skill" : "fifo";
185
+ const note = q.configured && q.rows === 0 ? pc.dim(" (configured, no ratings yet)") : !q.configured ? pc.dim(" (ratings only; not a configured queue)") : !q.skill ? pc.dim(" (ratings recorded, but this queue fills in arrival order)") : "";
186
+ out.push(`${q.queue.padEnd(nameWidth)} ${String(q.rows).padStart(7)} ${matching}${note}`);
187
+ }
188
+ return out;
189
+ }
190
+ async function runQueueList(options) {
191
+ const log = options.log ?? ((line) => console.log(line));
192
+ const controlUrl = await resolveControlUrlForUser(options.controlUrl);
193
+ const client = options.client ?? await createApiClient(controlUrl);
194
+ const body = await client.get(
195
+ `/v1/projects/${encodeURIComponent(options.project)}/ratings`
196
+ );
197
+ for (const line of formatQueueList(body.queues)) log(line);
198
+ return body.queues;
199
+ }
200
+ async function runRatingDelete(options) {
201
+ const log = options.log ?? ((line) => console.log(line));
202
+ const controlUrl = await resolveControlUrlForUser(options.controlUrl);
203
+ const client = options.client ?? await createApiClient(controlUrl);
204
+ const path = `/v1/projects/${encodeURIComponent(options.project)}/ratings/${encodeURIComponent(
205
+ options.queue
206
+ )}/${encodeURIComponent(options.player)}`;
207
+ if (!options.yes) {
208
+ log(
209
+ `this would delete ${pc.bold(options.player)}'s rating in queue ${pc.bold(options.queue)} in project ${options.project}`
210
+ );
211
+ log(pc.dim("nothing was deleted. This cannot be undone once it runs"));
212
+ log(`to proceed: irtio ratings delete ${options.queue} --player ${options.player} --yes`);
213
+ return false;
214
+ }
215
+ const body = await client.del(path);
216
+ log(
217
+ body.deleted ? pc.green(`deleted ${options.player}'s rating in queue ${options.queue}`) : pc.dim(`${options.player} had no rating in queue ${options.queue}; nothing to delete`)
218
+ );
219
+ return true;
220
+ }
221
+ async function ratings(args, deps = {}) {
222
+ const log = deps.log ?? ((line) => console.log(line));
223
+ const errorLog = deps.errorLog ?? ((line) => console.error(line));
224
+ try {
225
+ const parsed = parseRatingsArgs(args);
226
+ const config = await readProjectConfig(process.cwd(), "irtio ratings", parsed.config);
227
+ const project = parsed.project ?? config.project;
228
+ if (project === void 0) {
229
+ throw new Error(
230
+ "irtio ratings: no project id. Pass --project or run this from a project with irtio.json"
231
+ );
232
+ }
233
+ const common = {
234
+ project,
235
+ log,
236
+ ...parsed.url !== void 0 ? { controlUrl: parsed.url } : {},
237
+ ...deps.client !== void 0 ? { client: deps.client } : {}
238
+ };
239
+ if (parsed.action === "list") {
240
+ if (parsed.queue !== void 0) throw new Error("irtio ratings list: takes no queue name");
241
+ await runQueueList(common);
242
+ return;
243
+ }
244
+ if (parsed.action === "delete") {
245
+ if (parsed.queue === void 0) {
246
+ throw new Error(
247
+ "irtio ratings delete: which queue? e.g. irtio ratings delete ranked --player irt:abc --yes"
248
+ );
249
+ }
250
+ if (parsed.player === void 0) {
251
+ throw new Error("irtio ratings delete: --player is required; there is no whole-queue wipe");
252
+ }
253
+ await runRatingDelete({
254
+ ...common,
255
+ queue: parsed.queue,
256
+ player: parsed.player,
257
+ yes: parsed.yes === true
258
+ });
259
+ return;
260
+ }
261
+ if (parsed.queue === void 0) {
262
+ throw new Error("irtio ratings: which queue? e.g. irtio ratings ranked");
263
+ }
264
+ if (parsed.cursor !== void 0 && parsed.all === true) {
265
+ throw new Error("irtio ratings: --cursor and --all are two ways to do the same thing");
266
+ }
267
+ await runRatings({
268
+ ...common,
269
+ queue: parsed.queue,
270
+ ...parsed.limit !== void 0 ? { limit: parsed.limit } : {},
271
+ ...parsed.cursor !== void 0 ? { cursor: parsed.cursor } : {},
272
+ ...parsed.all !== void 0 ? { all: parsed.all } : {}
273
+ });
274
+ } catch (err) {
275
+ if (err instanceof HelpRequested) {
276
+ log(err.usage);
277
+ return;
278
+ }
279
+ if (isLoginRequired(err)) {
280
+ errorLog("irtio ratings: not logged in. Run `irtio login` first");
281
+ process.exitCode = 1;
282
+ return;
283
+ }
284
+ errorLog(err instanceof Error ? err.message : String(err));
285
+ process.exitCode = 1;
286
+ }
287
+ }
288
+ export {
289
+ USAGE,
290
+ formatQueueList,
291
+ formatRatings,
292
+ parseRatingsArgs,
293
+ ratings,
294
+ runQueueList,
295
+ runRatingDelete,
296
+ runRatings
297
+ };
@@ -1,14 +1,18 @@
1
1
  import {
2
2
  readProjectConfig
3
- } from "./chunk-32QTPKVT.js";
3
+ } from "./chunk-JL235KIE.js";
4
4
  import {
5
5
  HelpRequested,
6
- createApiClient,
7
6
  helpFor,
8
- helpRequested,
9
- isLoginRequired,
7
+ helpRequested
8
+ } from "./chunk-OCVALOGK.js";
9
+ import {
10
+ createApiClient,
11
+ isLoginRequired
12
+ } from "./chunk-IDF46P7R.js";
13
+ import {
10
14
  resolveControlUrlForUser
11
- } from "./chunk-TV66QHFP.js";
15
+ } from "./chunk-UPHQM6NZ.js";
12
16
 
13
17
  // src/rollback.ts
14
18
  import "fs";