@frontera-sdk/cli 1.52.31 → 1.52.32

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.
Files changed (2) hide show
  1. package/package.json +4 -4
  2. package/src/exit.ts +33 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontera-sdk/cli",
3
- "version": "1.52.31",
3
+ "version": "1.52.32",
4
4
  "description": "The frontera CLI — scaffold, pull, save and deploy Frontera apps and automations.",
5
5
  "keywords": [
6
6
  "frontera",
@@ -40,14 +40,14 @@
40
40
  "dependencies": {
41
41
  "@anthropic-ai/claude-agent-sdk": "^0.3.251",
42
42
  "@faker-js/faker": "10.6.0",
43
- "@frontera-sdk/core": "1.52.31",
44
- "@frontera-sdk/functions": "1.52.31",
43
+ "@frontera-sdk/core": "1.52.32",
44
+ "@frontera-sdk/functions": "1.52.32",
45
45
  "ai": "^6.0.116",
46
46
  "gray-matter": "^4.0.3",
47
47
  "yaml": "^2.9.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@frontera-sdk/forge-contracts": "1.52.31",
50
+ "@frontera-sdk/forge-contracts": "1.52.32",
51
51
  "@types/bun": "^1.3.14",
52
52
  "typescript": "^5.9.3"
53
53
  }
package/src/exit.ts CHANGED
@@ -174,9 +174,37 @@ function refusalDetail(details: unknown): string | undefined {
174
174
  return undefined
175
175
  }
176
176
 
177
+ /**
178
+ * Hints for one blocker of a `RELEASE_BLOCKED` publish. That code covers every
179
+ * reason a release cannot go out, so only a blocker this CLI knows earns advice.
180
+ */
181
+ const BLOCKER_HINTS: Record<string, string> = {
182
+ // A repeat of a publish that already landed ends here too, and retrying it
183
+ // changes nothing: the release is already live.
184
+ NO_UNPUBLISHED_CHANGES: 'nothing to publish — run `frontera blueprint status` to confirm the active release',
185
+ }
186
+
187
+ /**
188
+ * The blocker codes of a `RELEASE_BLOCKED` body. An `AppError`'s details sit
189
+ * under `details` in the response body; a bare body is read as well.
190
+ */
191
+ function releaseBlockerCodes(details: unknown): string[] {
192
+ if (typeof details !== 'object' || details === null) return []
193
+ const body = details as { blockers?: unknown; details?: unknown }
194
+ const nested = typeof body.details === 'object' && body.details !== null
195
+ ? (body.details as { blockers?: unknown }).blockers
196
+ : undefined
197
+ const blockers = Array.isArray(body.blockers) ? body.blockers : nested
198
+ if (!Array.isArray(blockers)) return []
199
+ return blockers
200
+ .map((blocker) => (typeof blocker === 'object' && blocker !== null ? (blocker as { code?: unknown }).code : undefined))
201
+ .filter((code): code is string => typeof code === 'string')
202
+ }
203
+
177
204
  /**
178
205
  * The best hint available for a service error: the refusal-specific one when
179
- * the body names a refusal this CLI knows, the per-code one otherwise.
206
+ * the body names a refusal or a release blocker this CLI knows, the per-code
207
+ * one otherwise.
180
208
  *
181
209
  * An unrecognised `detail` falls through to the code's hint rather than to
182
210
  * nothing, on the same reasoning `exitCodeFor` records: the service can add a
@@ -188,6 +216,10 @@ function hintFor(code: string, details: unknown): string | undefined {
188
216
  const specific = REFUSAL_HINTS[detail]
189
217
  if (specific) return specific
190
218
  }
219
+ if (code === 'RELEASE_BLOCKED') {
220
+ const blockerHint = releaseBlockerCodes(details).map((blocker) => BLOCKER_HINTS[blocker]).find(Boolean)
221
+ if (blockerHint) return blockerHint
222
+ }
191
223
  return SERVICE_HINTS[code]
192
224
  }
193
225