@greatwait/cli 0.2.1 → 0.2.3
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 +63 -5
- package/dist/commands.js +10 -0
- package/dist/operate.js +133 -6
- package/package.json +3 -3
package/dist/catalog.js
CHANGED
|
@@ -83,6 +83,30 @@ const API_URL = {
|
|
|
83
83
|
summary: "The GreatWait API to use, such as https://api.greatwait.dev. Defaults to the one you signed in to.",
|
|
84
84
|
value: "<origin>",
|
|
85
85
|
};
|
|
86
|
+
const CORRELATION_FILTER_OPTIONS = [
|
|
87
|
+
{
|
|
88
|
+
flag: "--external-identifier",
|
|
89
|
+
value: "<id>",
|
|
90
|
+
summary: "Exact host application identifier, such as a request ID.",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
flag: "--tag",
|
|
94
|
+
value: "<tag>",
|
|
95
|
+
summary: "Exact membership of one schedule tag.",
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
const CORRELATION_WRITE_OPTIONS = [
|
|
99
|
+
{
|
|
100
|
+
flag: "--external-identifier",
|
|
101
|
+
value: "<id>",
|
|
102
|
+
summary: "Searchable host application identifier; at most 256 UTF-8 bytes. Do not include secrets.",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
flag: "--tags",
|
|
106
|
+
value: "<json>",
|
|
107
|
+
summary: "JSON array of up to 16 distinct tags, each at most 64 UTF-8 bytes. Inline or @file.json.",
|
|
108
|
+
},
|
|
109
|
+
];
|
|
86
110
|
const PAGE_OPTIONS = [
|
|
87
111
|
{
|
|
88
112
|
flag: "--limit",
|
|
@@ -525,12 +549,21 @@ export const COMMAND_CATALOG = [
|
|
|
525
549
|
summary: "Start calling a disabled destination again.",
|
|
526
550
|
words: "destinations enable",
|
|
527
551
|
},
|
|
552
|
+
{
|
|
553
|
+
group: "Schedules",
|
|
554
|
+
operations: ["executions.list"],
|
|
555
|
+
options: [API_URL, ...CORRELATION_FILTER_OPTIONS, ...PAGE_OPTIONS],
|
|
556
|
+
positionals: [],
|
|
557
|
+
summary: "Find retained runs, newest first, by host identifier or tag.",
|
|
558
|
+
words: "executions list",
|
|
559
|
+
},
|
|
528
560
|
/* --- Schedules -------------------------------------------------------- */
|
|
529
561
|
{
|
|
530
562
|
group: "Schedules",
|
|
531
563
|
operations: ["schedules.list"],
|
|
532
564
|
options: [
|
|
533
565
|
API_URL,
|
|
566
|
+
...CORRELATION_FILTER_OPTIONS,
|
|
534
567
|
{
|
|
535
568
|
flag: "--endpoint",
|
|
536
569
|
summary: "Only the schedules that call this destination. Its id starts ep_.",
|
|
@@ -560,6 +593,7 @@ export const COMMAND_CATALOG = [
|
|
|
560
593
|
operations: ["schedules.create"],
|
|
561
594
|
options: [
|
|
562
595
|
API_URL,
|
|
596
|
+
...CORRELATION_WRITE_OPTIONS,
|
|
563
597
|
{
|
|
564
598
|
flag: "--endpoint",
|
|
565
599
|
summary: "The destination to call, by id, starting ep_. Or use --url instead.",
|
|
@@ -641,6 +675,7 @@ export const COMMAND_CATALOG = [
|
|
|
641
675
|
operations: ["schedules.replace"],
|
|
642
676
|
options: [
|
|
643
677
|
API_URL,
|
|
678
|
+
...CORRELATION_WRITE_OPTIONS,
|
|
644
679
|
{
|
|
645
680
|
flag: "--endpoint",
|
|
646
681
|
summary: "The destination to call, by id, starting ep_. Or use --url instead.",
|
|
@@ -1372,6 +1407,34 @@ export const COMMAND_CATALOG = [
|
|
|
1372
1407
|
summary: "Remove your saved card. Project fees stop renewing when the paid period ends, and automatic top-up turns off.",
|
|
1373
1408
|
words: "billing remove-card",
|
|
1374
1409
|
},
|
|
1410
|
+
{
|
|
1411
|
+
group: "Planning",
|
|
1412
|
+
operations: ["billing.promotions"],
|
|
1413
|
+
options: [API_URL],
|
|
1414
|
+
positionals: [],
|
|
1415
|
+
summary: "List your promotions: promotional credit, complimentary projects, perks and discounts, with their end dates.",
|
|
1416
|
+
words: "billing promotions",
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
confirms: true,
|
|
1420
|
+
group: "Planning",
|
|
1421
|
+
operations: ["billing.redeem"],
|
|
1422
|
+
options: [
|
|
1423
|
+
API_URL,
|
|
1424
|
+
{
|
|
1425
|
+
flag: "--target-project",
|
|
1426
|
+
summary: "The project a complimentary-project code should cover.",
|
|
1427
|
+
value: "<project-id>",
|
|
1428
|
+
},
|
|
1429
|
+
IDEMPOTENCY,
|
|
1430
|
+
CONFIRM,
|
|
1431
|
+
],
|
|
1432
|
+
positionals: [
|
|
1433
|
+
{ name: "<code>", summary: "The promotion code, as you received it." },
|
|
1434
|
+
],
|
|
1435
|
+
summary: "Redeem a promotion code. Nothing is charged.",
|
|
1436
|
+
words: "billing redeem",
|
|
1437
|
+
},
|
|
1375
1438
|
{
|
|
1376
1439
|
group: "Planning",
|
|
1377
1440
|
operations: ["billing.ledger"],
|
|
@@ -1626,11 +1689,6 @@ export const OPERATION_OMISSIONS = [
|
|
|
1626
1689
|
ground: "declared",
|
|
1627
1690
|
operation: "projects.delete",
|
|
1628
1691
|
},
|
|
1629
|
-
{
|
|
1630
|
-
because: "The Executions page is the accepted project-wide history surface in this story. The generated SDK and MCP expose the operation to automation; adding a second interactive CLI design is outside it.",
|
|
1631
|
-
ground: "declared",
|
|
1632
|
-
operation: "executions.list",
|
|
1633
|
-
},
|
|
1634
1692
|
{
|
|
1635
1693
|
because: "Grants are what a person gave to an application, and only that person may read them.",
|
|
1636
1694
|
ground: "principal-unreachable",
|
package/dist/commands.js
CHANGED
|
@@ -812,6 +812,10 @@ const schedulesRead = async (runtime, args) => {
|
|
|
812
812
|
autoPauseAfterFailures: view.schedule.autoPauseAfterFailures,
|
|
813
813
|
consecutiveFailedRuns: view.schedule.consecutiveFailedRuns,
|
|
814
814
|
autoPauseReason: view.schedule.autoPauseReason,
|
|
815
|
+
...(view.schedule.external_identifier === undefined
|
|
816
|
+
? {}
|
|
817
|
+
: { external_identifier: view.schedule.external_identifier }),
|
|
818
|
+
...(view.schedule.tags === undefined ? {} : { tags: view.schedule.tags }),
|
|
815
819
|
version: view.schedule.version,
|
|
816
820
|
},
|
|
817
821
|
human: [
|
|
@@ -825,6 +829,12 @@ const schedulesRead = async (runtime, args) => {
|
|
|
825
829
|
`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}`,
|
|
826
830
|
]),
|
|
827
831
|
`Recurrence ${view.schedule.recurrence.kind}`,
|
|
832
|
+
...(view.schedule.external_identifier === undefined
|
|
833
|
+
? []
|
|
834
|
+
: [`External ID ${view.schedule.external_identifier}`]),
|
|
835
|
+
...(view.schedule.tags === undefined || view.schedule.tags.length === 0
|
|
836
|
+
? []
|
|
837
|
+
: [`Tags ${view.schedule.tags.join(", ")}`]),
|
|
828
838
|
`Version ${String(view.schedule.version)}`,
|
|
829
839
|
...(etag === undefined ? [] : [`ETag ${etag}`]),
|
|
830
840
|
].join("\n"),
|
package/dist/operate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
|
-
import { projectNotificationSettingsUpdateRequestSchema, statusSigningKeyRequestSchema, autoPauseAfterFailuresSchema, httpRequestSchema, sanitizedDiagnosticText, diagnosticBodyText, responseBodySentence, requestCaptureSentence, attemptStatusGuidance, endpointHeaderChangesSchema, billingAutoTopUpApplyRequestSchema, billingAutoTopUpPreviewRequestSchema, billingAutoTopUpPreviewSchema, billingCeilingUpdateRequestSchema, billingFundingQuoteRequestSchema, billingFundingQuoteSchema, billingNotificationSettingsUpdateRequestSchema, billingRefundRequestCreateSchema, billingRefundResolutionRequestSchema, projectBillingQuoteSchema, projectBillingQuoteRequestSchema, } from "@greatwait/contracts";
|
|
2
|
+
import { projectNotificationSettingsUpdateRequestSchema, statusSigningKeyRequestSchema, autoPauseAfterFailuresSchema, httpRequestSchema, externalIdentifierSchema, scheduleTagsSchema, scheduleTagSchema, sanitizedDiagnosticText, diagnosticBodyText, responseBodySentence, requestCaptureSentence, attemptStatusGuidance, endpointHeaderChangesSchema, billingAutoTopUpApplyRequestSchema, billingAutoTopUpPreviewRequestSchema, billingAutoTopUpPreviewSchema, billingCeilingUpdateRequestSchema, billingFundingQuoteRequestSchema, billingFundingQuoteSchema, billingNotificationSettingsUpdateRequestSchema, billingRefundRequestCreateSchema, billingRefundResolutionRequestSchema, billingRedeemRequestSchema, projectBillingQuoteSchema, projectBillingQuoteRequestSchema, } from "@greatwait/contracts";
|
|
3
3
|
import { parseDuration } from "./args.js";
|
|
4
4
|
import { confirmChange } from "./confirm.js";
|
|
5
5
|
import { redactSecret } from "./output.js";
|
|
@@ -125,8 +125,18 @@ async function scheduleRequest(runtime, args, usage) {
|
|
|
125
125
|
throw new CliError('--auto-pause-after-failures must be a whole number from 1 to 100, or "never"', "usage");
|
|
126
126
|
const latencyClass = args.options["--latency-class"];
|
|
127
127
|
const partitionKey = args.options["--partition-key"];
|
|
128
|
+
const external_identifier = args.options["--external-identifier"];
|
|
129
|
+
if (external_identifier !== undefined &&
|
|
130
|
+
!externalIdentifierSchema.safeParse(external_identifier).success)
|
|
131
|
+
throw new CliError("--external-identifier must contain 1–256 UTF-8 bytes and no NUL characters", "usage");
|
|
132
|
+
const tags = await readJsonOption(runtime, "--tags", args.options["--tags"]);
|
|
133
|
+
const parsedTags = tags === undefined ? undefined : scheduleTagsSchema.safeParse(tags);
|
|
134
|
+
if (parsedTags !== undefined && !parsedTags.success)
|
|
135
|
+
throw new CliError("--tags must be up to 16 distinct strings, each 1–64 UTF-8 bytes", "usage");
|
|
128
136
|
return {
|
|
129
137
|
...(data === undefined ? {} : { data }),
|
|
138
|
+
...(external_identifier === undefined ? {} : { external_identifier }),
|
|
139
|
+
...(parsedTags?.success ? { tags: parsedTags.data } : {}),
|
|
130
140
|
...(http?.success ? { http: http.data } : {}),
|
|
131
141
|
...(endpoint === undefined ? {} : { endpoint }),
|
|
132
142
|
...(url === undefined ? {} : { url }),
|
|
@@ -311,18 +321,55 @@ const destinationsRotateKey = async (runtime, args) => {
|
|
|
311
321
|
/* -------------------------------------------------------------------------- */
|
|
312
322
|
/* Schedules */
|
|
313
323
|
/* -------------------------------------------------------------------------- */
|
|
324
|
+
function correlationFilters(args) {
|
|
325
|
+
const external_identifier = args.options["--external-identifier"];
|
|
326
|
+
const tag = args.options["--tag"];
|
|
327
|
+
if (external_identifier !== undefined &&
|
|
328
|
+
!externalIdentifierSchema.safeParse(external_identifier).success)
|
|
329
|
+
throw new CliError("--external-identifier must contain 1–256 UTF-8 bytes", "usage");
|
|
330
|
+
if (tag !== undefined && !scheduleTagSchema.safeParse(tag).success)
|
|
331
|
+
throw new CliError("--tag must contain 1–64 UTF-8 bytes", "usage");
|
|
332
|
+
return {
|
|
333
|
+
...(external_identifier === undefined ? {} : { external_identifier }),
|
|
334
|
+
...(tag === undefined ? {} : { tag }),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function correlationCommand(args) {
|
|
338
|
+
return ["--external-identifier", "--tag"]
|
|
339
|
+
.flatMap((flag) => args.options[flag] === undefined
|
|
340
|
+
? []
|
|
341
|
+
: [` ${flag} ${`'${args.options[flag].replaceAll("'", "'\"'\"'")}'`}`])
|
|
342
|
+
.join("");
|
|
343
|
+
}
|
|
344
|
+
const executionsList = async (runtime, args) => {
|
|
345
|
+
const filters = correlationFilters(args);
|
|
346
|
+
const active = await session(runtime, args);
|
|
347
|
+
return listing(runtime, args, {
|
|
348
|
+
columns: ["STATE", "PLANNED FOR", "SCHEDULE", "RUN"],
|
|
349
|
+
command: `${runtime.programName} executions list${correlationCommand(args)}`,
|
|
350
|
+
empty: "No retained runs match these filters.",
|
|
351
|
+
read: (request) => page("executions.list", () => active.client.request("executions_list", { ...request, ...filters })),
|
|
352
|
+
row: (run) => [run.state, run.plannedFor, run.schedule, run.id],
|
|
353
|
+
});
|
|
354
|
+
};
|
|
314
355
|
const schedulesList = async (runtime, args) => {
|
|
356
|
+
const filters = correlationFilters(args);
|
|
315
357
|
const active = await session(runtime, args);
|
|
316
358
|
const endpoint = args.options["--endpoint"];
|
|
317
359
|
return listing(runtime, args, {
|
|
318
360
|
columns: ["STATUS", "NAME", "RECURRENCE", "DELIVERS TO", "SCHEDULE"],
|
|
319
|
-
command: `${runtime.programName} schedules list${endpoint === undefined ? "" : ` --endpoint ${endpoint}`}`,
|
|
320
|
-
empty
|
|
321
|
-
|
|
322
|
-
|
|
361
|
+
command: `${runtime.programName} schedules list${endpoint === undefined ? "" : ` --endpoint ${endpoint}`}${correlationCommand(args)}`,
|
|
362
|
+
// An empty filtered page answers the filter, not the project: "no
|
|
363
|
+
// schedules yet" read as the project being empty when nothing matched.
|
|
364
|
+
empty: Object.keys(filters).length > 0
|
|
365
|
+
? "No schedule matches these filters."
|
|
366
|
+
: endpoint === undefined
|
|
367
|
+
? "This project has no schedules yet."
|
|
368
|
+
: "No schedule delivers to that destination.",
|
|
323
369
|
read: (request) => page("schedules.list", () => active.client.request("schedules_list", {
|
|
324
370
|
...request,
|
|
325
371
|
...(endpoint === undefined ? {} : { endpoint }),
|
|
372
|
+
...filters,
|
|
326
373
|
})),
|
|
327
374
|
row: (schedule) => [
|
|
328
375
|
schedule.status,
|
|
@@ -735,6 +782,16 @@ const billing = async (runtime, args) => {
|
|
|
735
782
|
...(view.trialExpiresAt === undefined
|
|
736
783
|
? []
|
|
737
784
|
: [`Trial ends ${view.trialExpiresAt}`]),
|
|
785
|
+
...(view.credit.byKind === undefined
|
|
786
|
+
? []
|
|
787
|
+
: ["purchased", "trial", "promotional"].flatMap((kind) => {
|
|
788
|
+
const balance = view.credit.byKind[kind];
|
|
789
|
+
return balance.granted.microdollars === "0"
|
|
790
|
+
? []
|
|
791
|
+
: [
|
|
792
|
+
`${kind.padEnd(12)} ${money(balance.available)} available, ${money(balance.reserved)} reserved, ${money(balance.consumed)} used${balance.forfeited.microdollars === "0" ? "" : `, ${money(balance.forfeited)} expired`}${balance.expiresAt === undefined ? "" : `; ends ${balance.expiresAt}`}`,
|
|
793
|
+
];
|
|
794
|
+
})),
|
|
738
795
|
...(view.activation.mode === "shadow" || view.blockingReasons.length === 0
|
|
739
796
|
? []
|
|
740
797
|
: [`Blocked ${view.blockingReasons.join(", ")}`]),
|
|
@@ -1050,6 +1107,63 @@ const billingAutoTopUpApply = async (runtime, args) => {
|
|
|
1050
1107
|
data: { confirmation, idempotencyKey: key, ...view },
|
|
1051
1108
|
};
|
|
1052
1109
|
};
|
|
1110
|
+
function promotionGrantLine(grant) {
|
|
1111
|
+
const benefit = grant.benefit;
|
|
1112
|
+
const what = benefit.kind === "credit"
|
|
1113
|
+
? `${money(benefit.amount)} promotional credit`
|
|
1114
|
+
: benefit.kind === "project-slot"
|
|
1115
|
+
? `complimentary project ${benefit.project}`
|
|
1116
|
+
: benefit.kind === "perk"
|
|
1117
|
+
? `perk ${benefit.perk}`
|
|
1118
|
+
: `${benefit.percentOff !== undefined ? `${String(benefit.percentOff)}%` : money(benefit.amountOff)} off project fees, ${benefit.duration === "once" ? "one invoice" : `${String(benefit.durationInMonths)} invoices`}`;
|
|
1119
|
+
return `${grant.id}: ${what} (${grant.state}, from ${grant.source}${grant.endsAt === undefined ? "" : `, ends ${grant.endsAt}`}${grant.revokedAt === undefined ? "" : `, revoked ${grant.revokedAt}`})`;
|
|
1120
|
+
}
|
|
1121
|
+
function promotionsLines(view) {
|
|
1122
|
+
return [
|
|
1123
|
+
`Projects: ${String(view.projectCapacity.included)} included, ${String(view.projectCapacity.paid)} paid, ${String(view.projectCapacity.complimentary)} complimentary`,
|
|
1124
|
+
...(view.grants.length === 0
|
|
1125
|
+
? ["No promotions."]
|
|
1126
|
+
: view.grants.map(promotionGrantLine)),
|
|
1127
|
+
...view.restrictions,
|
|
1128
|
+
];
|
|
1129
|
+
}
|
|
1130
|
+
const billingPromotions = async (runtime, args) => {
|
|
1131
|
+
const active = await session(runtime, args);
|
|
1132
|
+
const view = parseResponse("billing.promotions", await active.client.request("billing_promotions"));
|
|
1133
|
+
return { data: view, human: promotionsLines(view).join("\n") };
|
|
1134
|
+
};
|
|
1135
|
+
const billingRedeem = async (runtime, args) => {
|
|
1136
|
+
const parsed = billingRedeemRequestSchema.safeParse({
|
|
1137
|
+
code: positionals(args, ["<code>"], `${runtime.programName} billing redeem <code> [--target-project <project-id>]`)[0],
|
|
1138
|
+
confirmed: true,
|
|
1139
|
+
...(args.options["--target-project"] === undefined
|
|
1140
|
+
? {}
|
|
1141
|
+
: { project: args.options["--target-project"] }),
|
|
1142
|
+
});
|
|
1143
|
+
if (!parsed.success)
|
|
1144
|
+
throw new CliError("Provide the code, and --target-project when the code covers a project.", "usage");
|
|
1145
|
+
const confirmation = await confirmChange(runtime, args, {
|
|
1146
|
+
action: "Redeem this promotion code for your organization.",
|
|
1147
|
+
effect: [
|
|
1148
|
+
...(parsed.data.project === undefined
|
|
1149
|
+
? []
|
|
1150
|
+
: [`project ${parsed.data.project}`]),
|
|
1151
|
+
"Nothing is charged. Promotional value expires, has no cash value and cannot be refunded.",
|
|
1152
|
+
],
|
|
1153
|
+
});
|
|
1154
|
+
const active = await session(runtime, args);
|
|
1155
|
+
const key = idempotencyKey(runtime, args);
|
|
1156
|
+
const view = parseResponse("billing.redeem", await active.client.request("billing_redeem", parsed.data, {
|
|
1157
|
+
"Idempotency-Key": key,
|
|
1158
|
+
}));
|
|
1159
|
+
return {
|
|
1160
|
+
data: { confirmation, idempotencyKey: key, ...view },
|
|
1161
|
+
human: [
|
|
1162
|
+
`Redeemed ${promotionGrantLine(view.grant)}`,
|
|
1163
|
+
...promotionsLines(view.promotions),
|
|
1164
|
+
].join("\n"),
|
|
1165
|
+
};
|
|
1166
|
+
};
|
|
1053
1167
|
const billingRefundRequest = async (runtime, args) => {
|
|
1054
1168
|
const parsed = billingRefundRequestCreateSchema.safeParse({
|
|
1055
1169
|
amount: budgetMoney(args, "--amount"),
|
|
@@ -1139,8 +1253,13 @@ function projectBillingResult(view) {
|
|
|
1139
1253
|
human: [
|
|
1140
1254
|
`Monthly project fees ${money(view.recurringProjectFees)}`,
|
|
1141
1255
|
`Policy desired r${String(view.desiredRevision)}, effective r${String(view.effectiveRevision)}${view.pending ? " (pending)" : ""}`,
|
|
1142
|
-
...view.projects.map((project) => `${project.name} (${project.project}): ${project.access}${project.paidThroughAt ? `, paid through ${project.paidThroughAt}` : ""}${project.cancellationAt ? `, cancels ${project.cancellationAt}` : ""}${project.renewalGraceUntil ? `, renewal grace until ${project.renewalGraceUntil}` : ""}`),
|
|
1256
|
+
...view.projects.map((project) => `${project.name} (${project.project}): ${project.complimentary ? `complimentary until ${project.complimentary.endsAt}` : project.access}${project.paidThroughAt ? `, paid through ${project.paidThroughAt}` : ""}${project.cancellationAt ? `, cancels ${project.cancellationAt}` : ""}${project.renewalGraceUntil ? `, renewal grace until ${project.renewalGraceUntil}` : ""}`),
|
|
1143
1257
|
...view.pendingOperations.map((operation) => `${operation.project}: ${operation.state}${operation.reason ? ` (${operation.reason})` : ""}${operation.paymentUrl ? ` ${operation.paymentUrl}` : ""}`),
|
|
1258
|
+
...(view.discount === undefined
|
|
1259
|
+
? []
|
|
1260
|
+
: [
|
|
1261
|
+
`Discount ${view.discount.percentOff !== undefined ? `${String(view.discount.percentOff)}% off` : `${money(view.discount.amountOff)} off`} ${view.discount.duration === "once" ? "one invoice" : `${String(view.discount.durationInMonths)} monthly invoices`} (${view.discount.state})`,
|
|
1262
|
+
]),
|
|
1144
1263
|
].join("\n"),
|
|
1145
1264
|
};
|
|
1146
1265
|
}
|
|
@@ -1151,6 +1270,11 @@ const billingProjects = async (runtime, args) => {
|
|
|
1151
1270
|
function projectQuoteLines(quote) {
|
|
1152
1271
|
return [
|
|
1153
1272
|
`Project ${quote.project}${quote.fromProject ? `; former included ${quote.fromProject}` : ""}`,
|
|
1273
|
+
...(quote.discount === undefined
|
|
1274
|
+
? []
|
|
1275
|
+
: [
|
|
1276
|
+
`Before discount ${money(quote.discount.gross)}; discount ${money(quote.discount.amount)} for ${quote.discount.duration === "once" ? "this invoice" : `${String(quote.discount.durationInMonths)} monthly invoices`}`,
|
|
1277
|
+
]),
|
|
1154
1278
|
`Charge ${money(quote.charge)}; monthly project fees ${money(quote.monthlyProjectFees)}`,
|
|
1155
1279
|
`Effective ${quote.effectiveAt}; quote expires ${quote.expiresAt}`,
|
|
1156
1280
|
...(quote.formerIncludedWillBlock
|
|
@@ -1730,6 +1854,8 @@ export const OPERATE_COMMANDS = {
|
|
|
1730
1854
|
"billing notification-settings": billingNotificationSettings,
|
|
1731
1855
|
"billing update-notifications": billingNotificationSettingsUpdate,
|
|
1732
1856
|
"billing ledger": billingLedger,
|
|
1857
|
+
"billing promotions": billingPromotions,
|
|
1858
|
+
"billing redeem": billingRedeem,
|
|
1733
1859
|
"billing projects": billingProjects,
|
|
1734
1860
|
"billing purchase": billingPurchase,
|
|
1735
1861
|
"billing quote-funding": billingQuoteFunding,
|
|
@@ -1760,6 +1886,7 @@ export const OPERATE_COMMANDS = {
|
|
|
1760
1886
|
"schedules archive": schedulesArchive,
|
|
1761
1887
|
"schedules cancel": schedulesCancel,
|
|
1762
1888
|
"schedules list": schedulesList,
|
|
1889
|
+
"executions list": executionsList,
|
|
1763
1890
|
"schedules pause": schedulesPause,
|
|
1764
1891
|
"schedules replace": schedulesReplace,
|
|
1765
1892
|
"schedules resume": schedulesResume,
|
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.2.
|
|
11
|
-
"@greatwait/contracts": "0.2.
|
|
10
|
+
"@greatwait/api-client": "0.2.3",
|
|
11
|
+
"@greatwait/contracts": "0.2.3"
|
|
12
12
|
},
|
|
13
13
|
"description": "The GreatWait command line: sign in, schedule a call to a URL, and read its delivery outcome over the public API.",
|
|
14
14
|
"engines": {
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
},
|
|
49
49
|
"sideEffects": false,
|
|
50
50
|
"type": "module",
|
|
51
|
-
"version": "0.2.
|
|
51
|
+
"version": "0.2.3"
|
|
52
52
|
}
|