@zapier/zapier-sdk-cli 0.55.6 → 0.55.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/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command, CommanderError, Option } from 'commander';
3
3
  import { z } from 'zod';
4
- import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, injectCliLogin, BaseSdkOptionsSchema, getOrCreateApiClient, isPermanentHttpError, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, createZapierSdkStack as createZapierSdkStack$1, addPlugin as addPlugin$1, ZapierError, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, isPositional, runWithTelemetryContext, buildCapabilityMessage, formatErrorMessage, getOsInfo, getPlatformVersions, getAgent, getTtyContext, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
4
+ import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, injectCliLogin, BaseSdkOptionsSchema, getOrCreateApiClient, isPermanentHttpError, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, createZapierSdkStack as createZapierSdkStack$1, addPlugin as addPlugin$1, ZapierError, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, isPositional, runWithTelemetryContext, buildCapabilityMessage, formatErrorMessage, getOsInfo, getPlatformVersions, getAgent, getTtyContext, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId, ZapierApprovalError } from '@zapier/zapier-sdk';
5
5
  import inquirer from 'inquirer';
6
6
  import search from '@inquirer/search';
7
7
  import chalk from 'chalk';
@@ -33,6 +33,8 @@ import { fileURLToPath } from 'url';
33
33
  import { injectCliLogin as injectCliLogin$1, createZapierSdkStack, addPlugin } from '@zapier/zapier-sdk/experimental';
34
34
  import packageJsonLib, { VersionNotFoundError } from 'package-json';
35
35
  import semver from 'semver';
36
+ import React from 'react';
37
+ import { jsx, jsxs } from 'react/jsx-runtime';
36
38
 
37
39
  var __defProp = Object.defineProperty;
38
40
  var __export = (target, all) => {
@@ -1531,7 +1533,7 @@ var SHARED_COMMAND_CLI_OPTIONS = [
1531
1533
 
1532
1534
  // package.json
1533
1535
  var package_default = {
1534
- version: "0.55.6"};
1536
+ version: "0.55.7"};
1535
1537
 
1536
1538
  // src/telemetry/builders.ts
1537
1539
  function createCliBaseEvent(context = {}) {
@@ -1587,6 +1589,11 @@ function buildCliCommandExecutedEvent({
1587
1589
  subprocess_count: data.subprocess_count ?? null
1588
1590
  };
1589
1591
  }
1592
+ function getApprovalReason(error) {
1593
+ if (!(error instanceof ZapierApprovalError)) return void 0;
1594
+ const { reason } = error;
1595
+ return typeof reason === "string" && reason.trim().length > 0 ? reason.trim() : void 0;
1596
+ }
1590
1597
  function formatJsonOutput(data) {
1591
1598
  if (data === void 0) {
1592
1599
  return;
@@ -1727,7 +1734,14 @@ function buildJsonErrors(error) {
1727
1734
  }
1728
1735
  const code = error instanceof ZapierError ? error.code : "UNKNOWN_ERROR";
1729
1736
  const message = error instanceof Error ? error.message : String(error);
1730
- return [{ code, message }];
1737
+ const reason = getApprovalReason(error);
1738
+ return [
1739
+ {
1740
+ code,
1741
+ message,
1742
+ ...reason ? { reason } : {}
1743
+ }
1744
+ ];
1731
1745
  }
1732
1746
  async function unwrapHttpResponse(response) {
1733
1747
  const text = await response.text().catch(() => "[unable to read body]");
@@ -2368,7 +2382,10 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
2368
2382
  const commandObj = args[args.length - 1];
2369
2383
  const options = commandObj.opts();
2370
2384
  const interactiveMode = !options.json;
2371
- const renderer = interactiveMode ? createInteractiveRenderer({ sdk, params: resolvedParams }) : createJsonRenderer();
2385
+ const renderer = interactiveMode ? createInteractiveRenderer({
2386
+ sdk,
2387
+ params: resolvedParams
2388
+ }) : createJsonRenderer();
2372
2389
  try {
2373
2390
  emitDeprecationWarning({
2374
2391
  cliCommandName,
@@ -7195,7 +7212,7 @@ var watchTriggerInboxCliPlugin = definePlugin(
7195
7212
  // package.json with { type: 'json' }
7196
7213
  var package_default2 = {
7197
7214
  name: "@zapier/zapier-sdk-cli",
7198
- version: "0.55.6"};
7215
+ version: "0.55.7"};
7199
7216
 
7200
7217
  // src/sdk.ts
7201
7218
  injectCliLogin(login_exports);
@@ -7439,6 +7456,232 @@ async function checkAndNotifyUpdates({
7439
7456
  const versionInfo = await checkForUpdates({ packageName, currentVersion });
7440
7457
  displayUpdateNotification(versionInfo, packageName);
7441
7458
  }
7459
+ var FALLBACK_PANEL_WIDTH = 72;
7460
+ var MIN_PANEL_WIDTH = 36;
7461
+ var MAX_PANEL_WIDTH = 88;
7462
+ var SPINNER_INTERVAL_MS = 80;
7463
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7464
+ var inkImportPromise;
7465
+ function loadInk() {
7466
+ inkImportPromise ?? (inkImportPromise = import('ink'));
7467
+ return inkImportPromise;
7468
+ }
7469
+ function createInitialState() {
7470
+ return {
7471
+ messages: [],
7472
+ active: false
7473
+ };
7474
+ }
7475
+ function getPanelWidth() {
7476
+ const terminalColumns = process.stderr.columns;
7477
+ if (!terminalColumns) return FALLBACK_PANEL_WIDTH;
7478
+ return Math.min(
7479
+ Math.max(terminalColumns - 4, MIN_PANEL_WIDTH),
7480
+ MAX_PANEL_WIDTH
7481
+ );
7482
+ }
7483
+ function getPayloadString(event, key) {
7484
+ const value = event.payload?.[key];
7485
+ if (typeof value !== "string") return void 0;
7486
+ const trimmed = value.trim();
7487
+ return trimmed.length > 0 ? trimmed : void 0;
7488
+ }
7489
+ function getApprovedVerdict(decision, reason) {
7490
+ if (decision !== "approved") return void 0;
7491
+ return {
7492
+ icon: "\u2713",
7493
+ label: "Approved",
7494
+ color: "green",
7495
+ reason
7496
+ };
7497
+ }
7498
+ function Spinner({ text }) {
7499
+ const [frameIndex, setFrameIndex] = React.useState(0);
7500
+ const InkText = text;
7501
+ React.useEffect(() => {
7502
+ const timer = setInterval(() => {
7503
+ setFrameIndex((index) => (index + 1) % SPINNER_FRAMES.length);
7504
+ }, SPINNER_INTERVAL_MS);
7505
+ return () => clearInterval(timer);
7506
+ }, []);
7507
+ return /* @__PURE__ */ jsx(InkText, { color: "yellow", children: `${SPINNER_FRAMES[frameIndex]} Checking approval...` });
7508
+ }
7509
+ function ApprovalProgressView({
7510
+ state,
7511
+ box,
7512
+ text
7513
+ }) {
7514
+ if (!state.active) return null;
7515
+ const InkBox = box;
7516
+ const InkText = text;
7517
+ const recentMessages = state.messages.slice(-5);
7518
+ const hasMessages = recentMessages.length > 0;
7519
+ return /* @__PURE__ */ jsxs(
7520
+ InkBox,
7521
+ {
7522
+ borderColor: "cyan",
7523
+ borderStyle: "round",
7524
+ flexDirection: "column",
7525
+ paddingX: 1,
7526
+ width: getPanelWidth(),
7527
+ children: [
7528
+ /* @__PURE__ */ jsx(InkText, { bold: true, color: "cyan", children: "Approval review" }),
7529
+ /* @__PURE__ */ jsxs(InkBox, { flexDirection: "column", marginTop: 1, children: [
7530
+ !state.verdict && /* @__PURE__ */ jsx(Spinner, { text }),
7531
+ recentMessages.map((message, index) => /* @__PURE__ */ jsxs(InkBox, { flexDirection: "row", children: [
7532
+ /* @__PURE__ */ jsx(InkText, { color: "cyan", children: "> " }),
7533
+ /* @__PURE__ */ jsx(
7534
+ InkText,
7535
+ {
7536
+ dimColor: Boolean(state.verdict) || index < recentMessages.length - 1,
7537
+ wrap: "wrap",
7538
+ children: message
7539
+ }
7540
+ )
7541
+ ] }, `${index}-${message}`)),
7542
+ state.streamError && /* @__PURE__ */ jsxs(
7543
+ InkBox,
7544
+ {
7545
+ flexDirection: "column",
7546
+ marginTop: hasMessages || !state.verdict ? 1 : 0,
7547
+ children: [
7548
+ /* @__PURE__ */ jsx(InkText, { bold: true, color: "red", children: "Approval stream error" }),
7549
+ /* @__PURE__ */ jsx(InkText, { wrap: "wrap", children: ` ${state.streamError}` })
7550
+ ]
7551
+ }
7552
+ ),
7553
+ state.verdict && /* @__PURE__ */ jsxs(
7554
+ InkBox,
7555
+ {
7556
+ flexDirection: "column",
7557
+ marginTop: hasMessages || state.streamError ? 1 : 0,
7558
+ children: [
7559
+ /* @__PURE__ */ jsx(InkText, { bold: true, color: state.verdict.color, children: `${state.verdict.icon} ${state.verdict.label}` }),
7560
+ state.verdict.reason && /* @__PURE__ */ jsx(InkText, { wrap: "wrap", children: ` ${state.verdict.reason}` })
7561
+ ]
7562
+ }
7563
+ )
7564
+ ] })
7565
+ ]
7566
+ }
7567
+ );
7568
+ }
7569
+ function createApprovalProgressRenderer({
7570
+ enabled,
7571
+ onEvent
7572
+ }) {
7573
+ let instance;
7574
+ let currentState = createInitialState();
7575
+ let renderQueue = Promise.resolve();
7576
+ let renderGeneration = 0;
7577
+ let renderEnabled = enabled;
7578
+ let renderErrorReported = false;
7579
+ function update(nextState, { complete = false } = {}) {
7580
+ const generation = renderGeneration;
7581
+ currentState = complete ? createInitialState() : nextState;
7582
+ renderQueue = renderQueue.then(async () => {
7583
+ if (generation !== renderGeneration) return;
7584
+ const { Box, Text, render } = await loadInk();
7585
+ if (generation !== renderGeneration) return;
7586
+ const element = /* @__PURE__ */ jsx(ApprovalProgressView, { state: nextState, box: Box, text: Text });
7587
+ if (!instance) {
7588
+ instance = render(element, {
7589
+ stdout: process.stderr,
7590
+ stderr: process.stderr
7591
+ });
7592
+ } else {
7593
+ instance.rerender(element);
7594
+ }
7595
+ if (complete) {
7596
+ instance.unmount();
7597
+ instance = void 0;
7598
+ }
7599
+ }).catch((error) => {
7600
+ if (generation !== renderGeneration) return;
7601
+ renderEnabled = false;
7602
+ currentState = createInitialState();
7603
+ if (renderErrorReported) return;
7604
+ renderErrorReported = true;
7605
+ const message = error instanceof Error ? error.message : String(error);
7606
+ process.stderr.write(`Approval progress unavailable: ${message}
7607
+ `);
7608
+ });
7609
+ }
7610
+ function dismiss() {
7611
+ renderGeneration++;
7612
+ currentState = createInitialState();
7613
+ instance?.unmount();
7614
+ instance = void 0;
7615
+ }
7616
+ return {
7617
+ onEvent(event) {
7618
+ onEvent?.(event);
7619
+ if (!renderEnabled) return;
7620
+ if (event.type === "approval:required") {
7621
+ if (event.payload?.mode !== "auto") return;
7622
+ update({
7623
+ approvalId: getPayloadString(event, "approvalId"),
7624
+ messages: [],
7625
+ active: true
7626
+ });
7627
+ return;
7628
+ }
7629
+ if (event.type === "approval:review_message") {
7630
+ if (!currentState.active) return;
7631
+ const message = getPayloadString(event, "message");
7632
+ if (!message) return;
7633
+ update({
7634
+ ...currentState,
7635
+ messages: [...currentState.messages, message]
7636
+ });
7637
+ return;
7638
+ }
7639
+ if (event.type === "approval:review_decision") {
7640
+ if (!currentState.active) return;
7641
+ const verdict = getApprovedVerdict(
7642
+ event.payload?.decision,
7643
+ getPayloadString(event, "reason")
7644
+ );
7645
+ if (!verdict) return;
7646
+ update(
7647
+ {
7648
+ ...currentState,
7649
+ verdict
7650
+ },
7651
+ { complete: true }
7652
+ );
7653
+ return;
7654
+ }
7655
+ if (event.type === "approval:review_stream_error") {
7656
+ if (!currentState.active) return;
7657
+ update({
7658
+ ...currentState,
7659
+ streamError: getPayloadString(event, "message") ?? "Approval review stream failed"
7660
+ });
7661
+ return;
7662
+ }
7663
+ if (!currentState.active) return;
7664
+ if (event.type === "approval:approved") {
7665
+ update(
7666
+ {
7667
+ ...currentState,
7668
+ verdict: getApprovedVerdict(
7669
+ "approved",
7670
+ getPayloadString(event, "reason")
7671
+ )
7672
+ },
7673
+ { complete: true }
7674
+ );
7675
+ return;
7676
+ }
7677
+ if (event.type === "approval:denied" || event.type === "approval:failed" || event.type === "approval:timeout" || event.type === "approval:error") {
7678
+ dismiss();
7679
+ }
7680
+ }
7681
+ };
7682
+ }
7683
+
7684
+ // src/cli.ts
7442
7685
  var EXIT_GRACE_PERIOD_MS = 500;
7443
7686
  var program = new Command();
7444
7687
  var versionOption = getReservedCliOption("version" /* Version */);
@@ -7474,7 +7717,7 @@ for (const [key, fieldSchema] of Object.entries(
7474
7717
  }
7475
7718
  if (inner instanceof z.ZodBoolean && key !== "debug") {
7476
7719
  const kebab = key.replace(/([A-Z])/g, "-$1").toLowerCase();
7477
- const description = fieldSchema._zod?.def?.description ?? "";
7720
+ const description = fieldSchema.description ?? inner.description ?? "";
7478
7721
  booleanFlags.push({ camelName: key, kebabFlag: `--${kebab}` });
7479
7722
  program.option(`--${kebab}`, description);
7480
7723
  }
@@ -7544,6 +7787,9 @@ for (const { camelName, kebabFlag } of booleanFlags) {
7544
7787
  }
7545
7788
  var useExperimental = process.argv.includes("--experimental") || process.env.ZAPIER_EXPERIMENTAL === "1" || process.env.ZAPIER_EXPERIMENTAL === "true";
7546
7789
  var createZapierCliSdk3 = useExperimental ? createZapierCliSdk2 : createZapierCliSdk;
7790
+ var approvalProgress = createApprovalProgressRenderer({
7791
+ enabled: process.stderr.isTTY === true
7792
+ });
7547
7793
  program.exitOverride();
7548
7794
  (async () => {
7549
7795
  let exitCode = 0;
@@ -7556,6 +7802,7 @@ program.exitOverride();
7556
7802
  maxNetworkRetries,
7557
7803
  maxNetworkRetryDelayMs,
7558
7804
  maxConcurrentRequests,
7805
+ onEvent: approvalProgress.onEvent,
7559
7806
  ...flagOverrides,
7560
7807
  extensions
7561
7808
  });
@@ -4590,7 +4590,7 @@ var watchTriggerInboxCliPlugin = zapierSdk.definePlugin(
4590
4590
  // package.json with { type: 'json' }
4591
4591
  var package_default = {
4592
4592
  name: "@zapier/zapier-sdk-cli",
4593
- version: "0.55.6"};
4593
+ version: "0.55.7"};
4594
4594
 
4595
4595
  // src/experimental.ts
4596
4596
  experimental.injectCliLogin(login_exports);
@@ -4554,7 +4554,7 @@ var watchTriggerInboxCliPlugin = definePlugin(
4554
4554
  // package.json with { type: 'json' }
4555
4555
  var package_default = {
4556
4556
  name: "@zapier/zapier-sdk-cli",
4557
- version: "0.55.6"};
4557
+ version: "0.55.7"};
4558
4558
 
4559
4559
  // src/experimental.ts
4560
4560
  injectCliLogin(login_exports);
package/dist/index.cjs CHANGED
@@ -4589,7 +4589,7 @@ zapierSdk.definePlugin(
4589
4589
  // package.json with { type: 'json' }
4590
4590
  var package_default = {
4591
4591
  name: "@zapier/zapier-sdk-cli",
4592
- version: "0.55.6"};
4592
+ version: "0.55.7"};
4593
4593
 
4594
4594
  // src/sdk.ts
4595
4595
  zapierSdk.injectCliLogin(login_exports);
@@ -4617,7 +4617,7 @@ function createZapierCliSdk(options = {}) {
4617
4617
 
4618
4618
  // package.json
4619
4619
  var package_default2 = {
4620
- version: "0.55.6"};
4620
+ version: "0.55.7"};
4621
4621
 
4622
4622
  // src/telemetry/builders.ts
4623
4623
  function createCliBaseEvent(context = {}) {
package/dist/index.mjs CHANGED
@@ -4553,7 +4553,7 @@ definePlugin(
4553
4553
  // package.json with { type: 'json' }
4554
4554
  var package_default = {
4555
4555
  name: "@zapier/zapier-sdk-cli",
4556
- version: "0.55.6"};
4556
+ version: "0.55.7"};
4557
4557
 
4558
4558
  // src/sdk.ts
4559
4559
  injectCliLogin(login_exports);
@@ -4581,7 +4581,7 @@ function createZapierCliSdk(options = {}) {
4581
4581
 
4582
4582
  // package.json
4583
4583
  var package_default2 = {
4584
- version: "0.55.6"};
4584
+ version: "0.55.7"};
4585
4585
 
4586
4586
  // src/telemetry/builders.ts
4587
4587
  function createCliBaseEvent(context = {}) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.55.6",
3
+ "version": "0.55.7",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -80,8 +80,8 @@
80
80
  "access": "public"
81
81
  },
82
82
  "dependencies": {
83
- "@zapier/policy-schema": "0.11.0",
84
83
  "@inquirer/search": "^3.2.2",
84
+ "@zapier/policy-schema": "0.11.0",
85
85
  "@zapier/zapier-sdk": "workspace:*",
86
86
  "@zapier/zapier-sdk-mcp": "workspace:*",
87
87
  "chalk": "^5.3.0",
@@ -92,6 +92,7 @@
92
92
  "esbuild": "^0.25.5",
93
93
  "express": "^5.1.0",
94
94
  "handlebars": "^4.7.8",
95
+ "ink": "7.0.5",
95
96
  "inquirer": "^12.6.3",
96
97
  "is-installed-globally": "^1.0.0",
97
98
  "jsonwebtoken": "^9.0.2",
@@ -100,6 +101,7 @@
100
101
  "package-json": "^10.0.1",
101
102
  "pkce-challenge": "^5.0.0",
102
103
  "proper-lockfile": "^4.1.2",
104
+ "react": "19.2.6",
103
105
  "semver": "^7.7.3",
104
106
  "typescript": "^5.8.3",
105
107
  "wrap-ansi": "^10.0.0",
@@ -111,6 +113,7 @@
111
113
  "@types/jsonwebtoken": "^9.0.10",
112
114
  "@types/node": "^24.0.1",
113
115
  "@types/proper-lockfile": "^4.1.4",
116
+ "@types/react": "19.2.15",
114
117
  "@types/semver": "^7.7.1",
115
118
  "tsup": "^8.5.0",
116
119
  "vitest": "^4.1.4"
package/dist/src/cli.js CHANGED
@@ -10,6 +10,7 @@ import { checkAndNotifyUpdates } from "./utils/version-checker";
10
10
  import { BaseSdkOptionsSchema } from "@zapier/zapier-sdk";
11
11
  import { z } from "zod";
12
12
  import { ReservedCliParameter, getReservedCliOption, } from "./utils/cli-options";
13
+ import { createApprovalProgressRenderer } from "./utils/approval-progress";
13
14
  const EXIT_GRACE_PERIOD_MS = 500;
14
15
  const program = new Command();
15
16
  const versionOption = getReservedCliOption(ReservedCliParameter.Version);
@@ -44,7 +45,7 @@ BaseSdkOptionsSchema.shape)) {
44
45
  }
45
46
  if (inner instanceof z.ZodBoolean && key !== "debug") {
46
47
  const kebab = key.replace(/([A-Z])/g, "-$1").toLowerCase();
47
- const description = fieldSchema._zod?.def?.description ?? "";
48
+ const description = fieldSchema.description ?? inner.description ?? "";
48
49
  booleanFlags.push({ camelName: key, kebabFlag: `--${kebab}` });
49
50
  program.option(`--${kebab}`, description);
50
51
  }
@@ -155,6 +156,9 @@ const useExperimental = process.argv.includes("--experimental") ||
155
156
  const createZapierCliSdk = useExperimental
156
157
  ? createExperimentalCliSdk
157
158
  : createStableCliSdk;
159
+ const approvalProgress = createApprovalProgressRenderer({
160
+ enabled: process.stderr.isTTY === true,
161
+ });
158
162
  // Override Commander's default exit behavior to handle our custom errors
159
163
  program.exitOverride();
160
164
  (async () => {
@@ -172,6 +176,7 @@ program.exitOverride();
172
176
  maxNetworkRetries,
173
177
  maxNetworkRetryDelayMs,
174
178
  maxConcurrentRequests,
179
+ onEvent: approvalProgress.onEvent,
175
180
  ...flagOverrides,
176
181
  extensions,
177
182
  });
@@ -0,0 +1 @@
1
+ export declare function getApprovalReason(error: unknown): string | undefined;
@@ -0,0 +1,9 @@
1
+ import { ZapierApprovalError } from "@zapier/zapier-sdk";
2
+ export function getApprovalReason(error) {
3
+ if (!(error instanceof ZapierApprovalError))
4
+ return undefined;
5
+ const { reason } = error;
6
+ return typeof reason === "string" && reason.trim().length > 0
7
+ ? reason.trim()
8
+ : undefined;
9
+ }
@@ -0,0 +1,12 @@
1
+ export interface ApprovalProgressEvent {
2
+ type: string;
3
+ payload?: Record<string, unknown>;
4
+ timestamp?: number;
5
+ }
6
+ export interface ApprovalProgressRenderer {
7
+ onEvent(event: ApprovalProgressEvent): void;
8
+ }
9
+ export declare function createApprovalProgressRenderer({ enabled, onEvent, }: {
10
+ enabled: boolean;
11
+ onEvent?: (event: ApprovalProgressEvent) => void;
12
+ }): ApprovalProgressRenderer;
@@ -0,0 +1,179 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ const FALLBACK_PANEL_WIDTH = 72;
4
+ const MIN_PANEL_WIDTH = 36;
5
+ const MAX_PANEL_WIDTH = 88;
6
+ const MAX_VISIBLE_MESSAGES = 5;
7
+ const SPINNER_INTERVAL_MS = 80;
8
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
9
+ let inkImportPromise;
10
+ function loadInk() {
11
+ inkImportPromise ?? (inkImportPromise = import("ink"));
12
+ return inkImportPromise;
13
+ }
14
+ function createInitialState() {
15
+ return {
16
+ messages: [],
17
+ active: false,
18
+ };
19
+ }
20
+ function getPanelWidth() {
21
+ const terminalColumns = process.stderr.columns;
22
+ if (!terminalColumns)
23
+ return FALLBACK_PANEL_WIDTH;
24
+ return Math.min(Math.max(terminalColumns - 4, MIN_PANEL_WIDTH), MAX_PANEL_WIDTH);
25
+ }
26
+ function getPayloadString(event, key) {
27
+ const value = event.payload?.[key];
28
+ if (typeof value !== "string")
29
+ return undefined;
30
+ const trimmed = value.trim();
31
+ return trimmed.length > 0 ? trimmed : undefined;
32
+ }
33
+ function getApprovedVerdict(decision, reason) {
34
+ if (decision !== "approved")
35
+ return undefined;
36
+ return {
37
+ icon: "✓",
38
+ label: "Approved",
39
+ color: "green",
40
+ reason,
41
+ };
42
+ }
43
+ function Spinner({ text }) {
44
+ const [frameIndex, setFrameIndex] = React.useState(0);
45
+ const InkText = text;
46
+ React.useEffect(() => {
47
+ const timer = setInterval(() => {
48
+ setFrameIndex((index) => (index + 1) % SPINNER_FRAMES.length);
49
+ }, SPINNER_INTERVAL_MS);
50
+ return () => clearInterval(timer);
51
+ }, []);
52
+ return (_jsx(InkText, { color: "yellow", children: `${SPINNER_FRAMES[frameIndex]} Checking approval...` }));
53
+ }
54
+ function ApprovalProgressView({ state, box, text, }) {
55
+ if (!state.active)
56
+ return null;
57
+ const InkBox = box;
58
+ const InkText = text;
59
+ const recentMessages = state.messages.slice(-MAX_VISIBLE_MESSAGES);
60
+ const hasMessages = recentMessages.length > 0;
61
+ return (_jsxs(InkBox, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", paddingX: 1, width: getPanelWidth(), children: [_jsx(InkText, { bold: true, color: "cyan", children: "Approval review" }), _jsxs(InkBox, { flexDirection: "column", marginTop: 1, children: [!state.verdict && _jsx(Spinner, { text: text }), recentMessages.map((message, index) => (_jsxs(InkBox, { flexDirection: "row", children: [_jsx(InkText, { color: "cyan", children: "> " }), _jsx(InkText, { dimColor: Boolean(state.verdict) || index < recentMessages.length - 1, wrap: "wrap", children: message })] }, `${index}-${message}`))), state.streamError && (_jsxs(InkBox, { flexDirection: "column", marginTop: hasMessages || !state.verdict ? 1 : 0, children: [_jsx(InkText, { bold: true, color: "red", children: "Approval stream error" }), _jsx(InkText, { wrap: "wrap", children: ` ${state.streamError}` })] })), state.verdict && (_jsxs(InkBox, { flexDirection: "column", marginTop: hasMessages || state.streamError ? 1 : 0, children: [_jsx(InkText, { bold: true, color: state.verdict.color, children: `${state.verdict.icon} ${state.verdict.label}` }), state.verdict.reason && (_jsx(InkText, { wrap: "wrap", children: ` ${state.verdict.reason}` }))] }))] })] }));
62
+ }
63
+ export function createApprovalProgressRenderer({ enabled, onEvent, }) {
64
+ let instance;
65
+ let currentState = createInitialState();
66
+ let renderQueue = Promise.resolve();
67
+ let renderGeneration = 0;
68
+ let renderEnabled = enabled;
69
+ let renderErrorReported = false;
70
+ function update(nextState, { complete = false } = {}) {
71
+ const generation = renderGeneration;
72
+ currentState = complete ? createInitialState() : nextState;
73
+ renderQueue = renderQueue
74
+ .then(async () => {
75
+ if (generation !== renderGeneration)
76
+ return;
77
+ const { Box, Text, render } = await loadInk();
78
+ if (generation !== renderGeneration)
79
+ return;
80
+ const element = (_jsx(ApprovalProgressView, { state: nextState, box: Box, text: Text }));
81
+ if (!instance) {
82
+ instance = render(element, {
83
+ stdout: process.stderr,
84
+ stderr: process.stderr,
85
+ });
86
+ }
87
+ else {
88
+ instance.rerender(element);
89
+ }
90
+ if (complete) {
91
+ instance.unmount();
92
+ instance = undefined;
93
+ }
94
+ })
95
+ .catch((error) => {
96
+ if (generation !== renderGeneration)
97
+ return;
98
+ renderEnabled = false;
99
+ currentState = createInitialState();
100
+ if (renderErrorReported)
101
+ return;
102
+ renderErrorReported = true;
103
+ const message = error instanceof Error ? error.message : String(error);
104
+ process.stderr.write(`Approval progress unavailable: ${message}\n`);
105
+ });
106
+ }
107
+ function dismiss() {
108
+ renderGeneration++;
109
+ currentState = createInitialState();
110
+ instance?.unmount();
111
+ instance = undefined;
112
+ }
113
+ return {
114
+ onEvent(event) {
115
+ onEvent?.(event);
116
+ if (!renderEnabled)
117
+ return;
118
+ if (event.type === "approval:required") {
119
+ if (event.payload?.mode !== "auto")
120
+ return;
121
+ update({
122
+ approvalId: getPayloadString(event, "approvalId"),
123
+ messages: [],
124
+ active: true,
125
+ });
126
+ return;
127
+ }
128
+ if (event.type === "approval:review_message") {
129
+ if (!currentState.active)
130
+ return;
131
+ const message = getPayloadString(event, "message");
132
+ if (!message)
133
+ return;
134
+ update({
135
+ ...currentState,
136
+ messages: [...currentState.messages, message],
137
+ });
138
+ return;
139
+ }
140
+ if (event.type === "approval:review_decision") {
141
+ if (!currentState.active)
142
+ return;
143
+ const verdict = getApprovedVerdict(event.payload?.decision, getPayloadString(event, "reason"));
144
+ if (!verdict)
145
+ return;
146
+ update({
147
+ ...currentState,
148
+ verdict,
149
+ }, { complete: true });
150
+ return;
151
+ }
152
+ if (event.type === "approval:review_stream_error") {
153
+ if (!currentState.active)
154
+ return;
155
+ update({
156
+ ...currentState,
157
+ streamError: getPayloadString(event, "message") ??
158
+ "Approval review stream failed",
159
+ });
160
+ return;
161
+ }
162
+ if (!currentState.active)
163
+ return;
164
+ if (event.type === "approval:approved") {
165
+ update({
166
+ ...currentState,
167
+ verdict: getApprovedVerdict("approved", getPayloadString(event, "reason")),
168
+ }, { complete: true });
169
+ return;
170
+ }
171
+ if (event.type === "approval:denied" ||
172
+ event.type === "approval:failed" ||
173
+ event.type === "approval:timeout" ||
174
+ event.type === "approval:error") {
175
+ dismiss();
176
+ }
177
+ },
178
+ };
179
+ }
@@ -485,7 +485,10 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
485
485
  // interactiveMode is true by default; --json disables it
486
486
  const interactiveMode = !options.json;
487
487
  const renderer = interactiveMode
488
- ? createInteractiveRenderer({ sdk, params: resolvedParams })
488
+ ? createInteractiveRenderer({
489
+ sdk,
490
+ params: resolvedParams,
491
+ })
489
492
  : createJsonRenderer();
490
493
  try {
491
494
  emitDeprecationWarning({