From e5c5d23deab1b4a0504c3aa4e8ee88bffcaab5d1 Mon Sep 17 00:00:00 2001 From: Andrew Kopylev Date: Sun, 3 May 2026 21:07:39 +0300 Subject: [PATCH] =?UTF-8?q?Release=200.9.10=20=E2=80=94=20fix=20zero-byte?= =?UTF-8?q?=20download=20crash,=20simplify=20Password=20setDesc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.json | 2 +- package.json | 2 +- src/settings.ts | 2 +- src/sftp/transfer.ts | 13 ++++++++++--- src/sync/pull-engine.ts | 6 ++++-- versions.json | 3 ++- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/manifest.json b/manifest.json index a760f6a..aa7638e 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "vault-bridge-sftp", "name": "Vault Bridge SFTP", - "version": "0.9.9", + "version": "0.9.10", "minAppVersion": "1.5.0", "description": "Bridge your vault across devices through your own SSH/SFTP server. Bidirectional sync with conflict resolution, multi-device safety, and full self-hosting.", "author": "Andrew Kopylev", diff --git a/package.json b/package.json index e286777..991a116 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-vault-bridge-sftp", - "version": "0.9.9", + "version": "0.9.10", "description": "Vault Bridge SFTP — bidirectional Obsidian vault sync over SSH/SFTP", "main": "main.js", "scripts": { diff --git a/src/settings.ts b/src/settings.ts index c565008..00c5ddd 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -137,7 +137,7 @@ export class SftpSyncSettingTab extends PluginSettingTab { if (this.plugin.settings.authMethod === "password") { new Setting(containerEl) .setName("Password") - .setDesc("Encrypted at rest using a per-device key. The key never syncs to the server, so password disclosure requires local filesystem access. SSH keys are still preferred on shared machines.") + .setDesc("Encrypted on this device. Use SSH keys on shared machines.") .addText((t) => { t.inputEl.type = "password"; t.setValue(this.plugin.settings.password).onChange((v) => { diff --git a/src/sftp/transfer.ts b/src/sftp/transfer.ts index e44715f..2f5bb44 100644 --- a/src/sftp/transfer.ts +++ b/src/sftp/transfer.ts @@ -85,15 +85,22 @@ export async function readLocalAsBuffer(adapter: DataAdapter, vaultPath: string) return Buffer.from(ab); } -/** Read a remote file as a Buffer (full content in memory). */ +/** Read a remote file as a Buffer (full content in memory). + * Note: ssh2-sftp-client's `get()` (via the underlying concat-stream) returns an empty + * array `[]` instead of an empty Buffer when the remote file is zero bytes, so we + * defensively coerce to a real Buffer here. Callers downstream rely on `.buffer`, + * `.byteOffset`, and `Buffer.isBuffer` semantics that the empty-array sentinel + * doesn't satisfy. */ export async function downloadToBuffer( client: SftpClient, remoteRoot: string, vaultPath: string, ): Promise { const remotePath = remotePathOf(remoteRoot, vaultPath); - const buf = (await client.raw.get(remotePath)) as Buffer; - return buf; + const result = await client.raw.get(remotePath); + if (Buffer.isBuffer(result)) return result; + // 0-byte file → concat-stream gave us [] — return a real empty Buffer instead. + return Buffer.alloc(0); } /** Write a Buffer into the vault at `vaultPath`, atomically via the given tmp directory. diff --git a/src/sync/pull-engine.ts b/src/sync/pull-engine.ts index 6f06ce6..465e54f 100644 --- a/src/sync/pull-engine.ts +++ b/src/sync/pull-engine.ts @@ -174,10 +174,12 @@ export class PullEngine { ): Promise { const remotePath = remotePathOf(this.settings.remoteRoot, vaultPath); - // Get full file content. ssh2-sftp-client.get returns a Buffer when no stream destination is given. + // Get full file content. ssh2-sftp-client.get returns a Buffer for non-empty files, + // but an empty array `[]` (via concat-stream) for zero-byte files. Coerce to Buffer. let buf: Buffer; try { - buf = (await client.raw.get(remotePath)) as Buffer; + const result = await client.raw.get(remotePath); + buf = Buffer.isBuffer(result) ? result : Buffer.alloc(0); } catch (err) { throw new Error(`Cannot fetch remote ${remotePath}: ${(err as Error).message}`); } diff --git a/versions.json b/versions.json index 68385c6..f610ccb 100644 --- a/versions.json +++ b/versions.json @@ -2,5 +2,6 @@ "0.9.6": "1.5.0", "0.9.7": "1.5.0", "0.9.8": "1.5.0", - "0.9.9": "1.5.0" + "0.9.9": "1.5.0", + "0.9.10": "1.5.0" }