@greatwait/cli 0.1.0-alpha.6 → 0.1.0-alpha.7
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/catalog.js +18 -1
- package/dist/commands.js +10 -0
- package/dist/operate.js +52 -1
- package/package.json +3 -3
package/dist/catalog.js
CHANGED
|
@@ -529,6 +529,11 @@ export const COMMAND_CATALOG = [
|
|
|
529
529
|
summary: "Your own label, such as a customer id. Each label is paced on its own, so one busy label can't hold up the rest. Can't be changed later.",
|
|
530
530
|
value: "<key>",
|
|
531
531
|
},
|
|
532
|
+
{
|
|
533
|
+
flag: "--auto-pause-after-failures",
|
|
534
|
+
summary: 'Pause a recurring schedule after 1–100 consecutive failed runs, or "never". Defaults to 5 on create; omission on replace keeps the setting. Resume resets the count.',
|
|
535
|
+
value: "<count|never>",
|
|
536
|
+
},
|
|
532
537
|
{
|
|
533
538
|
flag: "--latency-class",
|
|
534
539
|
summary: "realtime or bulk. realtime goes first when both are due. By default a call due within two minutes is realtime, and a repeating one is bulk.",
|
|
@@ -605,6 +610,11 @@ export const COMMAND_CATALOG = [
|
|
|
605
610
|
summary: "Your own label, such as a customer id. Each label is paced on its own, so one busy label can't hold up the rest. Can't be changed later.",
|
|
606
611
|
value: "<key>",
|
|
607
612
|
},
|
|
613
|
+
{
|
|
614
|
+
flag: "--auto-pause-after-failures",
|
|
615
|
+
summary: 'Pause a recurring schedule after 1–100 consecutive failed runs, or "never". Defaults to 5 on create; omission on replace keeps the setting. Resume resets the count.',
|
|
616
|
+
value: "<count|never>",
|
|
617
|
+
},
|
|
608
618
|
{
|
|
609
619
|
flag: "--latency-class",
|
|
610
620
|
summary: "realtime or bulk. realtime goes first when both are due. By default a call due within two minutes is realtime, and a repeating one is bulk.",
|
|
@@ -834,7 +844,14 @@ export const COMMAND_CATALOG = [
|
|
|
834
844
|
{
|
|
835
845
|
group: "Occurrences and attempts",
|
|
836
846
|
operations: ["attempts.read"],
|
|
837
|
-
options: [
|
|
847
|
+
options: [
|
|
848
|
+
API_URL,
|
|
849
|
+
{
|
|
850
|
+
flag: "--response-file",
|
|
851
|
+
value: "path",
|
|
852
|
+
summary: "Save the captured response body in a new private file.",
|
|
853
|
+
},
|
|
854
|
+
],
|
|
838
855
|
positionals: [
|
|
839
856
|
{
|
|
840
857
|
name: "<schedule>",
|
package/dist/commands.js
CHANGED
|
@@ -805,11 +805,21 @@ const schedulesRead = async (runtime, args) => {
|
|
|
805
805
|
recurrence: view.schedule.recurrence.kind,
|
|
806
806
|
schedule: view.schedule.id,
|
|
807
807
|
status: view.schedule.status,
|
|
808
|
+
autoPauseAfterFailures: view.schedule.autoPauseAfterFailures,
|
|
809
|
+
consecutiveFailedRuns: view.schedule.consecutiveFailedRuns,
|
|
810
|
+
autoPauseReason: view.schedule.autoPauseReason,
|
|
808
811
|
version: view.schedule.version,
|
|
809
812
|
},
|
|
810
813
|
human: [
|
|
811
814
|
`Schedule ${view.schedule.id}`,
|
|
812
815
|
`Status ${view.schedule.status}`,
|
|
816
|
+
`Auto-pause ${String(view.schedule.autoPauseAfterFailures ?? 5)}`,
|
|
817
|
+
`Failed runs ${String(view.schedule.consecutiveFailedRuns ?? 0)}`,
|
|
818
|
+
...(view.schedule.autoPauseReason == null
|
|
819
|
+
? []
|
|
820
|
+
: [
|
|
821
|
+
`Paused ${String(view.schedule.autoPauseReason.failedRuns)} failed runs; ${view.schedule.autoPauseReason.status === undefined ? "no HTTP response" : `HTTP ${String(view.schedule.autoPauseReason.status)}`} (${view.schedule.autoPauseReason.diagnostic}) at ${view.schedule.autoPauseReason.pausedAt}`,
|
|
822
|
+
]),
|
|
813
823
|
`Recurrence ${view.schedule.recurrence.kind}`,
|
|
814
824
|
`Version ${String(view.schedule.version)}`,
|
|
815
825
|
...(etag === undefined ? [] : [`ETag ${etag}`]),
|
package/dist/operate.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { autoPauseAfterFailuresSchema, httpRequestSchema, sanitizedDiagnosticText, diagnosticBodyText, responseBodySentence, requestCaptureSentence, attemptStatusGuidance, endpointHeaderChangesSchema, billingAutoTopUpApplyRequestSchema, billingAutoTopUpPreviewRequestSchema, billingAutoTopUpPreviewSchema, billingCeilingUpdateRequestSchema, billingFundingQuoteRequestSchema, billingFundingQuoteSchema, billingNotificationSettingsUpdateRequestSchema, billingRefundRequestCreateSchema, billingRefundResolutionRequestSchema, projectBillingQuoteSchema, projectBillingQuoteRequestSchema, } from "@greatwait/contracts";
|
|
2
3
|
import { parseDuration } from "./args.js";
|
|
3
4
|
import { confirmChange } from "./confirm.js";
|
|
4
5
|
import { redactSecret } from "./output.js";
|
|
@@ -116,6 +117,12 @@ async function scheduleRequest(runtime, args, usage) {
|
|
|
116
117
|
: httpRequestSchema.safeParse(httpInput);
|
|
117
118
|
if (http !== undefined && !http.success)
|
|
118
119
|
throw new CliError("--http settings are invalid", "usage", http.error.issues.map((issue) => issue.message).join("; "));
|
|
120
|
+
const pauseInput = args.options["--auto-pause-after-failures"];
|
|
121
|
+
const pause = pauseInput === undefined
|
|
122
|
+
? undefined
|
|
123
|
+
: autoPauseAfterFailuresSchema.safeParse(pauseInput === "never" ? pauseInput : Number(pauseInput));
|
|
124
|
+
if (pause !== undefined && !pause.success)
|
|
125
|
+
throw new CliError('--auto-pause-after-failures must be a whole number from 1 to 100, or "never"', "usage");
|
|
119
126
|
const latencyClass = args.options["--latency-class"];
|
|
120
127
|
const partitionKey = args.options["--partition-key"];
|
|
121
128
|
return {
|
|
@@ -124,6 +131,7 @@ async function scheduleRequest(runtime, args, usage) {
|
|
|
124
131
|
...(endpoint === undefined ? {} : { endpoint }),
|
|
125
132
|
...(url === undefined ? {} : { url }),
|
|
126
133
|
...(latencyClass === undefined ? {} : { latencyClass }),
|
|
134
|
+
...(pause?.success ? { autoPauseAfterFailures: pause.data } : {}),
|
|
127
135
|
...(partitionKey === undefined ? {} : { partitionKey }),
|
|
128
136
|
...(timeZone === undefined ? {} : { timeZone }),
|
|
129
137
|
...recurrence,
|
|
@@ -494,9 +502,52 @@ const attemptsRead = async (runtime, args) => {
|
|
|
494
502
|
const active = await session(runtime, args);
|
|
495
503
|
const view = parseResponse("attempts.read", await active.client.request("attempts_read", schedule, occurrence, attempt));
|
|
496
504
|
const found = view.attempt;
|
|
505
|
+
const responseFile = args.options["--response-file"];
|
|
506
|
+
if (responseFile !== undefined) {
|
|
507
|
+
if (found.response?.body === undefined)
|
|
508
|
+
throw new CliError(responseBodySentence(found.response?.bodyState ?? "not_captured", found.responseStatus === undefined), "validation");
|
|
509
|
+
const body = found.response.body;
|
|
510
|
+
try {
|
|
511
|
+
await writeFile(responseFile, body.encoding === "base64"
|
|
512
|
+
? Buffer.from(body.content, "base64")
|
|
513
|
+
: Buffer.from(body.content, "utf8"), { mode: 0o600, flag: "wx" });
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
throw new CliError("The response file could not be created. Choose a path that does not already exist.", "usage");
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
const diagnosticLines = [];
|
|
520
|
+
if (found.response !== undefined) {
|
|
521
|
+
const response = found.response;
|
|
522
|
+
diagnosticLines.push(`Server response ${String(found.responseStatus ?? "")} ${response.statusText}`, ...response.headers.map((h) => `${h.name}: ${h.value}`));
|
|
523
|
+
if (response.body === undefined)
|
|
524
|
+
diagnosticLines.push(responseBodySentence(response.bodyState, found.responseStatus === undefined));
|
|
525
|
+
else {
|
|
526
|
+
const body = response.body;
|
|
527
|
+
if (body.incomplete)
|
|
528
|
+
diagnosticLines.push("The response stream stopped during capture; this is a partial answer.");
|
|
529
|
+
if (body.truncated)
|
|
530
|
+
diagnosticLines.push(`Response cut at 10 KiB (10,240 bytes)${body.originalBytes === undefined ? "" : `; original length ${String(body.originalBytes)} bytes`}.`);
|
|
531
|
+
diagnosticLines.push(diagnosticBodyText(body) !== undefined
|
|
532
|
+
? diagnosticBodyText(body)
|
|
533
|
+
: `Binary response (${String(body.capturedBytes)} bytes). Download with --response-file <path>.`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
diagnosticLines.push("What we sent");
|
|
537
|
+
if (found.request === undefined)
|
|
538
|
+
diagnosticLines.push(requestCaptureSentence(found.requestState, found.state === undefined));
|
|
539
|
+
else
|
|
540
|
+
diagnosticLines.push(`${found.request.method} ${found.request.url}`, ...found.request.headers.map((h) => `${h.name}: ${h.value}`), found.request.body === "" ? "We sent no body." : found.request.body);
|
|
541
|
+
const guidance = attemptStatusGuidance(found.responseStatus);
|
|
542
|
+
if (guidance !== undefined)
|
|
543
|
+
diagnosticLines.push(guidance);
|
|
497
544
|
return {
|
|
498
545
|
data: { attempt: found, freshness: freshnessOf(view) },
|
|
499
546
|
human: [
|
|
547
|
+
...diagnosticLines.map(sanitizedDiagnosticText),
|
|
548
|
+
...(responseFile === undefined
|
|
549
|
+
? []
|
|
550
|
+
: [`Response saved to ${sanitizedDiagnosticText(responseFile)}`]),
|
|
500
551
|
`Attempt ${found.id}`,
|
|
501
552
|
`Number ${String(found.attemptNumber)}`,
|
|
502
553
|
...(found.state === undefined ? [] : [`State ${found.state}`]),
|
package/package.json
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"url": "https://github.com/flowxo/greatwait/issues"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@greatwait/api-client": "0.1.0-alpha.
|
|
11
|
-
"@greatwait/contracts": "0.1.0-alpha.
|
|
10
|
+
"@greatwait/api-client": "0.1.0-alpha.7",
|
|
11
|
+
"@greatwait/contracts": "0.1.0-alpha.7"
|
|
12
12
|
},
|
|
13
13
|
"description": "The GreatWait command line: sign in, create a destination and a one-shot schedule, and wait for the callback, over the public API.",
|
|
14
14
|
"engines": {
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
},
|
|
49
49
|
"sideEffects": false,
|
|
50
50
|
"type": "module",
|
|
51
|
-
"version": "0.1.0-alpha.
|
|
51
|
+
"version": "0.1.0-alpha.7"
|
|
52
52
|
}
|