plaud 0.1.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.
@@ -0,0 +1,28 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest. This is a small, agent-first CLI and we try to keep it simple and robust.
4
+
5
+ ## Principles
6
+
7
+ - **No secret leakage**: never include tokens, HAR files, or signed URLs in issues/PRs.
8
+ - **Stable JSON contract**: keep `--json` output compatible with `docs/CONTRACT_V1.md`.
9
+ - **Agent-first UX**: stdout should be machine-friendly; progress/logs go to stderr.
10
+ - **Minimal deps**: prefer standard library; add dependencies only when they clearly improve correctness or maintainability.
11
+
12
+ ## Development
13
+
14
+ Requirements:
15
+ - Node.js 22+
16
+
17
+ Commands:
18
+
19
+ ```bash
20
+ npm install
21
+ npm test
22
+ ```
23
+
24
+ ## Submitting changes
25
+
26
+ - Keep PRs small and focused.
27
+ - Add or update tests when behavior changes.
28
+ - Update `README.md` and/or `docs/CONTRACT_V1.md` if you change CLI surface or JSON output.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Daniel Wilson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # plaud (CLI)
2
+
3
+ Export all your Plaud recordings with speaker-labeled transcripts and optional AI summaries.
4
+
5
+ ## Install (local)
6
+
7
+ ```bash
8
+ cd plaud/plaud-cli
9
+ npm install
10
+ npm link
11
+ ```
12
+
13
+ Requirements:
14
+ - Node.js 22+ (tested on Node 24)
15
+
16
+ ## Auth
17
+
18
+ Preferred (easy onboarding, stores token locally):
19
+
20
+ ```bash
21
+ plaud auth login
22
+ ```
23
+
24
+ Verify:
25
+
26
+ ```bash
27
+ plaud auth status
28
+ plaud doctor
29
+ ```
30
+
31
+ Fallbacks:
32
+
33
+ ```bash
34
+ plaud auth set --stdin
35
+ plaud auth import-har /path/to/web.plaud.ai.har
36
+ ```
37
+
38
+ Or via env var (no local storage):
39
+
40
+ ```bash
41
+ export PLAUD_AUTH_TOKEN="eyJ..."
42
+ ```
43
+
44
+ Tip (Node 22+): you can also use Node’s `--env-file` if you want to load a local `.env` without adding any dependency to the CLI:
45
+
46
+ ```bash
47
+ node --env-file .env "$(command -v plaud)" auth status --json
48
+ ```
49
+
50
+ ## Export
51
+
52
+ Create a single ZIP (default):
53
+
54
+ ```bash
55
+ plaud recordings export --zip
56
+ ```
57
+
58
+ Export to a directory:
59
+
60
+ ```bash
61
+ plaud recordings export --out ./plaud-transcripts --formats txt,json,md
62
+ ```
63
+
64
+ ## Download a single recording
65
+
66
+ ```bash
67
+ plaud recordings list --json --max 10
68
+ plaud recordings download <id> --out ./plaud-download --what transcript,summary,json
69
+ plaud recordings download <id> --out ./plaud-download --what audio --audio-format opus
70
+ ```
71
+
72
+ Notes:
73
+ - `plaud recordings export` prints a JSON summary to stdout; progress goes to stderr.
74
+ - Tokens are stored at `~/.config/plaud/config.json` with `0600` permissions.
75
+
76
+ ## Agent-first JSON contract
77
+
78
+ See `docs/CONTRACT_V1.md`.
79
+
80
+ ## Install (npm)
81
+
82
+ Global (recommended for frequent use):
83
+
84
+ ```bash
85
+ npm i -g plaud
86
+ plaud auth login
87
+ ```
88
+
89
+ No install (convenient for agents/one-offs):
90
+
91
+ ```bash
92
+ npx -y plaud auth status --json
93
+ ```
94
+
95
+ ## Install (skill)
96
+
97
+ Once this lives in a GitHub repo, you’ll be able to add the agent skill with:
98
+
99
+ ```bash
100
+ npx -y skills add danielgwilson/plaud --skill plaud -g -y
101
+ ```
package/SECURITY.md ADDED
@@ -0,0 +1,23 @@
1
+ # Security policy
2
+
3
+ This project is an **unofficial** CLI for Plaud. It works by capturing a Plaud bearer token from an authenticated browser session and calling private endpoints.
4
+
5
+ ## Reporting a security issue
6
+
7
+ Please **do not** open a public issue containing secrets.
8
+
9
+ If you find a security issue (token leakage, unsafe file permissions, etc.), report it privately to the maintainer.
10
+
11
+ ## Never share secrets
12
+
13
+ Do not paste any of the following into issues, PRs, logs, or screenshots:
14
+
15
+ - Plaud auth tokens (`Authorization: Bearer …`, typically JWTs starting with `eyJ`)
16
+ - HAR files (`*.har`, `*.har.gz`) — these often contain auth headers
17
+ - Signed file URLs returned by Plaud (may embed temporary credentials)
18
+
19
+ ## Local storage
20
+
21
+ By default the CLI stores the token at `~/.config/plaud/config.json` with file mode `0600`.
22
+
23
+ If you don’t want local storage, use the `PLAUD_AUTH_TOKEN` environment variable instead.
package/dist/auth.js ADDED
@@ -0,0 +1,155 @@
1
+ import fs from "node:fs/promises";
2
+ import { writeConfig } from "./config.js";
3
+ import { getMe } from "./plaud-api.js";
4
+ function cleanToken(token) {
5
+ return String(token || "")
6
+ .trim()
7
+ .replace(/^bearer\s+/i, "");
8
+ }
9
+ function isProbablyJwt(token) {
10
+ const t = cleanToken(token);
11
+ return t.startsWith("eyJ") && t.length > 20;
12
+ }
13
+ function stripUrlQuery(url) {
14
+ try {
15
+ const u = new URL(String(url));
16
+ u.search = "";
17
+ return u.toString();
18
+ }
19
+ catch {
20
+ return "";
21
+ }
22
+ }
23
+ function sanitizeMe(me) {
24
+ if (!me || typeof me !== "object")
25
+ return null;
26
+ const dataUser = me.data_user && typeof me.data_user === "object" ? me.data_user : null;
27
+ const dataState = me.data_state && typeof me.data_state === "object" ? me.data_state : null;
28
+ const user = dataUser
29
+ ? {
30
+ id: dataUser.id || null,
31
+ email: dataUser.email || null,
32
+ nickname: dataUser.nickname || null,
33
+ country: dataUser.country || null,
34
+ userAreaName: dataUser.user_area_name || null,
35
+ avatarUrl: dataUser.avatar ? stripUrlQuery(dataUser.avatar) : null,
36
+ }
37
+ : null;
38
+ const state = dataState
39
+ ? {
40
+ isMembership: dataState.is_membership ?? null,
41
+ membershipType: dataState.membership_type ?? null,
42
+ membershipFlag: dataState.membership_flag ?? null,
43
+ }
44
+ : null;
45
+ return { status: me.status ?? null, user, state };
46
+ }
47
+ export async function validateToken(token) {
48
+ if (!token)
49
+ return { ok: false, reason: "missing" };
50
+ try {
51
+ const me = await getMe({ token });
52
+ return { ok: true, me: sanitizeMe(me) };
53
+ }
54
+ catch (error) {
55
+ return { ok: false, reason: error?.message || "invalid" };
56
+ }
57
+ }
58
+ export async function saveToken(token) {
59
+ const clean = cleanToken(token);
60
+ if (!isProbablyJwt(clean))
61
+ throw new Error("Invalid token format");
62
+ await writeConfig({ authToken: clean });
63
+ return clean;
64
+ }
65
+ export async function importTokenFromHar(harPath) {
66
+ const raw = await fs.readFile(harPath, "utf8");
67
+ const har = JSON.parse(raw);
68
+ const entries = har?.log?.entries;
69
+ if (!Array.isArray(entries)) {
70
+ throw new Error("Invalid HAR: missing log.entries");
71
+ }
72
+ for (const entry of entries) {
73
+ const req = entry?.request;
74
+ const headers = req?.headers;
75
+ if (!Array.isArray(headers))
76
+ continue;
77
+ const auth = headers.find((h) => String(h?.name || "").toLowerCase() === "authorization")?.value;
78
+ if (!auth)
79
+ continue;
80
+ if (!String(auth).toLowerCase().startsWith("bearer "))
81
+ continue;
82
+ const token = cleanToken(auth);
83
+ if (!isProbablyJwt(token))
84
+ continue;
85
+ return token;
86
+ }
87
+ throw new Error("No bearer token found in HAR");
88
+ }
89
+ export async function captureTokenFromBrowser({ url = "https://app.plaud.ai", timeoutMs = 180_000, channel = "chrome", headless = false, onStatus, }) {
90
+ let playwright;
91
+ try {
92
+ playwright = await import("playwright-core");
93
+ }
94
+ catch {
95
+ throw new Error("Missing dependency: playwright-core");
96
+ }
97
+ const { chromium } = playwright;
98
+ const startedAt = Date.now();
99
+ const status = (msg) => {
100
+ if (typeof onStatus === "function")
101
+ onStatus({ msg, elapsedMs: Date.now() - startedAt });
102
+ };
103
+ status(`Launching browser (${channel}${headless ? ", headless" : ""})`);
104
+ let browser;
105
+ try {
106
+ browser = await chromium.launch({ headless, channel });
107
+ }
108
+ catch {
109
+ throw new Error(`Failed to launch browser channel "${channel}". Install Chrome/Edge, or use \`plaud auth import-har\`.`);
110
+ }
111
+ try {
112
+ const context = await browser.newContext();
113
+ const page = await context.newPage();
114
+ let capturedToken = "";
115
+ const started = Date.now();
116
+ const maybeCapture = (request) => {
117
+ try {
118
+ const reqUrl = request.url();
119
+ if (!reqUrl.includes("api.plaud.ai"))
120
+ return;
121
+ const headers = request.headers?.() || {};
122
+ const auth = headers.authorization || headers.Authorization;
123
+ if (!auth)
124
+ return;
125
+ if (!String(auth).toLowerCase().startsWith("bearer "))
126
+ return;
127
+ const token = cleanToken(auth);
128
+ if (isProbablyJwt(token)) {
129
+ capturedToken = token;
130
+ status("Captured Plaud bearer token");
131
+ }
132
+ }
133
+ catch {
134
+ // ignore
135
+ }
136
+ };
137
+ page.on("request", maybeCapture);
138
+ context.on("request", maybeCapture);
139
+ status("Opening Plaud login page (complete sign-in in the browser)");
140
+ await page.goto(url, { waitUntil: "domcontentloaded" });
141
+ status("Waiting for Plaud API request with auth header");
142
+ while (!capturedToken && Date.now() - started < timeoutMs) {
143
+ await page.waitForTimeout(250);
144
+ }
145
+ if (!capturedToken) {
146
+ throw new Error("Timed out waiting for Plaud API auth. Please complete login in the opened browser window, or use `plaud auth import-har`.");
147
+ }
148
+ return capturedToken;
149
+ }
150
+ finally {
151
+ status("Closing browser");
152
+ await browser.close();
153
+ }
154
+ }
155
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}