@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,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
USAGE,
|
|
3
|
+
parseStaticDeployArgs,
|
|
4
|
+
runStaticDeploy,
|
|
5
|
+
staticDeploy,
|
|
6
|
+
walkStaticDir
|
|
7
|
+
} from "./chunk-3HQMVCYA.js";
|
|
8
|
+
import "./chunk-RQSJZWQC.js";
|
|
9
|
+
import "./chunk-DKWG7MGO.js";
|
|
10
|
+
import "./chunk-ZD4ND6X6.js";
|
|
11
|
+
import "./chunk-RNAH5T4W.js";
|
|
12
|
+
import "./chunk-UPHQM6NZ.js";
|
|
13
|
+
export {
|
|
14
|
+
USAGE,
|
|
15
|
+
parseStaticDeployArgs,
|
|
16
|
+
runStaticDeploy,
|
|
17
|
+
staticDeploy,
|
|
18
|
+
walkStaticDir
|
|
19
|
+
};
|
|
@@ -0,0 +1,219 @@
|
|
|
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/status.ts
|
|
18
|
+
import pc from "picocolors";
|
|
19
|
+
var USAGE = `usage: irtio status [options]
|
|
20
|
+
|
|
21
|
+
Shows a project's live metrics.
|
|
22
|
+
|
|
23
|
+
With no --room: the latest value of every tenant-wide metric, then a per-room table of
|
|
24
|
+
connections and tick health, built from a 30s-per-tenant sample rollup.
|
|
25
|
+
|
|
26
|
+
With --room <id>: that one room's recent history instead, newest first.
|
|
27
|
+
|
|
28
|
+
options:
|
|
29
|
+
--room <id> show one room's recent history instead of the project-wide summary
|
|
30
|
+
--project <id> project id (default: the project file)
|
|
31
|
+
-c, --config <f> the project file to read (default irtio.json)
|
|
32
|
+
--url <control> control plane (default: your stored login)
|
|
33
|
+
-h, --help print this
|
|
34
|
+
`;
|
|
35
|
+
function parseStatusArgs(args) {
|
|
36
|
+
if (helpRequested(args)) throw helpFor(USAGE);
|
|
37
|
+
const parsed = {};
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
const arg = args[i];
|
|
40
|
+
switch (arg) {
|
|
41
|
+
case "--room": {
|
|
42
|
+
const value = args[++i];
|
|
43
|
+
if (value === void 0) throw new Error("irtio status: --room needs a value");
|
|
44
|
+
parsed.room = value;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "--project": {
|
|
48
|
+
const value = args[++i];
|
|
49
|
+
if (value === void 0) throw new Error("irtio status: --project needs a value");
|
|
50
|
+
parsed.project = value;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "-c":
|
|
54
|
+
case "--config": {
|
|
55
|
+
const value = args[++i];
|
|
56
|
+
if (value === void 0 || value === "")
|
|
57
|
+
throw new Error("irtio status: --config needs a value");
|
|
58
|
+
parsed.config = value;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "--url": {
|
|
62
|
+
const value = args[++i];
|
|
63
|
+
if (value === void 0) throw new Error("irtio status: --url needs a value");
|
|
64
|
+
parsed.url = value;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
throw new Error(`irtio status: unknown option ${JSON.stringify(arg)}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return parsed;
|
|
72
|
+
}
|
|
73
|
+
function formatTimestamp(iso) {
|
|
74
|
+
return iso.replace("T", " ").replace(/\.\d+Z$/, "Z");
|
|
75
|
+
}
|
|
76
|
+
function formatSampleAge(iso, now) {
|
|
77
|
+
const ms = now - Date.parse(iso);
|
|
78
|
+
if (!Number.isFinite(ms) || ms < 0) return "-";
|
|
79
|
+
const secs = Math.floor(ms / 1e3);
|
|
80
|
+
if (secs < 60) return `${secs}s ago`;
|
|
81
|
+
const mins = Math.floor(secs / 60);
|
|
82
|
+
if (mins < 60) return `${mins}m ago`;
|
|
83
|
+
const hours = Math.floor(mins / 60);
|
|
84
|
+
if (hours < 24) return `${hours}h ago`;
|
|
85
|
+
return `${Math.floor(hours / 24)}d ago`;
|
|
86
|
+
}
|
|
87
|
+
function formatAggregateMetrics(rows) {
|
|
88
|
+
if (rows.length === 0) return [pc.dim("no metrics reported yet")];
|
|
89
|
+
const newest = /* @__PURE__ */ new Map();
|
|
90
|
+
for (const row of rows) if (!newest.has(row.name)) newest.set(row.name, row);
|
|
91
|
+
const names = [...newest.keys()].sort();
|
|
92
|
+
const nameWidth = Math.max(6, ...names.map((n) => n.length));
|
|
93
|
+
const out = [pc.dim(`${"METRIC".padEnd(nameWidth)} VALUE AT`)];
|
|
94
|
+
for (const name of names) {
|
|
95
|
+
const row = newest.get(name);
|
|
96
|
+
out.push(
|
|
97
|
+
`${name.padEnd(nameWidth)} ${String(row.value).padEnd(10)} ${pc.dim(formatTimestamp(row.at))}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
function formatRoomsSnapshot(rows, now) {
|
|
103
|
+
if (rows.length === 0) {
|
|
104
|
+
return [
|
|
105
|
+
pc.dim("no per-room samples yet"),
|
|
106
|
+
pc.dim("an old guest rootfs, or a project with no rooms, both look like this")
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
const idWidth = Math.max(4, ...rows.map((r) => r.roomId.length));
|
|
110
|
+
const field = (metrics, name) => metrics[name] === void 0 ? "-" : String(metrics[name]);
|
|
111
|
+
const bytes = (metrics, name) => {
|
|
112
|
+
const value = metrics[name];
|
|
113
|
+
if (value === void 0) return "-";
|
|
114
|
+
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
115
|
+
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}k`;
|
|
116
|
+
return String(value);
|
|
117
|
+
};
|
|
118
|
+
const out = [
|
|
119
|
+
pc.dim(
|
|
120
|
+
`${"ROOM".padEnd(idWidth)} CONN TICKS MAXTICKMS OVERRUNS ELU IN OUT SAMPLE AGE`
|
|
121
|
+
)
|
|
122
|
+
];
|
|
123
|
+
for (const r of rows) {
|
|
124
|
+
out.push(
|
|
125
|
+
`${r.roomId.padEnd(idWidth)} ${field(r.metrics, "connections").padEnd(4)} ${field(r.metrics, "ticks").padEnd(10)} ${field(r.metrics, "maxTickMs").padEnd(10)} ${field(r.metrics, "overruns").padEnd(8)} ${field(r.metrics, "workerElu").padEnd(6)} ${bytes(r.metrics, "ingressBytes").padEnd(8)} ${bytes(r.metrics, "egressBytes").padEnd(8)} ${pc.dim(formatSampleAge(r.at, now))}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
var ROOM_HISTORY_NAMES = ["ticks", "maxTickMs", "overruns", "connections"];
|
|
131
|
+
function formatRoomHistory(rows) {
|
|
132
|
+
if (rows.length === 0) {
|
|
133
|
+
return [pc.dim("no samples for this room in the 7-day retention window")];
|
|
134
|
+
}
|
|
135
|
+
const order = [];
|
|
136
|
+
const samples = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const row of rows) {
|
|
138
|
+
let sample = samples.get(row.at);
|
|
139
|
+
if (sample === void 0) {
|
|
140
|
+
sample = {};
|
|
141
|
+
samples.set(row.at, sample);
|
|
142
|
+
order.push(row.at);
|
|
143
|
+
}
|
|
144
|
+
sample[row.name] = row.value;
|
|
145
|
+
}
|
|
146
|
+
const out = [];
|
|
147
|
+
for (const at of order.slice(0, 10)) {
|
|
148
|
+
const sample = samples.get(at);
|
|
149
|
+
const parts = ROOM_HISTORY_NAMES.filter((name) => sample[name] !== void 0).map(
|
|
150
|
+
(name) => `${name}=${sample[name]}`
|
|
151
|
+
);
|
|
152
|
+
out.push(`${pc.dim(formatTimestamp(at))} ${parts.join(" ")}`);
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
async function runStatus(options) {
|
|
157
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
158
|
+
const controlUrl = await resolveControlUrlForUser(options.controlUrl);
|
|
159
|
+
const client = options.client ?? await createApiClient(controlUrl);
|
|
160
|
+
if (options.room !== void 0) {
|
|
161
|
+
const rows = await client.get(
|
|
162
|
+
`/v1/projects/${options.project}/metrics`,
|
|
163
|
+
{ room: options.room }
|
|
164
|
+
);
|
|
165
|
+
for (const line of formatRoomHistory(rows)) log(line);
|
|
166
|
+
return { kind: "room", rows };
|
|
167
|
+
}
|
|
168
|
+
const [project, metrics, rooms] = await Promise.all([
|
|
169
|
+
client.get(`/v1/projects/${options.project}`),
|
|
170
|
+
client.get(`/v1/projects/${options.project}/metrics`),
|
|
171
|
+
client.get(`/v1/projects/${options.project}/metrics/rooms`)
|
|
172
|
+
]);
|
|
173
|
+
log(`shards: ${typeof project.shards === "number" ? project.shards : 1}`);
|
|
174
|
+
log("");
|
|
175
|
+
for (const line of formatAggregateMetrics(metrics)) log(line);
|
|
176
|
+
log("");
|
|
177
|
+
for (const line of formatRoomsSnapshot(rooms, options.now ?? Date.now())) log(line);
|
|
178
|
+
return { kind: "project", metrics, rooms };
|
|
179
|
+
}
|
|
180
|
+
async function status(args, deps = {}) {
|
|
181
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
182
|
+
const errorLog = deps.errorLog ?? ((line) => console.error(line));
|
|
183
|
+
try {
|
|
184
|
+
const parsed = parseStatusArgs(args);
|
|
185
|
+
const config = await readProjectConfig(process.cwd(), "irtio status", parsed.config);
|
|
186
|
+
const project = parsed.project ?? config.project;
|
|
187
|
+
if (project === void 0) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
"irtio status: no project id. Pass --project or run this from a project with irtio.json"
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
await runStatus({
|
|
193
|
+
project,
|
|
194
|
+
log,
|
|
195
|
+
...parsed.room !== void 0 ? { room: parsed.room } : {},
|
|
196
|
+
...parsed.url !== void 0 ? { controlUrl: parsed.url } : {},
|
|
197
|
+
...deps.client !== void 0 ? { client: deps.client } : {}
|
|
198
|
+
});
|
|
199
|
+
} catch (err) {
|
|
200
|
+
if (err instanceof HelpRequested) {
|
|
201
|
+
log(err.usage);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (isLoginRequired(err)) {
|
|
205
|
+
errorLog(pc.red("not logged in"));
|
|
206
|
+
errorLog("run: irtio login");
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
errorLog(pc.red(err instanceof Error ? err.message : String(err)));
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export {
|
|
215
|
+
USAGE,
|
|
216
|
+
parseStatusArgs,
|
|
217
|
+
runStatus,
|
|
218
|
+
status
|
|
219
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
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/usage.ts
|
|
18
|
+
import pc from "picocolors";
|
|
19
|
+
var USAGE = `usage: irtio usage [options]
|
|
20
|
+
|
|
21
|
+
Shows what a project used this billing period, per meter.
|
|
22
|
+
|
|
23
|
+
The period is the calendar month in UTC. Meters nothing has reported print "not reported"
|
|
24
|
+
rather than zero, because those are different facts.
|
|
25
|
+
|
|
26
|
+
On the card plan each meter shows its included allowance, the overage rate, and an estimated
|
|
27
|
+
overage cost where usage is past the bundle. The free plan shows no dollar figures: it stops
|
|
28
|
+
at its caps instead of billing.
|
|
29
|
+
|
|
30
|
+
options:
|
|
31
|
+
--windows also print the hourly windows behind the totals
|
|
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 parseUsageArgs(args) {
|
|
38
|
+
if (helpRequested(args)) throw helpFor(USAGE);
|
|
39
|
+
const parsed = {};
|
|
40
|
+
for (let i = 0; i < args.length; i++) {
|
|
41
|
+
const arg = args[i];
|
|
42
|
+
switch (arg) {
|
|
43
|
+
case "--windows":
|
|
44
|
+
parsed.windows = true;
|
|
45
|
+
break;
|
|
46
|
+
case "--project": {
|
|
47
|
+
const value = args[++i];
|
|
48
|
+
if (value === void 0) throw new Error("irtio usage: --project needs a value");
|
|
49
|
+
parsed.project = value;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case "-c":
|
|
53
|
+
case "--config": {
|
|
54
|
+
const value = args[++i];
|
|
55
|
+
if (value === void 0 || value === "")
|
|
56
|
+
throw new Error("irtio usage: --config needs a value");
|
|
57
|
+
parsed.config = value;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case "--url": {
|
|
61
|
+
const value = args[++i];
|
|
62
|
+
if (value === void 0) throw new Error("irtio usage: --url needs a value");
|
|
63
|
+
parsed.url = value;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
default:
|
|
67
|
+
throw new Error(`irtio usage: unknown option ${JSON.stringify(arg)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return parsed;
|
|
71
|
+
}
|
|
72
|
+
function formatBytes(bytes) {
|
|
73
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
74
|
+
let value = bytes;
|
|
75
|
+
let unit = 0;
|
|
76
|
+
while (value >= 1e3 && unit < units.length - 1) {
|
|
77
|
+
value /= 1e3;
|
|
78
|
+
unit++;
|
|
79
|
+
}
|
|
80
|
+
return `${unit === 0 ? String(Math.round(value)) : value.toFixed(2)} ${units[unit]}`;
|
|
81
|
+
}
|
|
82
|
+
function formatMeterValue(meter) {
|
|
83
|
+
if (meter.unit === "bytes") return formatBytes(meter.used);
|
|
84
|
+
if (meter.unit === "hours") return `${meter.used.toFixed(3)} hours`;
|
|
85
|
+
return `${meter.used.toFixed(2)} ${meter.unit}`;
|
|
86
|
+
}
|
|
87
|
+
function formatQuantity(value, unit) {
|
|
88
|
+
if (unit === "bytes") return formatBytes(value);
|
|
89
|
+
if (unit === "hours") return `${value} hours`;
|
|
90
|
+
return `${value} ${unit}`;
|
|
91
|
+
}
|
|
92
|
+
function estimatedOverage(meter) {
|
|
93
|
+
const p = meter.pricing;
|
|
94
|
+
if (p === void 0 || !meter.present) return void 0;
|
|
95
|
+
const used = meter.walledUsed ?? meter.used;
|
|
96
|
+
const over = used - (p.cardIncluded ?? 0);
|
|
97
|
+
if (!Number.isFinite(over) || over <= 0) return void 0;
|
|
98
|
+
return over / p.per * p.rate;
|
|
99
|
+
}
|
|
100
|
+
var CARD_BASE_FEE_USD = 9;
|
|
101
|
+
function formatUsage(body, showWindows) {
|
|
102
|
+
const out = [pc.dim(`period ${body.period.label} \xB7 project ${body.project}`), ""];
|
|
103
|
+
const width = Math.max(6, ...body.meters.map((m) => m.label.length));
|
|
104
|
+
const card = body.plan === "card";
|
|
105
|
+
let overageTotal = 0;
|
|
106
|
+
for (const meter of body.meters) {
|
|
107
|
+
if (!meter.present) {
|
|
108
|
+
out.push(`${meter.label.padEnd(width)} ${pc.dim("not reported")}`);
|
|
109
|
+
if (meter.note !== void 0) out.push(`${" ".repeat(width)} ${pc.dim(meter.note)}`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const gauge = meter.gauge ? pc.dim(" (high-water this period)") : "";
|
|
113
|
+
out.push(`${meter.label.padEnd(width)} ${formatMeterValue(meter)}${gauge}`);
|
|
114
|
+
if (meter.byEngine !== void 0) {
|
|
115
|
+
const parts = Object.entries(meter.byEngine).sort(([a], [b]) => a.localeCompare(b)).map(([engine, hours]) => `${engine}=${hours.toFixed(3)}`);
|
|
116
|
+
if (parts.length > 0)
|
|
117
|
+
out.push(`${" ".repeat(width)} ${pc.dim(`by engine: ${parts.join(" ")}`)}`);
|
|
118
|
+
}
|
|
119
|
+
if (card && meter.pricing !== void 0) {
|
|
120
|
+
const p = meter.pricing;
|
|
121
|
+
const bundle = p.cardIncluded !== void 0 ? `included ${formatQuantity(p.cardIncluded, meter.unit)}, then ${p.rateLabel}` : `no included bundle, ${p.rateLabel}`;
|
|
122
|
+
out.push(`${" ".repeat(width)} ${pc.dim(bundle)}`);
|
|
123
|
+
const over = estimatedOverage(meter);
|
|
124
|
+
if (over !== void 0) {
|
|
125
|
+
overageTotal += over;
|
|
126
|
+
out.push(`${" ".repeat(width)} estimated overage: $${over.toFixed(2)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
out.push("");
|
|
131
|
+
if (card) {
|
|
132
|
+
out.push(
|
|
133
|
+
overageTotal > 0 ? `estimated overage this period: $${overageTotal.toFixed(2)}, ` + pc.dim(
|
|
134
|
+
`plus the $${CARD_BASE_FEE_USD}/mo base fee. Estimates, not an invoice. This total is what counts against your monthly overage spend cap; at the cap every meter stops.`
|
|
135
|
+
) : pc.dim(
|
|
136
|
+
`within the included bundle: no overage so far this period (the $${CARD_BASE_FEE_USD}/mo base fee still applies)`
|
|
137
|
+
)
|
|
138
|
+
);
|
|
139
|
+
} else if (body.plan === "free") {
|
|
140
|
+
out.push(
|
|
141
|
+
pc.dim("free plan: usage stops at its caps and nothing is billed; add a card for overage")
|
|
142
|
+
);
|
|
143
|
+
} else {
|
|
144
|
+
out.push(pc.dim("these are measurements, not charges"));
|
|
145
|
+
}
|
|
146
|
+
if (showWindows) {
|
|
147
|
+
out.push("");
|
|
148
|
+
if (body.windows.length === 0) {
|
|
149
|
+
out.push(pc.dim("no windows in this period"));
|
|
150
|
+
} else {
|
|
151
|
+
out.push(pc.dim("WINDOW METER SOURCE ENGINE VALUE"));
|
|
152
|
+
for (const w of body.windows) {
|
|
153
|
+
out.push(
|
|
154
|
+
`${w.windowStart.replace("T", " ").replace(/:\d\d\.\d+Z$/, "Z").padEnd(20)} ${w.meter.padEnd(13)} ${(w.source || "-").padEnd(6)} ${(w.engine || "-").padEnd(8)} ${w.value}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
async function runUsage(options) {
|
|
162
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
163
|
+
const controlUrl = await resolveControlUrlForUser(options.controlUrl);
|
|
164
|
+
const client = options.client ?? await createApiClient(controlUrl);
|
|
165
|
+
const body = await client.get(`/v1/projects/${options.project}/usage`);
|
|
166
|
+
for (const line of formatUsage(body, options.windows === true)) log(line);
|
|
167
|
+
return body;
|
|
168
|
+
}
|
|
169
|
+
async function usage(args, deps = {}) {
|
|
170
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
171
|
+
const errorLog = deps.errorLog ?? ((line) => console.error(line));
|
|
172
|
+
try {
|
|
173
|
+
const parsed = parseUsageArgs(args);
|
|
174
|
+
const config = await readProjectConfig(process.cwd(), "irtio usage", parsed.config);
|
|
175
|
+
const project = parsed.project ?? config.project;
|
|
176
|
+
if (project === void 0) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
"irtio usage: no project id. Pass --project or run this from a project with irtio.json"
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
await runUsage({
|
|
182
|
+
project,
|
|
183
|
+
log,
|
|
184
|
+
...parsed.windows === true ? { windows: true } : {},
|
|
185
|
+
...parsed.url !== void 0 ? { controlUrl: parsed.url } : {},
|
|
186
|
+
...deps.client !== void 0 ? { client: deps.client } : {}
|
|
187
|
+
});
|
|
188
|
+
} catch (err) {
|
|
189
|
+
if (err instanceof HelpRequested) {
|
|
190
|
+
log(err.usage);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (isLoginRequired(err)) {
|
|
194
|
+
errorLog(pc.red("not logged in"));
|
|
195
|
+
errorLog("run: irtio login");
|
|
196
|
+
process.exitCode = 1;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
errorLog(pc.red(err instanceof Error ? err.message : String(err)));
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export {
|
|
204
|
+
CARD_BASE_FEE_USD,
|
|
205
|
+
USAGE,
|
|
206
|
+
estimatedOverage,
|
|
207
|
+
formatBytes,
|
|
208
|
+
formatMeterValue,
|
|
209
|
+
formatUsage,
|
|
210
|
+
parseUsageArgs,
|
|
211
|
+
runUsage,
|
|
212
|
+
usage
|
|
213
|
+
};
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HelpRequested,
|
|
3
|
+
helpFor,
|
|
4
|
+
helpRequested
|
|
5
|
+
} from "./chunk-ZD4ND6X6.js";
|
|
1
6
|
import {
|
|
2
7
|
createApiClient,
|
|
3
8
|
isLoginRequired
|
|
4
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RNAH5T4W.js";
|
|
5
10
|
import {
|
|
6
11
|
resolveControlUrlForUser
|
|
7
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-UPHQM6NZ.js";
|
|
8
13
|
|
|
9
14
|
// src/whoami.ts
|
|
10
15
|
import pc from "picocolors";
|
|
16
|
+
var USAGE = `usage: irtio whoami [--url <control>]
|
|
17
|
+
|
|
18
|
+
Prints the identity your stored credential resolves to, and the control plane it was asked.
|
|
19
|
+
|
|
20
|
+
options:
|
|
21
|
+
--url <control> the control plane to ask (default: your stored login)
|
|
22
|
+
-h, --help print this
|
|
23
|
+
`;
|
|
11
24
|
function parseWhoamiArgs(args) {
|
|
25
|
+
if (helpRequested(args)) throw helpFor(USAGE);
|
|
12
26
|
const parsed = {};
|
|
13
27
|
for (let i = 0; i < args.length; i++) {
|
|
14
28
|
const arg = args[i];
|
|
@@ -46,6 +60,10 @@ async function whoami(args, deps = {}) {
|
|
|
46
60
|
...deps.client !== void 0 ? { client: deps.client } : {}
|
|
47
61
|
});
|
|
48
62
|
} catch (err) {
|
|
63
|
+
if (err instanceof HelpRequested) {
|
|
64
|
+
log(err.usage);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
49
67
|
if (isLoginRequired(err)) {
|
|
50
68
|
errorLog(pc.red("not logged in"));
|
|
51
69
|
errorLog("run: irtio login");
|
|
@@ -57,6 +75,7 @@ async function whoami(args, deps = {}) {
|
|
|
57
75
|
}
|
|
58
76
|
}
|
|
59
77
|
export {
|
|
78
|
+
USAGE,
|
|
60
79
|
parseWhoamiArgs,
|
|
61
80
|
runWhoami,
|
|
62
81
|
whoami
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irtio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "irtio CLI: local dev server, room scaffolding, bot simulation, deploy, logs and rooms",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -17,6 +17,22 @@
|
|
|
17
17
|
"./bundle": {
|
|
18
18
|
"types": "./dist/bundle.d.ts",
|
|
19
19
|
"import": "./dist/bundle.js"
|
|
20
|
+
},
|
|
21
|
+
"./api": {
|
|
22
|
+
"types": "./dist/api.d.ts",
|
|
23
|
+
"import": "./dist/api.js"
|
|
24
|
+
},
|
|
25
|
+
"./credentials": {
|
|
26
|
+
"types": "./dist/credentials.d.ts",
|
|
27
|
+
"import": "./dist/credentials.js"
|
|
28
|
+
},
|
|
29
|
+
"./simulate": {
|
|
30
|
+
"types": "./dist/simulate.d.ts",
|
|
31
|
+
"import": "./dist/simulate.js"
|
|
32
|
+
},
|
|
33
|
+
"./deploy": {
|
|
34
|
+
"types": "./dist/deploy.d.ts",
|
|
35
|
+
"import": "./dist/deploy.js"
|
|
20
36
|
}
|
|
21
37
|
},
|
|
22
38
|
"bin": {
|
|
@@ -29,14 +45,15 @@
|
|
|
29
45
|
"esbuild": "^0.27.3",
|
|
30
46
|
"picocolors": "^1.1.1",
|
|
31
47
|
"ws": "^8.18.0",
|
|
32
|
-
"@irtio/client": "0.
|
|
33
|
-
"@irtio/
|
|
34
|
-
"@irtio/
|
|
35
|
-
"@irtio/
|
|
36
|
-
"@irtio/runtime": "0.
|
|
37
|
-
"@irtio/
|
|
48
|
+
"@irtio/client": "0.6.0",
|
|
49
|
+
"@irtio/bots": "0.6.0",
|
|
50
|
+
"@irtio/protocol": "0.6.0",
|
|
51
|
+
"@irtio/server": "0.6.0",
|
|
52
|
+
"@irtio/runtime": "0.6.0",
|
|
53
|
+
"@irtio/schema": "0.6.0"
|
|
38
54
|
},
|
|
39
55
|
"devDependencies": {
|
|
56
|
+
"@types/ws": "^8.5.13",
|
|
40
57
|
"@dimforge/rapier3d-compat": "0.20.0",
|
|
41
58
|
"@irtio/store": "0.0.0",
|
|
42
59
|
"@irtio/supervisor": "0.0.0"
|