ryugi-artifacts 1.63.0 → 1.65.0

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.
@@ -300,6 +300,20 @@ function result(data, completion = { kind: "succeeded" }, extra = {}) {
300
300
  return { data, completion, coverage: {}, effects: [], warnings: [], actions: [], ...extra };
301
301
  }
302
302
 
303
+ // src/cli/output/time.ts
304
+ var ISO = /^\d{4}-\d\d-\d\dT\d\d:\d\d(?::\d\d(?:\.\d+)?)?(?:Z|[+-]\d\d:?\d\d)$/;
305
+ var two = (n) => String(n).padStart(2, "0");
306
+ var isIsoTime = (value) => typeof value === "string" && ISO.test(value) && !Number.isNaN(Date.parse(value));
307
+ function formatTime(iso, now = /* @__PURE__ */ new Date()) {
308
+ const at = new Date(iso);
309
+ if (Number.isNaN(at.getTime())) return iso;
310
+ const clock = `${two(at.getHours())}:${two(at.getMinutes())}`;
311
+ if (at.getFullYear() !== now.getFullYear())
312
+ return `${at.getFullYear()}-${two(at.getMonth() + 1)}-${two(at.getDate())}`;
313
+ if (at.getMonth() === now.getMonth() && at.getDate() === now.getDate()) return clock;
314
+ return `${two(at.getMonth() + 1)}-${two(at.getDate())} ${clock}`;
315
+ }
316
+
303
317
  // src/cli/output/view.ts
304
318
  function recoveryActions(error, subject) {
305
319
  const details = error.details;
@@ -315,8 +329,52 @@ function clip(text, size = 160) {
315
329
  const segments = [...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text)];
316
330
  return segments.length > size ? `${segments.slice(0, size).map((s) => s.segment).join("")}\u2026` : text;
317
331
  }
318
- var scalar = (value) => value === null ? "none" : typeof value === "object" ? JSON.stringify(value) : String(value);
319
- var scopeText = (page) => `${clean(page.scope)} \xB7 ${page.returned} shown${page.total === null ? "" : ` / ${page.total} matched`}${page.hasMore ? " \xB7 more available" : ""}`;
332
+ function flatten(value, nested = false) {
333
+ if (value === null || value === void 0) return "";
334
+ if (Array.isArray(value)) return value.map((item) => flatten(item, true)).join(", ");
335
+ if (typeof value === "object") {
336
+ const text = Object.entries(value).filter(([, item]) => !absent(item)).map(([key, item]) => `${key} ${flatten(item, true)}`).join(nested ? ", " : " \xB7 ");
337
+ return nested ? `(${text})` : text;
338
+ }
339
+ return isIsoTime(value) ? formatTime(value) : String(value);
340
+ }
341
+ var scalar = (value, path5, loose) => {
342
+ if (value !== null && typeof value === "object") {
343
+ if (!loose)
344
+ throw new Error(`Brief cannot print the object at ${path5}; flatten it in the view.`);
345
+ return flatten(value);
346
+ }
347
+ return flatten(value);
348
+ };
349
+ var absent = (value, fallback) => value === void 0 || value === null || value === "" || value === false || fallback !== void 0 && value === fallback;
350
+ var EXIT_WORDS = {
351
+ 1: "FAILED",
352
+ 2: "NEEDS YOU",
353
+ 3: "ISSUES",
354
+ 4: "INCOMPLETE",
355
+ 5: "PARTIAL",
356
+ 130: "INTERRUPTED",
357
+ 143: "INTERRUPTED"
358
+ };
359
+ var RECOVERY = {
360
+ E_ARTIFACT_NOT_FOUND: "check --artifact and --project-dir; artifacts live in <project>/.ryugi/artifacts/<type>/<id>/",
361
+ E_ARGUMENT_INVALID: "run the command with --help for its options",
362
+ E_CURSOR_EXPIRED: "run the read again without --cursor or --snapshot",
363
+ E_CURSOR_MISMATCH: "run the read again without --cursor or --snapshot"
364
+ };
365
+ var statusWord = (status, code) => code ? EXIT_WORDS[code] ?? "FAILED" : clean(status).replaceAll("_", " ").toUpperCase();
366
+ var commandName = (id) => {
367
+ const words = id.replaceAll(/[._]/g, " ").trim();
368
+ return words ? words[0].toUpperCase() + words.slice(1) : "Command";
369
+ };
370
+ function pageText(page) {
371
+ const offset = page.offset ?? 0;
372
+ if (!page.hasMore && offset === 0) return null;
373
+ const of = page.total === null ? "" : ` of ${page.total}`;
374
+ if (page.offset === void 0)
375
+ return `${page.returned}${of} shown${page.total === null && page.hasMore ? " \xB7 more available" : ""}`;
376
+ return `${page.returned ? `${offset + 1}\u2013${offset + page.returned}` : "none"}${of}`;
377
+ }
320
378
  var quote = (value) => /^[\w./:@=+-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
321
379
  function actionText(action) {
322
380
  return action.kind === "command" ? action.argv.map(quote).join(" ") : action.url;
@@ -324,38 +382,72 @@ function actionText(action) {
324
382
  var effectLines = (effects) => effects.map(
325
383
  (effect) => `Effect: ${clean(effect.operation)} ${clean(effect.target)} \xB7 ${effect.state}`
326
384
  );
327
- function renderText(view, wire, options, collection, requiredFields = []) {
328
- const lines = [
329
- `${clean(view.heading)}: ${clean(view.status).toUpperCase()}${view.subject ? ` \xB7 ${clean(view.subject)}` : ""}`
330
- ];
385
+ var lastSegment = (path5) => path5.split(".").filter(Boolean).at(-1) ?? path5;
386
+ function renderText(view, wire, options, collection, requiredFields = [], code = 0) {
387
+ const head = [
388
+ statusWord(view.status, code),
389
+ view.subject && clean(view.subject),
390
+ view.summary && clean(view.summary)
391
+ ].filter(Boolean);
392
+ const lines = [head.length ? `${clean(view.heading)}: ${head.join(" \xB7 ")}` : clean(view.heading)];
331
393
  const omissions = [...wire.omitted ?? []];
332
394
  const selected = (key, required = false) => required || requiredFields.includes(key) || !options.fields || options.fields.includes(key);
333
- const value = (input, path5) => {
334
- const text = clean(scalar(input));
395
+ const value = (input, path5, loose = false) => {
396
+ const text = clean(scalar(input, path5, loose));
335
397
  const shown = options.detail ? text : clip(text);
336
398
  if (shown !== text) omissions.push({ path: path5, reason: "preview" });
337
399
  return shown;
338
400
  };
339
- const facts = (items) => {
340
- for (const item of items)
341
- if (collection || selected(item.binding.key, item.required))
342
- lines.push(
343
- `${clean(item.label)}: ${value(item.binding.value, `data.${item.binding.key}`)}`
344
- );
401
+ const cell = (label, key, input, path5, positional, fallback, loose = false) => {
402
+ if (absent(input, fallback)) return null;
403
+ const name = clean(label ?? key);
404
+ if (input === true) return name;
405
+ const text = value(input, path5, loose);
406
+ return positional ? text : `${name} ${text}`;
407
+ };
408
+ const facts = (items, loose = false) => {
409
+ for (const item of items) {
410
+ if (!collection && !selected(item.binding.key, item.required)) continue;
411
+ if (absent(item.binding.value)) continue;
412
+ const text = item.binding.value === true ? "yes" : value(item.binding.value, `data.${item.binding.key}`, loose);
413
+ lines.push(`${clean(item.label)}: ${text}`);
414
+ }
415
+ };
416
+ const metadata = (items, path5) => items.map(
417
+ (item) => cell(
418
+ item.label,
419
+ item.binding.key,
420
+ item.binding.value,
421
+ `${path5}.${item.binding.key}`,
422
+ item.positional
423
+ )
424
+ ).filter((text) => text !== null).join(" \xB7 ");
425
+ const title = (text, summary) => {
426
+ if (text) lines.push(summary ? `${clean(text)} \xB7 ${clean(summary)}` : clean(text));
427
+ };
428
+ const pageLine = (page) => {
429
+ const text = page && pageText(page);
430
+ if (text) lines.push(text);
345
431
  };
346
432
  facts(view.facts);
347
433
  const errorActions = wire.error ? recoveryActions(wire.error) : [];
348
434
  if (wire.error) {
349
435
  lines.push(`${clean(wire.error.code)}: ${clean(wire.error.message)}`);
350
- if (wire.error.httpStatus !== void 0) lines.push(`HTTP: ${wire.error.httpStatus}`);
351
- const details = wire.error.details;
352
- if (details && typeof details === "object" && !Array.isArray(details)) {
353
- for (const [key, detail] of Object.entries(details)) {
436
+ if (wire.error.httpStatus !== void 0 && wire.error.httpStatus >= 500)
437
+ lines.push(`HTTP: ${wire.error.httpStatus}`);
438
+ const hint = RECOVERY[wire.error.code];
439
+ if (hint) lines.push(`Next: ${hint}`);
440
+ const details2 = wire.error.details;
441
+ if (details2 && typeof details2 === "object" && !Array.isArray(details2)) {
442
+ for (const [key, detail] of Object.entries(details2)) {
354
443
  if (!errorActions.some((action) => action.label === key))
355
- facts([{ label: key, binding: { key, value: detail }, required: true }]);
444
+ facts([{ label: key, binding: { key, value: detail }, required: true }], true);
356
445
  }
357
- } else if (details !== void 0)
358
- facts([{ label: "Details", binding: { key: "details", value: details }, required: true }]);
446
+ } else if (details2 !== void 0)
447
+ facts(
448
+ [{ label: "Details", binding: { key: "details", value: details2 }, required: true }],
449
+ true
450
+ );
359
451
  }
360
452
  if (!collection && options.fields && wire.data && typeof wire.data === "object") {
361
453
  const bound = /* @__PURE__ */ new Set([
@@ -366,7 +458,8 @@ function renderText(view, wire, options, collection, requiredFields = []) {
366
458
  ]);
367
459
  const data = wire.data;
368
460
  facts(
369
- [.../* @__PURE__ */ new Set([...options.fields, ...requiredFields])].filter((key) => !bound.has(key) && Object.hasOwn(data, key)).map((key) => ({ label: key, binding: { key, value: data[key] } }))
461
+ [.../* @__PURE__ */ new Set([...options.fields, ...requiredFields])].filter((key) => !bound.has(key) && Object.hasOwn(data, key)).map((key) => ({ label: key, binding: { key, value: data[key] } })),
462
+ true
370
463
  );
371
464
  }
372
465
  for (const section of view.sections) {
@@ -375,51 +468,57 @@ function renderText(view, wire, options, collection, requiredFields = []) {
375
468
  continue;
376
469
  }
377
470
  if (!collection && !selected(section.key)) continue;
378
- if (section.page)
379
- lines.push(
380
- `${clean("title" in section && section.title || section.key)}: ${section.status ? `${clean(section.status)} \xB7 ` : ""}${scopeText(section.page)}`
381
- );
471
+ const named = "title" in section && section.title ? section.title : void 0;
382
472
  if (section.kind === "records") {
383
- if (section.title && !section.page) lines.push(clean(section.title));
473
+ title(named ?? (section.page ? section.key : void 0), section.summary);
384
474
  const declared = new Set(section.columns.map((column) => column.key));
385
- const extra = section.key === collection ? [.../* @__PURE__ */ new Set([...options.fields ?? [], ...requiredFields])].filter((key) => !declared.has(key)).map((key) => ({ key, label: key })) : [];
475
+ const extra = section.key === collection ? [.../* @__PURE__ */ new Set([...options.fields ?? [], ...options.fields ? requiredFields : []])].filter((key) => !declared.has(key)).map((key) => ({ key, label: key })) : [];
476
+ const loose = new Set(extra.map((column) => column.key));
386
477
  const columns = [...section.columns, ...extra].filter(
387
478
  (column) => section.key !== collection || selected(column.key, column.required)
388
479
  );
389
- const rows = section.rows.map(
390
- (row, i) => columns.map(
391
- (column) => value(
392
- row[column.key] === void 0 ? "" : row[column.key],
393
- `data.${section.key}.${i}.${column.key}`
394
- )
395
- )
396
- );
397
480
  if (options.format === "table") {
398
- const labels = columns.map((column) => column.label);
481
+ const labels = columns.map((column) => column.label ?? column.key);
482
+ const rows = section.rows.map(
483
+ (row, i) => columns.map(
484
+ (column) => absent(row[column.key], column.default) ? "" : value(
485
+ row[column.key],
486
+ `data.${section.key}.${i}.${column.key}`,
487
+ loose.has(column.key)
488
+ )
489
+ )
490
+ );
399
491
  const widths = labels.map(
400
492
  (label, i) => Math.max(label.length, ...rows.map((row) => row[i]?.length ?? 0))
401
493
  );
402
494
  lines.push(
403
495
  ...[labels, ...rows].map(
404
- (row) => row.map((cell, i) => cell.padEnd(widths[i] ?? cell.length)).join(" ").trimEnd()
496
+ (row) => row.map((text, i) => text.padEnd(widths[i] ?? text.length)).join(" ").trimEnd()
405
497
  )
406
498
  );
407
499
  } else
408
- for (const row of rows)
500
+ for (const [i, row] of section.rows.entries())
409
501
  lines.push(
410
- ` ${columns.flatMap((column, i) => {
411
- const cell = row[i];
412
- return cell === void 0 || cell === "" ? [] : [`${clean(column.label)}=${cell}`];
413
- }).join(" \xB7 ")}`
502
+ ` ${columns.map(
503
+ (column) => cell(
504
+ column.label,
505
+ column.key,
506
+ row[column.key],
507
+ `data.${section.key}.${i}.${column.key}`,
508
+ column.positional,
509
+ column.default,
510
+ loose.has(column.key)
511
+ )
512
+ ).filter((text) => text !== null).join(" \xB7 ")}`
414
513
  );
415
- if (!rows.length && !section.page) lines.push("No records.");
514
+ if (!section.rows.length && !named && !section.page) lines.push("No records.");
515
+ pageLine(section.page);
416
516
  } else if (section.kind === "messages") {
417
- if (section.title && !section.page) lines.push(`${clean(section.title)}:`);
418
- const metadata = (items) => items.map((item) => `${clean(item.label)}=${clean(scalar(item.binding.value))}`).join(" \xB7 ");
419
- for (const group of section.groups) {
420
- lines.push(` ${metadata(group.facts)}`);
421
- for (const message of group.messages) {
422
- lines.push(` ${metadata(message.facts)}`);
517
+ title(named, section.summary);
518
+ for (const [g, group] of section.groups.entries()) {
519
+ lines.push(` ${metadata(group.facts, `data.${section.key}.${g}`)}`);
520
+ for (const [m, message] of group.messages.entries()) {
521
+ lines.push(` ${metadata(message.facts, `data.${section.key}.${g}.messages.${m}`)}`);
423
522
  lines.push(
424
523
  ...message.text.split("\n").map(
425
524
  (line) => ` ${line.replaceAll(new RegExp("\\p{Cc}", "gu"), (char) => char === " " ? char : "")}`
@@ -427,7 +526,8 @@ function renderText(view, wire, options, collection, requiredFields = []) {
427
526
  );
428
527
  }
429
528
  }
430
- if (!section.groups.length && !section.page) lines.push("No messages.");
529
+ if (!section.groups.length && !named && !section.page) lines.push("No messages.");
530
+ pageLine(section.page);
431
531
  } else if (section.kind === "diagnostics") {
432
532
  const limit = options.detail ? section.items.length : section.preview ?? 3;
433
533
  for (const item of section.items.slice(0, limit))
@@ -441,7 +541,7 @@ function renderText(view, wire, options, collection, requiredFields = []) {
441
541
  count: section.items.length - limit
442
542
  });
443
543
  } else {
444
- if (section.title && !section.page) lines.push(`${clean(section.title)}:`);
544
+ title(named && `${named}:`, section.summary);
445
545
  lines.push(
446
546
  ...section.text.trimEnd().split("\n").map(
447
547
  (line) => ` ${line.replaceAll(new RegExp("\\p{Cc}", "gu"), (char) => char === " " || char === "\n" ? char : "")}`
@@ -449,26 +549,42 @@ function renderText(view, wire, options, collection, requiredFields = []) {
449
549
  );
450
550
  }
451
551
  }
452
- if (wire.page) lines.push(`Scope: ${scopeText(wire.page)}`);
453
- lines.push(...effectLines(wire.effects ?? []));
552
+ if (wire.page) pageLine(wire.page);
553
+ if (code || (wire.effects ?? []).some((effect) => effect.state !== "confirmed"))
554
+ lines.push(...effectLines(wire.effects ?? []));
454
555
  for (const warning of wire.warnings ?? []) lines.push(`Warning: ${clean(warning.message)}`);
455
- if (omissions.length)
456
- lines.push(
457
- `Omitted: ${omissions.map((item) => `${item.count === void 0 ? "" : `${item.count} `}${item.path} (${item.reason})`).join(", ")}`
458
- );
459
- for (const action of [...view.actions ?? wire.next ?? [], ...errorActions])
460
- lines.push(`${clean(action.label ?? "Next")}: ${actionText(action)}`);
556
+ const actions = [...view.actions ?? wire.next ?? [], ...errorActions];
557
+ const used = /* @__PURE__ */ new Set();
558
+ const details = actions.find((action) => action.label === "Details");
559
+ for (const omission of omissions) {
560
+ if (omission.count === void 0) continue;
561
+ const more = `${omission.count} more ${lastSegment(omission.path)}`;
562
+ if (details && !used.has(details)) {
563
+ used.add(details);
564
+ lines.push(`${more} \xB7 Details: ${actionText(details)}`);
565
+ } else lines.push(more);
566
+ }
567
+ if (!details && !options.detail && omissions.some((item) => item.reason === "preview" && item.count === void 0))
568
+ lines.push("Some values are clipped; add --detail to see them in full.");
569
+ for (const action of actions)
570
+ if (!used.has(action))
571
+ lines.push(
572
+ `${clean(action.label ?? "Next")}: ${action.reason ? `${clean(action.reason)} \xB7 ` : ""}${actionText(action)}`
573
+ );
461
574
  return `${lines.join("\n")}
462
575
  `;
463
576
  }
464
- function renderFailure(wire, options) {
577
+ function renderFailure(wire, options, code = 1) {
465
578
  return renderText(
466
- { heading: wire.command, status: wire.outcome, facts: [], sections: [] },
579
+ { heading: commandName(wire.command), status: "", facts: [], sections: [] },
467
580
  wire,
468
581
  {
469
582
  ...options,
470
583
  fields: void 0
471
- }
584
+ },
585
+ void 0,
586
+ [],
587
+ code
472
588
  );
473
589
  }
474
590