@astris-labs/cli 0.1.3 → 0.1.4
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/README.md +1 -1
- package/package.json +1 -1
- package/src/client.mjs +20 -3
- package/src/commands.mjs +60 -38
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ Optional flags are `--build-path`, `--dockerfile`, `--port`, `--build`, `--publi
|
|
|
97
97
|
|
|
98
98
|
`astris compose list` calls `GET /v1/compose`. Human output is one line per stack in the order the API returned: the id, the name, and the project name. An empty list prints nothing. `--json` prints one JSON object `{"compose":[...]}` of the stack rows.
|
|
99
99
|
|
|
100
|
-
`astris compose deploy --id <id>` calls `POST /v1/compose/:id/deploy` and sends no body.
|
|
100
|
+
`astris compose deploy --id <id>` calls `POST /v1/compose/:id/deploy` and sends no body. In the private preview the API refuses every redeploy, so the command prints the API's sentence and exits 1. Otherwise human output is the stack id, project id, name, git URL, branch, compose path, memory limit, created_at, and updated_at, one per line. `--json` prints one JSON object `{"compose":{...}}`.
|
|
101
101
|
|
|
102
102
|
`astris compose start --id <id>` calls `POST /v1/compose/:id/start` and sends no body. Human output is the status the API returned, then the same stack fields as deploy, one per line. `--json` prints one JSON object `{"compose":{...},"status":"<status>"}`.
|
|
103
103
|
|
package/package.json
CHANGED
package/src/client.mjs
CHANGED
|
@@ -18,6 +18,22 @@ function swallow(promise) {
|
|
|
18
18
|
promise.catch(() => undefined);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function retryAfterSecondsOf(headers) {
|
|
22
|
+
if (!headers) return null;
|
|
23
|
+
let raw;
|
|
24
|
+
if (typeof headers.get === "function") {
|
|
25
|
+
raw = headers.get("retry-after");
|
|
26
|
+
} else if (typeof headers === "object") {
|
|
27
|
+
raw = headers["retry-after"] ?? headers["Retry-After"];
|
|
28
|
+
}
|
|
29
|
+
if (typeof raw !== "string") return null;
|
|
30
|
+
const trimmed = raw.trim();
|
|
31
|
+
if (!/^[0-9]+$/.test(trimmed)) return null;
|
|
32
|
+
const seconds = Number(trimmed);
|
|
33
|
+
if (seconds < 1 || seconds > 3600) return null;
|
|
34
|
+
return seconds;
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
export async function astrisFetch({ key, baseUrl, path, method, body, fetchImpl }) {
|
|
22
38
|
const url = baseUrl + path;
|
|
23
39
|
const headers = { Authorization: `Bearer ${key}` };
|
|
@@ -54,14 +70,15 @@ export async function astrisFetch({ key, baseUrl, path, method, body, fetchImpl
|
|
|
54
70
|
}
|
|
55
71
|
swallow(reading);
|
|
56
72
|
const text = await Promise.race([reading, timeout]);
|
|
73
|
+
const retryAfterSeconds = retryAfterSecondsOf(response?.headers);
|
|
57
74
|
|
|
58
75
|
if (text === "") {
|
|
59
|
-
return { status: response.status, body: null };
|
|
76
|
+
return { status: response.status, body: null, retryAfterSeconds };
|
|
60
77
|
}
|
|
61
78
|
try {
|
|
62
|
-
return { status: response.status, body: JSON.parse(text) };
|
|
79
|
+
return { status: response.status, body: JSON.parse(text), retryAfterSeconds };
|
|
63
80
|
} catch {
|
|
64
|
-
return { status: response.status, body: null };
|
|
81
|
+
return { status: response.status, body: null, retryAfterSeconds };
|
|
65
82
|
}
|
|
66
83
|
} catch (err) {
|
|
67
84
|
throw new Error(redact(failureMessage(err), key));
|
package/src/commands.mjs
CHANGED
|
@@ -62,13 +62,22 @@ const HTTP_SENTENCE = new Map([
|
|
|
62
62
|
[502, SENTENCE.platform],
|
|
63
63
|
]);
|
|
64
64
|
|
|
65
|
-
function
|
|
65
|
+
function rateWaitSentence(seconds) {
|
|
66
|
+
if (typeof seconds === "number") {
|
|
67
|
+
return `This account asked for too many changes in a short time. Wait ${seconds} second${seconds === 1 ? "" : "s"}, then try again.`;
|
|
68
|
+
}
|
|
69
|
+
return "This account asked for too many changes in a short time. Wait a little, then try again.";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function sentenceFor(status, retryAfterSeconds = null) {
|
|
73
|
+
if (status === 429) return rateWaitSentence(retryAfterSeconds);
|
|
66
74
|
return HTTP_SENTENCE.get(status) ?? SENTENCE.failed;
|
|
67
75
|
}
|
|
68
76
|
|
|
69
|
-
function errorMessage(status, body) {
|
|
77
|
+
function errorMessage(status, body, retryAfterSeconds = null) {
|
|
78
|
+
if (status === 429) return rateWaitSentence(retryAfterSeconds);
|
|
70
79
|
if (isRecord(body) && typeof body.message === "string") return body.message;
|
|
71
|
-
return sentenceFor(status);
|
|
80
|
+
return sentenceFor(status, retryAfterSeconds);
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
function upgradeMessage(status, body) {
|
|
@@ -78,6 +87,14 @@ function upgradeMessage(status, body) {
|
|
|
78
87
|
return body.message;
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
// T-275: the API pauses compose redeploy in the private preview and says why.
|
|
91
|
+
function composeUnavailableMessage(status, body) {
|
|
92
|
+
if (status !== 403 || !isRecord(body) || body.error !== "compose_unavailable" || typeof body.message !== "string") {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return body.message;
|
|
96
|
+
}
|
|
97
|
+
|
|
81
98
|
function storageCreateMessage(status, body) {
|
|
82
99
|
if (!isRecord(body)) return null;
|
|
83
100
|
if (status === 403 && body.error === "bucket_limit") {
|
|
@@ -333,7 +350,7 @@ async function redeploy(io, key, json, appId) {
|
|
|
333
350
|
method: "POST",
|
|
334
351
|
});
|
|
335
352
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
336
|
-
if (result.status !== 202) return fail(io, key, json, sentenceFor(result.status));
|
|
353
|
+
if (result.status !== 202) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
337
354
|
const application = result.body?.application ?? {};
|
|
338
355
|
const id = text(application.id) || appId;
|
|
339
356
|
const name = text(application.name);
|
|
@@ -445,7 +462,7 @@ async function createThenDeploy(io, key, json, flags) {
|
|
|
445
462
|
try {
|
|
446
463
|
const listed = await call(io, key, { path: "/v1/projects", method: "GET" });
|
|
447
464
|
if (listed.network) return fail(io, key, json, SENTENCE.platform, created);
|
|
448
|
-
if (listed.status !== 200) return fail(io, key, json, sentenceFor(listed.status), created);
|
|
465
|
+
if (listed.status !== 200) return fail(io, key, json, sentenceFor(listed.status, listed.retryAfterSeconds), created);
|
|
449
466
|
let projectId = findProjectId(listed.body, project);
|
|
450
467
|
if (projectId === null) {
|
|
451
468
|
const made = await call(io, key, {
|
|
@@ -456,7 +473,7 @@ async function createThenDeploy(io, key, json, flags) {
|
|
|
456
473
|
if (made.network) return fail(io, key, json, SENTENCE.platform, created);
|
|
457
474
|
const madeId = made.body?.project?.id;
|
|
458
475
|
if (made.status !== 201 || typeof madeId !== "string" || madeId === "") {
|
|
459
|
-
return fail(io, key, json, made.status === 201 ? SENTENCE.failed : sentenceFor(made.status), created);
|
|
476
|
+
return fail(io, key, json, made.status === 201 ? SENTENCE.failed : sentenceFor(made.status, made.retryAfterSeconds), created);
|
|
460
477
|
}
|
|
461
478
|
projectId = madeId;
|
|
462
479
|
created.projectId = madeId;
|
|
@@ -484,7 +501,7 @@ async function createThenDeploy(io, key, json, flags) {
|
|
|
484
501
|
io,
|
|
485
502
|
key,
|
|
486
503
|
json,
|
|
487
|
-
passed ?? (madeApp.status === 201 ? SENTENCE.failed : sentenceFor(madeApp.status)),
|
|
504
|
+
passed ?? (madeApp.status === 201 ? SENTENCE.failed : sentenceFor(madeApp.status, madeApp.retryAfterSeconds)),
|
|
488
505
|
created,
|
|
489
506
|
);
|
|
490
507
|
}
|
|
@@ -518,7 +535,7 @@ async function createThenDeploy(io, key, json, flags) {
|
|
|
518
535
|
method: "POST",
|
|
519
536
|
});
|
|
520
537
|
if (deployed.network) return fail(io, key, json, SENTENCE.platform, created);
|
|
521
|
-
if (deployed.status !== 202) return fail(io, key, json, sentenceFor(deployed.status), created);
|
|
538
|
+
if (deployed.status !== 202) return fail(io, key, json, sentenceFor(deployed.status, deployed.retryAfterSeconds), created);
|
|
522
539
|
const shown = deployed.body?.application ?? application;
|
|
523
540
|
const id = text(shown.id) || applicationId;
|
|
524
541
|
const shownName = text(shown.name) || name;
|
|
@@ -577,7 +594,7 @@ export async function status(args, io) {
|
|
|
577
594
|
method: "GET",
|
|
578
595
|
});
|
|
579
596
|
if (result.network) return fail(io, opened.key, opened.json, SENTENCE.platform);
|
|
580
|
-
if (result.status !== 200) return fail(io, opened.key, opened.json, sentenceFor(result.status));
|
|
597
|
+
if (result.status !== 200) return fail(io, opened.key, opened.json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
581
598
|
const statusText = text(result.body?.status);
|
|
582
599
|
const hostnames = hostnamesOf(result.body);
|
|
583
600
|
const id = text(result.body?.application?.id) || required.appId;
|
|
@@ -603,7 +620,7 @@ async function hookUrl(io, key, json, appId) {
|
|
|
603
620
|
method: "GET",
|
|
604
621
|
});
|
|
605
622
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
606
|
-
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status));
|
|
623
|
+
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
607
624
|
const url = result.body?.hook?.url;
|
|
608
625
|
if (typeof url !== "string") return fail(io, key, json, SENTENCE.failed);
|
|
609
626
|
return succeed(io, key, json, { application_id: appId, url }, `${url}\n`);
|
|
@@ -617,7 +634,7 @@ async function envNames(io, key, json, appId) {
|
|
|
617
634
|
method: "GET",
|
|
618
635
|
});
|
|
619
636
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
620
|
-
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status));
|
|
637
|
+
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
621
638
|
const names = result.body?.names;
|
|
622
639
|
if (!Array.isArray(names) || !names.every((name) => typeof name === "string")) {
|
|
623
640
|
return fail(io, key, json, SENTENCE.failed);
|
|
@@ -645,7 +662,7 @@ export async function env(args, io) {
|
|
|
645
662
|
body,
|
|
646
663
|
});
|
|
647
664
|
if (result.network) return fail(io, opened.key, opened.json, SENTENCE.platform);
|
|
648
|
-
if (result.status !== 200) return fail(io, opened.key, opened.json, sentenceFor(result.status));
|
|
665
|
+
if (result.status !== 200) return fail(io, opened.key, opened.json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
649
666
|
return succeed(
|
|
650
667
|
io,
|
|
651
668
|
opened.key,
|
|
@@ -741,7 +758,7 @@ async function domainsBind(args, io, key) {
|
|
|
741
758
|
}
|
|
742
759
|
const result = await call(io, key, request);
|
|
743
760
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
744
|
-
if (result.status !== 201) return fail(io, key, json, sentenceFor(result.status));
|
|
761
|
+
if (result.status !== 201) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
745
762
|
const domain = isRecord(result.body) ? result.body.domain : undefined;
|
|
746
763
|
const hostname = text(domain?.hostname);
|
|
747
764
|
if (hostnameFlag === undefined) {
|
|
@@ -779,7 +796,7 @@ async function domainsVerify(args, io, key) {
|
|
|
779
796
|
method: "POST",
|
|
780
797
|
});
|
|
781
798
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
782
|
-
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status));
|
|
799
|
+
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
783
800
|
const body = isRecord(result.body) ? result.body : {};
|
|
784
801
|
const domain = isRecord(body.domain) ? body.domain : {};
|
|
785
802
|
const statusText = typeof domain.status === "string" ? domain.status : "";
|
|
@@ -812,7 +829,7 @@ export async function restart(args, io) {
|
|
|
812
829
|
method: "POST",
|
|
813
830
|
});
|
|
814
831
|
if (result.network) return fail(io, opened.key, opened.json, SENTENCE.platform);
|
|
815
|
-
if (result.status !== 200) return fail(io, opened.key, opened.json, sentenceFor(result.status));
|
|
832
|
+
if (result.status !== 200) return fail(io, opened.key, opened.json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
816
833
|
return succeed(
|
|
817
834
|
io,
|
|
818
835
|
opened.key,
|
|
@@ -863,7 +880,7 @@ export async function logs(args, io) {
|
|
|
863
880
|
method: "GET",
|
|
864
881
|
});
|
|
865
882
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform);
|
|
866
|
-
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status));
|
|
883
|
+
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
867
884
|
if (!isRecord(result.body) || typeof result.body.lines !== "string") {
|
|
868
885
|
return fail(io, loaded.key, json, SENTENCE.failed);
|
|
869
886
|
}
|
|
@@ -927,7 +944,7 @@ export async function list(args, io) {
|
|
|
927
944
|
resource === "apps" ? "/v1/apps" : resource === "databases" ? "/v1/databases" : "/v1/projects";
|
|
928
945
|
const result = await call(io, loaded.key, { path, method: "GET" });
|
|
929
946
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform);
|
|
930
|
-
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status));
|
|
947
|
+
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
931
948
|
if (resource !== "projects") {
|
|
932
949
|
const human = listText(resource, result.body);
|
|
933
950
|
if (human === null) return fail(io, loaded.key, json, SENTENCE.failed);
|
|
@@ -964,7 +981,7 @@ export async function usage(args, io) {
|
|
|
964
981
|
const json = parsed.values.json === true;
|
|
965
982
|
const result = await call(io, loaded.key, { path: "/v1/usage", method: "GET" });
|
|
966
983
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform);
|
|
967
|
-
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status));
|
|
984
|
+
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
968
985
|
const human = usageText(result.body);
|
|
969
986
|
if (human === null) return fail(io, loaded.key, json, SENTENCE.failed);
|
|
970
987
|
return succeed(io, loaded.key, json, result.body, human);
|
|
@@ -994,7 +1011,7 @@ export async function plans(args, io) {
|
|
|
994
1011
|
const json = parsed.values.json === true;
|
|
995
1012
|
const result = await call(io, loaded.key, { path: "/v1/plans", method: "GET" });
|
|
996
1013
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform);
|
|
997
|
-
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status));
|
|
1014
|
+
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
998
1015
|
const human = plansText(result.body);
|
|
999
1016
|
if (human === null) return fail(io, loaded.key, json, SENTENCE.failed);
|
|
1000
1017
|
return succeed(io, loaded.key, json, result.body, human);
|
|
@@ -1043,7 +1060,7 @@ export async function whoami(args, io) {
|
|
|
1043
1060
|
const json = parsed.values.json === true;
|
|
1044
1061
|
const result = await call(io, loaded.key, { path: "/v1/me", method: "GET" });
|
|
1045
1062
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform);
|
|
1046
|
-
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status));
|
|
1063
|
+
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1047
1064
|
const view = whoamiView(result.body);
|
|
1048
1065
|
if (view === null) return fail(io, loaded.key, json, SENTENCE.failed);
|
|
1049
1066
|
const plan = view.account.plan === null ? "none" : view.account.plan;
|
|
@@ -1184,7 +1201,7 @@ export async function diagnose(args, io) {
|
|
|
1184
1201
|
method: "GET",
|
|
1185
1202
|
});
|
|
1186
1203
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform);
|
|
1187
|
-
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status));
|
|
1204
|
+
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1188
1205
|
const human = diagnoseSentences(result.body);
|
|
1189
1206
|
if (human === null) return fail(io, loaded.key, json, SENTENCE.failed);
|
|
1190
1207
|
return succeed(io, loaded.key, json, result.body, human);
|
|
@@ -1212,7 +1229,7 @@ async function databaseCreate(args, io, key) {
|
|
|
1212
1229
|
});
|
|
1213
1230
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1214
1231
|
if (result.status !== 201) {
|
|
1215
|
-
return fail(io, key, json, upgradeMessage(result.status, result.body) ?? sentenceFor(result.status));
|
|
1232
|
+
return fail(io, key, json, upgradeMessage(result.status, result.body) ?? sentenceFor(result.status, result.retryAfterSeconds));
|
|
1216
1233
|
}
|
|
1217
1234
|
const view = databaseFields(isRecord(result.body) ? result.body.database : null);
|
|
1218
1235
|
const password = isRecord(result.body) ? result.body.password : undefined;
|
|
@@ -1235,7 +1252,7 @@ async function databaseStatus(args, io, key) {
|
|
|
1235
1252
|
method: "GET",
|
|
1236
1253
|
});
|
|
1237
1254
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1238
|
-
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status));
|
|
1255
|
+
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1239
1256
|
const view = databaseFields(isRecord(result.body) ? result.body.database : null);
|
|
1240
1257
|
const statusText = isRecord(result.body) ? result.body.status : undefined;
|
|
1241
1258
|
if (view === null || typeof statusText !== "string") return fail(io, key, json, SENTENCE.failed);
|
|
@@ -1271,7 +1288,7 @@ async function databaseBackup(args, io, key) {
|
|
|
1271
1288
|
});
|
|
1272
1289
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1273
1290
|
if (result.status === 409) return fail(io, key, json, SENTENCE.noBackup);
|
|
1274
|
-
if (result.status !== 202) return fail(io, key, json, sentenceFor(result.status));
|
|
1291
|
+
if (result.status !== 202) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1275
1292
|
return succeed(io, key, json, { database_id: id, backup: "run" }, "Backup run.\n");
|
|
1276
1293
|
}
|
|
1277
1294
|
|
|
@@ -1291,7 +1308,7 @@ async function databaseDelete(args, io, key) {
|
|
|
1291
1308
|
method: "DELETE",
|
|
1292
1309
|
});
|
|
1293
1310
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1294
|
-
if (result.status !== 204) return fail(io, key, json, sentenceFor(result.status));
|
|
1311
|
+
if (result.status !== 204) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1295
1312
|
return succeed(io, key, json, { database_id: id, removed: true }, "Database removed.\n");
|
|
1296
1313
|
}
|
|
1297
1314
|
|
|
@@ -1398,7 +1415,7 @@ async function storageCreate(args, io, key) {
|
|
|
1398
1415
|
if (refusal !== null) return fail(io, key, json, refusal);
|
|
1399
1416
|
if (result.status === 503) return fail(io, key, json, SENTENCE.storageOff);
|
|
1400
1417
|
if (result.status !== 201) {
|
|
1401
|
-
return fail(io, key, json, upgradeMessage(result.status, result.body) ?? sentenceFor(result.status));
|
|
1418
|
+
return fail(io, key, json, upgradeMessage(result.status, result.body) ?? sentenceFor(result.status, result.retryAfterSeconds));
|
|
1402
1419
|
}
|
|
1403
1420
|
const body = isRecord(result.body) ? result.body : null;
|
|
1404
1421
|
const view = body === null ? null : storageFields(body.bucket);
|
|
@@ -1440,7 +1457,7 @@ async function storageList(args, io, key) {
|
|
|
1440
1457
|
const result = await call(io, key, { path: "/v1/buckets", method: "GET" });
|
|
1441
1458
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1442
1459
|
if (result.status === 503) return fail(io, key, json, SENTENCE.storageOff);
|
|
1443
|
-
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status));
|
|
1460
|
+
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1444
1461
|
const items = isRecord(result.body) && Array.isArray(result.body.buckets) ? result.body.buckets : null;
|
|
1445
1462
|
if (items === null) return fail(io, key, json, SENTENCE.failed);
|
|
1446
1463
|
const views = [];
|
|
@@ -1467,7 +1484,7 @@ async function storageShow(args, io, key) {
|
|
|
1467
1484
|
});
|
|
1468
1485
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1469
1486
|
if (result.status === 503) return fail(io, key, json, SENTENCE.storageOff);
|
|
1470
|
-
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status));
|
|
1487
|
+
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1471
1488
|
const view = storageFields(isRecord(result.body) ? result.body.bucket : null);
|
|
1472
1489
|
if (view === null) return fail(io, key, json, SENTENCE.failed);
|
|
1473
1490
|
return succeed(io, key, json, { bucket: view }, `${storageLines(view).join("\n")}\n`);
|
|
@@ -1491,7 +1508,7 @@ async function storageDelete(args, io, key) {
|
|
|
1491
1508
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1492
1509
|
if (result.status === 409) return fail(io, key, json, SENTENCE.bucketNotEmpty);
|
|
1493
1510
|
if (result.status === 503) return fail(io, key, json, SENTENCE.storageOff);
|
|
1494
|
-
if (result.status !== 204) return fail(io, key, json, sentenceFor(result.status));
|
|
1511
|
+
if (result.status !== 204) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1495
1512
|
return succeed(io, key, json, { bucket_id: id, removed: true }, `${SENTENCE.bucketRemoved}\n`);
|
|
1496
1513
|
}
|
|
1497
1514
|
|
|
@@ -1525,7 +1542,7 @@ export async function login(args, io) {
|
|
|
1525
1542
|
if (key === "") return fail(io, "", false, SENTENCE.emptyKey);
|
|
1526
1543
|
const result = await call(io, key, { path: "/v1/me", method: "GET" });
|
|
1527
1544
|
if (result.network) return fail(io, key, false, SENTENCE.platform);
|
|
1528
|
-
if (result.status !== 200) return fail(io, key, false, sentenceFor(result.status));
|
|
1545
|
+
if (result.status !== 200) return fail(io, key, false, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1529
1546
|
saveApiKey(io.env, key);
|
|
1530
1547
|
io.stdout.write(`${SENTENCE.loggedIn}\n`);
|
|
1531
1548
|
return { exitCode: 0 };
|
|
@@ -1579,7 +1596,7 @@ async function appRemove(args, io, key) {
|
|
|
1579
1596
|
|
|
1580
1597
|
const got = await call(io, key, { path: `/v1/apps/${encodeURIComponent(appId)}`, method: "GET" });
|
|
1581
1598
|
if (got.network) return fail(io, key, json, SENTENCE.platform);
|
|
1582
|
-
if (got.status !== 200) return fail(io, key, json, sentenceFor(got.status));
|
|
1599
|
+
if (got.status !== 200) return fail(io, key, json, sentenceFor(got.status, got.retryAfterSeconds));
|
|
1583
1600
|
if (!isRecord(got.body)) return fail(io, key, json, SENTENCE.failed);
|
|
1584
1601
|
const application = isRecord(got.body.application) ? got.body.application : null;
|
|
1585
1602
|
if (application === null) return fail(io, key, json, SENTENCE.failed);
|
|
@@ -1593,7 +1610,7 @@ async function appRemove(args, io, key) {
|
|
|
1593
1610
|
|
|
1594
1611
|
const deleted = await call(io, key, { path: `/v1/apps/${encodeURIComponent(appId)}`, method: "DELETE" });
|
|
1595
1612
|
if (deleted.network) return fail(io, key, json, SENTENCE.platform);
|
|
1596
|
-
if (deleted.status !== 200) return fail(io, key, json, sentenceFor(deleted.status));
|
|
1613
|
+
if (deleted.status !== 200) return fail(io, key, json, sentenceFor(deleted.status, deleted.retryAfterSeconds));
|
|
1597
1614
|
return succeed(io, key, json, { app_id: appId, removed: true }, `${SENTENCE.appRemoved}\n`);
|
|
1598
1615
|
}
|
|
1599
1616
|
|
|
@@ -1751,7 +1768,7 @@ export async function preflight(args, io) {
|
|
|
1751
1768
|
});
|
|
1752
1769
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform, undefined, 2);
|
|
1753
1770
|
if (result.status < 200 || result.status >= 300) {
|
|
1754
|
-
return fail(io, loaded.key, json, errorMessage(result.status, result.body), undefined, 2);
|
|
1771
|
+
return fail(io, loaded.key, json, errorMessage(result.status, result.body, result.retryAfterSeconds), undefined, 2);
|
|
1755
1772
|
}
|
|
1756
1773
|
|
|
1757
1774
|
if (
|
|
@@ -1817,7 +1834,7 @@ export async function accountExport(args, io) {
|
|
|
1817
1834
|
});
|
|
1818
1835
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform, undefined, 2);
|
|
1819
1836
|
if (result.status < 200 || result.status >= 300) {
|
|
1820
|
-
return fail(io, loaded.key, json, errorMessage(result.status, result.body), undefined, 2);
|
|
1837
|
+
return fail(io, loaded.key, json, errorMessage(result.status, result.body, result.retryAfterSeconds), undefined, 2);
|
|
1821
1838
|
}
|
|
1822
1839
|
|
|
1823
1840
|
if (json) {
|
|
@@ -1843,7 +1860,7 @@ export async function next(args, io) {
|
|
|
1843
1860
|
const json = parsed.values.json === true;
|
|
1844
1861
|
const result = await call(io, loaded.key, { path: "/v1/me/next-step", method: "GET" });
|
|
1845
1862
|
if (result.network) return fail(io, loaded.key, json, SENTENCE.platform);
|
|
1846
|
-
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status));
|
|
1863
|
+
if (result.status !== 200) return fail(io, loaded.key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1847
1864
|
const body = isRecord(result.body) ? result.body : null;
|
|
1848
1865
|
const step = body?.step;
|
|
1849
1866
|
const sentence = body?.sentence;
|
|
@@ -1950,7 +1967,7 @@ async function composeList(args, io, key) {
|
|
|
1950
1967
|
const json = parsed.values.json === true;
|
|
1951
1968
|
const result = await call(io, key, { path: "/v1/compose", method: "GET" });
|
|
1952
1969
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1953
|
-
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status));
|
|
1970
|
+
if (result.status !== 200) return fail(io, key, json, sentenceFor(result.status, result.retryAfterSeconds));
|
|
1954
1971
|
const items = isRecord(result.body) && Array.isArray(result.body.compose) ? result.body.compose : null;
|
|
1955
1972
|
if (items === null) return fail(io, key, json, SENTENCE.failed);
|
|
1956
1973
|
const views = [];
|
|
@@ -1977,7 +1994,12 @@ async function composeDeploy(args, io, key) {
|
|
|
1977
1994
|
});
|
|
1978
1995
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
1979
1996
|
if (result.status !== 202) {
|
|
1980
|
-
return fail(
|
|
1997
|
+
return fail(
|
|
1998
|
+
io,
|
|
1999
|
+
key,
|
|
2000
|
+
json,
|
|
2001
|
+
upgradeMessage(result.status, result.body) ?? composeUnavailableMessage(result.status, result.body) ?? sentenceFor(result.status, result.retryAfterSeconds),
|
|
2002
|
+
);
|
|
1981
2003
|
}
|
|
1982
2004
|
const view = composeFields(isRecord(result.body) ? result.body.compose : null);
|
|
1983
2005
|
if (view === null) return fail(io, key, json, SENTENCE.failed);
|
|
@@ -1999,7 +2021,7 @@ async function composeStartStop(args, io, key, action) {
|
|
|
1999
2021
|
});
|
|
2000
2022
|
if (result.network) return fail(io, key, json, SENTENCE.platform);
|
|
2001
2023
|
if (result.status !== 200) {
|
|
2002
|
-
return fail(io, key, json, upgradeMessage(result.status, result.body) ?? sentenceFor(result.status));
|
|
2024
|
+
return fail(io, key, json, upgradeMessage(result.status, result.body) ?? sentenceFor(result.status, result.retryAfterSeconds));
|
|
2003
2025
|
}
|
|
2004
2026
|
const body = isRecord(result.body) ? result.body : null;
|
|
2005
2027
|
const view = body === null ? null : composeFields(body.compose);
|