@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.
- package/dist/index.js +352 -240
- 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
|
|
20462
|
-
import { join as
|
|
20463
|
-
import { homedir as
|
|
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 =
|
|
20469
|
-
this.tokenPath =
|
|
20258
|
+
this.configDir = join2(homedir2(), ".plaud");
|
|
20259
|
+
this.tokenPath = join2(this.configDir, filename);
|
|
20470
20260
|
}
|
|
20471
20261
|
async save(tokenSet) {
|
|
20472
|
-
await
|
|
20473
|
-
await
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
21386
|
-
const
|
|
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(
|
|
21501
|
+
console.log(chalk10.green(`${label} saved to ${opts.output}`));
|
|
21395
21502
|
} else {
|
|
21396
21503
|
console.log(chalk10.bold(`
|
|
21397
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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": {
|