premanmcp 1.0.3 → 1.0.4

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/bin/shared.js +42 -6
  2. package/package.json +2 -2
package/bin/shared.js CHANGED
@@ -482,16 +482,52 @@ export async function callBackendJson(
482
482
  * instead" reached a customer as `410 [object Object]` -- a remedy the response
483
483
  * carried and the screen never showed.
484
484
  */
485
+ /**
486
+ * A readable reason for a backend failure, always as a string.
487
+ *
488
+ * FastAPI answers a rejected request with an object and a validation error with
489
+ * an array of them, and every caller interpolates what comes back into a line
490
+ * somebody reads. The nested cases used to be handed back as they arrived --
491
+ * `detail.message` being itself an object was returned as an object -- so the
492
+ * line printed "[object Object]", which is the exact failure this function
493
+ * exists to prevent, reintroduced one level further down.
494
+ */
485
495
  export function describeFailure(result, fallback = "backend error") {
486
- const detail = result?.detail ?? result?.message ?? result?.raw;
487
- if (typeof detail === "string" && detail) return detail;
488
- if (Array.isArray(detail) && detail.length) {
489
- return detail.map((item) => item?.msg || JSON.stringify(item)).join("; ");
496
+ return readDetail(result?.detail ?? result?.message ?? result?.raw) || fallback;
497
+ }
498
+
499
+ /** The first thing in `detail` a person could read, however deeply it is wrapped. */
500
+ function readDetail(detail, depth = 0) {
501
+ if (typeof detail === "string") return detail.trim();
502
+ if (typeof detail === "number" || typeof detail === "boolean") return String(detail);
503
+ if (Array.isArray(detail)) {
504
+ return detail
505
+ .map((item) => readDetail(item, depth + 1))
506
+ .filter(Boolean)
507
+ .join("; ");
490
508
  }
491
509
  if (detail && typeof detail === "object") {
492
- return detail.message || detail.error || detail.code || JSON.stringify(detail);
510
+ // Bounded, because these arrive from somewhere else and a structure that
511
+ // refers to itself must not take the CLI down on its way to a skip message.
512
+ if (depth < 3) {
513
+ for (const key of ["msg", "message", "detail", "error", "description", "code"]) {
514
+ const nested = readDetail(detail[key], depth + 1);
515
+ if (nested) return nested;
516
+ }
517
+ }
518
+ try {
519
+ const json = JSON.stringify(detail);
520
+ // An empty object describes nothing. Printing "{}" as the reason a push
521
+ // was skipped is the same unhelpfulness as "[object Object]" in nicer
522
+ // punctuation; the caller's fallback at least names the kind of failure.
523
+ return json === "{}" || json === "[]" ? "" : json;
524
+ } catch {
525
+ // Circular, or otherwise not describable. The caller's fallback is better
526
+ // than throwing from the thing that was explaining a failure.
527
+ return "";
528
+ }
493
529
  }
494
- return fallback;
530
+ return "";
495
531
  }
496
532
 
497
533
  export function assertOk(result, action) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "premanmcp",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "PreMan CLI and stdio proxy for PreMan's hosted MCP server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,7 @@
15
15
  "test": "npm run build && npm run test:proxy",
16
16
  "test:proxy": "node --test scripts/smoke-proxy.mjs",
17
17
  "test:connect": "node scripts/smoke-connect.mjs",
18
- "test:node": "node --test --test-timeout=90000 scripts/smoke-account.mjs scripts/smoke-cli-entrypoint.mjs scripts/smoke-launcher-config.mjs scripts/smoke-runner.mjs scripts/smoke-repo-config.mjs scripts/smoke-onboard.mjs scripts/smoke-onboard-opening.mjs scripts/smoke-local-detect.mjs scripts/smoke-prepush-hook.mjs scripts/smoke-cli-identity.mjs scripts/smoke-runner-heartbeat.mjs scripts/smoke-verify-prepush.mjs scripts/smoke-push-diff.mjs scripts/smoke-progress-reporter.mjs scripts/smoke-verify-plan.mjs scripts/smoke-install-desktop.mjs scripts/smoke-desktop-session.mjs scripts/smoke-api-tools.mjs scripts/smoke-tests-workbench.mjs scripts/smoke-bin-scope.mjs",
18
+ "test:node": "node --test --test-timeout=90000 scripts/smoke-account.mjs scripts/smoke-cli-entrypoint.mjs scripts/smoke-launcher-config.mjs scripts/smoke-runner.mjs scripts/smoke-repo-config.mjs scripts/smoke-onboard.mjs scripts/smoke-onboard-opening.mjs scripts/smoke-local-detect.mjs scripts/smoke-prepush-hook.mjs scripts/smoke-cli-identity.mjs scripts/smoke-runner-heartbeat.mjs scripts/smoke-verify-prepush.mjs scripts/smoke-push-diff.mjs scripts/smoke-progress-reporter.mjs scripts/smoke-verify-plan.mjs scripts/smoke-install-desktop.mjs scripts/smoke-desktop-session.mjs scripts/smoke-api-tools.mjs scripts/smoke-tests-workbench.mjs scripts/smoke-bin-scope.mjs scripts/smoke-shared-errors.mjs",
19
19
  "test:dmg": "node --test --test-timeout=300000 scripts/smoke-install-desktop-volume.mjs"
20
20
  },
21
21
  "devDependencies": {