@irtio/cli 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +52 -0
- package/dist/api.js +15 -0
- package/dist/bundle.js +1 -1
- package/dist/{static-deploy-BP3MDXCP.js → chunk-3HQMVCYA.js} +89 -48
- package/dist/chunk-DKWG7MGO.js +93 -0
- package/dist/{chunk-KRQUAEN2.js → chunk-OTSFRVJN.js} +12 -6
- package/dist/{chunk-I37DLT7K.js → chunk-RNAH5T4W.js} +14 -3
- package/dist/chunk-RQSJZWQC.js +452 -0
- package/dist/{chunk-NVUKSP5U.js → chunk-UPHQM6NZ.js} +12 -1
- package/dist/chunk-ZD4ND6X6.js +31 -0
- package/dist/chunk-ZK5JLUD4.js +94 -0
- package/dist/credentials.d.ts +61 -0
- package/dist/credentials.js +20 -0
- package/dist/delete-project-VENS2B44.js +118 -0
- package/dist/deploy.d.ts +149 -0
- package/dist/deploy.js +567 -0
- package/dist/{dev-7UZZGE4U.js → dev-QM26ONKS.js} +3095 -294
- package/dist/index.js +113 -28
- package/dist/init.d.ts +2 -1
- package/dist/init.js +42 -1
- package/dist/{keys-KEKO3EJ6.js → keys-JHLMEGRA.js} +49 -18
- package/dist/leaderboard-SYPSBPS3.js +352 -0
- package/dist/{login-OV2EFNTJ.js → login-2M73HBZT.js} +25 -1
- package/dist/{logs-5OUDPAIX.js → logs-2W7CPZO5.js} +42 -17
- package/dist/{migrate-UD245ULI.js → migrate-T3DZJREY.js} +44 -19
- package/dist/ratings-VG32WFDG.js +297 -0
- package/dist/{rollback-CLQVYFHW.js → rollback-SO74MVZV.js} +41 -17
- package/dist/{rooms-OJ3JLYHD.js → rooms-VI33P4RA.js} +73 -20
- package/dist/simulate.d.ts +215 -3
- package/dist/simulate.js +865 -64
- package/dist/static-deploy-KOWFKWZA.js +19 -0
- package/dist/status-HF3ZEKB7.js +219 -0
- package/dist/usage-4G23QXCH.js +213 -0
- package/dist/{whoami-S73O6KJF.js → whoami-KTMTQNHM.js} +21 -2
- package/package.json +24 -7
- package/dist/chunk-BPE452KF.js +0 -180
- package/dist/chunk-D7CDJRFF.js +0 -24
- package/dist/deploy-YVCVDVMS.js +0 -396
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readProjectConfig
|
|
3
|
+
} from "./chunk-DKWG7MGO.js";
|
|
4
|
+
import {
|
|
5
|
+
HelpRequested,
|
|
6
|
+
helpFor,
|
|
7
|
+
helpRequested
|
|
8
|
+
} from "./chunk-ZD4ND6X6.js";
|
|
9
|
+
import {
|
|
10
|
+
createApiClient,
|
|
11
|
+
isLoginRequired
|
|
12
|
+
} from "./chunk-RNAH5T4W.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,17 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readProjectConfig
|
|
3
|
+
} from "./chunk-DKWG7MGO.js";
|
|
4
|
+
import {
|
|
5
|
+
HelpRequested,
|
|
6
|
+
helpFor,
|
|
7
|
+
helpRequested
|
|
8
|
+
} from "./chunk-ZD4ND6X6.js";
|
|
1
9
|
import {
|
|
2
10
|
createApiClient,
|
|
3
11
|
isLoginRequired
|
|
4
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-RNAH5T4W.js";
|
|
5
13
|
import {
|
|
6
14
|
resolveControlUrlForUser
|
|
7
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-UPHQM6NZ.js";
|
|
8
16
|
|
|
9
17
|
// src/rollback.ts
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
18
|
+
import "fs";
|
|
19
|
+
import "fs/promises";
|
|
20
|
+
import "path";
|
|
13
21
|
import pc from "picocolors";
|
|
22
|
+
var USAGE = `usage: irtio rollback <version> [options]
|
|
23
|
+
|
|
24
|
+
Re-promotes deployment <version> and brings rooms that migrated past it back to their
|
|
25
|
+
pre-migration state. This DISCARDS whatever those rooms wrote since their migration.
|
|
26
|
+
|
|
27
|
+
options:
|
|
28
|
+
<version> the deployment version to go back to
|
|
29
|
+
--project <id> project id (default: the project file)
|
|
30
|
+
-c, --config <f> the project file to read (default irtio.json)
|
|
31
|
+
--url <control> control plane (default: your stored login)
|
|
32
|
+
-h, --help print this
|
|
33
|
+
`;
|
|
14
34
|
function parseRollbackArgs(args) {
|
|
35
|
+
if (helpRequested(args)) throw helpFor(USAGE);
|
|
15
36
|
const parsed = {};
|
|
16
37
|
for (let i = 0; i < args.length; i++) {
|
|
17
38
|
const arg = args[i];
|
|
@@ -22,6 +43,14 @@ function parseRollbackArgs(args) {
|
|
|
22
43
|
parsed.project = value;
|
|
23
44
|
break;
|
|
24
45
|
}
|
|
46
|
+
case "-c":
|
|
47
|
+
case "--config": {
|
|
48
|
+
const value = args[++i];
|
|
49
|
+
if (value === void 0 || value === "")
|
|
50
|
+
throw new Error("irtio rollback: --config needs a value");
|
|
51
|
+
parsed.config = value;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
25
54
|
case "--url": {
|
|
26
55
|
const value = args[++i];
|
|
27
56
|
if (value === void 0) throw new Error("irtio rollback: --url needs a value");
|
|
@@ -75,23 +104,13 @@ async function runRollback(options) {
|
|
|
75
104
|
}
|
|
76
105
|
return result;
|
|
77
106
|
}
|
|
78
|
-
async function readIrtioJsonProject(cwd) {
|
|
79
|
-
const file = path.join(cwd, "irtio.json");
|
|
80
|
-
if (!existsSync(file)) return void 0;
|
|
81
|
-
let parsed;
|
|
82
|
-
try {
|
|
83
|
-
parsed = JSON.parse(await readFile(file, "utf8"));
|
|
84
|
-
} catch {
|
|
85
|
-
throw new Error(`irtio rollback: ${file} is not valid JSON`);
|
|
86
|
-
}
|
|
87
|
-
return typeof parsed.project === "string" && parsed.project.length > 0 ? parsed.project : void 0;
|
|
88
|
-
}
|
|
89
107
|
async function rollback(args, deps = {}) {
|
|
90
108
|
const log = deps.log ?? ((line) => console.log(line));
|
|
91
109
|
const errorLog = deps.errorLog ?? ((line) => console.error(line));
|
|
92
110
|
try {
|
|
93
111
|
const parsed = parseRollbackArgs(args);
|
|
94
|
-
const
|
|
112
|
+
const config = await readProjectConfig(process.cwd(), "irtio rollback", parsed.config);
|
|
113
|
+
const project = parsed.project ?? config.project;
|
|
95
114
|
if (project === void 0) {
|
|
96
115
|
throw new Error(
|
|
97
116
|
"irtio rollback: no project id \u2014 pass --project or run this from a project with irtio.json"
|
|
@@ -105,6 +124,10 @@ async function rollback(args, deps = {}) {
|
|
|
105
124
|
...deps.client !== void 0 ? { client: deps.client } : {}
|
|
106
125
|
});
|
|
107
126
|
} catch (err) {
|
|
127
|
+
if (err instanceof HelpRequested) {
|
|
128
|
+
log(err.usage);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
108
131
|
if (isLoginRequired(err)) {
|
|
109
132
|
errorLog(pc.red("not logged in"));
|
|
110
133
|
errorLog("run: irtio login");
|
|
@@ -116,6 +139,7 @@ async function rollback(args, deps = {}) {
|
|
|
116
139
|
}
|
|
117
140
|
}
|
|
118
141
|
export {
|
|
142
|
+
USAGE,
|
|
119
143
|
parseRollbackArgs,
|
|
120
144
|
rollback,
|
|
121
145
|
runRollback
|
|
@@ -1,21 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readProjectConfig
|
|
3
|
+
} from "./chunk-DKWG7MGO.js";
|
|
4
|
+
import {
|
|
5
|
+
HelpRequested,
|
|
6
|
+
helpFor,
|
|
7
|
+
helpRequested
|
|
8
|
+
} from "./chunk-ZD4ND6X6.js";
|
|
1
9
|
import {
|
|
2
10
|
createApiClient,
|
|
3
11
|
isLoginRequired
|
|
4
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-RNAH5T4W.js";
|
|
5
13
|
import {
|
|
6
14
|
resolveControlUrlForUser
|
|
7
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-UPHQM6NZ.js";
|
|
8
16
|
|
|
9
17
|
// src/rooms.ts
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
18
|
+
import "fs";
|
|
19
|
+
import "fs/promises";
|
|
20
|
+
import "path";
|
|
13
21
|
import pc from "picocolors";
|
|
22
|
+
var USAGE = `usage: irtio rooms [options]
|
|
23
|
+
irtio rooms saves <room> [options]
|
|
24
|
+
irtio rooms restore <room> --save <id> [options]
|
|
25
|
+
irtio rooms delete <room> [options]
|
|
26
|
+
|
|
27
|
+
Lists a project's rooms, with the retention window each one's type declares. Rows not reported for
|
|
28
|
+
longer than the control plane's row-pruning window (30 days by default) are pruned; that prunes the
|
|
29
|
+
listing only and never a room's stored state.
|
|
30
|
+
|
|
31
|
+
subcommands:
|
|
32
|
+
saves <room> list a room's save generations, newest first
|
|
33
|
+
restore <room> --save <id>
|
|
34
|
+
bring a room back from a save. This DISCARDS its current state, and
|
|
35
|
+
stops the tenant if it is live
|
|
36
|
+
delete <room> delete a room's stored state: its snapshot, its saves and its alarms.
|
|
37
|
+
This is permanent. Refused while the room is awake
|
|
38
|
+
|
|
39
|
+
options:
|
|
40
|
+
--project <id> project id (default: the project file)
|
|
41
|
+
-c, --config <f> the project file to read (default irtio.json)
|
|
42
|
+
--url <control> control plane (default: your stored login)
|
|
43
|
+
-h, --help print this
|
|
44
|
+
`;
|
|
14
45
|
function parseRoomsArgs(args) {
|
|
46
|
+
if (helpRequested(args)) throw helpFor(USAGE);
|
|
15
47
|
const parsed = { sub: "list" };
|
|
16
48
|
let rest = args;
|
|
17
49
|
const first = args[0];
|
|
18
|
-
if (first === "saves" || first === "restore") {
|
|
50
|
+
if (first === "saves" || first === "restore" || first === "delete") {
|
|
19
51
|
parsed.sub = first;
|
|
20
52
|
const room = args[1];
|
|
21
53
|
if (room === void 0 || room.startsWith("-")) {
|
|
@@ -39,6 +71,14 @@ function parseRoomsArgs(args) {
|
|
|
39
71
|
parsed.project = value;
|
|
40
72
|
break;
|
|
41
73
|
}
|
|
74
|
+
case "-c":
|
|
75
|
+
case "--config": {
|
|
76
|
+
const value = args[++i];
|
|
77
|
+
if (value === void 0 || value === "")
|
|
78
|
+
throw new Error("irtio rooms: --config needs a value");
|
|
79
|
+
parsed.config = value;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
42
82
|
case "--url": {
|
|
43
83
|
const value = rest[++i];
|
|
44
84
|
if (value === void 0) throw new Error("irtio rooms: --url needs a value");
|
|
@@ -77,10 +117,11 @@ function statusColor(status) {
|
|
|
77
117
|
function formatTable(rows) {
|
|
78
118
|
if (rows.length === 0) return [pc.dim("no rooms")];
|
|
79
119
|
const idWidth = Math.max(4, ...rows.map((r) => r.roomId.length));
|
|
80
|
-
const out = [pc.dim(`${"ROOM".padEnd(idWidth)} STATUS LAST SEEN`)];
|
|
120
|
+
const out = [pc.dim(`${"ROOM".padEnd(idWidth)} STATUS RETAIN LAST SEEN`)];
|
|
81
121
|
for (const r of rows) {
|
|
122
|
+
const retention = r.retention ?? "forever";
|
|
82
123
|
out.push(
|
|
83
|
-
`${r.roomId.padEnd(idWidth)} ${statusColor(r.status).padEnd(10)} ${pc.dim(r.lastSeen)}`
|
|
124
|
+
`${r.roomId.padEnd(idWidth)} ${statusColor(r.status).padEnd(10)} ${retention.padEnd(6)} ${pc.dim(r.lastSeen)}`
|
|
84
125
|
);
|
|
85
126
|
}
|
|
86
127
|
return out;
|
|
@@ -132,6 +173,20 @@ async function runRestore(options) {
|
|
|
132
173
|
}
|
|
133
174
|
return result;
|
|
134
175
|
}
|
|
176
|
+
async function runDeleteRoom(options) {
|
|
177
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
178
|
+
const controlUrl = await resolveControlUrlForUser(options.controlUrl);
|
|
179
|
+
const client = options.client ?? await createApiClient(controlUrl);
|
|
180
|
+
log(
|
|
181
|
+
`${pc.yellow("deleting")} room ${pc.bold(options.room)}: its snapshot, its saves and its alarms`
|
|
182
|
+
);
|
|
183
|
+
const result = await client.del(
|
|
184
|
+
`/v1/projects/${options.project}/rooms/${encodeURIComponent(options.room)}`
|
|
185
|
+
);
|
|
186
|
+
log(pc.green(`deleted ${options.room}: ${result.objects} stored object(s) removed`));
|
|
187
|
+
log(pc.dim("player data and leaderboard scores are keyed to the player and are untouched"));
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
135
190
|
async function runRooms(options) {
|
|
136
191
|
const log = options.log ?? ((line) => console.log(line));
|
|
137
192
|
const controlUrl = await resolveControlUrlForUser(options.controlUrl);
|
|
@@ -140,23 +195,13 @@ async function runRooms(options) {
|
|
|
140
195
|
for (const line of formatTable(rows)) log(line);
|
|
141
196
|
return rows;
|
|
142
197
|
}
|
|
143
|
-
async function readIrtioJsonProject(cwd) {
|
|
144
|
-
const file = path.join(cwd, "irtio.json");
|
|
145
|
-
if (!existsSync(file)) return void 0;
|
|
146
|
-
let parsed;
|
|
147
|
-
try {
|
|
148
|
-
parsed = JSON.parse(await readFile(file, "utf8"));
|
|
149
|
-
} catch {
|
|
150
|
-
throw new Error(`irtio rooms: ${file} is not valid JSON`);
|
|
151
|
-
}
|
|
152
|
-
return typeof parsed.project === "string" && parsed.project.length > 0 ? parsed.project : void 0;
|
|
153
|
-
}
|
|
154
198
|
async function rooms(args, deps = {}) {
|
|
155
199
|
const log = deps.log ?? ((line) => console.log(line));
|
|
156
200
|
const errorLog = deps.errorLog ?? ((line) => console.error(line));
|
|
157
201
|
try {
|
|
158
202
|
const parsed = parseRoomsArgs(args);
|
|
159
|
-
const
|
|
203
|
+
const config = await readProjectConfig(process.cwd(), "irtio rooms", parsed.config);
|
|
204
|
+
const project = parsed.project ?? config.project;
|
|
160
205
|
if (project === void 0) {
|
|
161
206
|
throw new Error(
|
|
162
207
|
"irtio rooms: no project id \u2014 pass --project or run this from a project with irtio.json"
|
|
@@ -170,6 +215,8 @@ async function rooms(args, deps = {}) {
|
|
|
170
215
|
};
|
|
171
216
|
if (parsed.sub === "saves") {
|
|
172
217
|
await runSaves({ ...common, room: parsed.room });
|
|
218
|
+
} else if (parsed.sub === "delete") {
|
|
219
|
+
await runDeleteRoom({ ...common, room: parsed.room });
|
|
173
220
|
} else if (parsed.sub === "restore") {
|
|
174
221
|
await runRestore({
|
|
175
222
|
...common,
|
|
@@ -180,6 +227,10 @@ async function rooms(args, deps = {}) {
|
|
|
180
227
|
await runRooms(common);
|
|
181
228
|
}
|
|
182
229
|
} catch (err) {
|
|
230
|
+
if (err instanceof HelpRequested) {
|
|
231
|
+
log(err.usage);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
183
234
|
if (isLoginRequired(err)) {
|
|
184
235
|
errorLog(pc.red("not logged in"));
|
|
185
236
|
errorLog("run: irtio login");
|
|
@@ -191,8 +242,10 @@ async function rooms(args, deps = {}) {
|
|
|
191
242
|
}
|
|
192
243
|
}
|
|
193
244
|
export {
|
|
245
|
+
USAGE,
|
|
194
246
|
parseRoomsArgs,
|
|
195
247
|
rooms,
|
|
248
|
+
runDeleteRoom,
|
|
196
249
|
runRestore,
|
|
197
250
|
runRooms,
|
|
198
251
|
runSaves
|