@greatwait/cli 0.1.0-alpha.7 → 0.1.0-alpha.8

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 CHANGED
@@ -255,6 +255,77 @@ export const MEMBER_COMMANDS = [
255
255
  },
256
256
  ];
257
257
  export const COMMAND_CATALOG = [
258
+ {
259
+ group: "Planning",
260
+ words: "notifications show",
261
+ summary: "Show this project and environment's delivery notification settings.",
262
+ operations: ["projectnotifications.read"],
263
+ positionals: [
264
+ {
265
+ name: "<project>",
266
+ summary: "The project identifier in this credential's authority.",
267
+ },
268
+ ],
269
+ options: [API_URL],
270
+ },
271
+ {
272
+ group: "Planning",
273
+ words: "notifications update",
274
+ summary: "Change failure email and failed-event webhook preferences at the revision you read.",
275
+ confirms: true,
276
+ operations: ["projectnotifications.update"],
277
+ positionals: [
278
+ {
279
+ name: "<project>",
280
+ summary: "The project identifier in this credential's authority.",
281
+ },
282
+ ],
283
+ options: [
284
+ API_URL,
285
+ {
286
+ flag: "--settings",
287
+ value: "<json|@file|->",
288
+ summary: "JSON expectedRevision and email/statusWebhook settings; null statusWebhook disables it. No signing secret is accepted.",
289
+ },
290
+ IDEMPOTENCY,
291
+ CONFIRM,
292
+ ],
293
+ },
294
+ {
295
+ group: "Planning",
296
+ words: "notifications signing-key",
297
+ summary: "Mint or rotate a failed-event webhook signing secret, shown once.",
298
+ confirms: true,
299
+ operations: ["projectnotifications.rotateSigningKey"],
300
+ positionals: [
301
+ {
302
+ name: "<project>",
303
+ summary: "The project identifier in this credential's authority.",
304
+ },
305
+ ],
306
+ options: [
307
+ API_URL,
308
+ {
309
+ flag: "--expected-revision",
310
+ value: "<revision>",
311
+ summary: "The current revision from notifications show; a stale retry is refused.",
312
+ },
313
+ CONFIRM,
314
+ ],
315
+ },
316
+ {
317
+ group: "Planning",
318
+ words: "notifications deliveries",
319
+ summary: "List failed-event webhook deliveries and retries.",
320
+ operations: ["statusdeliveries.list"],
321
+ positionals: [
322
+ {
323
+ name: "<project>",
324
+ summary: "The project identifier in this credential's authority.",
325
+ },
326
+ ],
327
+ options: [API_URL, ...PAGE_OPTIONS],
328
+ },
258
329
  /* --- Session ---------------------------------------------------------- */
259
330
  {
260
331
  group: "Session",
package/dist/operate.js CHANGED
@@ -1,5 +1,5 @@
1
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
+ 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";
3
3
  import { parseDuration } from "./args.js";
4
4
  import { confirmChange } from "./confirm.js";
5
5
  import { redactSecret } from "./output.js";
@@ -1621,11 +1621,104 @@ const sinksDelivery = async (runtime, args) => {
1621
1621
  ].join("\n"),
1622
1622
  };
1623
1623
  };
1624
+ function notificationPreferenceResult(settings) {
1625
+ const webhook = settings.statusWebhook;
1626
+ return {
1627
+ data: settings,
1628
+ human: [
1629
+ `Project ${settings.project}`,
1630
+ `Revision ${String(settings.revision)}`,
1631
+ `Email ${settings.email.enabled ? "enabled" : "disabled"}; ${settings.email.scheduleKinds}`,
1632
+ `Recipients ${settings.email.recipients.kind === "admins" ? "organization admins" : settings.email.recipients.memberIds.join(", ")}`,
1633
+ `Webhook ${webhook === undefined ? "disabled" : webhook.url}`,
1634
+ ...(webhook === undefined
1635
+ ? []
1636
+ : [
1637
+ `Events ${webhook.events.join(", ")}`,
1638
+ `Signing key ${webhook.signingKey?.id ?? "not provisioned"}`,
1639
+ ]),
1640
+ ].join("\n"),
1641
+ };
1642
+ }
1624
1643
  /* -------------------------------------------------------------------------- */
1625
1644
  /* Dispatch */
1626
1645
  /* -------------------------------------------------------------------------- */
1627
1646
  /** The operate-and-diagnose commands, keyed by the words that select them. */
1628
1647
  export const OPERATE_COMMANDS = {
1648
+ "notifications show": async (runtime, args) => {
1649
+ const [project] = positionals(args, ["<project>"], `${runtime.programName} notifications show <project>`);
1650
+ const active = await session(runtime, args);
1651
+ const settings = parseResponse("projectnotifications.read", await active.client.request("projectnotifications_read", project));
1652
+ return notificationPreferenceResult(settings);
1653
+ },
1654
+ "notifications update": async (runtime, args) => {
1655
+ const [project] = positionals(args, ["<project>"], `${runtime.programName} notifications update <project> --settings <json|@file|->`);
1656
+ const parsed = projectNotificationSettingsUpdateRequestSchema.safeParse(await readJsonOption(runtime, "--settings", args.options["--settings"]));
1657
+ if (!parsed.success)
1658
+ throw new CliError("--settings must contain expectedRevision and valid email/statusWebhook preferences", "usage");
1659
+ const confirmation = await confirmChange(runtime, args, {
1660
+ action: `Update delivery notifications for ${project}.`,
1661
+ effect: [
1662
+ "Changes failure email recipients and/or failed-event webhook destination.",
1663
+ "Failed-event hooks are included and free, including retries.",
1664
+ "The signing secret is never returned by this settings change.",
1665
+ ],
1666
+ });
1667
+ const active = await session(runtime, args);
1668
+ const key = idempotencyKey(runtime, args);
1669
+ runtime.stderr(`Retry key ${key}; retain it if the response is lost.\n`);
1670
+ const settings = parseResponse("projectnotifications.update", await active.client.request("projectnotifications_update", project, parsed.data, { "Idempotency-Key": key }));
1671
+ return {
1672
+ ...notificationPreferenceResult(settings),
1673
+ data: { settings, confirmation, idempotencyKey: key },
1674
+ };
1675
+ },
1676
+ "notifications signing-key": async (runtime, args) => {
1677
+ const [project] = positionals(args, ["<project>"], `${runtime.programName} notifications signing-key <project> --expected-revision <revision>`);
1678
+ const request = statusSigningKeyRequestSchema.safeParse({
1679
+ expectedRevision: expectedRevision(args),
1680
+ });
1681
+ if (!request.success)
1682
+ throw new CliError("--expected-revision must be a positive whole number", "usage");
1683
+ const confirmation = await confirmChange(runtime, args, {
1684
+ action: `Mint or rotate the status signing key for ${project}.`,
1685
+ effect: [
1686
+ "The previous key is replaced; queued notifications using that old key are suppressed.",
1687
+ "The secret is shown once in this response and cannot be read back. A lost response needs a fresh revision read and a new rotation.",
1688
+ ],
1689
+ });
1690
+ const active = await session(runtime, args);
1691
+ const result = parseResponse("projectnotifications.rotateSigningKey", await active.client.request("projectnotifications_rotateSigningKey", project, request.data));
1692
+ return {
1693
+ data: { ...result, confirmation },
1694
+ human: [
1695
+ `Signing key ${result.keyId}`,
1696
+ `Secret ${result.secret}`,
1697
+ `Revision ${String(result.settings.revision)}`,
1698
+ "Save this secret now. It will not be returned again.",
1699
+ ].join("\n"),
1700
+ };
1701
+ },
1702
+ "notifications deliveries": async (runtime, args) => {
1703
+ const [project] = positionals(args, ["<project>"], `${runtime.programName} notifications deliveries <project>`);
1704
+ const active = await session(runtime, args);
1705
+ return listing(runtime, args, {
1706
+ columns: ["Event", "State", "Calls", "Delivery"],
1707
+ command: `${runtime.programName} notifications deliveries ${project}`,
1708
+ empty: "No status notifications have been queued.",
1709
+ read: async (request) => {
1710
+ const result = parseResponse("statusdeliveries.list", await active.client.request("statusdeliveries_list", project, request));
1711
+ return {
1712
+ data: result.data,
1713
+ freshness: { kind: "authoritative" },
1714
+ ...(result.nextCursor === undefined
1715
+ ? {}
1716
+ : { nextCursor: result.nextCursor }),
1717
+ };
1718
+ },
1719
+ row: (row) => [row.event, row.state, String(row.attempts), row.id],
1720
+ });
1721
+ },
1629
1722
  "limits show": limitsShow,
1630
1723
  "attempts read": attemptsRead,
1631
1724
  billing,
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.7",
11
- "@greatwait/contracts": "0.1.0-alpha.7"
10
+ "@greatwait/api-client": "0.1.0-alpha.8",
11
+ "@greatwait/contracts": "0.1.0-alpha.8"
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.7"
51
+ "version": "0.1.0-alpha.8"
52
52
  }