v2.2.0
Minor Changes
-
f3eefcc: UI apps are GA (BEX-290).
brevo app createoffers the UI app type in every build (interactive-only — no--typeflag), andbrevo app install [account-id]/brevo app uninstall [account-id]now ship in the published CLI. Public app distribution (--distribution public,app submit/app status/app withdraw) remains pre-GA and excluded from published builds.Per-entry card
size(BEX-416). Eachui_app.surface_point_listentry accepts an optionalsizewithwidth/heightas CSS lengths —"<positive integer>px"or"<1-100>%"— overriding the host page's default card size for that placement. Both axes are optional; an omitted axis keeps the host default.CTA fields moved into each entry (BEX-426).
label,more_info,redirect_linkandmodal_iframe_urlnow live on eachsurface_point_listentry instead of theui_approot, so every placement can carry its own label and destination. The root spellings are refused with a migration hint, and the per-type rules run per entry (actionLinkrequiresredirect_linkand refusesmodal_iframe_url;iframeExtensionthe reverse).brevo app createauthors exactly one placement — add more by editingapp-config.jsonand runningbrevo app upload.Install/uninstall improvements. Both commands refuse an app that is not a UI app (previously an OAuth app "installed" with nothing to render), name the resolved target account —
installed into Acme Retail (account 4043630)— with an additiveaccountNamein--json, anduninstall's corporate sub-account picker asks "uninstall from" instead of "install into". Their interactive app picker now offers only UI apps — an OAuth app in the list was a choice whose only outcome was that refusal one step later — and errors with a pointer atbrevo app createwhen there is no UI app to offer.brevo app uploaddiffs theui_appblock, and warns before it changes an installed app. The summary used to print the block's desired state with a bare(changed)marker; it now diffs it against the server placement by placement — every changed value asbefore → after, added placements tagged(new), dropped ones trailing(removed), matched by slot slug so a reorderedsurface_point_listis not a change. For a UI app the command then warns that the app may already be installed in Brevo accounts (the configuration is shared by every install, and an upload is live there immediately, with no re-install) and asks "Proceed with upload and update every account this app is installed in?".--yesskips the question but still prints the warning;--jsonprints neither and stays a single parseable document.brevo app installshows what it is about to install. Before the confirmation it prints the app's configuration as stored on the server — app ID, name,version,extension_typeand every placement — because that snapshot, not the localapp-config.json, is what the account will render.--jsongains additiveversionandui_appkeys carrying the same facts. When the app came from a linked project whoseui_appblock has drifted from the stored one, the command says so and points atbrevo app upload, then installs anyway (exit0) — installing an already-uploaded configuration is legitimate, so this is a notice rather than a refusal.brevo app credentialsrefuses a UI app. A UI app has no OAuth credentials, so the command now exits1with a typed message pointing atbrevo app list, instead of printing an empty credential form (blank client ID, "(none)" scopes and URLs) and caching the emptiness locally.brevo app deletewarns before the confirm that deletion also removes the app from every account where it is installed or published, and that installs and credentials cannot be recovered.--forcestill deletes without prompting, but now prints the same consequence line so a scripted delete leaves a record; it is kept out of--jsonoutput, which stays parseable JSON only.A
502 Bad Gatewayis now retried only for idempotent methods. The automatic retry replays GET, PUT and DELETE; POST and PATCH surface the 502 as an error instead. A 502 comes from a gateway, so the origin may already have processed the request — blindly replaying a create could duplicate the resource (observed:brevo app createproducing two identical apps). If it happens onapp create, checkbrevo app listbefore retrying, since the app may already exist.The
app install/app uninstallapp picker is refused when it cannot be drawn. Omitting--app-idoutside a linked project under--json(or off a TTY) now exits1naming the--app-idform, instead of rendering the picker's choice list into what a script is parsing and then aborting on a raw readline error. Matches the guardapp delete,app credentialsandapp withdrawalready carry.A scaffolded UI app's project docs now describe a UI app.
CLAUDE.md,AGENTS.mdandREADME.mdbranch on the app type, so a UI-app project documents theui_appblock, the per-entry placement fields and thebrevo app upload→brevo app installflow. Previously all three were written from the OAuth templates regardless of type, which told the partner toyarn --cwd src/oauth installandbrevo app start oauthagainst asrc/oauth/directory a UI app never receives. OAuth projects get byte-identical files to before.Smaller changes:
app create's placement prompt is labelled from the registry's own names; the UI integration-type prompt offers Link only until iframe authoring is ready (a hand-authorediframeExtensionblock still uploads);--helpexamples for--app-idshow a UUID instead of42; asurface_point_listentry missing itssurface_point_namekey is reported as a missing key — with a rename hint when the pre-renamesurface_pointspelling is present — instead of as a blank slot name.