mnemospark 0.9.0 → 0.9.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/dist/cli.js +114 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.js +114 -14
- 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, presignedStreamContentLengthBytes) {
|
|
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,39 @@ 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
|
-
|
|
4741
|
-
|
|
4817
|
+
if (encryptedTempPath?.trim()) {
|
|
4818
|
+
const len = presignedStreamContentLengthBytes ?? (await stat2(encryptedTempPath.trim())).size;
|
|
4819
|
+
if (!headers.has("content-length")) {
|
|
4820
|
+
headers.set("content-length", String(len));
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
const { body, duplex } = presignedPutBodyInit(encryptedContent, encryptedTempPath);
|
|
4824
|
+
const firstInit = {
|
|
4742
4825
|
method: "PUT",
|
|
4743
4826
|
headers,
|
|
4744
|
-
body
|
|
4827
|
+
body,
|
|
4745
4828
|
redirect: "manual"
|
|
4746
|
-
}
|
|
4829
|
+
};
|
|
4830
|
+
if (duplex) {
|
|
4831
|
+
firstInit.duplex = duplex;
|
|
4832
|
+
}
|
|
4833
|
+
const firstAttempt = await fetchImpl(uploadResponse.upload_url, firstInit);
|
|
4747
4834
|
if (firstAttempt.ok) {
|
|
4748
4835
|
return;
|
|
4749
4836
|
}
|
|
4750
4837
|
if ((firstAttempt.status === 307 || firstAttempt.status === 308) && firstAttempt.headers.has("location")) {
|
|
4751
4838
|
const location = firstAttempt.headers.get("location")?.trim();
|
|
4752
4839
|
if (location) {
|
|
4753
|
-
const
|
|
4840
|
+
const retryBody = presignedPutBodyInit(encryptedContent, encryptedTempPath);
|
|
4841
|
+
const retryInit = {
|
|
4754
4842
|
method: "PUT",
|
|
4755
4843
|
headers,
|
|
4756
|
-
body:
|
|
4757
|
-
}
|
|
4844
|
+
body: retryBody.body
|
|
4845
|
+
};
|
|
4846
|
+
if (retryBody.duplex) {
|
|
4847
|
+
retryInit.duplex = retryBody.duplex;
|
|
4848
|
+
}
|
|
4849
|
+
const redirectedAttempt = await fetchImpl(location, retryInit);
|
|
4758
4850
|
if (redirectedAttempt.ok) {
|
|
4759
4851
|
return;
|
|
4760
4852
|
}
|
|
@@ -6167,6 +6259,7 @@ operation-id: ${operationId}`,
|
|
|
6167
6259
|
executionContext.forcedOperationId ?? idempotencyKeyFn(),
|
|
6168
6260
|
executionContext.forcedTraceId
|
|
6169
6261
|
);
|
|
6262
|
+
let preparedPayload;
|
|
6170
6263
|
try {
|
|
6171
6264
|
const loggedQuote = await datastore.findQuoteById(parsed.uploadRequest.quote_id);
|
|
6172
6265
|
if (!loggedQuote) {
|
|
@@ -6238,7 +6331,7 @@ operation-id: ${operationId}`,
|
|
|
6238
6331
|
isError: true
|
|
6239
6332
|
};
|
|
6240
6333
|
}
|
|
6241
|
-
|
|
6334
|
+
preparedPayload = await prepareUploadPayload(
|
|
6242
6335
|
archivePath,
|
|
6243
6336
|
parsed.uploadRequest.wallet_address,
|
|
6244
6337
|
mnemosparkHomeDir
|
|
@@ -6282,7 +6375,9 @@ operation-id: ${operationId}`,
|
|
|
6282
6375
|
uploadResponse,
|
|
6283
6376
|
preparedPayload.payload.mode,
|
|
6284
6377
|
preparedPayload.encryptedContent,
|
|
6285
|
-
|
|
6378
|
+
preparedPayload.encryptedTempPath,
|
|
6379
|
+
fetchImpl,
|
|
6380
|
+
preparedPayload.encryptedTempPath ? preparedPayload.payload.content_length_bytes : void 0
|
|
6286
6381
|
);
|
|
6287
6382
|
let finalizedUploadResponse = uploadResponse;
|
|
6288
6383
|
if (preparedPayload.payload.mode === "presigned" && uploadResponse.confirmation_required === true) {
|
|
@@ -6419,6 +6514,11 @@ operation-id: ${operationId}`,
|
|
|
6419
6514
|
text: uploadErrorMessage ?? "Cannot upload storage object",
|
|
6420
6515
|
isError: true
|
|
6421
6516
|
};
|
|
6517
|
+
} finally {
|
|
6518
|
+
if (preparedPayload?.encryptedTempPath) {
|
|
6519
|
+
await rm(preparedPayload.encryptedTempPath, { force: true }).catch(() => {
|
|
6520
|
+
});
|
|
6521
|
+
}
|
|
6422
6522
|
}
|
|
6423
6523
|
}
|
|
6424
6524
|
if (parsed.mode === "ls") {
|