@plaud-ai/cli 0.3.10 → 0.3.12

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 +352 -240
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -20244,237 +20244,27 @@ function extractIdentity(user) {
20244
20244
  };
20245
20245
  }
20246
20246
 
20247
- // src/telemetry.ts
20248
- import { randomUUID as randomUUID2 } from "crypto";
20249
-
20250
- // src/exit-codes.ts
20251
- var ExitCode = {
20252
- OK: 0,
20253
- ERROR: 1,
20254
- // 参数错误、未知异常
20255
- AUTH_FAILED: 2,
20256
- // Token 无效或过期(HTTP 401)
20257
- UNREACHABLE: 3,
20258
- // 后端服务不可达(网络错误)
20259
- TIMEOUT: 4
20260
- // 请求超时
20261
- };
20262
-
20263
- // src/telemetry.ts
20264
- var currentCommand = "unknown";
20265
- var currentRequestId = "";
20266
- var startedAt = 0;
20267
- var currentFileId;
20268
- var HARD_EXIT_GRACE_MS = 500;
20269
- function drainAndExit(code) {
20270
- process.exitCode = code;
20271
- setTimeout(() => process.exit(code), HARD_EXIT_GRACE_MS).unref();
20272
- return new Promise(() => {
20273
- });
20274
- }
20275
- function errorTypeForExit(code) {
20276
- switch (code) {
20277
- case ExitCode.AUTH_FAILED:
20278
- return "auth";
20279
- case ExitCode.UNREACHABLE:
20280
- return "network";
20281
- case ExitCode.TIMEOUT:
20282
- return "timeout";
20283
- default:
20284
- return "unknown";
20285
- }
20286
- }
20287
- function telemetryClick(command, fileId) {
20288
- currentCommand = command;
20289
- currentRequestId = randomUUID2();
20290
- startedAt = Date.now();
20291
- currentFileId = fileId;
20292
- capture("cli:command:click", {
20293
- command_name: command,
20294
- request_id: currentRequestId,
20295
- passive: false,
20296
- ...fileId ? { file_id: fileId } : {}
20297
- });
20298
- }
20299
- async function telemetryExit(code) {
20300
- const fileProp = currentFileId ? { file_id: currentFileId } : {};
20301
- if (code === ExitCode.OK) {
20302
- capture("cli:command:success", {
20303
- command_name: currentCommand,
20304
- request_id: currentRequestId,
20305
- duration_ms: Date.now() - startedAt,
20306
- passive: true,
20307
- ...fileProp
20308
- });
20309
- } else {
20310
- capture("cli:command:error", {
20311
- command_name: currentCommand,
20312
- request_id: currentRequestId,
20313
- passive: true,
20314
- error_type: errorTypeForExit(code),
20315
- ...fileProp
20316
- });
20317
- }
20318
- await shutdown();
20319
- return drainAndExit(code);
20320
- }
20321
-
20322
- // src/commands/version.ts
20323
- import { Command as Command2 } from "commander";
20324
- import chalk3 from "chalk";
20325
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
20326
- import { homedir as homedir2 } from "os";
20327
- import { join as join2, dirname as dirname2 } from "path";
20328
-
20329
- // src/commands/update.ts
20330
- import { Command } from "commander";
20331
- import chalk2 from "chalk";
20332
- import ora from "ora";
20333
-
20334
- // src/error.ts
20335
- import chalk from "chalk";
20336
- function printError(code, message, detail) {
20337
- console.error(chalk.red(`\u2717 [${code}] ${message}`));
20338
- if (detail) console.error(chalk.gray(String(detail)));
20339
- }
20340
- function isAuthError(err) {
20341
- if (!(err instanceof Error)) return false;
20342
- return err.message.includes("Not authenticated") || err.message.includes("401");
20343
- }
20344
- function isNetworkError(err) {
20345
- if (!(err instanceof Error)) return false;
20346
- return err instanceof TypeError || err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND") || err.message.includes("fetch failed");
20347
- }
20348
- function isTimeoutError(err) {
20349
- if (!(err instanceof Error)) return false;
20350
- return err.name === "AbortError" || err.message.toLowerCase().includes("timeout");
20351
- }
20352
-
20353
- // src/commands/update.ts
20354
- var PKG_NAME = "@plaud-ai/cli";
20355
- var REGISTRY = "https://registry.npmjs.org";
20356
- async function fetchLatestVersion(timeoutMs = 5e3) {
20357
- const ctrl = new AbortController();
20358
- const t = setTimeout(() => ctrl.abort(), timeoutMs);
20359
- try {
20360
- const res = await fetch(`${REGISTRY}/${encodeURIComponent(PKG_NAME)}/latest`, { signal: ctrl.signal });
20361
- if (!res.ok) return null;
20362
- const json = await res.json();
20363
- return json.version ?? null;
20364
- } catch {
20365
- return null;
20366
- } finally {
20367
- clearTimeout(t);
20368
- }
20369
- }
20370
- function isNewer(current, latest) {
20371
- const c = current.split(".").map(Number);
20372
- const l = latest.split(".").map(Number);
20373
- for (let i = 0; i < Math.max(c.length, l.length); i++) {
20374
- const cv = c[i] ?? 0;
20375
- const lv = l[i] ?? 0;
20376
- if (lv > cv) return true;
20377
- if (lv < cv) return false;
20378
- }
20379
- return false;
20380
- }
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.10";
20383
- const spinner = ora("Checking npm for latest version...").start();
20384
- const latest = await fetchLatestVersion();
20385
- spinner.stop();
20386
- if (!latest) {
20387
- printError("UNREACHABLE", "Could not reach npm registry to check for updates.");
20388
- await telemetryExit(ExitCode.UNREACHABLE);
20389
- return;
20390
- }
20391
- if (!isNewer(current, latest)) {
20392
- console.log(chalk2.green(`You're on the latest version (${current}).`));
20393
- return;
20394
- }
20395
- console.log(chalk2.yellow(`A newer version is available: ${current} \u2192 ${latest}`));
20396
- console.log();
20397
- console.log("Run this command to upgrade:");
20398
- console.log();
20399
- console.log(chalk2.bold(` npm install -g ${PKG_NAME}@latest`));
20400
- console.log();
20401
- });
20402
-
20403
- // src/commands/version.ts
20404
- var CACHE_PATH = join2(homedir2(), ".plaud", "version-check.json");
20405
- var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
20406
- async function readCache() {
20407
- try {
20408
- const raw = await readFile2(CACHE_PATH, "utf-8");
20409
- const parsed = JSON.parse(raw);
20410
- if (typeof parsed.checked_at !== "number" || typeof parsed.latest !== "string") return null;
20411
- return parsed;
20412
- } catch {
20413
- return null;
20414
- }
20415
- }
20416
- async function writeCache(entry) {
20417
- try {
20418
- await mkdir2(dirname2(CACHE_PATH), { recursive: true });
20419
- await writeFile2(CACHE_PATH, JSON.stringify(entry), "utf-8");
20420
- } catch {
20421
- }
20422
- }
20423
- async function checkForUpdate(current) {
20424
- const cached = await readCache();
20425
- const now = Date.now();
20426
- let latest = null;
20427
- if (cached && now - cached.checked_at < CACHE_TTL_MS) {
20428
- latest = cached.latest;
20429
- } else {
20430
- latest = await fetchLatestVersion(2e3);
20431
- if (latest) await writeCache({ checked_at: now, latest });
20432
- }
20433
- if (!latest) return null;
20434
- return isNewer(current, latest) ? latest : null;
20435
- }
20436
- var versionCommand = new Command2("version").description("Show CLI version information").action(async () => {
20437
- const current = "0.3.10";
20438
- console.log(`plaud ${current}`);
20439
- if ("1afbfa8") console.log(`commit ${"1afbfa8"}`);
20440
- if ("2026-08-19T04:14:57.144Z") console.log(`built ${"2026-08-19T04:14:57.144Z"}`);
20441
- if (current === "unknown") return;
20442
- const newer = await checkForUpdate(current);
20443
- if (newer) {
20444
- console.log();
20445
- console.log(chalk3.yellow(`A newer version is available: ${current} \u2192 ${newer}`));
20446
- console.log(chalk3.gray(`Run \`plaud update\` for upgrade instructions.`));
20447
- }
20448
- });
20449
-
20450
- // src/commands/login.ts
20451
- import { Command as Command3 } from "commander";
20452
- import { createServer as createServer2 } from "net";
20453
- import open from "open";
20454
- import chalk4 from "chalk";
20455
- import ora2 from "ora";
20456
-
20457
20247
  // ../shared/dist/oauth.js
20458
20248
  import { randomBytes, createHash } from "crypto";
20459
20249
 
20460
20250
  // ../shared/dist/token-store.js
20461
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, rm as rm2 } from "fs/promises";
20462
- import { join as join3 } from "path";
20463
- import { homedir as homedir3 } from "os";
20251
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rm as rm2 } from "fs/promises";
20252
+ import { join as join2 } from "path";
20253
+ import { homedir as homedir2 } from "os";
20464
20254
  var TokenStore = class {
20465
20255
  configDir;
20466
20256
  tokenPath;
20467
20257
  constructor(filename = "tokens.json") {
20468
- this.configDir = join3(homedir3(), ".plaud");
20469
- this.tokenPath = join3(this.configDir, filename);
20258
+ this.configDir = join2(homedir2(), ".plaud");
20259
+ this.tokenPath = join2(this.configDir, filename);
20470
20260
  }
20471
20261
  async save(tokenSet) {
20472
- await mkdir3(this.configDir, { recursive: true });
20473
- await writeFile3(this.tokenPath, JSON.stringify(tokenSet, null, 2), "utf-8");
20262
+ await mkdir2(this.configDir, { recursive: true });
20263
+ await writeFile2(this.tokenPath, JSON.stringify(tokenSet, null, 2), "utf-8");
20474
20264
  }
20475
20265
  async load() {
20476
20266
  try {
20477
- const data = await readFile3(this.tokenPath, "utf-8");
20267
+ const data = await readFile2(this.tokenPath, "utf-8");
20478
20268
  return JSON.parse(data);
20479
20269
  } catch {
20480
20270
  return null;
@@ -20522,6 +20312,38 @@ function classifyError(err) {
20522
20312
  return "client_error";
20523
20313
  return "unknown";
20524
20314
  }
20315
+ function isTransportError(err) {
20316
+ const type = classifyError(err);
20317
+ return type === "network" || type === "timeout" || type === "server_error";
20318
+ }
20319
+ var MAX_CAUSE_DEPTH = 5;
20320
+ var MAX_DESCRIPTION_LENGTH = 400;
20321
+ function summarizeError(err) {
20322
+ if (!(err instanceof Error))
20323
+ return String(err ?? "unknown error");
20324
+ const code = err.code;
20325
+ const suffix = typeof code === "string" && !err.message.includes(code) ? ` (${code})` : "";
20326
+ return `${err.name}: ${err.message}${suffix}`;
20327
+ }
20328
+ function describeError(err) {
20329
+ const chain = [];
20330
+ const seen = /* @__PURE__ */ new Set();
20331
+ let current = err;
20332
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && current && !seen.has(current); depth++) {
20333
+ seen.add(current);
20334
+ chain.push(summarizeError(current));
20335
+ const aggregated = current instanceof AggregateError ? current.errors : void 0;
20336
+ if (aggregated && aggregated.length > 0) {
20337
+ chain.push(aggregated.map(summarizeError).join("; "));
20338
+ break;
20339
+ }
20340
+ current = current instanceof Error ? current.cause : void 0;
20341
+ }
20342
+ const text = chain.join(" | cause: ");
20343
+ if (text.length === 0)
20344
+ return "unknown error";
20345
+ return text.length > MAX_DESCRIPTION_LENGTH ? `${text.slice(0, MAX_DESCRIPTION_LENGTH - 1)}\u2026` : text;
20346
+ }
20525
20347
  function oauthCallbackErrorType(status) {
20526
20348
  switch (status) {
20527
20349
  case "denied":
@@ -20632,6 +20454,21 @@ var OAuth = class {
20632
20454
  await this.tokenStore.save(tokenSet);
20633
20455
  return tokenSet;
20634
20456
  }
20457
+ /** Are there stored credentials? Local-only — never touches the network, so
20458
+ * logout can tell "nothing to do" from "cannot reach Plaud". */
20459
+ async hasStoredToken() {
20460
+ return await this.tokenStore.load() !== null;
20461
+ }
20462
+ /**
20463
+ * The current access token, refreshing it first when it is about to expire.
20464
+ *
20465
+ * `null` means genuinely unauthenticated: nothing stored, no refresh token, or
20466
+ * the refresh token was rejected. A transport failure instead THROWS — it says
20467
+ * nothing about the credentials, and reporting it as unauthenticated is what
20468
+ * pushed #670875's reporter into a re-login loop (and would have let callers
20469
+ * delete a perfectly good token file). Callers that only want to know whether
20470
+ * we are logged in should use `isTransportError` to tell the two apart.
20471
+ */
20635
20472
  async getAccessToken() {
20636
20473
  const tokenSet = await this.tokenStore.load();
20637
20474
  if (!tokenSet)
@@ -20641,8 +20478,10 @@ var OAuth = class {
20641
20478
  try {
20642
20479
  const refreshed = await this.refresh(tokenSet.refresh_token);
20643
20480
  return refreshed.access_token;
20644
- } catch {
20645
- return null;
20481
+ } catch (err) {
20482
+ if (!isTransportError(err))
20483
+ return null;
20484
+ throw err;
20646
20485
  }
20647
20486
  }
20648
20487
  return null;
@@ -20768,6 +20607,46 @@ var PlaudClient = class {
20768
20607
  }
20769
20608
  };
20770
20609
 
20610
+ // ../shared/dist/proxy.js
20611
+ var PROXY_ENV_VARS = [
20612
+ "HTTP_PROXY",
20613
+ "http_proxy",
20614
+ "HTTPS_PROXY",
20615
+ "https_proxy",
20616
+ "ALL_PROXY",
20617
+ "all_proxy"
20618
+ ];
20619
+ function proxyEnvVars(env = process.env) {
20620
+ return PROXY_ENV_VARS.filter((key) => (env[key] ?? "").trim().length > 0);
20621
+ }
20622
+ async function installProxyDispatcher(env = process.env) {
20623
+ const active = proxyEnvVars(env);
20624
+ if (active.length === 0)
20625
+ return null;
20626
+ try {
20627
+ const { EnvHttpProxyAgent, setGlobalDispatcher } = await import("undici");
20628
+ setGlobalDispatcher(withoutExperimentalWarning(() => new EnvHttpProxyAgent()));
20629
+ return active;
20630
+ } catch {
20631
+ return null;
20632
+ }
20633
+ }
20634
+ function withoutExperimentalWarning(build) {
20635
+ const original = process.emitWarning;
20636
+ process.emitWarning = ((warning, ...rest) => {
20637
+ const [first, second] = rest;
20638
+ const code = typeof first === "object" && first !== null ? first.code : second;
20639
+ if (code === "UNDICI-EHPA")
20640
+ return;
20641
+ return original.call(process, warning, ...rest);
20642
+ });
20643
+ try {
20644
+ return build();
20645
+ } finally {
20646
+ process.emitWarning = original;
20647
+ }
20648
+ }
20649
+
20771
20650
  // ../shared/dist/oauth-callback-server.js
20772
20651
  import { createServer } from "http";
20773
20652
  var SUCCESS_HTML = '<!doctype html><html><head><meta charset="utf-8"><title>Plaud</title></head><body style="font-family:system-ui;padding:2rem;text-align:center;"><h1>Authorization successful!</h1><p>You can close this tab.</p></body></html>';
@@ -20945,6 +20824,216 @@ function parseDateOnly(value) {
20945
20824
  return [y, m, d];
20946
20825
  }
20947
20826
 
20827
+ // src/telemetry.ts
20828
+ import { randomUUID as randomUUID2 } from "crypto";
20829
+
20830
+ // src/exit-codes.ts
20831
+ var ExitCode = {
20832
+ OK: 0,
20833
+ ERROR: 1,
20834
+ // 参数错误、未知异常
20835
+ AUTH_FAILED: 2,
20836
+ // Token 无效或过期(HTTP 401)
20837
+ UNREACHABLE: 3,
20838
+ // 后端服务不可达(网络错误)
20839
+ TIMEOUT: 4
20840
+ // 请求超时
20841
+ };
20842
+
20843
+ // src/telemetry.ts
20844
+ var currentCommand = "unknown";
20845
+ var currentRequestId = "";
20846
+ var startedAt = 0;
20847
+ var currentFileId;
20848
+ var HARD_EXIT_GRACE_MS = 500;
20849
+ function drainAndExit(code) {
20850
+ process.exitCode = code;
20851
+ setTimeout(() => process.exit(code), HARD_EXIT_GRACE_MS).unref();
20852
+ return new Promise(() => {
20853
+ });
20854
+ }
20855
+ function errorTypeForExit(code) {
20856
+ switch (code) {
20857
+ case ExitCode.AUTH_FAILED:
20858
+ return "auth";
20859
+ case ExitCode.UNREACHABLE:
20860
+ return "network";
20861
+ case ExitCode.TIMEOUT:
20862
+ return "timeout";
20863
+ default:
20864
+ return "unknown";
20865
+ }
20866
+ }
20867
+ function telemetryClick(command, fileId) {
20868
+ currentCommand = command;
20869
+ currentRequestId = randomUUID2();
20870
+ startedAt = Date.now();
20871
+ currentFileId = fileId;
20872
+ capture("cli:command:click", {
20873
+ command_name: command,
20874
+ request_id: currentRequestId,
20875
+ passive: false,
20876
+ ...fileId ? { file_id: fileId } : {}
20877
+ });
20878
+ }
20879
+ async function telemetryExit(code) {
20880
+ const fileProp = currentFileId ? { file_id: currentFileId } : {};
20881
+ if (code === ExitCode.OK) {
20882
+ capture("cli:command:success", {
20883
+ command_name: currentCommand,
20884
+ request_id: currentRequestId,
20885
+ duration_ms: Date.now() - startedAt,
20886
+ passive: true,
20887
+ ...fileProp
20888
+ });
20889
+ } else {
20890
+ capture("cli:command:error", {
20891
+ command_name: currentCommand,
20892
+ request_id: currentRequestId,
20893
+ passive: true,
20894
+ error_type: errorTypeForExit(code),
20895
+ ...fileProp
20896
+ });
20897
+ }
20898
+ await shutdown();
20899
+ return drainAndExit(code);
20900
+ }
20901
+
20902
+ // src/commands/version.ts
20903
+ import { Command as Command2 } from "commander";
20904
+ import chalk3 from "chalk";
20905
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
20906
+ import { homedir as homedir3 } from "os";
20907
+ import { join as join3, dirname as dirname2 } from "path";
20908
+
20909
+ // src/commands/update.ts
20910
+ import { Command } from "commander";
20911
+ import chalk2 from "chalk";
20912
+ import ora from "ora";
20913
+
20914
+ // src/error.ts
20915
+ import chalk from "chalk";
20916
+ function printError(code, message, detail) {
20917
+ console.error(chalk.red(`\u2717 [${code}] ${message}`));
20918
+ if (detail) console.error(chalk.gray(describeError(detail)));
20919
+ }
20920
+ function isAuthError(err) {
20921
+ if (!(err instanceof Error)) return false;
20922
+ return err.message.includes("Not authenticated") || err.message.includes("401");
20923
+ }
20924
+ function isNetworkError(err) {
20925
+ if (!(err instanceof Error)) return false;
20926
+ return err instanceof TypeError || err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND") || err.message.includes("fetch failed");
20927
+ }
20928
+ function isTimeoutError(err) {
20929
+ if (!(err instanceof Error)) return false;
20930
+ return err.name === "AbortError" || err.message.toLowerCase().includes("timeout");
20931
+ }
20932
+
20933
+ // src/commands/update.ts
20934
+ var PKG_NAME = "@plaud-ai/cli";
20935
+ var REGISTRY = "https://registry.npmjs.org";
20936
+ async function fetchLatestVersion(timeoutMs = 5e3) {
20937
+ const ctrl = new AbortController();
20938
+ const t = setTimeout(() => ctrl.abort(), timeoutMs);
20939
+ try {
20940
+ const res = await fetch(`${REGISTRY}/${encodeURIComponent(PKG_NAME)}/latest`, { signal: ctrl.signal });
20941
+ if (!res.ok) return null;
20942
+ const json = await res.json();
20943
+ return json.version ?? null;
20944
+ } catch {
20945
+ return null;
20946
+ } finally {
20947
+ clearTimeout(t);
20948
+ }
20949
+ }
20950
+ function isNewer(current, latest) {
20951
+ const c = current.split(".").map(Number);
20952
+ const l = latest.split(".").map(Number);
20953
+ for (let i = 0; i < Math.max(c.length, l.length); i++) {
20954
+ const cv = c[i] ?? 0;
20955
+ const lv = l[i] ?? 0;
20956
+ if (lv > cv) return true;
20957
+ if (lv < cv) return false;
20958
+ }
20959
+ return false;
20960
+ }
20961
+ var updateCommand = new Command("update").description("Check npm for the latest Plaud CLI and print the upgrade command").action(async () => {
20962
+ const current = "0.3.12";
20963
+ const spinner = ora("Checking npm for latest version...").start();
20964
+ const latest = await fetchLatestVersion();
20965
+ spinner.stop();
20966
+ if (!latest) {
20967
+ printError("UNREACHABLE", "Could not reach npm registry to check for updates.");
20968
+ await telemetryExit(ExitCode.UNREACHABLE);
20969
+ return;
20970
+ }
20971
+ if (!isNewer(current, latest)) {
20972
+ console.log(chalk2.green(`You're on the latest version (${current}).`));
20973
+ return;
20974
+ }
20975
+ console.log(chalk2.yellow(`A newer version is available: ${current} \u2192 ${latest}`));
20976
+ console.log();
20977
+ console.log("Run this command to upgrade:");
20978
+ console.log();
20979
+ console.log(chalk2.bold(` npm install -g ${PKG_NAME}@latest`));
20980
+ console.log();
20981
+ });
20982
+
20983
+ // src/commands/version.ts
20984
+ var CACHE_PATH = join3(homedir3(), ".plaud", "version-check.json");
20985
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
20986
+ async function readCache() {
20987
+ try {
20988
+ const raw = await readFile3(CACHE_PATH, "utf-8");
20989
+ const parsed = JSON.parse(raw);
20990
+ if (typeof parsed.checked_at !== "number" || typeof parsed.latest !== "string") return null;
20991
+ return parsed;
20992
+ } catch {
20993
+ return null;
20994
+ }
20995
+ }
20996
+ async function writeCache(entry) {
20997
+ try {
20998
+ await mkdir3(dirname2(CACHE_PATH), { recursive: true });
20999
+ await writeFile3(CACHE_PATH, JSON.stringify(entry), "utf-8");
21000
+ } catch {
21001
+ }
21002
+ }
21003
+ async function checkForUpdate(current) {
21004
+ const cached = await readCache();
21005
+ const now = Date.now();
21006
+ let latest = null;
21007
+ if (cached && now - cached.checked_at < CACHE_TTL_MS) {
21008
+ latest = cached.latest;
21009
+ } else {
21010
+ latest = await fetchLatestVersion(2e3);
21011
+ if (latest) await writeCache({ checked_at: now, latest });
21012
+ }
21013
+ if (!latest) return null;
21014
+ return isNewer(current, latest) ? latest : null;
21015
+ }
21016
+ var versionCommand = new Command2("version").description("Show CLI version information").action(async () => {
21017
+ const current = "0.3.12";
21018
+ console.log(`plaud ${current}`);
21019
+ if ("516b086") console.log(`commit ${"516b086"}`);
21020
+ if ("2026-09-09T09:53:45.245Z") console.log(`built ${"2026-09-09T09:53:45.245Z"}`);
21021
+ if (current === "unknown") return;
21022
+ const newer = await checkForUpdate(current);
21023
+ if (newer) {
21024
+ console.log();
21025
+ console.log(chalk3.yellow(`A newer version is available: ${current} \u2192 ${newer}`));
21026
+ console.log(chalk3.gray(`Run \`plaud update\` for upgrade instructions.`));
21027
+ }
21028
+ });
21029
+
21030
+ // src/commands/login.ts
21031
+ import { Command as Command3 } from "commander";
21032
+ import { createServer as createServer2 } from "net";
21033
+ import open from "open";
21034
+ import chalk4 from "chalk";
21035
+ import ora2 from "ora";
21036
+
20948
21037
  // src/config.ts
20949
21038
  import { readFileSync, existsSync } from "fs";
20950
21039
  import { homedir as homedir4 } from "os";
@@ -21024,7 +21113,11 @@ var loginCommand = new Command3("login").description("Authenticate with Plaud vi
21024
21113
  }
21025
21114
  }
21026
21115
  }
21027
- } catch {
21116
+ } catch (err) {
21117
+ if (isTransportError(err)) {
21118
+ printError("UNREACHABLE", "Cannot reach Plaud servers to refresh the saved credentials. Check your network.", err);
21119
+ await telemetryExit(ExitCode.UNREACHABLE);
21120
+ }
21028
21121
  await client2.auth.logout();
21029
21122
  }
21030
21123
  const portError = await probeCallbackPort(CALLBACK_PORT);
@@ -21107,8 +21200,7 @@ import { Command as Command4 } from "commander";
21107
21200
  import chalk5 from "chalk";
21108
21201
  var logoutCommand = new Command4("logout").description("Log out and revoke authorization").action(async () => {
21109
21202
  const client2 = getClient2();
21110
- const token = await client2.auth.getAccessToken();
21111
- if (!token) {
21203
+ if (!await client2.auth.hasStoredToken()) {
21112
21204
  console.log(chalk5.yellow("Not logged in."));
21113
21205
  return;
21114
21206
  }
@@ -21350,12 +21442,28 @@ import { Command as Command9 } from "commander";
21350
21442
  import chalk10 from "chalk";
21351
21443
  import ora7 from "ora";
21352
21444
  import { writeFile as writeFile4 } from "fs/promises";
21353
- var BLOCKS = ["transaction", "transaction_polish", "outline"];
21445
+ var BLOCKS = ["transaction", "transaction_polish", "outline", "mark_memo"];
21446
+ function renderBlock(block, content) {
21447
+ let parsed;
21448
+ try {
21449
+ parsed = JSON.parse(content);
21450
+ } catch {
21451
+ return content;
21452
+ }
21453
+ if (block === "mark_memo" || !Array.isArray(parsed)) return JSON.stringify(parsed, null, 2);
21454
+ return parsed.map((seg) => {
21455
+ const hasStart = typeof seg?.start_time === "number";
21456
+ const hasEnd = typeof seg?.end_time === "number";
21457
+ const time = hasStart ? hasEnd ? `[${formatTime(seg.start_time)} - ${formatTime(seg.end_time)}]` : `[${formatTime(seg.start_time)}]` : "";
21458
+ const speaker = seg?.speaker ? `${seg.speaker}: ` : "";
21459
+ return [time, `${speaker}${seg?.content ?? seg?.topic ?? ""}`].filter(Boolean).join(" ");
21460
+ }).join("\n");
21461
+ }
21354
21462
  var transcriptCommand = new Command9("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").option(
21355
21463
  "--block <block>",
21356
21464
  `Which block to fetch: ${BLOCKS.join(" | ")} (default: transaction)`
21357
- ).option("--polished", "Shortcut for --block transaction_polish (AI-cleaned transcript)").action(async (fileId, opts) => {
21358
- const block = opts.polished ? "transaction_polish" : opts.block ?? "transaction";
21465
+ ).option("--polished", "Shortcut for --block transaction_polish (AI-cleaned transcript)").option("--highlights", "Shortcut for --block mark_memo (moments flagged with the device's highlight button)").action(async (fileId, opts) => {
21466
+ const block = opts.polished ? "transaction_polish" : opts.highlights ? "mark_memo" : opts.block ?? "transaction";
21359
21467
  if (!BLOCKS.includes(block)) {
21360
21468
  printError("BAD_ARGS", `Unknown --block "${block}". Choose one of: ${BLOCKS.join(", ")}.`);
21361
21469
  await telemetryExit(ExitCode.ERROR);
@@ -21369,7 +21477,11 @@ var transcriptCommand = new Command9("transcript").description("Get the transcri
21369
21477
  if (!source) {
21370
21478
  spinner.stop();
21371
21479
  const available = sourceList.map((s) => s.data_type).filter(Boolean).join(", ") || "(none)";
21372
- console.log(chalk10.yellow(`No "${block}" transcript for this recording. Available: ${available}.`));
21480
+ console.log(
21481
+ chalk10.yellow(
21482
+ block === "mark_memo" ? `No highlights (mark_memo) on this recording \u2014 the highlight button was not pressed while recording. Available blocks: ${available}.` : `No "${block}" transcript for this recording. Available: ${available}.`
21483
+ )
21484
+ );
21373
21485
  return;
21374
21486
  }
21375
21487
  const content = await loadBlockContent(source);
@@ -21377,24 +21489,19 @@ var transcriptCommand = new Command9("transcript").description("Get the transcri
21377
21489
  if (!content) {
21378
21490
  console.log(
21379
21491
  chalk10.yellow(
21380
- block === "transaction_polish" ? "The cleaned-up (polished) transcript hasn't been generated for this recording yet." : `The "${block}" transcript is not available for this recording.`
21492
+ block === "transaction_polish" ? "The cleaned-up (polished) transcript hasn't been generated for this recording yet." : block === "mark_memo" ? "This recording has a highlights block, but it is empty \u2014 no highlight was recorded." : `The "${block}" transcript is not available for this recording.`
21381
21493
  )
21382
21494
  );
21383
21495
  return;
21384
21496
  }
21385
- const segments = JSON.parse(content);
21386
- const lines = segments.map((seg) => {
21387
- const time = `[${formatTime(seg.start_time)} - ${formatTime(seg.end_time)}]`;
21388
- const speaker = seg.speaker ? `${seg.speaker}: ` : "";
21389
- return `${time} ${speaker}${seg.content ?? seg.topic ?? ""}`;
21390
- });
21391
- const output = lines.join("\n");
21497
+ const output = renderBlock(block, content);
21498
+ const label = block === "mark_memo" ? "Highlights" : "Transcript";
21392
21499
  if (opts.output) {
21393
21500
  await writeFile4(opts.output, output, "utf-8");
21394
- console.log(chalk10.green(`Transcript saved to ${opts.output}`));
21501
+ console.log(chalk10.green(`${label} saved to ${opts.output}`));
21395
21502
  } else {
21396
21503
  console.log(chalk10.bold(`
21397
- Transcript: ${file.name}
21504
+ ${label}: ${file.name}
21398
21505
  `));
21399
21506
  console.log(output);
21400
21507
  console.log();
@@ -21732,7 +21839,11 @@ async function ensureLoggedIn() {
21732
21839
  try {
21733
21840
  const token = await client2.auth.getAccessToken();
21734
21841
  if (token) return true;
21735
- } catch {
21842
+ } catch (err) {
21843
+ if (isTransportError(err)) {
21844
+ printError("UNREACHABLE", "Cannot reach Plaud servers. Check your network and try again.", err);
21845
+ return false;
21846
+ }
21736
21847
  }
21737
21848
  const go = await confirm({ message: "You are not logged in. Open the browser to log in now?", default: true });
21738
21849
  if (!go) return false;
@@ -21803,10 +21914,11 @@ async function runWizard() {
21803
21914
  }
21804
21915
 
21805
21916
  // src/index.ts
21917
+ await installProxyDispatcher();
21806
21918
  try {
21807
21919
  await initTelemetry({
21808
21920
  surface: "cli",
21809
- appVersion: "0.3.10"
21921
+ appVersion: "0.3.12"
21810
21922
  });
21811
21923
  } catch {
21812
21924
  }
@@ -21815,7 +21927,7 @@ async function notifyUpdate() {
21815
21927
  if (sub === "version" || sub === "update") return;
21816
21928
  if (process.env.PLAUD_NO_UPDATE_NOTIFIER) return;
21817
21929
  if (!process.stderr.isTTY) return;
21818
- const current = "0.3.10";
21930
+ const current = "0.3.12";
21819
21931
  if (current === "0.0.0") return;
21820
21932
  try {
21821
21933
  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.10",
3
+ "version": "0.3.12",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "plaud": "dist/index.js"
@@ -20,6 +20,7 @@
20
20
  "dotenv": "^17.3.1",
21
21
  "open": "^10.2.0",
22
22
  "ora": "^8.1.0",
23
+ "undici": "^6.28.1",
23
24
  "yaml": "^2.8.3"
24
25
  },
25
26
  "devDependencies": {