@statuser/cli 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +448 -2
- package/dist/client.js +163 -0
- package/dist/client.js.map +1 -0
- package/dist/commander-i18n.js +52 -0
- package/dist/commander-i18n.js.map +1 -0
- package/dist/commands/api.js +104 -0
- package/dist/commands/api.js.map +1 -0
- package/dist/commands/auth.js +104 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/catalog.js +72 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/config.js +302 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/heartbeat.js +199 -0
- package/dist/commands/heartbeat.js.map +1 -0
- package/dist/commands/incidents.js +265 -0
- package/dist/commands/incidents.js.map +1 -0
- package/dist/commands/maintenance.js +211 -0
- package/dist/commands/maintenance.js.map +1 -0
- package/dist/commands/reports.js +170 -0
- package/dist/commands/reports.js.map +1 -0
- package/dist/commands/scenarios.js +361 -0
- package/dist/commands/scenarios.js.map +1 -0
- package/dist/commands/servers-add.js +152 -0
- package/dist/commands/servers-add.js.map +1 -0
- package/dist/commands/servers-checks.js +56 -0
- package/dist/commands/servers-checks.js.map +1 -0
- package/dist/commands/servers.js +424 -0
- package/dist/commands/servers.js.map +1 -0
- package/dist/config.js +107 -0
- package/dist/config.js.map +1 -0
- package/dist/confirm.js +34 -0
- package/dist/confirm.js.map +1 -0
- package/dist/context.js +27 -0
- package/dist/context.js.map +1 -0
- package/dist/duration.js +20 -0
- package/dist/duration.js.map +1 -0
- package/dist/errors.js +73 -0
- package/dist/errors.js.map +1 -0
- package/dist/files.js +36 -0
- package/dist/files.js.map +1 -0
- package/dist/generated/openapi.js +6 -0
- package/dist/generated/openapi.js.map +1 -0
- package/dist/i18n.js +757 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/json-error.js +10 -0
- package/dist/json-error.js.map +1 -0
- package/dist/output.js +43 -0
- package/dist/output.js.map +1 -0
- package/dist/scenarios/config.js +228 -0
- package/dist/scenarios/config.js.map +1 -0
- package/dist/scenarios/plan.js +159 -0
- package/dist/scenarios/plan.js.map +1 -0
- package/dist/servers-config/export.js +72 -0
- package/dist/servers-config/export.js.map +1 -0
- package/dist/servers-config/fields.js +172 -0
- package/dist/servers-config/fields.js.map +1 -0
- package/dist/servers-config/forbidden-host.js +75 -0
- package/dist/servers-config/forbidden-host.js.map +1 -0
- package/dist/servers-config/host-pattern.js +6 -0
- package/dist/servers-config/host-pattern.js.map +1 -0
- package/dist/servers-config/parse.js +260 -0
- package/dist/servers-config/parse.js.map +1 -0
- package/dist/servers-config/plan.js +492 -0
- package/dist/servers-config/plan.js.map +1 -0
- package/dist/servers-update.js +157 -0
- package/dist/servers-update.js.map +1 -0
- package/dist/status-page.js +15 -0
- package/dist/status-page.js.map +1 -0
- package/dist/text-length.js +8 -0
- package/dist/text-length.js.map +1 -0
- package/dist/write-failure.js +37 -0
- package/dist/write-failure.js.map +1 -0
- package/package.json +70 -4
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { authedClient, globalOptions } from '../context.js';
|
|
3
|
+
import { CliError, ExitCode, StatuserApiError } from '../errors.js';
|
|
4
|
+
import { t } from '../i18n.js';
|
|
5
|
+
import { printErr, printJson, printLine, printTable } from '../output.js';
|
|
6
|
+
import { SCENARIO_LOCATIONS } from '../scenarios/config.js';
|
|
7
|
+
import { bulkTargetProblem, planServerPatch, } from '../servers-update.js';
|
|
8
|
+
import { duplicateKey, isDuplicateOf, isSecretHeader, MANAGED_PROTOCOLS, } from '../servers-config/fields.js';
|
|
9
|
+
import { patchPlanProblem } from '../servers-config/plan.js';
|
|
10
|
+
import { describeWriteFailure } from '../write-failure.js';
|
|
11
|
+
import { confirmDeletion } from '../confirm.js';
|
|
12
|
+
import { registerServersAdd } from './servers-add.js';
|
|
13
|
+
import { registerServersChecks } from './servers-checks.js';
|
|
14
|
+
export async function listServers(client, projectId) {
|
|
15
|
+
// Without `limit` the API returns the whole list in one response.
|
|
16
|
+
return client.call({
|
|
17
|
+
method: 'GET',
|
|
18
|
+
path: '/v1/servers',
|
|
19
|
+
query: { project_id: projectId },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The heartbeat token and secret header values are live credentials: a
|
|
24
|
+
* `servers get` in a CI log would hand them to anyone reading it. They stay
|
|
25
|
+
* reachable through `statuser api` for whoever needs them on purpose.
|
|
26
|
+
*/
|
|
27
|
+
export function redactServer(server) {
|
|
28
|
+
return {
|
|
29
|
+
...server,
|
|
30
|
+
heartbeat_token: server.heartbeat_token ? '***' : server.heartbeat_token,
|
|
31
|
+
headers: (server.headers ?? []).map((header) => isSecretHeader(header.key) ? { ...header, value: '***' } : header),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function parseId(raw) {
|
|
35
|
+
const id = Number(raw);
|
|
36
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
37
|
+
throw new CliError(t.invalidId(raw), ExitCode.Usage);
|
|
38
|
+
}
|
|
39
|
+
return id;
|
|
40
|
+
}
|
|
41
|
+
export function registerServersCommands(program) {
|
|
42
|
+
const servers = program.command('servers').description(t.cmdServers);
|
|
43
|
+
servers
|
|
44
|
+
.command('list')
|
|
45
|
+
.description(t.cmdServersList)
|
|
46
|
+
.option('--project <id>', t.optProject, parseId)
|
|
47
|
+
.option('--protocol <protocol>', t.optProtocol, parseProtocol)
|
|
48
|
+
.action(async (opts, command) => {
|
|
49
|
+
const { client } = await authedClient(command);
|
|
50
|
+
let list = await listServers(client, opts.project);
|
|
51
|
+
if (opts.protocol) {
|
|
52
|
+
list = list.filter((server) => server.protocol === opts.protocol);
|
|
53
|
+
}
|
|
54
|
+
if (globalOptions(command).json) {
|
|
55
|
+
printJson(list.map(redactServer));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!list.length) {
|
|
59
|
+
printLine(t.serversEmpty);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
printTable([
|
|
63
|
+
t.colId,
|
|
64
|
+
t.colName,
|
|
65
|
+
t.colProtocol,
|
|
66
|
+
t.colStatus,
|
|
67
|
+
t.colHost,
|
|
68
|
+
t.colProject,
|
|
69
|
+
], list.map((s) => [
|
|
70
|
+
s.id,
|
|
71
|
+
s.name,
|
|
72
|
+
s.protocol,
|
|
73
|
+
s.status,
|
|
74
|
+
s.host,
|
|
75
|
+
s.project_id,
|
|
76
|
+
]));
|
|
77
|
+
});
|
|
78
|
+
servers
|
|
79
|
+
.command('get')
|
|
80
|
+
.description(t.cmdServersGet)
|
|
81
|
+
.argument('<id>', 'id', parseId)
|
|
82
|
+
.action(async (id, _opts, command) => {
|
|
83
|
+
const { client } = await authedClient(command);
|
|
84
|
+
printJson(redactServer(await client.call({
|
|
85
|
+
method: 'GET',
|
|
86
|
+
path: `/v1/servers/${id}`,
|
|
87
|
+
})));
|
|
88
|
+
});
|
|
89
|
+
registerServersAdd(servers);
|
|
90
|
+
registerServersChecks(servers);
|
|
91
|
+
servers
|
|
92
|
+
.command('delete')
|
|
93
|
+
.description(t.cmdServersDelete)
|
|
94
|
+
.argument('<ids...>', 'id')
|
|
95
|
+
.option('--yes', t.optYes)
|
|
96
|
+
.action(async (ids, opts, command) => {
|
|
97
|
+
const { client } = await authedClient(command);
|
|
98
|
+
// Only explicit ids: a whole project is never deleted by one flag.
|
|
99
|
+
const targets = pickTargets(await listServers(client), ids, undefined);
|
|
100
|
+
await confirmDeletion(t.confirmDeleteServers(targets.map((s) => `«${s.name || s.host}» (#${s.id})`).join(', ')), opts.yes);
|
|
101
|
+
const result = await bulkApply(targets, () => null, (server) => client.call({ method: 'DELETE', path: `/v1/servers/${server.id}` }));
|
|
102
|
+
report(command, result, t.deletedOne);
|
|
103
|
+
});
|
|
104
|
+
servers
|
|
105
|
+
.command('test-notify')
|
|
106
|
+
.description(t.cmdServersTestNotify)
|
|
107
|
+
.argument('<id>', 'id', parseId)
|
|
108
|
+
.action(async (id, _opts, command) => {
|
|
109
|
+
const { client } = await authedClient(command);
|
|
110
|
+
await client.call({
|
|
111
|
+
method: 'POST',
|
|
112
|
+
path: `/v1/servers/${id}/test-notify`,
|
|
113
|
+
});
|
|
114
|
+
if (globalOptions(command).json)
|
|
115
|
+
printJson({ sent: true, id });
|
|
116
|
+
else
|
|
117
|
+
printLine(t.testNotifySent(id));
|
|
118
|
+
});
|
|
119
|
+
servers
|
|
120
|
+
.command('pause')
|
|
121
|
+
.description(t.cmdServersPause)
|
|
122
|
+
.argument('[ids...]', 'id')
|
|
123
|
+
.option('--project <id>', t.optProject, parseId)
|
|
124
|
+
.action(async (ids, opts, command) => {
|
|
125
|
+
const { client } = await authedClient(command);
|
|
126
|
+
const all = await listServers(client, opts.project);
|
|
127
|
+
const targets = pickTargets(all, ids, opts.project);
|
|
128
|
+
// Already paused servers are skipped and left out of `done`: a
|
|
129
|
+
// `resume --from` must not switch on checks someone turned off on purpose.
|
|
130
|
+
const result = await bulk(client, targets, 'pause', (server) => server.status === 'paused' ? t.alreadyPaused(server.id) : null);
|
|
131
|
+
report(command, result, t.pausedOne);
|
|
132
|
+
});
|
|
133
|
+
servers
|
|
134
|
+
.command('resume')
|
|
135
|
+
.description(t.cmdServersResume)
|
|
136
|
+
.argument('[ids...]', 'id')
|
|
137
|
+
.option('--project <id>', t.optProject, parseId)
|
|
138
|
+
.option('--from <file>', t.optFromFile)
|
|
139
|
+
.action(async (ids, opts, command) => {
|
|
140
|
+
const { client } = await authedClient(command);
|
|
141
|
+
const fromIds = opts.from ? await readPausedIds(opts.from) : [];
|
|
142
|
+
if (opts.from && !ids.length && !fromIds.length) {
|
|
143
|
+
// The pause step had nothing to pause — nothing to resume either.
|
|
144
|
+
report(command, { done: [], skipped: [], failed: [] }, t.resumedOne);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const all = await listServers(client, opts.project);
|
|
148
|
+
const targets = pickTargets(all, [...ids, ...fromIds.map(String)], opts.from ? undefined : opts.project);
|
|
149
|
+
const result = await bulk(client, targets, 'unpause', (server) => server.status !== 'paused' ? t.notPaused(server.id) : null);
|
|
150
|
+
report(command, result, t.resumedOne);
|
|
151
|
+
});
|
|
152
|
+
servers
|
|
153
|
+
.command('update')
|
|
154
|
+
.description(t.cmdServersUpdate)
|
|
155
|
+
.argument('[ids...]', 'id')
|
|
156
|
+
.option('--project <id>', t.optProject, parseId)
|
|
157
|
+
.option('--protocol <protocol>', t.optProtocol, parseProtocol)
|
|
158
|
+
.option('--name <name>', t.optServerName)
|
|
159
|
+
.option('--host <host>', t.optServerHost)
|
|
160
|
+
.option('--description <text>', t.optServerDescription)
|
|
161
|
+
.option('--interval <seconds>', t.optInterval, parseNonNegative)
|
|
162
|
+
.option('--timeout <seconds>', t.optRequestTimeout, parseNonNegative)
|
|
163
|
+
.option('--regions <codes>', t.optRegions)
|
|
164
|
+
.option('--confirm-delay <seconds>', t.optConfirmDelay, parseNonNegative)
|
|
165
|
+
.option('--ssl', t.optSsl)
|
|
166
|
+
.option('--no-ssl', t.optNoSsl)
|
|
167
|
+
.option('--domain', t.optDomain)
|
|
168
|
+
.option('--no-domain', t.optNoDomain)
|
|
169
|
+
.option('--blocklist', t.optBlocklist)
|
|
170
|
+
.option('--no-blocklist', t.optNoBlocklist)
|
|
171
|
+
.option('--latency-alert <ms>', t.optLatencyAlert, parseNonNegative)
|
|
172
|
+
.option('--no-latency-alert', t.optNoLatencyAlert)
|
|
173
|
+
.option('--dry-run', t.optDryRun)
|
|
174
|
+
.action(async (ids, opts, command) => {
|
|
175
|
+
const change = toBulkChange(opts);
|
|
176
|
+
const { client } = await authedClient(command);
|
|
177
|
+
let all = await listServers(client, opts.project);
|
|
178
|
+
if (opts.protocol) {
|
|
179
|
+
all = all.filter((server) => server.protocol === opts.protocol);
|
|
180
|
+
}
|
|
181
|
+
const targets = pickTargets(all, ids, opts.project);
|
|
182
|
+
const identity = change.name !== undefined ||
|
|
183
|
+
change.host !== undefined ||
|
|
184
|
+
change.description !== undefined;
|
|
185
|
+
if (identity && targets.length !== 1) {
|
|
186
|
+
throw new CliError(t.updateSingleOnly, ExitCode.Usage);
|
|
187
|
+
}
|
|
188
|
+
const plans = new Map(targets.map((server) => [server.id, planServerPatch(server, change)]));
|
|
189
|
+
const current = await client.call({
|
|
190
|
+
method: 'GET',
|
|
191
|
+
path: '/v1/billing/current-plan',
|
|
192
|
+
});
|
|
193
|
+
for (const server of targets) {
|
|
194
|
+
const plan = plans.get(server.id);
|
|
195
|
+
const problem = bulkTargetProblem(server, plan) ??
|
|
196
|
+
(plan.fields.length
|
|
197
|
+
? patchPlanProblem(server, plan.body, current.features)
|
|
198
|
+
: null);
|
|
199
|
+
if (problem) {
|
|
200
|
+
throw new CliError(t.updateValueInvalid(server.id, problem), ExitCode.Usage);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (identity) {
|
|
204
|
+
await checkIdentity(client, targets[0], plans.get(targets[0].id).body);
|
|
205
|
+
}
|
|
206
|
+
const json = globalOptions(command).json;
|
|
207
|
+
if (opts.dryRun) {
|
|
208
|
+
const planned = targets.map((server) => {
|
|
209
|
+
const plan = plans.get(server.id);
|
|
210
|
+
return {
|
|
211
|
+
id: server.id,
|
|
212
|
+
fields: plan.fields,
|
|
213
|
+
not_applicable: plan.notApplicable,
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
if (json)
|
|
217
|
+
printJson({ dry_run: true, planned });
|
|
218
|
+
else {
|
|
219
|
+
for (const item of planned) {
|
|
220
|
+
printLine(describeUpdate(item.id, item.fields, item.not_applicable));
|
|
221
|
+
}
|
|
222
|
+
printLine(t.deployDryRunDone);
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const result = await bulkApply(targets, (server) => {
|
|
227
|
+
const plan = plans.get(server.id);
|
|
228
|
+
if (plan.fields.length)
|
|
229
|
+
return null;
|
|
230
|
+
return plan.notApplicable.length
|
|
231
|
+
? t.updateNotApplicable(server.id, plan.notApplicable.join(', '))
|
|
232
|
+
: t.updateUnchanged(server.id);
|
|
233
|
+
}, (server) => client.call({
|
|
234
|
+
method: 'PATCH',
|
|
235
|
+
path: `/v1/servers/${server.id}`,
|
|
236
|
+
body: plans.get(server.id).body,
|
|
237
|
+
}));
|
|
238
|
+
report(command, result, (id) => {
|
|
239
|
+
const plan = plans.get(id);
|
|
240
|
+
return describeUpdate(id, plan.fields, plan.notApplicable);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const CONFIRM_DELAY_MAX = 3600;
|
|
245
|
+
const LATENCY_MIN = 50;
|
|
246
|
+
const LATENCY_MAX = 60_000;
|
|
247
|
+
function toBulkChange(opts) {
|
|
248
|
+
const regions = opts.regions
|
|
249
|
+
?.split(',')
|
|
250
|
+
.map((r) => r.trim())
|
|
251
|
+
.filter(Boolean);
|
|
252
|
+
if (regions) {
|
|
253
|
+
if (!regions.length)
|
|
254
|
+
throw new CliError(t.regionsEmpty, ExitCode.Usage);
|
|
255
|
+
for (const region of regions) {
|
|
256
|
+
if (!SCENARIO_LOCATIONS.includes(region)) {
|
|
257
|
+
throw new CliError(t.regionInvalid(region), ExitCode.Usage);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// API bounds of the flags that do not depend on the protocol.
|
|
262
|
+
if (opts.confirmDelay !== undefined &&
|
|
263
|
+
opts.confirmDelay > CONFIRM_DELAY_MAX) {
|
|
264
|
+
throw new CliError(t.numberRange(0, CONFIRM_DELAY_MAX), ExitCode.Usage);
|
|
265
|
+
}
|
|
266
|
+
if (typeof opts.latencyAlert === 'number' &&
|
|
267
|
+
(opts.latencyAlert < LATENCY_MIN || opts.latencyAlert > LATENCY_MAX)) {
|
|
268
|
+
throw new CliError(t.numberRange(LATENCY_MIN, LATENCY_MAX), ExitCode.Usage);
|
|
269
|
+
}
|
|
270
|
+
const change = {
|
|
271
|
+
name: opts.name,
|
|
272
|
+
host: opts.host,
|
|
273
|
+
description: opts.description,
|
|
274
|
+
interval: opts.interval,
|
|
275
|
+
timeout: opts.timeout,
|
|
276
|
+
regions,
|
|
277
|
+
confirmDelay: opts.confirmDelay,
|
|
278
|
+
ssl: opts.ssl,
|
|
279
|
+
domain: opts.domain,
|
|
280
|
+
blocklist: opts.blocklist,
|
|
281
|
+
latency: opts.latencyAlert,
|
|
282
|
+
};
|
|
283
|
+
if (Object.values(change).every((value) => value === undefined)) {
|
|
284
|
+
throw new CliError(t.updateNothing, ExitCode.Usage);
|
|
285
|
+
}
|
|
286
|
+
return change;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* A new name of a heartbeat or a new address is checked like a create:
|
|
290
|
+
* the API refuses a write that makes the server a duplicate of another,
|
|
291
|
+
* anywhere in the account. Only when the identity really changes, as there.
|
|
292
|
+
*/
|
|
293
|
+
async function checkIdentity(client, server, body) {
|
|
294
|
+
const before = server;
|
|
295
|
+
const after = { ...before, ...body };
|
|
296
|
+
if (duplicateKey(before) === duplicateKey(after))
|
|
297
|
+
return;
|
|
298
|
+
const other = (await listServers(client)).find((s) => s.id !== server.id &&
|
|
299
|
+
isDuplicateOf(after, s));
|
|
300
|
+
if (other) {
|
|
301
|
+
throw new CliError(t.updateDuplicate(server.id, other.id), ExitCode.Usage);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function describeUpdate(id, fields, notApplicable) {
|
|
305
|
+
const parts = [fields.length ? fields.join(', ') : t.noChanges];
|
|
306
|
+
if (notApplicable.length) {
|
|
307
|
+
parts.push(t.notApplicableNote(notApplicable.join(', ')));
|
|
308
|
+
}
|
|
309
|
+
return `#${id}: ${parts.join('; ')}`;
|
|
310
|
+
}
|
|
311
|
+
function parseNonNegative(raw) {
|
|
312
|
+
const value = Number(raw);
|
|
313
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
314
|
+
throw new CliError(t.numberInvalid(raw), ExitCode.Usage);
|
|
315
|
+
}
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
export function pickTargets(all, rawIds, projectId) {
|
|
319
|
+
if (!rawIds.length) {
|
|
320
|
+
if (projectId === undefined) {
|
|
321
|
+
throw new CliError(t.serversNoTargets, ExitCode.Usage);
|
|
322
|
+
}
|
|
323
|
+
return all;
|
|
324
|
+
}
|
|
325
|
+
const byId = new Map(all.map((server) => [server.id, server]));
|
|
326
|
+
// A repeated id would be written twice: a second DELETE gets 404.
|
|
327
|
+
const ids = [...new Set(rawIds.map(parseId))];
|
|
328
|
+
return ids.map((id) => {
|
|
329
|
+
const server = byId.get(id);
|
|
330
|
+
if (!server) {
|
|
331
|
+
throw new StatuserApiError(404, t.notFound(id), 'server_not_found');
|
|
332
|
+
}
|
|
333
|
+
return server;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
async function bulk(client, targets, action, skipReason) {
|
|
337
|
+
return bulkApply(targets, skipReason, (server) => client.call({
|
|
338
|
+
method: 'PATCH',
|
|
339
|
+
path: `/v1/servers/${server.id}/${action}`,
|
|
340
|
+
}));
|
|
341
|
+
}
|
|
342
|
+
export async function bulkApply(targets, skipReason, apply) {
|
|
343
|
+
const result = { done: [], skipped: [], failed: [] };
|
|
344
|
+
for (const server of targets) {
|
|
345
|
+
const reason = skipReason(server);
|
|
346
|
+
if (reason) {
|
|
347
|
+
printErr(reason);
|
|
348
|
+
result.skipped.push(server.id);
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
try {
|
|
352
|
+
await apply(server);
|
|
353
|
+
result.done.push(server.id);
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
if (error instanceof StatuserApiError && error.status === 401) {
|
|
357
|
+
// The key stopped working: every next call fails the same way. What
|
|
358
|
+
// was done so far is still reported, so `resume --from` can use it.
|
|
359
|
+
result.aborted = 'unauthorized';
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
const failure = describeWriteFailure(`#${server.id}`, error);
|
|
363
|
+
const message = error instanceof StatuserApiError
|
|
364
|
+
? error.format()
|
|
365
|
+
: error instanceof Error
|
|
366
|
+
? error.message
|
|
367
|
+
: String(error);
|
|
368
|
+
printErr(t.bulkFailed(server.id, message));
|
|
369
|
+
result.failed.push({
|
|
370
|
+
id: server.id,
|
|
371
|
+
message,
|
|
372
|
+
outcome_unknown: failure.outcome_unknown,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
export function report(command, result, doneLine) {
|
|
379
|
+
if (globalOptions(command).json) {
|
|
380
|
+
// `paused`/`resumed` naming would split the contract; one shape for both.
|
|
381
|
+
printJson(result);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
for (const id of result.done)
|
|
385
|
+
printLine(doneLine(id));
|
|
386
|
+
printLine(t.bulkSummary(result.done.length, result.failed.length));
|
|
387
|
+
}
|
|
388
|
+
if (result.aborted) {
|
|
389
|
+
printErr(t.unauthorized);
|
|
390
|
+
process.exitCode = ExitCode.Auth;
|
|
391
|
+
}
|
|
392
|
+
else if (result.failed.length) {
|
|
393
|
+
process.exitCode = ExitCode.Failure;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function readPausedIds(path) {
|
|
397
|
+
try {
|
|
398
|
+
const parsed = JSON.parse(await readFile(path, 'utf8'));
|
|
399
|
+
if (Array.isArray(parsed.done) &&
|
|
400
|
+
parsed.done.every((id) => Number.isInteger(id))) {
|
|
401
|
+
// A pause whose answer was lost may have been applied: it is resumed
|
|
402
|
+
// too, and a server that is not paused is skipped.
|
|
403
|
+
const uncertain = Array.isArray(parsed.failed)
|
|
404
|
+
? parsed.failed
|
|
405
|
+
.filter((f) => f?.outcome_unknown === true && Number.isInteger(f.id))
|
|
406
|
+
.map((f) => f.id)
|
|
407
|
+
: [];
|
|
408
|
+
return [...parsed.done, ...uncertain];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
// Reported below with the file name.
|
|
413
|
+
}
|
|
414
|
+
throw new CliError(t.fromFileInvalid(path), ExitCode.Usage);
|
|
415
|
+
}
|
|
416
|
+
const PROTOCOLS = [...MANAGED_PROTOCOLS, 'scenario'];
|
|
417
|
+
/** A typo would filter out every server and report an empty success. */
|
|
418
|
+
function parseProtocol(raw) {
|
|
419
|
+
if (!PROTOCOLS.includes(raw)) {
|
|
420
|
+
throw new CliError(`--protocol: ${t.cfgOneOf(PROTOCOLS.join(', '))}`, ExitCode.Usage);
|
|
421
|
+
}
|
|
422
|
+
return raw;
|
|
423
|
+
}
|
|
424
|
+
//# sourceMappingURL=servers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"servers.js","sourceRoot":"","sources":["../../src/commands/servers.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { chmod, mkdir, readFile, rename, rm, writeFile, } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { CliError, ExitCode } from './errors.js';
|
|
6
|
+
import { t } from './i18n.js';
|
|
7
|
+
export const DEFAULT_API_URL = 'https://api.statuser.cloud';
|
|
8
|
+
export function configPath(env = process.env) {
|
|
9
|
+
if (process.platform === 'win32' && env.APPDATA) {
|
|
10
|
+
return join(env.APPDATA, 'statuser', 'config.json');
|
|
11
|
+
}
|
|
12
|
+
const base = env.XDG_CONFIG_HOME || join(homedir(), '.config');
|
|
13
|
+
return join(base, 'statuser', 'config.json');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* http is allowed only for local development: anywhere else the key would
|
|
17
|
+
* travel in clear text.
|
|
18
|
+
*/
|
|
19
|
+
export function normalizeApiUrl(raw) {
|
|
20
|
+
let url;
|
|
21
|
+
try {
|
|
22
|
+
url = new URL(raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new CliError(t.apiUrlInvalid(raw), ExitCode.Usage);
|
|
26
|
+
}
|
|
27
|
+
const isLocal = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
28
|
+
if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLocal)) {
|
|
29
|
+
throw new CliError(t.apiUrlInsecure(raw), ExitCode.Usage);
|
|
30
|
+
}
|
|
31
|
+
return url.origin + url.pathname.replace(/\/+$/, '');
|
|
32
|
+
}
|
|
33
|
+
export async function readSavedConfig() {
|
|
34
|
+
let text;
|
|
35
|
+
try {
|
|
36
|
+
text = await readFile(configPath(), 'utf8');
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(text);
|
|
43
|
+
if (typeof parsed.api_key === 'string' && parsed.api_key) {
|
|
44
|
+
return {
|
|
45
|
+
api_key: parsed.api_key,
|
|
46
|
+
api_url: typeof parsed.api_url === 'string' ? parsed.api_url : DEFAULT_API_URL,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// A broken file is treated as absent; `login` overwrites it.
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
export async function saveConfig(config) {
|
|
56
|
+
const path = configPath();
|
|
57
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
58
|
+
// A fresh file renamed over the old one: writing into an existing file
|
|
59
|
+
// would keep its permissions, and the new key would sit readable by others
|
|
60
|
+
// until a chmod. The temporary name is random and removed only once this
|
|
61
|
+
// call has created it.
|
|
62
|
+
const temp = `${path}.${randomUUID()}.tmp`;
|
|
63
|
+
await writeFile(temp, `${JSON.stringify(config, null, 2)}\n`, {
|
|
64
|
+
encoding: 'utf8',
|
|
65
|
+
mode: 0o600,
|
|
66
|
+
flag: 'wx',
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
if (process.platform !== 'win32')
|
|
70
|
+
await chmod(temp, 0o600);
|
|
71
|
+
await rename(temp, path);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
await rm(temp, { force: true });
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
return path;
|
|
78
|
+
}
|
|
79
|
+
export async function removeConfig() {
|
|
80
|
+
const saved = await readSavedConfig();
|
|
81
|
+
await rm(configPath(), { force: true });
|
|
82
|
+
return saved !== null;
|
|
83
|
+
}
|
|
84
|
+
export async function resolveApiUrl(flag) {
|
|
85
|
+
const saved = await readSavedConfig();
|
|
86
|
+
return normalizeApiUrl(flag || process.env.STATUSER_API_URL || saved?.api_url || DEFAULT_API_URL);
|
|
87
|
+
}
|
|
88
|
+
export async function resolveAuth(flag) {
|
|
89
|
+
const apiUrl = await resolveApiUrl(flag);
|
|
90
|
+
const envKey = process.env.STATUSER_API_KEY?.trim();
|
|
91
|
+
if (envKey)
|
|
92
|
+
return { apiUrl, apiKey: envKey, source: { kind: 'env' } };
|
|
93
|
+
const saved = await readSavedConfig();
|
|
94
|
+
if (!saved)
|
|
95
|
+
throw new CliError(t.noApiKey, ExitCode.Auth);
|
|
96
|
+
const savedOrigin = new URL(normalizeApiUrl(saved.api_url)).origin;
|
|
97
|
+
const currentOrigin = new URL(apiUrl).origin;
|
|
98
|
+
if (savedOrigin !== currentOrigin) {
|
|
99
|
+
throw new CliError(t.keyOtherOrigin(savedOrigin, currentOrigin), ExitCode.Auth);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
apiUrl,
|
|
103
|
+
apiKey: saved.api_key,
|
|
104
|
+
source: { kind: 'file', path: configPath() },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/confirm.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
import { CliError, ExitCode } from './errors.js';
|
|
3
|
+
import { t } from './i18n.js';
|
|
4
|
+
/**
|
|
5
|
+
* Asks before a deletion, like `gh repo delete`. `--yes` skips the
|
|
6
|
+
* question; without a terminal there is no one to answer, so the command
|
|
7
|
+
* refuses instead of deleting silently. A refusal at the prompt is not an
|
|
8
|
+
* error, but nothing was done, so it ends with exit code 1.
|
|
9
|
+
*/
|
|
10
|
+
export async function confirmDeletion(question, yes) {
|
|
11
|
+
if (yes)
|
|
12
|
+
return;
|
|
13
|
+
if (!process.stdin.isTTY) {
|
|
14
|
+
throw new CliError(t.confirmNeedsYes, ExitCode.Usage);
|
|
15
|
+
}
|
|
16
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
17
|
+
const answer = await new Promise((resolve) => {
|
|
18
|
+
let answered = false;
|
|
19
|
+
rl.question(question, (text) => {
|
|
20
|
+
answered = true;
|
|
21
|
+
rl.close();
|
|
22
|
+
resolve(text);
|
|
23
|
+
});
|
|
24
|
+
// Ctrl+D closes the prompt without an answer: that is a no.
|
|
25
|
+
rl.on('close', () => {
|
|
26
|
+
if (!answered)
|
|
27
|
+
resolve('');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
if (!/^(y|yes|д|да)$/i.test(answer.trim())) {
|
|
31
|
+
throw new CliError(t.cancelled, ExitCode.Failure);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=confirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../src/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,WAAW,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,GAAwB;IAExB,IAAI,GAAG;QAAE,OAAO;IAChB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,eAAe,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACnD,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;YAC7B,QAAQ,GAAG,IAAI,CAAC;YAChB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,4DAA4D;QAC5D,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAClB,IAAI,CAAC,QAAQ;gBAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { StatuserClient } from './client.js';
|
|
2
|
+
import { resolveApiUrl, resolveAuth } from './config.js';
|
|
3
|
+
export function globalOptions(command) {
|
|
4
|
+
return command.optsWithGlobals();
|
|
5
|
+
}
|
|
6
|
+
/** Client with the API key: fails with exit code 3 when there is none. */
|
|
7
|
+
export async function authedClient(command) {
|
|
8
|
+
const opts = globalOptions(command);
|
|
9
|
+
const auth = await resolveAuth(opts.apiUrl);
|
|
10
|
+
return {
|
|
11
|
+
client: new StatuserClient({
|
|
12
|
+
apiUrl: auth.apiUrl,
|
|
13
|
+
apiKey: auth.apiKey,
|
|
14
|
+
debug: opts.debug,
|
|
15
|
+
}),
|
|
16
|
+
auth,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Client without a key, for heartbeat pings that authenticate by token. */
|
|
20
|
+
export async function anonymousClient(command) {
|
|
21
|
+
const opts = globalOptions(command);
|
|
22
|
+
return new StatuserClient({
|
|
23
|
+
apiUrl: await resolveApiUrl(opts.apiUrl),
|
|
24
|
+
debug: opts.debug,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAqB,MAAM,aAAa,CAAC;AAQ5E,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO,OAAO,CAAC,eAAe,EAAiB,CAAC;AAClD,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAgB;IAEhB,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO;QACL,MAAM,EAAE,IAAI,cAAc,CAAC;YACzB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;QACF,IAAI;KACL,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAgB;IAEhB,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,IAAI,cAAc,CAAC;QACxB,MAAM,EAAE,MAAM,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC;QACxC,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC,CAAC;AACL,CAAC"}
|
package/dist/duration.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CliError, ExitCode } from './errors.js';
|
|
2
|
+
import { t } from './i18n.js';
|
|
3
|
+
/** A century: longer windows and look-backs are typos. */
|
|
4
|
+
export const MAX_DURATION_MS = 100 * 365 * 86_400_000;
|
|
5
|
+
/** `30m`, `2h`, `1h30m`, `45s` → milliseconds. */
|
|
6
|
+
export function parseDuration(raw) {
|
|
7
|
+
const match = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/.exec(raw.trim());
|
|
8
|
+
if (!match || !raw.trim()) {
|
|
9
|
+
throw new CliError(t.durationInvalid(raw), ExitCode.Usage);
|
|
10
|
+
}
|
|
11
|
+
const [, h = '0', m = '0', s = '0'] = match;
|
|
12
|
+
const ms = ((+h * 60 + +m) * 60 + +s) * 1000;
|
|
13
|
+
// A date beyond what Date can hold would fail later, outside the
|
|
14
|
+
// argument check, with exit code 1.
|
|
15
|
+
if (ms <= 0 || ms > MAX_DURATION_MS) {
|
|
16
|
+
throw new CliError(t.durationInvalid(raw), ExitCode.Usage);
|
|
17
|
+
}
|
|
18
|
+
return ms;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=duration.js.map
|