bloby-bot 0.50.0 → 0.50.2
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/package.json +1 -1
- package/supervisor/index.ts +1 -0
- package/workspace/skills/plaud/SKILL.md +158 -90
package/package.json
CHANGED
package/supervisor/index.ts
CHANGED
|
@@ -392,6 +392,7 @@ export async function startSupervisor() {
|
|
|
392
392
|
'POST /api/channels/whatsapp/react',
|
|
393
393
|
'POST /api/channels/send',
|
|
394
394
|
'POST /api/channels/alexa/handle',
|
|
395
|
+
'POST /api/whisper/transcribe-file',
|
|
395
396
|
];
|
|
396
397
|
|
|
397
398
|
function isExemptRoute(method: string, url: string): boolean {
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
A channel for getting **recordings off the user's Plaud Note device** and into your workspace as `(audio file, transcript)` pairs you can read and act on.
|
|
6
6
|
|
|
7
|
-
Plaud is a tiny voice recorder
|
|
7
|
+
Plaud is a tiny voice recorder. When the user records something — a meeting, a lecture, a thought on a walk — the device syncs to Plaud's cloud over Bluetooth/Wi-Fi. **You don't talk to the device.** You talk to Plaud's cloud, pull the audio, and transcribe it yourself.
|
|
8
8
|
|
|
9
9
|
There is **no Plaud CLI, no Plaud webhook, no official Plaud API.** Plaud's mobile/web app uses an undocumented HTTP API. This skill uses the same one — same shape OpenPlaud uses (`https://github.com/openplaud/openplaud`).
|
|
10
10
|
|
|
11
|
-
The user already
|
|
11
|
+
The user already has Whisper enabled via the Bloby wizard. We use that OpenAI key — no new key, no new subscription, no Plaud AI plan needed.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -16,18 +16,36 @@ The user already pays Plaud $0 if they don't want Plaud's transcription subscrip
|
|
|
16
16
|
|
|
17
17
|
| Thing | Where | How you use it |
|
|
18
18
|
|---|---|---|
|
|
19
|
-
| Whisper-on-disk endpoint | `POST http://localhost:7400/api/whisper/transcribe-file` | Send a path under `workspace/files/`, get a transcript back. Optional `saveTranscriptNext: true` writes `foo.mp3.txt` next to `foo.mp3`. |
|
|
20
|
-
|
|
|
21
|
-
| Workspace
|
|
19
|
+
| Whisper-on-disk endpoint | `POST http://localhost:7400/api/whisper/transcribe-file` | Send a path under `workspace/files/`, get a transcript back. Optional `saveTranscriptNext: true` writes `foo.mp3.txt` next to `foo.mp3`. Auth-exempt, no Bearer needed. |
|
|
20
|
+
| Workspace files dir | `workspace/files/audio/plaud/` | Drop downloaded audio here. Supervisor serves it at `/api/files/audio/plaud/<name>`. |
|
|
21
|
+
| Workspace file tools | `Read` / `Write` / `Edit` | Store Plaud auth state in `workspace/.plaud.json` (see below). No `/api/settings` calls — that endpoint requires a portal Bearer token the skill can't easily produce. |
|
|
22
22
|
| Scheduling | `workspace/CRONS.json` or `workspace/PULSE.json` | Run sync periodically. See "Cadence" below. |
|
|
23
23
|
|
|
24
|
-
Use `http://localhost:7400` from Bash
|
|
24
|
+
Use `http://localhost:7400` from Bash for the Whisper endpoint. Everything else is the open internet (Plaud's API) or your own filesystem.
|
|
25
|
+
|
|
26
|
+
### State file: `workspace/.plaud.json`
|
|
27
|
+
|
|
28
|
+
You manage all Plaud connection state in a single JSON file at workspace root. Read with `Read`, write with `Write`. Shape:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"email": "bruno@bertapeli.com",
|
|
33
|
+
"apiBase": "https://api.plaud.ai",
|
|
34
|
+
"userToken": "eyJ...",
|
|
35
|
+
"workspaceId": "ws_xxxxx",
|
|
36
|
+
"workspaceToken": "eyJ...",
|
|
37
|
+
"workspaceTokenMintedAt": "2026-05-22T19:30:00.000Z",
|
|
38
|
+
"lastSyncVersionMs": 1716412800000
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Initialize empty (`{}`) if the file doesn't exist. Never commit secrets — `.plaud.json` is gitignored by default (starts with `.`).
|
|
25
43
|
|
|
26
44
|
---
|
|
27
45
|
|
|
28
46
|
## Plaud's API in 60 seconds
|
|
29
47
|
|
|
30
|
-
Three regions. Pick one when pairing.
|
|
48
|
+
Three regions. Pick one when pairing. A token from one region won't work on another.
|
|
31
49
|
|
|
32
50
|
| Region | Base URL |
|
|
33
51
|
|---|---|
|
|
@@ -35,9 +53,16 @@ Three regions. Pick one when pairing. Token from one region won't work on anothe
|
|
|
35
53
|
| EU | `https://api-euc1.plaud.ai` |
|
|
36
54
|
| Asia-Pacific | `https://api-apse1.plaud.ai` |
|
|
37
55
|
|
|
38
|
-
If the user doesn't know their region, start with Global. If `POST /auth/otp-send-code` returns `status: -302` with `data.domains.api`,
|
|
56
|
+
If the user doesn't know their region, start with Global. If `POST /auth/otp-send-code` returns `status: -302` with `data.domains.api`, retry against that base — the user's account lives in a different region. Save whichever base actually succeeded.
|
|
39
57
|
|
|
40
|
-
**
|
|
58
|
+
**Two token kinds.** This is the part that bites everyone:
|
|
59
|
+
|
|
60
|
+
- **User Token (UT)** — what `/auth/otp-login` returns. Authenticates `/user/me`, the workspace-list endpoint, and the workspace-token mint endpoint. **It does NOT authenticate recording endpoints.** Calling `/file/simple/web` or `/device/list` with a UT silently returns HTTP 200 + empty list. This is exactly the "I have no recordings but my Plaud app shows 3 files" symptom.
|
|
61
|
+
- **Workspace Token (WT)** — minted from the UT. Required on all recording endpoints. ~24h lifetime. Re-mint when expired.
|
|
62
|
+
|
|
63
|
+
**You always need both.** UT lives long, WT is short-lived. Workflow: OTP → UT → list workspaces (with UT) → mint WT for the personal workspace (with UT) → use WT for everything recording-related.
|
|
64
|
+
|
|
65
|
+
**User-Agent matters.** Plaud blocks some defaults. Always send a normal browser UA:
|
|
41
66
|
|
|
42
67
|
```
|
|
43
68
|
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
|
|
@@ -56,7 +81,7 @@ Bloby: Which email do you use on plaud.ai? I'll have them send you a 6-digit cod
|
|
|
56
81
|
Human: bruno@example.com
|
|
57
82
|
```
|
|
58
83
|
|
|
59
|
-
If the human mentions they signed up with **Google or Apple**, jump to the "Paste-token fallback" section
|
|
84
|
+
If the human mentions they signed up with **Google or Apple**, jump to the "Paste-token fallback" section — OTP only works for email+password Plaud identities.
|
|
60
85
|
|
|
61
86
|
### Step 2 — Send the OTP
|
|
62
87
|
|
|
@@ -84,34 +109,81 @@ curl -s -X POST '<apiBase>/auth/otp-login' \
|
|
|
84
109
|
-d '{"code":"<6 DIGITS>","token":"<OTP TOKEN FROM STEP 2>"}'
|
|
85
110
|
```
|
|
86
111
|
|
|
87
|
-
Expected `access_token` (a long `eyJ...` JWT). **This is the
|
|
112
|
+
Expected `access_token` (a long `eyJ...` JWT). **This is the User Token (UT). Save it as `userToken`.**
|
|
113
|
+
|
|
114
|
+
> ⚠️ **Don't be misled by `is_new_user: true`** in this response. It's an informational flag for the Plaud client — it does NOT mean Plaud just created a fresh account for you. Your real account is intact. The empty `data_devices: []` you'll see next is because UT can't read recording/device endpoints — that's the workspace-token issue, not "wrong account."
|
|
88
115
|
|
|
89
|
-
### Step 5 —
|
|
116
|
+
### Step 5 — Write initial state to `workspace/.plaud.json`
|
|
117
|
+
|
|
118
|
+
Use the `Write` tool. No `/api/settings` calls.
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"email": "<EMAIL>",
|
|
123
|
+
"apiBase": "<BASE THAT WORKED>",
|
|
124
|
+
"userToken": "<UT FROM STEP 4>"
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Step 6 — Smoke test the UT (don't try `/device/list` yet)
|
|
90
129
|
|
|
91
130
|
```bash
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
-H '
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
131
|
+
curl -s '<BASE>/user/me' \
|
|
132
|
+
-H 'Authorization: Bearer <UT>' \
|
|
133
|
+
-H 'User-Agent: Mozilla/5.0 ...'
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Should return the user's profile (email matches the one you used to pair). If 401, the UT is bad — start over. If 200 but the email is different from what the human gave you, the OTP went to a different identity (Google/Apple collision) — explain and go to paste-token fallback.
|
|
137
|
+
|
|
138
|
+
### Step 7 — Mint the Workspace Token (REQUIRED)
|
|
139
|
+
|
|
140
|
+
This is the step that makes the difference between "0 recordings" and "all 3 of my recordings."
|
|
141
|
+
|
|
142
|
+
**7a. List workspaces** (auth: UT):
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
curl -s '<BASE>/team-app/workspaces/list?need_personal_workspace=true' \
|
|
146
|
+
-H 'Authorization: Bearer <UT>' \
|
|
147
|
+
-H 'User-Agent: Mozilla/5.0 ...'
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Response shape: `{ status: 0, data: { workspaces: [{ workspace_id, workspace_type, ... }] } }`.
|
|
151
|
+
|
|
152
|
+
Pick the **personal** workspace — the one where `workspace_type === "0"`. If no workspace has type `"0"` (rare), use the first entry. Save its `workspace_id` as `workspaceId`.
|
|
153
|
+
|
|
154
|
+
**7b. Mint a WT for that workspace** (auth: UT, body is literally `{}`):
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
curl -s -X POST '<BASE>/user-app/auth/workspace/token/<WORKSPACE_ID>' \
|
|
158
|
+
-H 'Authorization: Bearer <UT>' \
|
|
100
159
|
-H 'Content-Type: application/json' \
|
|
101
|
-
-
|
|
160
|
+
-H 'User-Agent: Mozilla/5.0 ...' \
|
|
161
|
+
-d '{}'
|
|
102
162
|
```
|
|
103
163
|
|
|
104
|
-
|
|
164
|
+
Response: `{ status: 0, data: { workspace_token: "eyJ..." } }`.
|
|
105
165
|
|
|
106
|
-
|
|
166
|
+
**Save it** as `workspaceToken` and `workspaceTokenMintedAt: <now ISO 8601>` in `.plaud.json`. Now Update the file via `Write`.
|
|
167
|
+
|
|
168
|
+
### Step 8 — Real smoke test (with WT)
|
|
107
169
|
|
|
108
170
|
```bash
|
|
109
171
|
curl -s '<BASE>/device/list' \
|
|
110
|
-
-H 'Authorization: Bearer <
|
|
172
|
+
-H 'Authorization: Bearer <WT>' \
|
|
111
173
|
-H 'User-Agent: Mozilla/5.0 ...'
|
|
112
174
|
```
|
|
113
175
|
|
|
114
|
-
|
|
176
|
+
Now you should see devices. Tell the human: *"Paired. Your Plaud (serial ending ...XXXX) is connected. Want me to pull in everything you've recorded so far?"*
|
|
177
|
+
|
|
178
|
+
If `data_devices` is still empty here — odd, but possible for accounts that haven't synced any device in a while. Try the recordings list directly:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
curl -s '<BASE>/file/simple/web?skip=0&limit=10&is_trash=0' \
|
|
182
|
+
-H 'Authorization: Bearer <WT>' \
|
|
183
|
+
-H 'User-Agent: Mozilla/5.0 ...'
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
If `data_file_list` has entries, you're good — devices list can be empty even when recordings exist.
|
|
115
187
|
|
|
116
188
|
---
|
|
117
189
|
|
|
@@ -123,9 +195,8 @@ If OTP just won't work and the human signed up with Google or Apple, get the bea
|
|
|
123
195
|
2. Open DevTools (F12 or Cmd+Option+I) → Network tab → refresh.
|
|
124
196
|
3. Click any request to `api.plaud.ai`, `api-euc1.plaud.ai`, or `api-apse1.plaud.ai`.
|
|
125
197
|
4. Under **Request Headers**, find `Authorization`. Copy everything after `Bearer ` (the long `eyJ...`).
|
|
126
|
-
5.
|
|
127
|
-
|
|
128
|
-
JWTs from this path expire too. The skill behaviour on 401 is the same (see "Re-auth" below).
|
|
198
|
+
5. The human pastes it to you in chat. Save it as `userToken` and set `apiBase` to whichever host they pulled it from.
|
|
199
|
+
6. **Still run Step 7** — mint a workspace token. The paste-token gives you a UT, same as OTP. WT is still required.
|
|
129
200
|
|
|
130
201
|
---
|
|
131
202
|
|
|
@@ -134,18 +205,22 @@ JWTs from this path expire too. The skill behaviour on 401 is the same (see "Re-
|
|
|
134
205
|
The shape of a sync run:
|
|
135
206
|
|
|
136
207
|
```
|
|
137
|
-
GET /file/simple/web → list recent recordings (paginated)
|
|
208
|
+
GET /file/simple/web → list recent recordings (paginated) [auth: WT]
|
|
138
209
|
for each new one:
|
|
139
|
-
GET /file/temp-url/<id>?is_opus=0 → get a short-lived S3 link
|
|
140
|
-
curl -o workspace/files/audio/plaud/<id>.mp3 → download
|
|
141
|
-
POST /api/whisper/transcribe-file
|
|
210
|
+
GET /file/temp-url/<id>?is_opus=0 → get a short-lived S3 link [auth: WT]
|
|
211
|
+
curl -o workspace/files/audio/plaud/<id>.mp3 → download (no auth, signed URL)
|
|
212
|
+
POST /api/whisper/transcribe-file → produces <id>.mp3.txt alongside
|
|
142
213
|
```
|
|
143
214
|
|
|
144
|
-
###
|
|
215
|
+
### Pre-sync: check WT freshness
|
|
216
|
+
|
|
217
|
+
Read `.plaud.json`. If `workspaceToken` is missing or `workspaceTokenMintedAt` is more than ~20 hours old, re-mint (Step 7b above) and update the file before starting the sync. WT lifetime is ~24h; refresh defensively.
|
|
218
|
+
|
|
219
|
+
### List recordings (auth: WT)
|
|
145
220
|
|
|
146
221
|
```bash
|
|
147
222
|
curl -s '<BASE>/file/simple/web?skip=0&limit=50&is_trash=0&sort_by=edit_time&is_desc=true' \
|
|
148
|
-
-H 'Authorization: Bearer <
|
|
223
|
+
-H 'Authorization: Bearer <WT>' \
|
|
149
224
|
-H 'User-Agent: Mozilla/5.0 ...'
|
|
150
225
|
```
|
|
151
226
|
|
|
@@ -161,37 +236,37 @@ The response has `data_file_list` — an array of recording objects. Fields you'
|
|
|
161
236
|
| `serial_number` | Which Plaud device. |
|
|
162
237
|
| `is_trash` | Skip if 1. |
|
|
163
238
|
|
|
164
|
-
Page with `skip
|
|
239
|
+
Page with `skip=`; do 50 at a time. Stop when a page comes back smaller than `limit` or empty.
|
|
165
240
|
|
|
166
241
|
### Dedup
|
|
167
242
|
|
|
168
243
|
You don't want to re-download what you already have. Two ways, pick one:
|
|
169
244
|
|
|
170
245
|
- **Filesystem**: if `workspace/files/audio/plaud/<id>.mp3` exists, skip it.
|
|
171
|
-
- **Cursor**: save the newest `version_ms` you've seen as `
|
|
246
|
+
- **Cursor**: save the newest `version_ms` you've seen as `lastSyncVersionMs` in `.plaud.json`. Skip anything `<=` that cursor next time.
|
|
172
247
|
|
|
173
248
|
If `version_ms` changed on a recording you already downloaded, the user edited the filename or trimmed it. Re-fetch and overwrite.
|
|
174
249
|
|
|
175
|
-
### Get the download URL
|
|
250
|
+
### Get the download URL (auth: WT)
|
|
176
251
|
|
|
177
252
|
```bash
|
|
178
253
|
curl -s '<BASE>/file/temp-url/<FILE_ID>?is_opus=0' \
|
|
179
|
-
-H 'Authorization: Bearer <
|
|
254
|
+
-H 'Authorization: Bearer <WT>' \
|
|
180
255
|
-H 'User-Agent: Mozilla/5.0 ...'
|
|
181
256
|
```
|
|
182
257
|
|
|
183
|
-
`is_opus=0` returns
|
|
258
|
+
`is_opus=0` returns mp3 in `temp_url`. `is_opus=1` returns opus in `temp_url_opus`. **Use mp3** — Whisper handles it natively, opus would need ffmpeg.
|
|
184
259
|
|
|
185
|
-
|
|
260
|
+
The URL expires in minutes. Download immediately.
|
|
186
261
|
|
|
187
|
-
### Download
|
|
262
|
+
### Download (no auth — URL is signed)
|
|
188
263
|
|
|
189
264
|
```bash
|
|
190
265
|
mkdir -p workspace/files/audio/plaud
|
|
191
266
|
curl -s -o "workspace/files/audio/plaud/<FILE_ID>.mp3" '<TEMP URL>'
|
|
192
267
|
```
|
|
193
268
|
|
|
194
|
-
### Transcribe
|
|
269
|
+
### Transcribe (no auth — endpoint is exempt)
|
|
195
270
|
|
|
196
271
|
```bash
|
|
197
272
|
curl -s -X POST 'http://localhost:7400/api/whisper/transcribe-file' \
|
|
@@ -199,18 +274,15 @@ curl -s -X POST 'http://localhost:7400/api/whisper/transcribe-file' \
|
|
|
199
274
|
-d '{"path":"audio/plaud/<FILE_ID>.mp3","saveTranscriptNext":true}'
|
|
200
275
|
```
|
|
201
276
|
|
|
202
|
-
Returns `{ "transcript": "...", "transcriptPath": "audio/plaud/<FILE_ID>.mp3.txt" }`. The `.txt` file is
|
|
203
|
-
|
|
204
|
-
The user's `whisper_key` from the wizard is what powers this — you don't need to know or handle the OpenAI key.
|
|
277
|
+
Returns `{ "transcript": "...", "transcriptPath": "audio/plaud/<FILE_ID>.mp3.txt" }`. The `.txt` file is sitting next to the audio. Read it with the `Read` tool like any other file.
|
|
205
278
|
|
|
206
|
-
If
|
|
279
|
+
If Whisper fails (file >25MB is Whisper's own hard cap; rate-limit; network), leave the audio in place and skip the `.txt`. The human can ask you to split/compress later.
|
|
207
280
|
|
|
208
281
|
### Pretty filenames (optional)
|
|
209
282
|
|
|
210
|
-
Tell the human you can keep
|
|
283
|
+
Tell the human you can keep raw `<id>.mp3` filenames or also create human-readable copies. If they want pretty names:
|
|
211
284
|
|
|
212
285
|
```bash
|
|
213
|
-
# After successful transcribe, also write a symlink or copy with a nicer name:
|
|
214
286
|
NICE="$(date -d "<start_time>" +%Y-%m-%d_%H%M)_<sanitised filename>"
|
|
215
287
|
ln -s "<FILE_ID>.mp3" "workspace/files/audio/plaud/${NICE}.mp3"
|
|
216
288
|
ln -s "<FILE_ID>.mp3.txt" "workspace/files/audio/plaud/${NICE}.txt"
|
|
@@ -218,79 +290,70 @@ ln -s "<FILE_ID>.mp3.txt" "workspace/files/audio/plaud/${NICE}.txt"
|
|
|
218
290
|
|
|
219
291
|
(Sanitise `filename` by stripping `/\\:*?"<>|`.)
|
|
220
292
|
|
|
221
|
-
Don't rename
|
|
293
|
+
Don't rename originals — `<id>.mp3` stays canonical so dedup keeps working.
|
|
222
294
|
|
|
223
295
|
---
|
|
224
296
|
|
|
225
297
|
## Cadence — CRON or PULSE?
|
|
226
298
|
|
|
227
|
-
**
|
|
299
|
+
**This skill installs no automatic schedule.** You and your human decide together.
|
|
228
300
|
|
|
229
301
|
### Pattern A — CRON every N minutes
|
|
230
302
|
|
|
231
|
-
When the human wants near-real-time freshness
|
|
303
|
+
When the human wants near-real-time freshness, add an entry to `workspace/CRONS.json`:
|
|
232
304
|
|
|
233
305
|
```json
|
|
234
306
|
{
|
|
235
307
|
"id": "plaud-sync",
|
|
236
308
|
"schedule": "*/15 * * * *",
|
|
237
|
-
"task": "Run a Plaud sync: list new recordings, download
|
|
309
|
+
"task": "Run a Plaud sync per the plaud skill: refresh WT if needed, list new recordings, download into workspace/files/audio/plaud/, and transcribe via /api/whisper/transcribe-file. If new recordings were found, summarise to the human in chat. If nothing new, stay silent.",
|
|
238
310
|
"enabled": true,
|
|
239
311
|
"oneShot": false
|
|
240
312
|
}
|
|
241
313
|
```
|
|
242
314
|
|
|
243
|
-
Tune `*/15` to taste. `*/5` for aggressive, `0 * * * *`
|
|
315
|
+
Tune `*/15` to taste. `*/5` for aggressive, `0 * * * *` for quiet.
|
|
244
316
|
|
|
245
317
|
### Pattern B — PULSE memo
|
|
246
318
|
|
|
247
|
-
When the human prefers their bloby just *check* during normal pulse wake-ups, add one line to
|
|
319
|
+
When the human prefers their bloby just *check* during normal pulse wake-ups, add one line to `MYSELF.md` or `MEMORY.md`:
|
|
248
320
|
|
|
249
321
|
```
|
|
250
322
|
- Each pulse, briefly check Plaud for new recordings via the plaud skill. If there's something new, transcribe and decide whether to surface it. If nothing new, move on silently.
|
|
251
323
|
```
|
|
252
324
|
|
|
253
|
-
Pulse runs every 30 min by default.
|
|
325
|
+
Pulse runs every 30 min by default.
|
|
254
326
|
|
|
255
327
|
### Or: don't auto-sync at all
|
|
256
328
|
|
|
257
|
-
|
|
329
|
+
Manual only. Keep the skill installed, no CRON, no pulse memo, sync when asked.
|
|
258
330
|
|
|
259
|
-
**
|
|
331
|
+
**Default to Pattern B for new installs unless the human says otherwise.**
|
|
260
332
|
|
|
261
333
|
---
|
|
262
334
|
|
|
263
335
|
## Re-auth (401 handling)
|
|
264
336
|
|
|
265
|
-
|
|
337
|
+
Two different 401s, two different fixes.
|
|
338
|
+
|
|
339
|
+
| Endpoint that 401'd | What expired | Fix |
|
|
340
|
+
|---|---|---|
|
|
341
|
+
| `/file/simple/web`, `/file/temp-url/*`, `/device/list` (auth: WT) | Workspace token expired | Re-mint a WT from the cached UT (Step 7b). Don't bother the human. |
|
|
342
|
+
| `/user-app/auth/workspace/token/...`, `/team-app/workspaces/list`, `/user/me` (auth: UT) | User token expired | Tell the human, re-run OTP from Step 1. |
|
|
266
343
|
|
|
267
|
-
|
|
268
|
-
2. If they say yes, re-run the OTP flow from Step 1. Overwrite the `plaud_token` setting.
|
|
269
|
-
3. If they signed up with Google/Apple originally, prompt for the paste-token fallback instead.
|
|
270
|
-
4. Don't keep retrying with the dead token — pause the sync until re-paired.
|
|
344
|
+
If you can't tell which token expired (e.g. you tried to mint a WT and got 401), assume UT is dead → re-OTP.
|
|
271
345
|
|
|
272
346
|
---
|
|
273
347
|
|
|
274
348
|
## Disconnect
|
|
275
349
|
|
|
350
|
+
Delete the state file:
|
|
351
|
+
|
|
276
352
|
```bash
|
|
277
|
-
|
|
278
|
-
-H 'Content-Type: application/json' \
|
|
279
|
-
-d '{"key":"plaud_token","value":""}'
|
|
280
|
-
curl -s -X POST 'http://localhost:7400/api/settings' \
|
|
281
|
-
-H 'Content-Type: application/json' \
|
|
282
|
-
-d '{"key":"plaud_api_base","value":""}'
|
|
353
|
+
rm -f workspace/.plaud.json
|
|
283
354
|
```
|
|
284
355
|
|
|
285
|
-
Recordings already on disk stay. The
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
## Workspaces (advanced)
|
|
290
|
-
|
|
291
|
-
Plaud's "workspace" is their multi-account team feature. Personal accounts don't usually need to worry about this — the API responds correctly without a workspace token. If a human ever reports recordings missing that they can see in the Plaud app, it's likely a workspace-scoped recording.
|
|
292
|
-
|
|
293
|
-
To resolve a workspace token: there's an undocumented `/workspace/...` endpoint that mints a workspace-scoped token. OpenPlaud's `src/lib/plaud/workspace.ts` is the reference if you ever need it. Don't bother unless the human hits this case.
|
|
356
|
+
Recordings already on disk stay. The human can also disable the CRON entry / remove it from `CRONS.json`.
|
|
294
357
|
|
|
295
358
|
---
|
|
296
359
|
|
|
@@ -299,27 +362,32 @@ To resolve a workspace token: there's an undocumented `/workspace/...` endpoint
|
|
|
299
362
|
- **No Plaud transcription.** We transcribe ourselves with Whisper. Plaud's own AI subscription is bypassed entirely.
|
|
300
363
|
- **No dashboard.** OpenPlaud has a slick UI for browsing recordings. We don't. The bloby's job is to *read* the transcripts and act on them — summaries, action items, emails — using the normal workspace tools. If the human wants a UI, build one into `workspace/client/` as a normal workspace app.
|
|
301
364
|
- **No push from Plaud.** No webhooks exist. You only know about new recordings when you ask.
|
|
302
|
-
- **No editing recordings.** The Plaud API technically supports `PATCH /file/<id>` to rename. We don't expose it
|
|
303
|
-
- **No real-time streaming.** Plaud syncs to its cloud *after* the recording finishes. Expect
|
|
365
|
+
- **No editing recordings.** The Plaud API technically supports `PATCH /file/<id>` to rename. We don't expose it — keep canonical `<id>.mp3` names.
|
|
366
|
+
- **No real-time streaming.** Plaud syncs to its cloud *after* the recording finishes. Expect seconds-to-minutes of lag between "user stopped recording" and "file appears in `/file/simple/web`."
|
|
304
367
|
|
|
305
368
|
---
|
|
306
369
|
|
|
307
370
|
## Quick Reference
|
|
308
371
|
|
|
309
|
-
| Action | curl |
|
|
310
|
-
|
|
311
|
-
| Send OTP | `POST <base>/auth/otp-send-code` body `{username}` |
|
|
312
|
-
| Verify OTP | `POST <base>/auth/otp-login` body `{code, token}` |
|
|
313
|
-
|
|
|
314
|
-
| List
|
|
315
|
-
|
|
|
316
|
-
|
|
|
317
|
-
|
|
|
372
|
+
| Action | curl | Auth |
|
|
373
|
+
|---|---|---|
|
|
374
|
+
| Send OTP | `POST <base>/auth/otp-send-code` body `{username}` | none |
|
|
375
|
+
| Verify OTP → UT | `POST <base>/auth/otp-login` body `{code, token}` | none |
|
|
376
|
+
| Profile | `GET <base>/user/me` | UT |
|
|
377
|
+
| List workspaces | `GET <base>/team-app/workspaces/list?need_personal_workspace=true` | UT |
|
|
378
|
+
| Mint WT | `POST <base>/user-app/auth/workspace/token/<workspaceId>` body `{}` | UT |
|
|
379
|
+
| List devices | `GET <base>/device/list` | **WT** |
|
|
380
|
+
| List recordings | `GET <base>/file/simple/web?skip=0&limit=50&is_trash=0&sort_by=edit_time&is_desc=true` | **WT** |
|
|
381
|
+
| Get download URL | `GET <base>/file/temp-url/<id>?is_opus=0` | **WT** |
|
|
382
|
+
| Download audio | `GET <temp_url>` | none (signed) |
|
|
383
|
+
| Transcribe local file | `POST http://localhost:7400/api/whisper/transcribe-file` body `{path, saveTranscriptNext}` | none (exempt) |
|
|
384
|
+
|
|
385
|
+
State file: `workspace/.plaud.json` — read/write with `Read` / `Write`. **No `/api/settings` calls** — that endpoint requires a portal Bearer token the skill can't easily produce.
|
|
318
386
|
|
|
319
|
-
All Plaud requests need
|
|
387
|
+
All Plaud requests need a browser-style `User-Agent`.
|
|
320
388
|
|
|
321
389
|
---
|
|
322
390
|
|
|
323
391
|
## Credit
|
|
324
392
|
|
|
325
|
-
Plaud API shape is the same one [OpenPlaud](https://github.com/openplaud/openplaud) uses — they did the reverse-engineering work. This skill reimplements just the parts a bloby needs.
|
|
393
|
+
Plaud API shape is the same one [OpenPlaud](https://github.com/openplaud/openplaud) uses — they did the reverse-engineering work, including the painful workspace-token discovery (their issue #66). This skill reimplements just the parts a bloby needs.
|