proton-drive-mcp 1.0.34 → 1.0.35

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.35 — 2026-09-03
4
+
5
+ Continued the live-testing bug sweep. This round found a serious silent-data-corruption bug in `drive_move`, confirmed by a full live reproduction against the real account, plus the same root cause in `drive_copy`. The same fix hardens three more structurally identical code paths (`trash`, `restore`, `delete`) against the same failure mode.
6
+
7
+ ### Fixed
8
+ - **`drive_move` could silently rename the WRONG file and report false success**, when moving across folders into a destination that already has an item with the same name. Root cause confirmed live: `filesystem move` exits **0** with `[{ok:false, error:{name:"NodeWithSameNameExistsValidationError", ...}}]` on a name collision — not a non-zero exit code. `move()`'s cross-folder composition (`move` the item, then `rename` it) discarded this result entirely, assumed the move always succeeded, and blindly renamed *whatever item happened to already exist* at the computed destination path — which, on a collision, was the pre-existing unrelated file, not the one that was supposed to move. Reproduced the full scenario live: source file stayed in its original folder, untouched, while a completely different file in the destination folder got silently renamed, and the tool reported "Moved: ..." success throughout. Now `move()` checks the result and fails cleanly with a clear error instead of proceeding.
9
+ - **`drive_copy` reported false success on the same kind of destination-name collision.** Same root cause, confirmed live the same way (`filesystem copy` also returns `[{ok:false, ...}]` with exit 0) — `copy()` discarded the result and unconditionally reported success. Now checked the same way.
10
+ - **Same defensive check added to `trash`, `restore`, and `delete`**, which share the identical CLI response shape (all support batch operation via `path...` and report per-item `{uid, ok, error}` results) — hardening them against the same silent-failure class even though a live collision wasn't independently reproduced for each.
11
+
3
12
  ## 1.0.34 — 2026-09-03
4
13
 
5
14
  Continued the live-testing bug sweep from v1.0.33. 7 more real, confirmed bugs across two rounds — every one reproduced live against the real Proton Drive CLI and account before being called a bug, and every fix re-verified live afterward.
@@ -29,6 +29,27 @@ function unwrapResult(value, fallback = "") {
29
29
  function escapeNameForPath(name) {
30
30
  return name.replace(/\//g, "\\/");
31
31
  }
32
+ // filesystem move/copy/trash/restore/delete all accept multiple paths in one
33
+ // call and report per-item success as an array of {uid, ok, error} — NOT via
34
+ // a non-zero exit code. Confirmed live: a name collision at the destination
35
+ // makes `filesystem move`/`filesystem copy` exit 0 with
36
+ // [{ok:false, error:{name:"NodeWithSameNameExistsValidationError", ...}}].
37
+ // Every one of these wrappers previously discarded the result outright, so a
38
+ // collision was silently reported as success. Worst case was move(): it then
39
+ // went on to rename whatever OTHER item happened to already be sitting at the
40
+ // computed destination path — silently renaming an unrelated file while the
41
+ // real source never moved.
42
+ function assertItemsOk(result, action) {
43
+ if (!Array.isArray(result))
44
+ return;
45
+ for (const item of result) {
46
+ if (item && item.ok === false) {
47
+ const err = (item.error ?? {});
48
+ const code = typeof err.code !== "undefined" ? ` (code ${err.code})` : "";
49
+ throw new Error(`${action} failed: ${err.name ?? "unknown error"}${code} — likely a name collision at the destination.`);
50
+ }
51
+ }
52
+ }
32
53
  // Strips "<package-name>@" and "+<hash>" from a version token like
33
54
  // "cli-drive@0.8.0+06e8c605", leaving "0.8.0". Falls back to the raw
34
55
  // token if it doesn't match, so an unexpected future format still shows
@@ -185,7 +206,8 @@ export class DriveService {
185
206
  await this.rename(sourcePath, dstName);
186
207
  return;
187
208
  }
188
- await this.run(["filesystem", "move", sourcePath, dstParent]);
209
+ const moveResult = await this.run(["filesystem", "move", sourcePath, dstParent]);
210
+ assertItemsOk(moveResult, "Move");
189
211
  if (srcName !== dstName) {
190
212
  const movedPath = posixPath.join(dstParent, srcName);
191
213
  await this.rename(movedPath, dstName);
@@ -195,7 +217,7 @@ export class DriveService {
195
217
  // /trash or /photos-trash (the CLI rejects live paths). No --confirm flag
196
218
  // exists on the CLI side; our own confirmed-gate lives in the MCP layer.
197
219
  async delete(remotePath) {
198
- await this.run(["filesystem", "delete", remotePath]);
220
+ assertItemsOk(await this.run(["filesystem", "delete", remotePath]), "Delete");
199
221
  }
200
222
  // Sharing
201
223
  //
@@ -285,16 +307,16 @@ export class DriveService {
285
307
  return this.list("/trash");
286
308
  }
287
309
  async trash(remotePath) {
288
- await this.run(["filesystem", "trash", remotePath]);
310
+ assertItemsOk(await this.run(["filesystem", "trash", remotePath]), "Trash");
289
311
  }
290
312
  async restore(remotePath) {
291
- await this.run(["filesystem", "restore", remotePath]);
313
+ assertItemsOk(await this.run(["filesystem", "restore", remotePath]), "Restore");
292
314
  }
293
315
  async emptyTrash() {
294
316
  await this.run(["filesystem", "empty-trash"]);
295
317
  }
296
318
  async copy(remoteSrc, remoteDst) {
297
- await this.run(["filesystem", "copy", remoteSrc, remoteDst]);
319
+ assertItemsOk(await this.run(["filesystem", "copy", remoteSrc, remoteDst]), "Copy");
298
320
  }
299
321
  async listInvitations() {
300
322
  const result = await this.run(["invitation", "list"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proton-drive-mcp",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "MCP server and CLI that gives Claude full access to Proton Drive — upload, download, share, and manage your end-to-end encrypted files without leaving the conversation.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",