@plaud-ai/cli 0.1.5 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +465 -30
  2. package/package.json +13 -8
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import "dotenv/config";
5
- import { Command as Command10 } from "commander";
5
+ import { Command as Command13 } from "commander";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import { Command } from "commander";
@@ -61,6 +61,9 @@ function generateCodeVerifier() {
61
61
  function generateCodeChallenge(verifier) {
62
62
  return createHash("sha256").update(verifier).digest("base64url");
63
63
  }
64
+ function generateState() {
65
+ return randomBytes(16).toString("base64url");
66
+ }
64
67
  var OAuth = class {
65
68
  config;
66
69
  tokenStore;
@@ -77,17 +80,19 @@ var OAuth = class {
77
80
  createAuthorizationRequest() {
78
81
  const codeVerifier = generateCodeVerifier();
79
82
  const codeChallenge = generateCodeChallenge(codeVerifier);
83
+ const state = generateState();
80
84
  const params = new URLSearchParams({
81
85
  client_id: this.config.clientId,
82
86
  redirect_uri: this.config.redirectUri,
83
87
  response_type: "code",
84
88
  code_challenge: codeChallenge,
85
- code_challenge_method: "S256"
89
+ code_challenge_method: "S256",
90
+ state
86
91
  });
87
92
  return {
88
93
  url: `${this.authorizationUrl}?${params.toString()}`,
89
94
  codeVerifier,
90
- state: ""
95
+ state
91
96
  };
92
97
  }
93
98
  /**
@@ -96,7 +101,7 @@ var OAuth = class {
96
101
  getAuthorizationUrl() {
97
102
  return this.createAuthorizationRequest().url;
98
103
  }
99
- async exchangeCode(code, codeVerifier) {
104
+ async exchangeCode(code, codeVerifier, state) {
100
105
  const basicAuth = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString("base64");
101
106
  const body = {
102
107
  code,
@@ -105,6 +110,9 @@ var OAuth = class {
105
110
  if (codeVerifier) {
106
111
  body.code_verifier = codeVerifier;
107
112
  }
113
+ if (state) {
114
+ body.state = state;
115
+ }
108
116
  const res = await fetch(this.tokenUrl, {
109
117
  method: "POST",
110
118
  headers: {
@@ -182,16 +190,18 @@ var PlaudClient = class {
182
190
  oauth;
183
191
  apiBase;
184
192
  extraHeaders;
193
+ staticToken;
185
194
  constructor(config) {
186
195
  this.oauth = new OAuth(config);
187
196
  this.apiBase = config.apiBase ?? DEFAULT_API_BASE;
188
197
  this.extraHeaders = config.extraHeaders ?? {};
198
+ this.staticToken = config.staticToken;
189
199
  }
190
200
  get auth() {
191
201
  return this.oauth;
192
202
  }
193
203
  async request(path, init) {
194
- const token = await this.oauth.getAccessToken();
204
+ const token = this.staticToken ?? await this.oauth.getAccessToken();
195
205
  if (!token) {
196
206
  throw new Error("Not authenticated. Please login first.");
197
207
  }
@@ -322,7 +332,7 @@ var loginCommand = new Command("login").description("Authenticate with Plaud via
322
332
  } catch {
323
333
  await client2.auth.logout();
324
334
  }
325
- const { url, codeVerifier } = client2.auth.createAuthorizationRequest();
335
+ const { url, codeVerifier, state } = client2.auth.createAuthorizationRequest();
326
336
  const spinner = ora("Waiting for browser authentication...").start();
327
337
  const server = createServer(async (req, res) => {
328
338
  if (req.method === "OPTIONS") {
@@ -346,7 +356,7 @@ var loginCommand = new Command("login").description("Authenticate with Plaud via
346
356
  return;
347
357
  }
348
358
  try {
349
- await client2.auth.exchangeCode(code, codeVerifier);
359
+ await client2.auth.exchangeCode(code, codeVerifier, state);
350
360
  res.writeHead(200, { "Content-Type": "text/html", ...CORS_HEADERS });
351
361
  res.end("<h1>Authentication successful!</h1><p>You can close this tab.</p>");
352
362
  spinner.succeed("Logged in successfully!");
@@ -424,6 +434,42 @@ var meCommand = new Command3("me").description("Show current authenticated user
424
434
  import { Command as Command4 } from "commander";
425
435
  import chalk5 from "chalk";
426
436
  import ora3 from "ora";
437
+
438
+ // src/format.ts
439
+ function formatDuration(ms) {
440
+ if (!ms || ms < 0) return "-";
441
+ const totalSeconds = Math.floor(ms / 1e3);
442
+ const hours = Math.floor(totalSeconds / 3600);
443
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
444
+ const seconds = totalSeconds % 60;
445
+ if (hours > 0) return `${hours}h${String(minutes).padStart(2, "0")}m`;
446
+ if (minutes > 0) return `${minutes}m${String(seconds).padStart(2, "0")}s`;
447
+ return `${seconds}s`;
448
+ }
449
+ function formatDate(iso) {
450
+ if (!iso) return "-";
451
+ const d = new Date(iso);
452
+ if (Number.isNaN(d.getTime())) return iso;
453
+ const y = d.getFullYear();
454
+ const m = String(d.getMonth() + 1).padStart(2, "0");
455
+ const day = String(d.getDate()).padStart(2, "0");
456
+ return `${y}-${m}-${day}`;
457
+ }
458
+ function formatTime(ms) {
459
+ const totalSeconds = Math.floor(ms / 1e3);
460
+ const minutes = Math.floor(totalSeconds / 60);
461
+ const seconds = totalSeconds % 60;
462
+ return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
463
+ }
464
+
465
+ // src/commands/list-files.ts
466
+ var ID_WIDTH = 34;
467
+ var NAME_WIDTH = 36;
468
+ var DATE_WIDTH = 12;
469
+ function truncate(s, width) {
470
+ if (s.length <= width) return s.padEnd(width);
471
+ return s.slice(0, width - 1) + "\u2026";
472
+ }
427
473
  var listFilesCommand = new Command4("files").description("List your Plaud recordings").option("-p, --page <number>", "Page number", "1").option("-s, --page-size <number>", "Page size", "20").action(async (opts) => {
428
474
  const page = parseInt(opts.page);
429
475
  const pageSize = parseInt(opts.pageSize);
@@ -443,10 +489,15 @@ var listFilesCommand = new Command4("files").description("List your Plaud record
443
489
  console.log(chalk5.bold(`
444
490
  Files on this page: ${result.data.length}
445
491
  `));
492
+ const header = ` ${chalk5.bold("ID".padEnd(ID_WIDTH))} ${chalk5.bold("NAME".padEnd(NAME_WIDTH))} ${chalk5.bold("DATE".padEnd(DATE_WIDTH))} ${chalk5.bold("DURATION")}`;
493
+ console.log(header);
494
+ console.log(chalk5.gray(" " + "\u2500".repeat(ID_WIDTH + NAME_WIDTH + DATE_WIDTH + 16)));
446
495
  for (const file of result.data) {
447
- const date = new Date(file.created_at).toLocaleDateString();
448
- const duration = file.duration ? `${Math.round(file.duration / 60)}min` : "";
449
- console.log(` ${chalk5.cyan(file.id)} ${file.name} ${chalk5.gray(date)} ${chalk5.gray(duration)}`);
496
+ const id = chalk5.cyan(file.id.padEnd(ID_WIDTH));
497
+ const name = truncate(file.name ?? "", NAME_WIDTH);
498
+ const date = chalk5.gray(formatDate(file.created_at).padEnd(DATE_WIDTH));
499
+ const duration = chalk5.gray(formatDuration(file.duration));
500
+ console.log(` ${id} ${name} ${date} ${duration}`);
450
501
  }
451
502
  console.log(chalk5.gray(`
452
503
  Page ${result.page}`));
@@ -473,12 +524,6 @@ Page ${result.page}`));
473
524
  import { Command as Command5 } from "commander";
474
525
  import chalk6 from "chalk";
475
526
  import ora4 from "ora";
476
- function formatDuration(ms) {
477
- const totalSeconds = Math.floor(ms / 1e3);
478
- const minutes = Math.floor(totalSeconds / 60);
479
- const seconds = totalSeconds % 60;
480
- return minutes > 0 ? `${minutes}min ${seconds}sec` : `${seconds}sec`;
481
- }
482
527
  var getFileCommand = new Command5("file").description("Get details of a specific Plaud recording").argument("<file_id>", "The file ID to retrieve").action(async (fileId) => {
483
528
  const client2 = getClient();
484
529
  const spinner = ora4("Fetching file...").start();
@@ -494,7 +539,7 @@ var getFileCommand = new Command5("file").description("Get details of a specific
494
539
  console.log(` ${chalk6.cyan("name")}: ${file.name}`);
495
540
  console.log(` ${chalk6.cyan("created_at")}: ${file.created_at}`);
496
541
  console.log(` ${chalk6.cyan("start_at")}: ${file.start_at ?? "-"}`);
497
- console.log(` ${chalk6.cyan("duration")}: ${file.duration ? formatDuration(file.duration) : "-"}`);
542
+ console.log(` ${chalk6.cyan("duration")}: ${formatDuration(file.duration)}`);
498
543
  console.log(` ${chalk6.cyan("serial_number")}: ${file.serial_number ?? "-"}`);
499
544
  console.log(` ${chalk6.cyan("audio")}: ${file.presigned_url ? chalk6.green("available") : chalk6.gray("unavailable")}`);
500
545
  console.log(` ${chalk6.cyan("transcript")}: ${hasTranscript ? chalk6.green("available") : chalk6.gray("unavailable")}`);
@@ -565,12 +610,6 @@ import { Command as Command7 } from "commander";
565
610
  import chalk8 from "chalk";
566
611
  import ora6 from "ora";
567
612
  import { writeFile as writeFile2 } from "fs/promises";
568
- function formatTime(ms) {
569
- const totalSeconds = Math.floor(ms / 1e3);
570
- const minutes = Math.floor(totalSeconds / 60);
571
- const seconds = totalSeconds % 60;
572
- return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
573
- }
574
613
  var transcriptCommand = new Command7("transcript").description("Get the transcript for a Plaud recording").argument("<file_id>", "The file ID to retrieve transcript for").option("-o, --output <file>", "Save transcript to a file").action(async (fileId, opts) => {
575
614
  const client2 = getClient();
576
615
  const spinner = ora6("Fetching transcript...").start();
@@ -667,16 +706,408 @@ Summary: ${file.name}
667
706
  });
668
707
 
669
708
  // src/commands/version.ts
709
+ import { Command as Command10 } from "commander";
710
+ import chalk11 from "chalk";
711
+ import { readFile as readFile2, writeFile as writeFile4, mkdir as mkdir2 } from "fs/promises";
712
+ import { homedir as homedir3 } from "os";
713
+ import { join as join3, dirname } from "path";
714
+
715
+ // src/commands/update.ts
670
716
  import { Command as Command9 } from "commander";
671
- var versionCommand = new Command9("version").description("Show CLI version information").action(() => {
672
- console.log(`plaud ${"0.1.5"}`);
673
- if ("8ad7434") console.log(`commit ${"8ad7434"}`);
674
- if ("2026-04-16T09:19:58.868Z") console.log(`built ${"2026-04-16T09:19:58.868Z"}`);
717
+ import chalk10 from "chalk";
718
+ import ora8 from "ora";
719
+ var PKG_NAME = "@plaud-ai/cli";
720
+ var REGISTRY = "https://registry.npmjs.org";
721
+ async function fetchLatestVersion(timeoutMs = 5e3) {
722
+ const ctrl = new AbortController();
723
+ const t = setTimeout(() => ctrl.abort(), timeoutMs);
724
+ try {
725
+ const res = await fetch(`${REGISTRY}/${encodeURIComponent(PKG_NAME)}/latest`, { signal: ctrl.signal });
726
+ if (!res.ok) return null;
727
+ const json = await res.json();
728
+ return json.version ?? null;
729
+ } catch {
730
+ return null;
731
+ } finally {
732
+ clearTimeout(t);
733
+ }
734
+ }
735
+ function isNewer(current, latest) {
736
+ const c = current.split(".").map(Number);
737
+ const l = latest.split(".").map(Number);
738
+ for (let i = 0; i < Math.max(c.length, l.length); i++) {
739
+ const cv = c[i] ?? 0;
740
+ const lv = l[i] ?? 0;
741
+ if (lv > cv) return true;
742
+ if (lv < cv) return false;
743
+ }
744
+ return false;
745
+ }
746
+ var updateCommand = new Command9("update").description("Check npm for the latest Plaud CLI and print the upgrade command").action(async () => {
747
+ const current = "0.2.0";
748
+ const spinner = ora8("Checking npm for latest version...").start();
749
+ const latest = await fetchLatestVersion();
750
+ spinner.stop();
751
+ if (!latest) {
752
+ printError("UNREACHABLE", "Could not reach npm registry to check for updates.");
753
+ process.exit(ExitCode.UNREACHABLE);
754
+ }
755
+ if (!isNewer(current, latest)) {
756
+ console.log(chalk10.green(`You're on the latest version (${current}).`));
757
+ return;
758
+ }
759
+ console.log(chalk10.yellow(`A newer version is available: ${current} \u2192 ${latest}`));
760
+ console.log();
761
+ console.log("Run this command to upgrade:");
762
+ console.log();
763
+ console.log(chalk10.bold(` npm install -g ${PKG_NAME}@latest`));
764
+ console.log();
765
+ });
766
+
767
+ // src/commands/version.ts
768
+ var CACHE_PATH = join3(homedir3(), ".plaud", "version-check.json");
769
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
770
+ async function readCache() {
771
+ try {
772
+ const raw = await readFile2(CACHE_PATH, "utf-8");
773
+ const parsed = JSON.parse(raw);
774
+ if (typeof parsed.checked_at !== "number" || typeof parsed.latest !== "string") return null;
775
+ return parsed;
776
+ } catch {
777
+ return null;
778
+ }
779
+ }
780
+ async function writeCache(entry) {
781
+ try {
782
+ await mkdir2(dirname(CACHE_PATH), { recursive: true });
783
+ await writeFile4(CACHE_PATH, JSON.stringify(entry), "utf-8");
784
+ } catch {
785
+ }
786
+ }
787
+ async function checkForUpdate(current) {
788
+ const cached = await readCache();
789
+ const now = Date.now();
790
+ let latest = null;
791
+ if (cached && now - cached.checked_at < CACHE_TTL_MS) {
792
+ latest = cached.latest;
793
+ } else {
794
+ latest = await fetchLatestVersion(2e3);
795
+ if (latest) await writeCache({ checked_at: now, latest });
796
+ }
797
+ if (!latest) return null;
798
+ return isNewer(current, latest) ? latest : null;
799
+ }
800
+ var versionCommand = new Command10("version").description("Show CLI version information").action(async () => {
801
+ const current = "0.2.0";
802
+ console.log(`plaud ${current}`);
803
+ if ("9ac224a") console.log(`commit ${"9ac224a"}`);
804
+ if ("2026-04-24T08:28:52.747Z") console.log(`built ${"2026-04-24T08:28:52.747Z"}`);
805
+ if (current === "unknown") return;
806
+ const newer = await checkForUpdate(current);
807
+ if (newer) {
808
+ console.log();
809
+ console.log(chalk11.yellow(`A newer version is available: ${current} \u2192 ${newer}`));
810
+ console.log(chalk11.gray(`Run \`plaud update\` for upgrade instructions.`));
811
+ }
812
+ });
813
+
814
+ // src/commands/search.ts
815
+ import { Command as Command11 } from "commander";
816
+ import chalk12 from "chalk";
817
+ import ora9 from "ora";
818
+ var MAX_PAGES = 5;
819
+ var PAGE_SIZE = 100;
820
+ function parseDate(s) {
821
+ if (!s) return null;
822
+ const d = new Date(s);
823
+ return Number.isNaN(d.getTime()) ? null : d.getTime();
824
+ }
825
+ 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) => {
826
+ const max = parseInt(opts.max);
827
+ if (isNaN(max) || max < 1 || max > 500) {
828
+ printError("INVALID_ARGS", "--max must be a number between 1 and 500");
829
+ process.exit(ExitCode.ERROR);
830
+ }
831
+ const from = parseDate(opts.from);
832
+ const toRaw = parseDate(opts.to);
833
+ const to = toRaw !== null ? toRaw + 24 * 60 * 60 * 1e3 - 1 : null;
834
+ if (opts.from && from === null) {
835
+ printError("INVALID_ARGS", `Invalid --from date: ${opts.from}`);
836
+ process.exit(ExitCode.ERROR);
837
+ }
838
+ if (opts.to && toRaw === null) {
839
+ printError("INVALID_ARGS", `Invalid --to date: ${opts.to}`);
840
+ process.exit(ExitCode.ERROR);
841
+ }
842
+ const client2 = getClient();
843
+ const spinner = ora9(`Searching for "${keyword}"...`).start();
844
+ const q = keyword.toLowerCase();
845
+ try {
846
+ const matches = [];
847
+ let scanned = 0;
848
+ let truncated = false;
849
+ for (let page = 1; page <= MAX_PAGES; page++) {
850
+ spinner.text = `Searching for "${keyword}"... (page ${page})`;
851
+ const result = await client2.listFiles(page, PAGE_SIZE);
852
+ scanned += result.data.length;
853
+ for (const file of result.data) {
854
+ if (!(file.name ?? "").toLowerCase().includes(q)) continue;
855
+ if (from !== null || to !== null) {
856
+ const created = parseDate(file.created_at);
857
+ if (created === null) continue;
858
+ if (from !== null && created < from) continue;
859
+ if (to !== null && created > to) continue;
860
+ }
861
+ matches.push(file);
862
+ if (matches.length >= max) break;
863
+ }
864
+ if (matches.length >= max) break;
865
+ if (result.data.length < PAGE_SIZE) break;
866
+ if (page === MAX_PAGES) truncated = true;
867
+ }
868
+ spinner.stop();
869
+ if (matches.length === 0) {
870
+ console.log(chalk12.yellow(`No recordings matched "${keyword}" in ${scanned} scanned.`));
871
+ if (truncated) {
872
+ console.log(chalk12.gray(`(Scanned first ${MAX_PAGES * PAGE_SIZE}; narrow the window with --from/--to if your target is older.)`));
873
+ }
874
+ return;
875
+ }
876
+ console.log(chalk12.bold(`
877
+ Matched ${matches.length}${truncated ? `+` : ""} of ${scanned} scanned
878
+ `));
879
+ for (const file of matches) {
880
+ const id = chalk12.cyan(file.id);
881
+ const date = chalk12.gray(formatDate(file.created_at));
882
+ const duration = chalk12.gray(formatDuration(file.duration));
883
+ console.log(` ${id} ${file.name ?? ""} ${date} ${duration}`);
884
+ }
885
+ if (truncated) {
886
+ console.log(chalk12.gray(`
887
+ (Truncated at ${MAX_PAGES} pages; results beyond the most recent ${MAX_PAGES * PAGE_SIZE} not scanned.)`));
888
+ }
889
+ console.log();
890
+ } catch (err) {
891
+ spinner.stop();
892
+ if (isAuthError(err)) {
893
+ printError("AUTH_FAILED", "Token invalid or expired. Run `plaud login`.");
894
+ process.exit(ExitCode.AUTH_FAILED);
895
+ }
896
+ if (isNetworkError(err)) {
897
+ printError("UNREACHABLE", "Cannot reach Plaud servers. Check your network.", err);
898
+ process.exit(ExitCode.UNREACHABLE);
899
+ }
900
+ if (isTimeoutError(err)) {
901
+ printError("TIMEOUT", "Request timed out.");
902
+ process.exit(ExitCode.TIMEOUT);
903
+ }
904
+ printError("FETCH_FAILED", "Failed to search files.", err);
905
+ process.exit(ExitCode.ERROR);
906
+ }
907
+ });
908
+
909
+ // src/commands/recent.ts
910
+ import { Command as Command12 } from "commander";
911
+ import chalk13 from "chalk";
912
+ import ora10 from "ora";
913
+ var MAX_PAGES2 = 3;
914
+ var PAGE_SIZE2 = 100;
915
+ var recentCommand = new Command12("recent").description("List recordings from the last N days (default 7)").option("-d, --days <n>", "Number of days to include", "7").action(async (opts) => {
916
+ const days = parseInt(opts.days);
917
+ if (isNaN(days) || days < 1 || days > 365) {
918
+ printError("INVALID_ARGS", "--days must be a number between 1 and 365");
919
+ process.exit(ExitCode.ERROR);
920
+ }
921
+ const from = Date.now() - days * 24 * 60 * 60 * 1e3;
922
+ const client2 = getClient();
923
+ const spinner = ora10(`Fetching recordings from the last ${days} days...`).start();
924
+ try {
925
+ const matches = [];
926
+ for (let page = 1; page <= MAX_PAGES2; page++) {
927
+ const result = await client2.listFiles(page, PAGE_SIZE2);
928
+ let allOlder = true;
929
+ let hasValidTimestamps = false;
930
+ for (const file of result.data) {
931
+ const created = new Date(file.created_at).getTime();
932
+ if (Number.isNaN(created)) continue;
933
+ hasValidTimestamps = true;
934
+ if (created >= from) {
935
+ matches.push(file);
936
+ allOlder = false;
937
+ }
938
+ }
939
+ if (hasValidTimestamps && allOlder && result.data.length > 0) break;
940
+ if (result.data.length < PAGE_SIZE2) break;
941
+ }
942
+ spinner.stop();
943
+ if (matches.length === 0) {
944
+ console.log(chalk13.yellow(`No recordings in the last ${days} days.`));
945
+ return;
946
+ }
947
+ console.log(chalk13.bold(`
948
+ Recordings in the last ${days} days: ${matches.length}
949
+ `));
950
+ for (const file of matches) {
951
+ const id = chalk13.cyan(file.id);
952
+ const date = chalk13.gray(formatDate(file.created_at));
953
+ const duration = chalk13.gray(formatDuration(file.duration));
954
+ console.log(` ${id} ${file.name ?? ""} ${date} ${duration}`);
955
+ }
956
+ console.log();
957
+ } catch (err) {
958
+ spinner.stop();
959
+ if (isAuthError(err)) {
960
+ printError("AUTH_FAILED", "Token invalid or expired. Run `plaud login`.");
961
+ process.exit(ExitCode.AUTH_FAILED);
962
+ }
963
+ if (isNetworkError(err)) {
964
+ printError("UNREACHABLE", "Cannot reach Plaud servers. Check your network.", err);
965
+ process.exit(ExitCode.UNREACHABLE);
966
+ }
967
+ if (isTimeoutError(err)) {
968
+ printError("TIMEOUT", "Request timed out.");
969
+ process.exit(ExitCode.TIMEOUT);
970
+ }
971
+ printError("FETCH_FAILED", "Failed to fetch recent files.", err);
972
+ process.exit(ExitCode.ERROR);
973
+ }
974
+ });
975
+ var todayCommand = new Command12("today").description("List recordings created today").action(async () => {
976
+ const start = /* @__PURE__ */ new Date();
977
+ start.setHours(0, 0, 0, 0);
978
+ const startMs = start.getTime();
979
+ const client2 = getClient();
980
+ const spinner = ora10("Fetching today's recordings...").start();
981
+ try {
982
+ const result = await client2.listFiles(1, 50);
983
+ spinner.stop();
984
+ const matches = result.data.filter((f) => {
985
+ const t = new Date(f.created_at).getTime();
986
+ return !Number.isNaN(t) && t >= startMs;
987
+ });
988
+ if (matches.length === 0) {
989
+ console.log(chalk13.yellow("No recordings created today."));
990
+ return;
991
+ }
992
+ console.log(chalk13.bold(`
993
+ Today's recordings: ${matches.length}
994
+ `));
995
+ for (const file of matches) {
996
+ const id = chalk13.cyan(file.id);
997
+ const date = chalk13.gray(formatDate(file.created_at));
998
+ const duration = chalk13.gray(formatDuration(file.duration));
999
+ console.log(` ${id} ${file.name ?? ""} ${date} ${duration}`);
1000
+ }
1001
+ console.log();
1002
+ } catch (err) {
1003
+ spinner.stop();
1004
+ if (isAuthError(err)) {
1005
+ printError("AUTH_FAILED", "Token invalid or expired. Run `plaud login`.");
1006
+ process.exit(ExitCode.AUTH_FAILED);
1007
+ }
1008
+ if (isNetworkError(err)) {
1009
+ printError("UNREACHABLE", "Cannot reach Plaud servers. Check your network.", err);
1010
+ process.exit(ExitCode.UNREACHABLE);
1011
+ }
1012
+ if (isTimeoutError(err)) {
1013
+ printError("TIMEOUT", "Request timed out.");
1014
+ process.exit(ExitCode.TIMEOUT);
1015
+ }
1016
+ printError("FETCH_FAILED", "Failed to fetch today's files.", err);
1017
+ process.exit(ExitCode.ERROR);
1018
+ }
675
1019
  });
676
1020
 
1021
+ // src/commands/wizard.ts
1022
+ import chalk14 from "chalk";
1023
+ import { select, input, confirm } from "@inquirer/prompts";
1024
+ import { spawnSync } from "child_process";
1025
+ function runSelf(args) {
1026
+ const result = spawnSync(process.execPath, [process.argv[1] ?? "", ...args], {
1027
+ stdio: "inherit"
1028
+ });
1029
+ return result.status ?? 0;
1030
+ }
1031
+ async function ensureLoggedIn() {
1032
+ const client2 = getClient();
1033
+ try {
1034
+ const token = await client2.auth.getAccessToken();
1035
+ if (token) return true;
1036
+ } catch {
1037
+ }
1038
+ const go = await confirm({ message: "You are not logged in. Open the browser to log in now?", default: true });
1039
+ if (!go) return false;
1040
+ const code = runSelf(["login"]);
1041
+ return code === 0;
1042
+ }
1043
+ async function runWizard() {
1044
+ console.log(chalk14.bold("\nWelcome to Plaud\n"));
1045
+ try {
1046
+ const ok = await ensureLoggedIn();
1047
+ if (!ok) {
1048
+ console.log(chalk14.gray("Exiting. Run `plaud login` when ready."));
1049
+ return;
1050
+ }
1051
+ const action = await select({
1052
+ message: "What do you want to do?",
1053
+ choices: [
1054
+ { name: "Browse my recordings", value: "browse" },
1055
+ { name: "Search by name", value: "search" },
1056
+ { name: "Recent recordings (7 days)", value: "recent" },
1057
+ { name: "Today's recordings", value: "today" },
1058
+ { name: "Read a transcript", value: "transcript" },
1059
+ { name: "Read an AI summary", value: "summary" },
1060
+ { name: "Who am I logged in as", value: "me" },
1061
+ { name: "Log out", value: "logout" },
1062
+ { name: "Quit", value: "quit" }
1063
+ ]
1064
+ });
1065
+ switch (action) {
1066
+ case "browse":
1067
+ runSelf(["files"]);
1068
+ break;
1069
+ case "search": {
1070
+ const keyword = await input({ message: "Keyword to search for:", validate: (v) => v.trim().length > 0 || "Enter a keyword" });
1071
+ runSelf(["search", keyword.trim()]);
1072
+ break;
1073
+ }
1074
+ case "recent":
1075
+ runSelf(["recent"]);
1076
+ break;
1077
+ case "today":
1078
+ runSelf(["today"]);
1079
+ break;
1080
+ case "transcript": {
1081
+ const id = await input({ message: "File ID:", validate: (v) => v.trim().length > 0 || "Enter a file ID" });
1082
+ runSelf(["transcript", id.trim()]);
1083
+ break;
1084
+ }
1085
+ case "summary": {
1086
+ const id = await input({ message: "File ID:", validate: (v) => v.trim().length > 0 || "Enter a file ID" });
1087
+ runSelf(["summary", id.trim()]);
1088
+ break;
1089
+ }
1090
+ case "me":
1091
+ runSelf(["me"]);
1092
+ break;
1093
+ case "logout":
1094
+ runSelf(["logout"]);
1095
+ break;
1096
+ case "quit":
1097
+ return;
1098
+ }
1099
+ } catch (err) {
1100
+ const name = err?.name;
1101
+ if (name === "ExitPromptError") return;
1102
+ throw err;
1103
+ }
1104
+ }
1105
+
677
1106
  // src/index.ts
678
- var program = new Command10();
679
- program.name("plaud").description("Plaud CLI - manage your Plaud recordings");
1107
+ var program = new Command13();
1108
+ program.name("plaud").description("Plaud CLI - manage your Plaud recordings").action(async () => {
1109
+ await runWizard();
1110
+ });
680
1111
  program.addCommand(loginCommand);
681
1112
  program.addCommand(logoutCommand);
682
1113
  program.addCommand(meCommand);
@@ -685,5 +1116,9 @@ program.addCommand(getFileCommand);
685
1116
  program.addCommand(audioCommand);
686
1117
  program.addCommand(transcriptCommand);
687
1118
  program.addCommand(summaryCommand);
1119
+ program.addCommand(searchCommand);
1120
+ program.addCommand(recentCommand);
1121
+ program.addCommand(todayCommand);
1122
+ program.addCommand(updateCommand);
688
1123
  program.addCommand(versionCommand);
689
- program.parse();
1124
+ program.parseAsync();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaud-ai/cli",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "plaud": "dist/index.js"
@@ -8,12 +8,12 @@
8
8
  "files": [
9
9
  "dist"
10
10
  ],
11
- "scripts": {
12
- "build": "tsup",
13
- "dev": "tsup --watch",
14
- "clean": "rm -rf dist"
11
+ "publishConfig": {
12
+ "registry": "https://registry.npmjs.org/",
13
+ "access": "public"
15
14
  },
16
15
  "dependencies": {
16
+ "@inquirer/prompts": "^7.2.0",
17
17
  "chalk": "^5.4.0",
18
18
  "commander": "^13.0.0",
19
19
  "dotenv": "^17.3.1",
@@ -22,8 +22,13 @@
22
22
  "yaml": "^2.8.3"
23
23
  },
24
24
  "devDependencies": {
25
- "@plaud-ai/shared": "workspace:*",
26
25
  "@types/node": "^25.5.0",
27
- "typescript": "^5.7.0"
26
+ "typescript": "^5.7.0",
27
+ "@plaud-ai/shared": "0.1.0"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "dev": "tsup --watch",
32
+ "clean": "rm -rf dist"
28
33
  }
29
- }
34
+ }