@plaud-ai/cli 0.3.7 → 0.3.9

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/dist/index.js +117 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20379,7 +20379,7 @@ function isNewer(current, latest) {
20379
20379
  return false;
20380
20380
  }
20381
20381
  var updateCommand = new Command("update").description("Check npm for the latest Plaud CLI and print the upgrade command").action(async () => {
20382
- const current = "0.3.7";
20382
+ const current = "0.3.9";
20383
20383
  const spinner = ora("Checking npm for latest version...").start();
20384
20384
  const latest = await fetchLatestVersion();
20385
20385
  spinner.stop();
@@ -20434,10 +20434,10 @@ async function checkForUpdate(current) {
20434
20434
  return isNewer(current, latest) ? latest : null;
20435
20435
  }
20436
20436
  var versionCommand = new Command2("version").description("Show CLI version information").action(async () => {
20437
- const current = "0.3.7";
20437
+ const current = "0.3.9";
20438
20438
  console.log(`plaud ${current}`);
20439
- if ("bacaae9") console.log(`commit ${"bacaae9"}`);
20440
- if ("2026-08-05T03:31:25.414Z") console.log(`built ${"2026-08-05T03:31:25.414Z"}`);
20439
+ if ("e0fb5d6") console.log(`commit ${"e0fb5d6"}`);
20440
+ if ("2026-08-18T03:07:34.503Z") console.log(`built ${"2026-08-18T03:07:34.503Z"}`);
20441
20441
  if (current === "unknown") return;
20442
20442
  const newer = await checkForUpdate(current);
20443
20443
  if (newer) {
@@ -20902,6 +20902,49 @@ async function loadBlockContent(block) {
20902
20902
  return "";
20903
20903
  }
20904
20904
 
20905
+ // ../shared/dist/time.js
20906
+ var HAS_TIMEZONE = /(?:[Zz]|[+-]\d{2}(?::?\d{2})?)$/;
20907
+ var HAS_TIME = /\d{2}:\d{2}/;
20908
+ var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
20909
+ function parseApiTimestamp(value) {
20910
+ if (!value)
20911
+ return null;
20912
+ const trimmed = value.trim();
20913
+ if (!trimmed)
20914
+ return null;
20915
+ const normalised = HAS_TIME.test(trimmed) && !HAS_TIMEZONE.test(trimmed) ? `${trimmed}Z` : trimmed;
20916
+ const ms = new Date(normalised).getTime();
20917
+ return Number.isNaN(ms) ? null : ms;
20918
+ }
20919
+ function localDayStart(value) {
20920
+ const parts = parseDateOnly(value);
20921
+ if (!parts)
20922
+ return null;
20923
+ const [y, m, d] = parts;
20924
+ return new Date(y, m - 1, d, 0, 0, 0, 0).getTime();
20925
+ }
20926
+ function localDayEnd(value) {
20927
+ const parts = parseDateOnly(value);
20928
+ if (!parts)
20929
+ return null;
20930
+ const [y, m, d] = parts;
20931
+ return new Date(y, m - 1, d + 1, 0, 0, 0, 0).getTime() - 1;
20932
+ }
20933
+ function parseDateOnly(value) {
20934
+ if (!value)
20935
+ return null;
20936
+ const match = DATE_ONLY.exec(value.trim());
20937
+ if (!match)
20938
+ return null;
20939
+ const y = Number(match[1]);
20940
+ const m = Number(match[2]);
20941
+ const d = Number(match[3]);
20942
+ const probe = new Date(y, m - 1, d);
20943
+ if (probe.getFullYear() !== y || probe.getMonth() !== m - 1 || probe.getDate() !== d)
20944
+ return null;
20945
+ return [y, m, d];
20946
+ }
20947
+
20905
20948
  // src/config.ts
20906
20949
  import { readFileSync, existsSync } from "fs";
20907
20950
  import { homedir as homedir4 } from "os";
@@ -21138,8 +21181,9 @@ function formatDuration(ms) {
21138
21181
  }
21139
21182
  function formatDate(iso) {
21140
21183
  if (!iso) return "-";
21141
- const d = new Date(iso);
21142
- if (Number.isNaN(d.getTime())) return iso;
21184
+ const ms = parseApiTimestamp(iso);
21185
+ if (ms === null) return iso;
21186
+ const d = new Date(ms);
21143
21187
  const y = d.getFullYear();
21144
21188
  const m = String(d.getMonth() + 1).padStart(2, "0");
21145
21189
  const day = String(d.getDate()).padStart(2, "0");
@@ -21231,7 +21275,8 @@ var getFileCommand = new Command7("file").description("Get details of a specific
21231
21275
  console.log(` ${chalk8.cyan("start_at")}: ${file.start_at ?? "-"}`);
21232
21276
  console.log(` ${chalk8.cyan("duration")}: ${formatDuration(file.duration)}`);
21233
21277
  console.log(` ${chalk8.cyan("serial_number")}: ${file.serial_number ?? "-"}`);
21234
- console.log(` ${chalk8.cyan("audio")}: ${file.presigned_url ? chalk8.green("available") : chalk8.gray("unavailable")}`);
21278
+ const audioState = file.presigned_url ? chalk8.green("available") : chalk8.gray("unknown (run `plaud audio` to check)");
21279
+ console.log(` ${chalk8.cyan("audio")}: ${audioState}`);
21235
21280
  console.log(` ${chalk8.cyan("transcript")}: ${hasTranscript ? chalk8.green("available") : chalk8.gray("unavailable")}`);
21236
21281
  console.log(` ${chalk8.cyan("summary")}: ${hasSummary ? chalk8.green("available") : chalk8.gray("unavailable")}`);
21237
21282
  console.log();
@@ -21378,27 +21423,74 @@ import { Command as Command10 } from "commander";
21378
21423
  import chalk11 from "chalk";
21379
21424
  import ora8 from "ora";
21380
21425
  import { writeFile as writeFile5 } from "fs/promises";
21381
- var summaryCommand = new Command10("summary").description("Get the AI summary for a Plaud recording").argument("<file_id>", "The file ID to retrieve summary for").option("-o, --output <file>", "Save summary to a file").action(async (fileId, opts) => {
21426
+ var SUMMARY_TYPE = "auto_sum_note";
21427
+ async function noteContent(note) {
21428
+ try {
21429
+ return await loadBlockContent(note);
21430
+ } catch {
21431
+ return "";
21432
+ }
21433
+ }
21434
+ var summaryCommand = new Command10("summary").description("Get the AI summary for a Plaud recording").argument("<file_id>", "The file ID to retrieve summary for").option("-o, --output <file>", "Save summary to a file").option("--all", "Print every note on the recording, not just the first summary").option("--json", "Print note_list verbatim as JSON (same payload as the MCP get_note tool)").action(async (fileId, opts) => {
21382
21435
  const client2 = getClient2();
21383
21436
  const spinner = ora8("Fetching summary...").start();
21384
21437
  try {
21385
21438
  const file = await client2.getFile(fileId);
21386
21439
  spinner.stop();
21387
21440
  const noteList = file.note_list ?? [];
21388
- const note = noteList.find((n) => n.data_type === "auto_sum_note");
21389
- if (!note || !note.data_content) {
21390
- console.log(chalk11.yellow("Summary not available for this recording."));
21441
+ if (opts.json) {
21442
+ const payload = JSON.stringify(noteList, null, 2);
21443
+ if (opts.output) {
21444
+ await writeFile5(opts.output, payload, "utf-8");
21445
+ console.log(chalk11.green(`Notes saved to ${opts.output}`));
21446
+ } else {
21447
+ console.log(payload);
21448
+ }
21449
+ return;
21450
+ }
21451
+ const summaries = noteList.filter((n) => n.data_type === SUMMARY_TYPE);
21452
+ if (summaries.length === 0) {
21453
+ if (noteList.length === 0) {
21454
+ console.log(chalk11.yellow("Summary not available for this recording."));
21455
+ } else {
21456
+ const available = noteList.map((n) => n.data_type).filter(Boolean).join(", ") || "(none)";
21457
+ console.log(
21458
+ chalk11.yellow(
21459
+ `No "${SUMMARY_TYPE}" note for this recording. Available: ${available}.`
21460
+ )
21461
+ );
21462
+ }
21391
21463
  return;
21392
21464
  }
21393
- const content = note.data_content;
21465
+ const selected = opts.all ? noteList : summaries.slice(0, 1);
21466
+ if (!opts.all && noteList.length > 1) {
21467
+ const detail = summaries.length > 1 ? `${summaries.length} summary notes` : `${noteList.length} notes (1 summary)`;
21468
+ console.error(
21469
+ chalk11.yellow(
21470
+ `Note: this recording has ${detail}; showing the first. Use --all to print them all, or --json for the raw payload.`
21471
+ )
21472
+ );
21473
+ }
21474
+ const sections = [];
21475
+ for (const [i, note] of selected.entries()) {
21476
+ const content = await noteContent(note);
21477
+ const type = typeof note.data_type === "string" ? note.data_type : "unknown";
21478
+ const body = content || `(this note has no content yet \u2014 nothing was returned for "${type}")`;
21479
+ sections.push(
21480
+ opts.all ? `\u2500\u2500\u2500 note ${i + 1}/${selected.length} \xB7 ${type} \u2500\u2500\u2500
21481
+
21482
+ ${body}` : body
21483
+ );
21484
+ }
21485
+ const output = sections.join("\n\n");
21394
21486
  if (opts.output) {
21395
- await writeFile5(opts.output, content, "utf-8");
21487
+ await writeFile5(opts.output, output, "utf-8");
21396
21488
  console.log(chalk11.green(`Summary saved to ${opts.output}`));
21397
21489
  } else {
21398
21490
  console.log(chalk11.bold(`
21399
- Summary: ${file.name}
21491
+ ${opts.all ? "Notes" : "Summary"}: ${file.name}
21400
21492
  `));
21401
- console.log(content);
21493
+ console.log(output);
21402
21494
  console.log();
21403
21495
  }
21404
21496
  } catch (err) {
@@ -21426,25 +21518,19 @@ import chalk12 from "chalk";
21426
21518
  import ora9 from "ora";
21427
21519
  var MAX_PAGES = 5;
21428
21520
  var PAGE_SIZE = 100;
21429
- function parseDate(s) {
21430
- if (!s) return null;
21431
- const d = new Date(s);
21432
- return Number.isNaN(d.getTime()) ? null : d.getTime();
21433
- }
21434
21521
  var searchCommand = new Command11("search").description("Search recordings by name keyword (client-side, scans up to 500 most recent recordings)").argument("<keyword>", "Case-insensitive substring to match against recording names").option("--from <date>", "Start date inclusive, YYYY-MM-DD").option("--to <date>", "End date inclusive, YYYY-MM-DD").option("--max <n>", "Maximum matches to display", "50").action(async (keyword, opts) => {
21435
21522
  const max = parseInt(opts.max);
21436
21523
  if (isNaN(max) || max < 1 || max > 500) {
21437
21524
  printError("INVALID_ARGS", "--max must be a number between 1 and 500");
21438
21525
  await telemetryExit(ExitCode.ERROR);
21439
21526
  }
21440
- const from = parseDate(opts.from);
21441
- const toRaw = parseDate(opts.to);
21442
- const to = toRaw !== null ? toRaw + 24 * 60 * 60 * 1e3 - 1 : null;
21527
+ const from = localDayStart(opts.from);
21528
+ const to = localDayEnd(opts.to);
21443
21529
  if (opts.from && from === null) {
21444
21530
  printError("INVALID_ARGS", `Invalid --from date: ${opts.from}`);
21445
21531
  await telemetryExit(ExitCode.ERROR);
21446
21532
  }
21447
- if (opts.to && toRaw === null) {
21533
+ if (opts.to && to === null) {
21448
21534
  printError("INVALID_ARGS", `Invalid --to date: ${opts.to}`);
21449
21535
  await telemetryExit(ExitCode.ERROR);
21450
21536
  }
@@ -21462,7 +21548,7 @@ var searchCommand = new Command11("search").description("Search recordings by na
21462
21548
  for (const file of result.data) {
21463
21549
  if (!(file.name ?? "").toLowerCase().includes(q)) continue;
21464
21550
  if (from !== null || to !== null) {
21465
- const created = parseDate(file.created_at);
21551
+ const created = parseApiTimestamp(file.created_at);
21466
21552
  if (created === null) continue;
21467
21553
  if (from !== null && created < from) continue;
21468
21554
  if (to !== null && created > to) continue;
@@ -21537,8 +21623,8 @@ var recentCommand = new Command12("recent").description("List recordings from th
21537
21623
  let allOlder = true;
21538
21624
  let hasValidTimestamps = false;
21539
21625
  for (const file of result.data) {
21540
- const created = new Date(file.created_at).getTime();
21541
- if (Number.isNaN(created)) continue;
21626
+ const created = parseApiTimestamp(file.created_at);
21627
+ if (created === null) continue;
21542
21628
  hasValidTimestamps = true;
21543
21629
  if (created >= from) {
21544
21630
  matches.push(file);
@@ -21591,8 +21677,8 @@ var todayCommand = new Command12("today").description("List recordings created t
21591
21677
  const result = await client2.listFiles(1, 50);
21592
21678
  spinner.stop();
21593
21679
  const matches = result.data.filter((f2) => {
21594
- const t = new Date(f2.created_at).getTime();
21595
- return !Number.isNaN(t) && t >= startMs;
21680
+ const t = parseApiTimestamp(f2.created_at);
21681
+ return t !== null && t >= startMs;
21596
21682
  });
21597
21683
  if (matches.length === 0) {
21598
21684
  console.log(chalk13.yellow("No recordings created today."));
@@ -21716,7 +21802,7 @@ async function runWizard() {
21716
21802
  try {
21717
21803
  await initTelemetry({
21718
21804
  surface: "cli",
21719
- appVersion: "0.3.7"
21805
+ appVersion: "0.3.9"
21720
21806
  });
21721
21807
  } catch {
21722
21808
  }
@@ -21725,7 +21811,7 @@ async function notifyUpdate() {
21725
21811
  if (sub === "version" || sub === "update") return;
21726
21812
  if (process.env.PLAUD_NO_UPDATE_NOTIFIER) return;
21727
21813
  if (!process.stderr.isTTY) return;
21728
- const current = "0.3.7";
21814
+ const current = "0.3.9";
21729
21815
  if (current === "0.0.0") return;
21730
21816
  try {
21731
21817
  const newer = await checkForUpdate(current);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaud-ai/cli",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "plaud": "dist/index.js"