clawkeep 0.2.2 → 0.2.3
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/src/core/backup.js +9 -1
- package/src/core/transport.js +30 -0
package/package.json
CHANGED
package/src/core/backup.js
CHANGED
|
@@ -140,10 +140,11 @@ class BackupManager {
|
|
|
140
140
|
if (!target) throw new Error('No backup target configured');
|
|
141
141
|
|
|
142
142
|
let result;
|
|
143
|
+
let transport;
|
|
143
144
|
if (target === 'local' || target === 's3' || target === 'cloud') {
|
|
144
145
|
// Encrypted incremental sync (local, S3, or cloud)
|
|
145
146
|
if (!password) throw new Error('Password required for encrypted sync');
|
|
146
|
-
|
|
147
|
+
transport = createTransport(backup, this.claw);
|
|
147
148
|
const sm = new SyncManager(this.claw, transport, password);
|
|
148
149
|
result = await sm.sync();
|
|
149
150
|
} else if (target === 'git') {
|
|
@@ -156,6 +157,13 @@ class BackupManager {
|
|
|
156
157
|
freshConfig.backup.lastSync = new Date().toISOString();
|
|
157
158
|
this.claw.saveConfig(freshConfig);
|
|
158
159
|
|
|
160
|
+
// Report sync stats to cloud API (fire-and-forget)
|
|
161
|
+
if (target === 'cloud' && transport.reportSync) {
|
|
162
|
+
const chunkCount = freshConfig.backup.chunkCount || result.chunkCount || 0;
|
|
163
|
+
const totalSize = result.totalSize || 0;
|
|
164
|
+
transport.reportSync({ chunkCount, totalSize }).catch(() => {});
|
|
165
|
+
}
|
|
166
|
+
|
|
159
167
|
return { ...result, lastSync: freshConfig.backup.lastSync };
|
|
160
168
|
}
|
|
161
169
|
|
package/src/core/transport.js
CHANGED
|
@@ -181,6 +181,36 @@ class CloudTransport extends BackupTransport {
|
|
|
181
181
|
: now + 3600000;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Report sync stats to the cloud API (fire-and-forget).
|
|
186
|
+
*/
|
|
187
|
+
async reportSync({ chunkCount, totalSize }) {
|
|
188
|
+
const url = `${this.endpoint}/api/workspaces/${this.workspace}/sync-report`;
|
|
189
|
+
const body = JSON.stringify({ chunk_count: chunkCount, storage_bytes: totalSize });
|
|
190
|
+
try {
|
|
191
|
+
await new Promise((resolve, reject) => {
|
|
192
|
+
const parsed = new URL(url);
|
|
193
|
+
const mod = parsed.protocol === 'https:' ? https : http;
|
|
194
|
+
const req = mod.request(url, {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: {
|
|
197
|
+
'Authorization': 'Bearer ' + this.apiKey,
|
|
198
|
+
'Content-Type': 'application/json',
|
|
199
|
+
'Accept': 'application/json',
|
|
200
|
+
},
|
|
201
|
+
}, (res) => {
|
|
202
|
+
res.resume(); // drain response
|
|
203
|
+
res.on('end', resolve);
|
|
204
|
+
});
|
|
205
|
+
req.on('error', reject);
|
|
206
|
+
req.setTimeout(15000, () => req.destroy(new Error('Sync report timeout')));
|
|
207
|
+
req.end(body);
|
|
208
|
+
});
|
|
209
|
+
} catch {
|
|
210
|
+
// Fire-and-forget: don't fail the sync if report fails
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
184
214
|
async writeFile(remotePath, buffer) {
|
|
185
215
|
await this._ensureCredentials();
|
|
186
216
|
return this._inner.writeFile(remotePath, buffer);
|