@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
@@ -1,167 +0,0 @@
1
- // src/credentials.ts
2
- import { existsSync } from "fs";
3
- import { chmod, mkdir, readFile, writeFile } from "fs/promises";
4
- import * as os from "os";
5
- import * as path from "path";
6
- function credentialsPath() {
7
- if (process.env.IRT_CREDENTIALS_FILE) return process.env.IRT_CREDENTIALS_FILE;
8
- if (process.platform === "win32") {
9
- const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
10
- return path.join(appData, "irtio", "credentials.json");
11
- }
12
- return path.join(os.homedir(), ".config", "irtio", "credentials.json");
13
- }
14
- async function readCredentials() {
15
- const file = credentialsPath();
16
- if (!existsSync(file)) return {};
17
- try {
18
- const raw = await readFile(file, "utf8");
19
- const parsed = JSON.parse(raw);
20
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
21
- return parsed;
22
- } catch {
23
- return {};
24
- }
25
- }
26
- async function readCredential(controlUrl) {
27
- const all = await readCredentials();
28
- return all[controlUrl];
29
- }
30
- async function writeCredential(controlUrl, credential) {
31
- const file = credentialsPath();
32
- await mkdir(path.dirname(file), { recursive: true });
33
- const all = await readCredentials();
34
- all[controlUrl] = credential;
35
- await writeFile(file, `${JSON.stringify(all, null, 2)}
36
- `, { mode: 384 });
37
- await chmod(file, 384).catch(() => {
38
- });
39
- return file;
40
- }
41
- var DEFAULT_CONTROL_URL = "https://control.irt.io";
42
- function resolveControlUrl(flag) {
43
- return flag ?? process.env.IRT_CONTROL_URL ?? DEFAULT_CONTROL_URL;
44
- }
45
- async function resolveControlUrlForUser(flag) {
46
- const explicit = flag ?? process.env.IRT_CONTROL_URL;
47
- if (explicit !== void 0) return explicit;
48
- const all = await readCredentials();
49
- const now = Date.now();
50
- const live = Object.entries(all).filter(([, cred]) => new Date(cred.expiresAt).getTime() > now).map(([url]) => url);
51
- const urls = live.length > 0 ? live : Object.keys(all);
52
- return urls.length === 1 ? urls[0] : DEFAULT_CONTROL_URL;
53
- }
54
-
55
- // src/api-client.ts
56
- var ApiClientError = class extends Error {
57
- name = "ApiClientError";
58
- status;
59
- code;
60
- changes;
61
- hint;
62
- constructor(status, body) {
63
- super(body.message);
64
- this.status = status;
65
- this.code = body.code;
66
- this.changes = body.changes;
67
- this.hint = body.hint;
68
- }
69
- };
70
- var NotLoggedInError = class extends Error {
71
- constructor(controlUrl) {
72
- super(`not logged in to ${controlUrl} \u2014 run: irtio login`);
73
- this.controlUrl = controlUrl;
74
- }
75
- controlUrl;
76
- name = "NotLoggedInError";
77
- };
78
- async function createApiClient(controlUrl) {
79
- const credential = await readCredential(controlUrl);
80
- if (!credential) throw new NotLoggedInError(controlUrl);
81
- return createApiClientWithToken(controlUrl, credential.token);
82
- }
83
- function createApiClientWithToken(controlUrl, token) {
84
- async function request(method, path2, init = {}) {
85
- const headers = {
86
- Authorization: `Bearer ${token}`,
87
- Accept: "application/json"
88
- };
89
- let requestBody;
90
- if (init.bytes !== void 0) {
91
- headers["content-type"] = "application/octet-stream";
92
- requestBody = init.bytes;
93
- } else if (init.body !== void 0) {
94
- headers["content-type"] = "application/json";
95
- requestBody = JSON.stringify(init.body);
96
- }
97
- const response = await fetch(`${controlUrl}${path2}`, {
98
- method,
99
- headers,
100
- ...requestBody !== void 0 ? { body: requestBody } : {}
101
- });
102
- const text = await response.text();
103
- const parsed = text.length > 0 ? JSON.parse(text) : void 0;
104
- if (!response.ok) {
105
- const body = parsed !== void 0 && typeof parsed === "object" && parsed !== null && "code" in parsed && "message" in parsed ? parsed : {
106
- code: "E_UNKNOWN",
107
- message: text || `${method} ${path2} failed with ${response.status}`
108
- };
109
- throw new ApiClientError(response.status, body);
110
- }
111
- return parsed;
112
- }
113
- return {
114
- controlUrl,
115
- get(path2, query) {
116
- const qs = query ? Object.entries(query).filter((e) => e[1] !== void 0).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&") : "";
117
- return request("GET", qs ? `${path2}?${qs}` : path2);
118
- },
119
- post(path2, body) {
120
- return request("POST", path2, { body });
121
- },
122
- patch(path2, body) {
123
- return request("PATCH", path2, { body });
124
- },
125
- postBytes(path2, bytes) {
126
- return request("POST", path2, { bytes });
127
- }
128
- };
129
- }
130
- function isLoginRequired(err) {
131
- return err instanceof NotLoggedInError || err instanceof ApiClientError && err.status === 401;
132
- }
133
-
134
- // src/help.ts
135
- function helpRequested(args) {
136
- return args.some((arg) => arg === "--help" || arg === "-h");
137
- }
138
- var HelpRequested = class extends Error {
139
- constructor(usage) {
140
- super(usage);
141
- this.usage = usage;
142
- }
143
- usage;
144
- name = "HelpRequested";
145
- };
146
- function helpFor(usage) {
147
- return new HelpRequested(usage);
148
- }
149
- function trailerFor(err, hints = []) {
150
- if (err instanceof ApiClientError && err.hint !== void 0 && err.hint !== "") return err.hint;
151
- const grounded = hints.filter((h) => h !== "");
152
- return grounded.length > 0 ? grounded.join("\n") : void 0;
153
- }
154
-
155
- export {
156
- credentialsPath,
157
- writeCredential,
158
- resolveControlUrl,
159
- resolveControlUrlForUser,
160
- ApiClientError,
161
- createApiClient,
162
- isLoginRequired,
163
- helpRequested,
164
- HelpRequested,
165
- helpFor,
166
- trailerFor
167
- };
@@ -1,226 +0,0 @@
1
- import {
2
- readProjectConfig
3
- } from "./chunk-32QTPKVT.js";
4
- import {
5
- HelpRequested,
6
- createApiClient,
7
- helpFor,
8
- helpRequested,
9
- isLoginRequired,
10
- resolveControlUrlForUser
11
- } from "./chunk-TV66QHFP.js";
12
-
13
- // src/rooms.ts
14
- import "fs";
15
- import "fs/promises";
16
- import "path";
17
- import pc from "picocolors";
18
- var USAGE = `usage: irtio rooms [options]
19
- irtio rooms saves <room> [options]
20
- irtio rooms restore <room> --save <id> [options]
21
-
22
- Lists a project's rooms. Rows not reported for longer than the control plane's retention window
23
- (30 days by default) are pruned.
24
-
25
- subcommands:
26
- saves <room> list a room's save generations, newest first
27
- restore <room> --save <id>
28
- bring a room back from a save. This DISCARDS its current state, and
29
- stops the tenant if it is live
30
-
31
- options:
32
- --project <id> project id (default: the project file)
33
- -c, --config <f> the project file to read (default irtio.json)
34
- --url <control> control plane (default: your stored login)
35
- -h, --help print this
36
- `;
37
- function parseRoomsArgs(args) {
38
- if (helpRequested(args)) throw helpFor(USAGE);
39
- const parsed = { sub: "list" };
40
- let rest = args;
41
- const first = args[0];
42
- if (first === "saves" || first === "restore") {
43
- parsed.sub = first;
44
- const room = args[1];
45
- if (room === void 0 || room.startsWith("-")) {
46
- throw new Error(`irtio rooms ${first}: needs a room id`);
47
- }
48
- parsed.room = room;
49
- rest = args.slice(2);
50
- }
51
- for (let i = 0; i < rest.length; i++) {
52
- const arg = rest[i];
53
- switch (arg) {
54
- case "--save": {
55
- const value = rest[++i];
56
- if (value === void 0) throw new Error("irtio rooms: --save needs a value");
57
- parsed.save = value;
58
- break;
59
- }
60
- case "--project": {
61
- const value = rest[++i];
62
- if (value === void 0) throw new Error("irtio rooms: --project needs a value");
63
- parsed.project = value;
64
- break;
65
- }
66
- case "-c":
67
- case "--config": {
68
- const value = args[++i];
69
- if (value === void 0 || value === "")
70
- throw new Error("irtio rooms: --config needs a value");
71
- parsed.config = value;
72
- break;
73
- }
74
- case "--url": {
75
- const value = rest[++i];
76
- if (value === void 0) throw new Error("irtio rooms: --url needs a value");
77
- parsed.url = value;
78
- break;
79
- }
80
- default:
81
- throw new Error(`irtio rooms: unknown option ${JSON.stringify(arg)}`);
82
- }
83
- }
84
- if (parsed.sub === "restore" && parsed.save === void 0) {
85
- throw new Error(
86
- "irtio rooms restore: --save <id> is required \u2014 run `irtio rooms saves <room>` to see them"
87
- );
88
- }
89
- return parsed;
90
- }
91
- function formatBytes(bytes) {
92
- if (bytes < 1024) return `${bytes} B`;
93
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
94
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
95
- }
96
- function formatAge(iso, now) {
97
- const ms = now - Date.parse(iso);
98
- if (!Number.isFinite(ms) || ms < 0) return "\u2014";
99
- const mins = Math.floor(ms / 6e4);
100
- if (mins < 1) return "just now";
101
- if (mins < 60) return `${mins}m ago`;
102
- const hours = Math.floor(mins / 60);
103
- if (hours < 24) return `${hours}h ago`;
104
- return `${Math.floor(hours / 24)}d ago`;
105
- }
106
- function statusColor(status) {
107
- return status === "active" || status === "running" ? pc.green(status) : status === "draining" ? pc.yellow(status) : pc.dim(status);
108
- }
109
- function formatTable(rows) {
110
- if (rows.length === 0) return [pc.dim("no rooms")];
111
- const idWidth = Math.max(4, ...rows.map((r) => r.roomId.length));
112
- const out = [pc.dim(`${"ROOM".padEnd(idWidth)} STATUS LAST SEEN`)];
113
- for (const r of rows) {
114
- out.push(
115
- `${r.roomId.padEnd(idWidth)} ${statusColor(r.status).padEnd(10)} ${pc.dim(r.lastSeen)}`
116
- );
117
- }
118
- return out;
119
- }
120
- function formatSaves(rows, now) {
121
- if (rows.length === 0) {
122
- return [pc.dim("no saves"), pc.dim("a room writes one when its code calls room.save()")];
123
- }
124
- const idWidth = Math.max(7, ...rows.map((r) => r.saveId.length));
125
- const out = [pc.dim(`${"SAVE ID".padEnd(idWidth)} AGE SIZE DEPLOY`)];
126
- for (const r of rows) {
127
- out.push(
128
- `${r.saveId.padEnd(idWidth)} ${formatAge(r.createdAt, now).padEnd(10)} ${formatBytes(r.bytes).padEnd(8)} ${pc.dim(`v${r.version}`)}`
129
- );
130
- }
131
- return out;
132
- }
133
- async function runSaves(options) {
134
- const log = options.log ?? ((line) => console.log(line));
135
- const controlUrl = await resolveControlUrlForUser(options.controlUrl);
136
- const client = options.client ?? await createApiClient(controlUrl);
137
- const rows = await client.get(
138
- `/v1/projects/${options.project}/rooms/${encodeURIComponent(options.room)}/saves`
139
- );
140
- for (const line of formatSaves(rows, options.now ?? Date.now())) log(line);
141
- return rows;
142
- }
143
- async function runRestore(options) {
144
- const log = options.log ?? ((line) => console.log(line));
145
- const controlUrl = await resolveControlUrlForUser(options.controlUrl);
146
- const client = options.client ?? await createApiClient(controlUrl);
147
- log(
148
- `${pc.yellow("discarding")} the current state of room ${pc.bold(options.room)} and restoring save ${pc.bold(options.saveId)}`
149
- );
150
- const result = await client.post(
151
- `/v1/projects/${options.project}/rooms/${encodeURIComponent(options.room)}/restore`,
152
- { saveId: options.saveId }
153
- );
154
- if (result.applied === "stopped") {
155
- log(pc.green(`restored: ${options.room} was live, so its tenant was stopped`));
156
- log(
157
- pc.dim(
158
- "the room comes back from the save on the next join; every other room in this project was stopped too and comes back from its own snapshot, unchanged"
159
- )
160
- );
161
- } else {
162
- log(pc.green(`queued: ${options.room} was not running`));
163
- log(pc.dim("the restore is applied at the next placement \u2014 the next join starts it"));
164
- }
165
- return result;
166
- }
167
- async function runRooms(options) {
168
- const log = options.log ?? ((line) => console.log(line));
169
- const controlUrl = await resolveControlUrlForUser(options.controlUrl);
170
- const client = options.client ?? await createApiClient(controlUrl);
171
- const rows = await client.get(`/v1/projects/${options.project}/rooms`);
172
- for (const line of formatTable(rows)) log(line);
173
- return rows;
174
- }
175
- async function rooms(args, deps = {}) {
176
- const log = deps.log ?? ((line) => console.log(line));
177
- const errorLog = deps.errorLog ?? ((line) => console.error(line));
178
- try {
179
- const parsed = parseRoomsArgs(args);
180
- const config = await readProjectConfig(process.cwd(), "irtio rooms", parsed.config);
181
- const project = parsed.project ?? config.project;
182
- if (project === void 0) {
183
- throw new Error(
184
- "irtio rooms: no project id \u2014 pass --project or run this from a project with irtio.json"
185
- );
186
- }
187
- const common = {
188
- project,
189
- log,
190
- ...parsed.url !== void 0 ? { controlUrl: parsed.url } : {},
191
- ...deps.client !== void 0 ? { client: deps.client } : {}
192
- };
193
- if (parsed.sub === "saves") {
194
- await runSaves({ ...common, room: parsed.room });
195
- } else if (parsed.sub === "restore") {
196
- await runRestore({
197
- ...common,
198
- room: parsed.room,
199
- saveId: parsed.save
200
- });
201
- } else {
202
- await runRooms(common);
203
- }
204
- } catch (err) {
205
- if (err instanceof HelpRequested) {
206
- log(err.usage);
207
- return;
208
- }
209
- if (isLoginRequired(err)) {
210
- errorLog(pc.red("not logged in"));
211
- errorLog("run: irtio login");
212
- process.exitCode = 1;
213
- return;
214
- }
215
- errorLog(pc.red(err instanceof Error ? err.message : String(err)));
216
- process.exitCode = 1;
217
- }
218
- }
219
- export {
220
- USAGE,
221
- parseRoomsArgs,
222
- rooms,
223
- runRestore,
224
- runRooms,
225
- runSaves
226
- };