@cardstack/boxel-cli 0.3.0-unstable.13 → 0.3.0-unstable.87

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 (35) hide show
  1. package/bundled-types/base/card-api.d.ts +2 -2
  2. package/bundled-types/base/card-serialization.d.ts +1 -1
  3. package/bundled-types/base/field-support.d.ts +6 -0
  4. package/bundled-types/boxel-ui/icons.gts +0 -3
  5. package/bundled-types/host-app/commands/create-submission-workflow.ts +2 -2
  6. package/bundled-types/host-app/commands/get-available-realm-identifiers.ts +1 -1
  7. package/bundled-types/host-app/commands/get-catalog-realm-identifiers.ts +1 -1
  8. package/bundled-types/host-app/commands/search-cards.ts +1 -1
  9. package/bundled-types/host-app/components/card-catalog/modal.gts +3 -3
  10. package/bundled-types/host-app/components/card-search/panel.gts +1 -1
  11. package/bundled-types/host-app/components/card-search/search-content.gts +2 -2
  12. package/bundled-types/host-app/components/operator-mode/code-submode/format-chooser.gts +12 -9
  13. package/bundled-types/host-app/components/operator-mode/code-submode/module-inspector.gts +1 -1
  14. package/bundled-types/host-app/components/operator-mode/code-submode/playground/playground-panel.gts +3 -3
  15. package/bundled-types/host-app/components/operator-mode/create-file-modal.gts +2 -2
  16. package/bundled-types/host-app/components/operator-mode/stack-item.gts +9 -1
  17. package/bundled-types/host-app/components/operator-mode/workspace-chooser/index.gts +32 -24
  18. package/bundled-types/host-app/components/operator-mode/workspace-chooser/workspace.gts +36 -23
  19. package/bundled-types/host-app/components/realm-picker/index.gts +2 -2
  20. package/bundled-types/host-app/components/search-sheet/index.gts +1 -1
  21. package/bundled-types/host-app/components/with-known-realms-loaded.gts +1 -1
  22. package/bundled-types/host-app/router.ts +1 -0
  23. package/bundled-types/host-app/services/host-mode-service.ts +21 -2
  24. package/bundled-types/host-app/services/matrix-service.ts +9 -8
  25. package/bundled-types/host-app/services/operator-mode-state-service.ts +8 -6
  26. package/bundled-types/host-app/services/realm-server.ts +18 -16
  27. package/bundled-types/host-app/services/store.ts +1 -1
  28. package/bundled-types/host-app/utils/card-search/url.ts +6 -2
  29. package/bundled-types/host-tests/helpers/index.gts +2 -2
  30. package/dist/index.js +69 -69
  31. package/package.json +2 -2
  32. package/src/commands/realm/publish.ts +10 -1
  33. package/src/commands/realm/sync.ts +28 -2
  34. package/src/commands/realm/unpublish.ts +2 -3
  35. package/src/lib/describe-fetch-error.ts +25 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardstack/boxel-cli",
3
- "version": "0.3.0-unstable.13",
3
+ "version": "0.3.0-unstable.87",
4
4
  "license": "MIT",
5
5
  "description": "CLI tools for Boxel workspace management",
6
6
  "main": "./dist/index.js",
@@ -58,8 +58,8 @@
58
58
  "ts-node": "^10.9.1",
59
59
  "vite": "^6.3.2",
60
60
  "vitest": "^2.1.9",
61
- "@cardstack/local-types": "0.0.0",
62
61
  "@cardstack/postgres": "0.0.0",
62
+ "@cardstack/local-types": "0.0.0",
63
63
  "@cardstack/runtime-common": "1.0.0"
64
64
  },
65
65
  "publishConfig": {
@@ -7,6 +7,7 @@ import {
7
7
  } from '../../lib/profile-manager';
8
8
  import { unpublishRealm } from './unpublish';
9
9
  import { FG_CYAN, FG_GREEN, FG_RED, RESET } from '../../lib/colors';
10
+ import { describeFetchError } from '../../lib/describe-fetch-error';
10
11
 
11
12
  const DEFAULT_TIMEOUT_MS = 300_000;
12
13
  const READINESS_POLL_INTERVAL_MS = 1000;
@@ -193,7 +194,7 @@ async function waitForPublishedRealmReady(
193
194
  }
194
195
  lastError = `HTTP ${response.status}`;
195
196
  } catch (error) {
196
- lastError = error instanceof Error ? error.message : String(error);
197
+ lastError = describeFetchError(error);
197
198
  }
198
199
  let remaining = timeoutMs - (Date.now() - startedAt);
199
200
  if (remaining <= 0) break;
@@ -276,6 +277,14 @@ export function registerPublishCommand(realm: Command): void {
276
277
  console.error(
277
278
  `${FG_RED}Error:${RESET} ${err instanceof Error ? err.message : String(err)}`,
278
279
  );
280
+ // Node's fetch surfaces the actual transport error (ECONNRESET,
281
+ // TLS failure, undici socket error, etc.) on `error.cause`. Print
282
+ // it so opaque "fetch failed" messages don't strand the caller.
283
+ // `!= null` rather than a truthy check so we don't drop
284
+ // falsy-but-defined causes (`''`, `0`, `false`, `NaN`).
285
+ if (err instanceof Error && err.cause != null) {
286
+ console.error(`${FG_RED}Caused by:${RESET}`, err.cause);
287
+ }
279
288
  process.exit(1);
280
289
  }
281
290
  },
@@ -51,6 +51,11 @@ class RealmSyncer extends RealmSyncBase {
51
51
  remoteDeletedFiles: string[] = [];
52
52
  localDeletedFiles: string[] = [];
53
53
  skippedConflicts: string[] = [];
54
+ // Top-level message from a failed /_atomic batch (e.g. "Atomic upload
55
+ // failed: 500 Internal Server Error"). Surfaced in `SyncResult.error`
56
+ // so callers don't have to scrape stderr to learn why the batch
57
+ // failed.
58
+ uploadFatalMessage?: string;
54
59
 
55
60
  constructor(
56
61
  private syncOptions: BiSyncOptions,
@@ -320,7 +325,24 @@ class RealmSyncer extends RealmSyncBase {
320
325
  this.pushedFiles.push(...result.succeeded);
321
326
  if (result.error) {
322
327
  this.hasError = true;
323
- console.error(result.error.message);
328
+ // Fold the per-file titles into the surfaced message so
329
+ // SyncResult.error carries the server's JSON:API error
330
+ // payload (e.g. "Write Error"), not just the HTTP status
331
+ // line. Distinct titles only — repeated identical titles
332
+ // (the common case for a top-level write failure) would
333
+ // otherwise produce noisy duplicates. The summary line is
334
+ // re-echoed by registerSyncCommand at the end of the run
335
+ // via `Error: ${result.error}`, so we no longer also emit
336
+ // the standalone status line inline — that would duplicate
337
+ // it in CLI output. The per-file loop stays because it
338
+ // carries path-level detail the summary aggregates away.
339
+ let titles = Array.from(
340
+ new Set(result.error.perFile.map((e) => e.title)),
341
+ );
342
+ this.uploadFatalMessage =
343
+ titles.length > 0
344
+ ? `${result.error.message} (${titles.join('; ')})`
345
+ : result.error.message;
324
346
  for (const entry of result.error.perFile) {
325
347
  console.error(` ${entry.path}: ${entry.title}`);
326
348
  }
@@ -679,7 +701,11 @@ function buildSyncErrorMessage(syncer: RealmSyncer): string {
679
701
  `${syncer.skippedConflicts.length} conflicts skipped`,
680
702
  ].join(', ');
681
703
 
682
- return `Sync completed with errors. ${summary}.`;
704
+ let base = `Sync completed with errors. ${summary}.`;
705
+ if (syncer.uploadFatalMessage) {
706
+ return `${base} ${syncer.uploadFatalMessage}`;
707
+ }
708
+ return base;
683
709
  }
684
710
  function emptyResult(partial: Pick<SyncResult, 'error'>): SyncResult {
685
711
  return {
@@ -6,6 +6,7 @@ import {
6
6
  type ProfileManager,
7
7
  } from '../../lib/profile-manager';
8
8
  import { FG_CYAN, FG_GREEN, FG_RED, RESET } from '../../lib/colors';
9
+ import { describeFetchError } from '../../lib/describe-fetch-error';
9
10
 
10
11
  export interface UnpublishOptions {
11
12
  /**
@@ -67,9 +68,7 @@ export async function unpublishRealm(
67
68
  return {
68
69
  publishedRealmURL: normalized,
69
70
  unpublished: false,
70
- error: `Failed to reach realm server: ${
71
- err instanceof Error ? err.message : String(err)
72
- }`,
71
+ error: `Failed to reach realm server: ${describeFetchError(err)}`,
73
72
  };
74
73
  }
75
74
 
@@ -0,0 +1,25 @@
1
+ // Node's `fetch` error surface is shallow: the outer error is always
2
+ // `TypeError: fetch failed`, and the *real* reason (ECONNRESET, TLS
3
+ // failure, undici socket error, etc.) lives on `error.cause`. Inline
4
+ // both when summarizing a failed fetch for log output or for embedding
5
+ // in a result string returned from a higher-level operation, so that
6
+ // opaque "fetch failed" lines don't reach the operator without context.
7
+ //
8
+ // `error.cause != null` rather than a truthy check so we don't drop
9
+ // falsy-but-defined causes (`''`, `0`, `false`, `NaN`). `!= null`
10
+ // matches both `null` and `undefined` — i.e., the absence markers —
11
+ // and lets every explicit value through.
12
+ //
13
+ // For user-facing CLI output where the full nested Error (including
14
+ // stack frames) is useful, prefer logging `err` and `err.cause` as
15
+ // separate console.error arguments so Node pretty-prints them. This
16
+ // helper is for the case where the output needs to be a single string.
17
+ export function describeFetchError(error: unknown): string {
18
+ let msg = error instanceof Error ? error.message : String(error);
19
+ if (error instanceof Error && error.cause != null) {
20
+ let cause = error.cause;
21
+ let causeMsg = cause instanceof Error ? cause.message : String(cause);
22
+ return `${msg} (caused by: ${causeMsg})`;
23
+ }
24
+ return msg;
25
+ }