mnemospark 0.9.0 → 0.9.1
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/dist/cli.js +106 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.js +106 -13
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2973,10 +2973,12 @@ import {
|
|
|
2973
2973
|
randomBytes as randomBytesNode,
|
|
2974
2974
|
randomUUID as randomUUID3
|
|
2975
2975
|
} from "crypto";
|
|
2976
|
-
import { createReadStream as createReadStream2, statfsSync } from "fs";
|
|
2976
|
+
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, statfsSync } from "fs";
|
|
2977
2977
|
import { lstat, mkdir as mkdir5, readFile as readFile3, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
2978
|
-
import { homedir as homedir6 } from "os";
|
|
2978
|
+
import { homedir as homedir6, tmpdir } from "os";
|
|
2979
2979
|
import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
|
|
2980
|
+
import { Readable } from "stream";
|
|
2981
|
+
import { finished } from "stream/promises";
|
|
2980
2982
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
2981
2983
|
|
|
2982
2984
|
// src/cloud-ls-format.ts
|
|
@@ -3766,6 +3768,7 @@ var DEFAULT_BACKUP_DIR = join8(homedir6(), BACKUP_DIR_SUBPATH);
|
|
|
3766
3768
|
var BLOCKRUN_WALLET_KEY_SUBPATH = join8(".openclaw", "blockrun", "wallet.key");
|
|
3767
3769
|
var MNEMOSPARK_WALLET_KEY_SUBPATH = join8(".openclaw", "mnemospark", "wallet", "wallet.key");
|
|
3768
3770
|
var INLINE_UPLOAD_MAX_BYTES = 45e5;
|
|
3771
|
+
var NODE_FS_MAX_READFILE_BYTES = 2147483648;
|
|
3769
3772
|
var PAYMENT_CRON_SCHEDULE = "0 0 1 * *";
|
|
3770
3773
|
var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
|
|
3771
3774
|
var QUOTE_VALIDITY_USER_NOTE = "Quotes are valid for one hour. Please run price-storage again if you need a new quote.";
|
|
@@ -4689,6 +4692,39 @@ function encryptAesGcm(plaintext, key, randomFn = randomBytesNode) {
|
|
|
4689
4692
|
const tag = cipher.getAuthTag();
|
|
4690
4693
|
return Buffer.concat([nonce, ciphertext, tag]);
|
|
4691
4694
|
}
|
|
4695
|
+
async function encryptPlaintextFileToAesGcmPath(plaintextPath, dek, outPath, randomFn = randomBytesNode) {
|
|
4696
|
+
if (dek.length !== 32) {
|
|
4697
|
+
throw new Error("Expected 32-byte AES key");
|
|
4698
|
+
}
|
|
4699
|
+
const nonce = randomFn(AES_GCM_NONCE_BYTES);
|
|
4700
|
+
const cipher = createCipheriv("aes-256-gcm", dek, nonce);
|
|
4701
|
+
const writeStream = createWriteStream2(outPath, { flags: "w" });
|
|
4702
|
+
writeStream.write(nonce);
|
|
4703
|
+
try {
|
|
4704
|
+
for await (const chunk of createReadStream2(plaintextPath)) {
|
|
4705
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
4706
|
+
const out = cipher.update(buf);
|
|
4707
|
+
if (out.length) {
|
|
4708
|
+
await new Promise((resolve3, reject) => {
|
|
4709
|
+
writeStream.write(out, (err) => err ? reject(err) : resolve3());
|
|
4710
|
+
});
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
const final = cipher.final();
|
|
4714
|
+
const tag = cipher.getAuthTag();
|
|
4715
|
+
await new Promise((resolve3, reject) => {
|
|
4716
|
+
writeStream.write(final, (err) => err ? reject(err) : resolve3());
|
|
4717
|
+
});
|
|
4718
|
+
await new Promise((resolve3, reject) => {
|
|
4719
|
+
writeStream.write(tag, (err) => err ? reject(err) : resolve3());
|
|
4720
|
+
});
|
|
4721
|
+
writeStream.end();
|
|
4722
|
+
await finished(writeStream);
|
|
4723
|
+
} catch (err) {
|
|
4724
|
+
writeStream.destroy();
|
|
4725
|
+
throw err;
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4692
4728
|
async function loadOrCreateKek(walletAddress, homeDir) {
|
|
4693
4729
|
const keyPath = resolveWalletKekPath(walletAddress, homeDir);
|
|
4694
4730
|
await mkdir5(dirname5(keyPath), { recursive: true });
|
|
@@ -4705,11 +4741,42 @@ async function loadOrCreateKek(walletAddress, homeDir) {
|
|
|
4705
4741
|
return { kek: generated, keyPath };
|
|
4706
4742
|
}
|
|
4707
4743
|
async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
4708
|
-
const
|
|
4744
|
+
const archiveStat = await stat2(archivePath);
|
|
4745
|
+
if (!archiveStat.isFile()) {
|
|
4746
|
+
throw new Error(`Cannot read backup archive: not a file (${archivePath}).`);
|
|
4747
|
+
}
|
|
4709
4748
|
const { kek, keyPath } = await loadOrCreateKek(walletAddress, homeDir);
|
|
4710
4749
|
const dek = randomBytesNode(32);
|
|
4711
|
-
const encryptedContent = encryptAesGcm(plaintext, dek);
|
|
4712
4750
|
const wrappedDek = encryptAesGcm(dek, kek);
|
|
4751
|
+
if (archiveStat.size >= NODE_FS_MAX_READFILE_BYTES) {
|
|
4752
|
+
const encryptedTempPath = join8(tmpdir(), `mnemospark-upload-${randomUUID3()}.enc`);
|
|
4753
|
+
try {
|
|
4754
|
+
await encryptPlaintextFileToAesGcmPath(archivePath, dek, encryptedTempPath);
|
|
4755
|
+
const encStat = await stat2(encryptedTempPath);
|
|
4756
|
+
const payloadHash2 = await sha256File(encryptedTempPath);
|
|
4757
|
+
const payload2 = {
|
|
4758
|
+
mode: "presigned",
|
|
4759
|
+
content_base64: void 0,
|
|
4760
|
+
content_sha256: payloadHash2,
|
|
4761
|
+
content_length_bytes: encStat.size,
|
|
4762
|
+
wrapped_dek: wrappedDek.toString("base64"),
|
|
4763
|
+
encryption_algorithm: "AES-256-GCM",
|
|
4764
|
+
bucket_name_hint: bucketNameForWallet(walletAddress),
|
|
4765
|
+
key_store_path_hint: keyPath
|
|
4766
|
+
};
|
|
4767
|
+
return {
|
|
4768
|
+
payload: payload2,
|
|
4769
|
+
encryptedContent: null,
|
|
4770
|
+
encryptedTempPath
|
|
4771
|
+
};
|
|
4772
|
+
} catch (err) {
|
|
4773
|
+
await rm(encryptedTempPath, { force: true }).catch(() => {
|
|
4774
|
+
});
|
|
4775
|
+
throw err;
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
const plaintext = await readFile3(archivePath);
|
|
4779
|
+
const encryptedContent = encryptAesGcm(plaintext, dek);
|
|
4713
4780
|
const payloadHash = sha256Buffer(encryptedContent);
|
|
4714
4781
|
const payload = {
|
|
4715
4782
|
mode: encryptedContent.length <= INLINE_UPLOAD_MAX_BYTES ? "inline" : "presigned",
|
|
@@ -4726,7 +4793,17 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
|
4726
4793
|
encryptedContent
|
|
4727
4794
|
};
|
|
4728
4795
|
}
|
|
4729
|
-
|
|
4796
|
+
function presignedPutBodyInit(encryptedContent, encryptedTempPath) {
|
|
4797
|
+
if (encryptedTempPath?.trim()) {
|
|
4798
|
+
const body = Readable.toWeb(createReadStream2(encryptedTempPath));
|
|
4799
|
+
return { body, duplex: "half" };
|
|
4800
|
+
}
|
|
4801
|
+
if (encryptedContent) {
|
|
4802
|
+
return { body: new Uint8Array(encryptedContent) };
|
|
4803
|
+
}
|
|
4804
|
+
throw new Error("Cannot upload storage object: missing encrypted payload body.");
|
|
4805
|
+
}
|
|
4806
|
+
async function uploadPresignedObjectIfNeeded(uploadResponse, uploadMode, encryptedContent, encryptedTempPath, fetchImpl = fetch) {
|
|
4730
4807
|
if (!uploadResponse.upload_url) {
|
|
4731
4808
|
if (uploadMode === "presigned") {
|
|
4732
4809
|
throw new Error("Cannot upload storage object: missing presigned upload URL.");
|
|
@@ -4737,24 +4814,33 @@ async function uploadPresignedObjectIfNeeded(uploadResponse, uploadMode, encrypt
|
|
|
4737
4814
|
if (!headers.has("content-type")) {
|
|
4738
4815
|
headers.set("content-type", "application/octet-stream");
|
|
4739
4816
|
}
|
|
4740
|
-
const
|
|
4741
|
-
const
|
|
4817
|
+
const { body, duplex } = presignedPutBodyInit(encryptedContent, encryptedTempPath);
|
|
4818
|
+
const firstInit = {
|
|
4742
4819
|
method: "PUT",
|
|
4743
4820
|
headers,
|
|
4744
|
-
body
|
|
4821
|
+
body,
|
|
4745
4822
|
redirect: "manual"
|
|
4746
|
-
}
|
|
4823
|
+
};
|
|
4824
|
+
if (duplex) {
|
|
4825
|
+
firstInit.duplex = duplex;
|
|
4826
|
+
}
|
|
4827
|
+
const firstAttempt = await fetchImpl(uploadResponse.upload_url, firstInit);
|
|
4747
4828
|
if (firstAttempt.ok) {
|
|
4748
4829
|
return;
|
|
4749
4830
|
}
|
|
4750
4831
|
if ((firstAttempt.status === 307 || firstAttempt.status === 308) && firstAttempt.headers.has("location")) {
|
|
4751
4832
|
const location = firstAttempt.headers.get("location")?.trim();
|
|
4752
4833
|
if (location) {
|
|
4753
|
-
const
|
|
4834
|
+
const retryBody = presignedPutBodyInit(encryptedContent, encryptedTempPath);
|
|
4835
|
+
const retryInit = {
|
|
4754
4836
|
method: "PUT",
|
|
4755
4837
|
headers,
|
|
4756
|
-
body:
|
|
4757
|
-
}
|
|
4838
|
+
body: retryBody.body
|
|
4839
|
+
};
|
|
4840
|
+
if (retryBody.duplex) {
|
|
4841
|
+
retryInit.duplex = retryBody.duplex;
|
|
4842
|
+
}
|
|
4843
|
+
const redirectedAttempt = await fetchImpl(location, retryInit);
|
|
4758
4844
|
if (redirectedAttempt.ok) {
|
|
4759
4845
|
return;
|
|
4760
4846
|
}
|
|
@@ -6167,6 +6253,7 @@ operation-id: ${operationId}`,
|
|
|
6167
6253
|
executionContext.forcedOperationId ?? idempotencyKeyFn(),
|
|
6168
6254
|
executionContext.forcedTraceId
|
|
6169
6255
|
);
|
|
6256
|
+
let preparedPayload;
|
|
6170
6257
|
try {
|
|
6171
6258
|
const loggedQuote = await datastore.findQuoteById(parsed.uploadRequest.quote_id);
|
|
6172
6259
|
if (!loggedQuote) {
|
|
@@ -6238,7 +6325,7 @@ operation-id: ${operationId}`,
|
|
|
6238
6325
|
isError: true
|
|
6239
6326
|
};
|
|
6240
6327
|
}
|
|
6241
|
-
|
|
6328
|
+
preparedPayload = await prepareUploadPayload(
|
|
6242
6329
|
archivePath,
|
|
6243
6330
|
parsed.uploadRequest.wallet_address,
|
|
6244
6331
|
mnemosparkHomeDir
|
|
@@ -6282,6 +6369,7 @@ operation-id: ${operationId}`,
|
|
|
6282
6369
|
uploadResponse,
|
|
6283
6370
|
preparedPayload.payload.mode,
|
|
6284
6371
|
preparedPayload.encryptedContent,
|
|
6372
|
+
preparedPayload.encryptedTempPath,
|
|
6285
6373
|
fetchImpl
|
|
6286
6374
|
);
|
|
6287
6375
|
let finalizedUploadResponse = uploadResponse;
|
|
@@ -6419,6 +6507,11 @@ operation-id: ${operationId}`,
|
|
|
6419
6507
|
text: uploadErrorMessage ?? "Cannot upload storage object",
|
|
6420
6508
|
isError: true
|
|
6421
6509
|
};
|
|
6510
|
+
} finally {
|
|
6511
|
+
if (preparedPayload?.encryptedTempPath) {
|
|
6512
|
+
await rm(preparedPayload.encryptedTempPath, { force: true }).catch(() => {
|
|
6513
|
+
});
|
|
6514
|
+
}
|
|
6422
6515
|
}
|
|
6423
6516
|
}
|
|
6424
6517
|
if (parsed.mode === "ls") {
|