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/index.js
CHANGED
|
@@ -3024,10 +3024,12 @@ import {
|
|
|
3024
3024
|
randomBytes as randomBytesNode,
|
|
3025
3025
|
randomUUID as randomUUID3
|
|
3026
3026
|
} from "crypto";
|
|
3027
|
-
import { createReadStream as createReadStream2, statfsSync } from "fs";
|
|
3027
|
+
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, statfsSync } from "fs";
|
|
3028
3028
|
import { lstat, mkdir as mkdir5, readFile as readFile3, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
3029
|
-
import { homedir as homedir6 } from "os";
|
|
3029
|
+
import { homedir as homedir6, tmpdir } from "os";
|
|
3030
3030
|
import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
|
|
3031
|
+
import { Readable } from "stream";
|
|
3032
|
+
import { finished } from "stream/promises";
|
|
3031
3033
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
3032
3034
|
|
|
3033
3035
|
// src/cloud-ls-format.ts
|
|
@@ -3817,6 +3819,7 @@ var DEFAULT_BACKUP_DIR = join8(homedir6(), BACKUP_DIR_SUBPATH);
|
|
|
3817
3819
|
var BLOCKRUN_WALLET_KEY_SUBPATH = join8(".openclaw", "blockrun", "wallet.key");
|
|
3818
3820
|
var MNEMOSPARK_WALLET_KEY_SUBPATH = join8(".openclaw", "mnemospark", "wallet", "wallet.key");
|
|
3819
3821
|
var INLINE_UPLOAD_MAX_BYTES = 45e5;
|
|
3822
|
+
var NODE_FS_MAX_READFILE_BYTES = 2147483648;
|
|
3820
3823
|
var PAYMENT_CRON_SCHEDULE = "0 0 1 * *";
|
|
3821
3824
|
var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
|
|
3822
3825
|
var QUOTE_VALIDITY_USER_NOTE = "Quotes are valid for one hour. Please run price-storage again if you need a new quote.";
|
|
@@ -4740,6 +4743,39 @@ function encryptAesGcm(plaintext, key, randomFn = randomBytesNode) {
|
|
|
4740
4743
|
const tag = cipher.getAuthTag();
|
|
4741
4744
|
return Buffer.concat([nonce, ciphertext, tag]);
|
|
4742
4745
|
}
|
|
4746
|
+
async function encryptPlaintextFileToAesGcmPath(plaintextPath, dek, outPath, randomFn = randomBytesNode) {
|
|
4747
|
+
if (dek.length !== 32) {
|
|
4748
|
+
throw new Error("Expected 32-byte AES key");
|
|
4749
|
+
}
|
|
4750
|
+
const nonce = randomFn(AES_GCM_NONCE_BYTES);
|
|
4751
|
+
const cipher = createCipheriv("aes-256-gcm", dek, nonce);
|
|
4752
|
+
const writeStream = createWriteStream2(outPath, { flags: "w" });
|
|
4753
|
+
writeStream.write(nonce);
|
|
4754
|
+
try {
|
|
4755
|
+
for await (const chunk of createReadStream2(plaintextPath)) {
|
|
4756
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
4757
|
+
const out = cipher.update(buf);
|
|
4758
|
+
if (out.length) {
|
|
4759
|
+
await new Promise((resolve3, reject) => {
|
|
4760
|
+
writeStream.write(out, (err) => err ? reject(err) : resolve3());
|
|
4761
|
+
});
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
const final = cipher.final();
|
|
4765
|
+
const tag = cipher.getAuthTag();
|
|
4766
|
+
await new Promise((resolve3, reject) => {
|
|
4767
|
+
writeStream.write(final, (err) => err ? reject(err) : resolve3());
|
|
4768
|
+
});
|
|
4769
|
+
await new Promise((resolve3, reject) => {
|
|
4770
|
+
writeStream.write(tag, (err) => err ? reject(err) : resolve3());
|
|
4771
|
+
});
|
|
4772
|
+
writeStream.end();
|
|
4773
|
+
await finished(writeStream);
|
|
4774
|
+
} catch (err) {
|
|
4775
|
+
writeStream.destroy();
|
|
4776
|
+
throw err;
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4743
4779
|
async function loadOrCreateKek(walletAddress, homeDir) {
|
|
4744
4780
|
const keyPath = resolveWalletKekPath(walletAddress, homeDir);
|
|
4745
4781
|
await mkdir5(dirname5(keyPath), { recursive: true });
|
|
@@ -4756,11 +4792,42 @@ async function loadOrCreateKek(walletAddress, homeDir) {
|
|
|
4756
4792
|
return { kek: generated, keyPath };
|
|
4757
4793
|
}
|
|
4758
4794
|
async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
4759
|
-
const
|
|
4795
|
+
const archiveStat = await stat2(archivePath);
|
|
4796
|
+
if (!archiveStat.isFile()) {
|
|
4797
|
+
throw new Error(`Cannot read backup archive: not a file (${archivePath}).`);
|
|
4798
|
+
}
|
|
4760
4799
|
const { kek, keyPath } = await loadOrCreateKek(walletAddress, homeDir);
|
|
4761
4800
|
const dek = randomBytesNode(32);
|
|
4762
|
-
const encryptedContent = encryptAesGcm(plaintext, dek);
|
|
4763
4801
|
const wrappedDek = encryptAesGcm(dek, kek);
|
|
4802
|
+
if (archiveStat.size >= NODE_FS_MAX_READFILE_BYTES) {
|
|
4803
|
+
const encryptedTempPath = join8(tmpdir(), `mnemospark-upload-${randomUUID3()}.enc`);
|
|
4804
|
+
try {
|
|
4805
|
+
await encryptPlaintextFileToAesGcmPath(archivePath, dek, encryptedTempPath);
|
|
4806
|
+
const encStat = await stat2(encryptedTempPath);
|
|
4807
|
+
const payloadHash2 = await sha256File(encryptedTempPath);
|
|
4808
|
+
const payload2 = {
|
|
4809
|
+
mode: "presigned",
|
|
4810
|
+
content_base64: void 0,
|
|
4811
|
+
content_sha256: payloadHash2,
|
|
4812
|
+
content_length_bytes: encStat.size,
|
|
4813
|
+
wrapped_dek: wrappedDek.toString("base64"),
|
|
4814
|
+
encryption_algorithm: "AES-256-GCM",
|
|
4815
|
+
bucket_name_hint: bucketNameForWallet(walletAddress),
|
|
4816
|
+
key_store_path_hint: keyPath
|
|
4817
|
+
};
|
|
4818
|
+
return {
|
|
4819
|
+
payload: payload2,
|
|
4820
|
+
encryptedContent: null,
|
|
4821
|
+
encryptedTempPath
|
|
4822
|
+
};
|
|
4823
|
+
} catch (err) {
|
|
4824
|
+
await rm(encryptedTempPath, { force: true }).catch(() => {
|
|
4825
|
+
});
|
|
4826
|
+
throw err;
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
const plaintext = await readFile3(archivePath);
|
|
4830
|
+
const encryptedContent = encryptAesGcm(plaintext, dek);
|
|
4764
4831
|
const payloadHash = sha256Buffer(encryptedContent);
|
|
4765
4832
|
const payload = {
|
|
4766
4833
|
mode: encryptedContent.length <= INLINE_UPLOAD_MAX_BYTES ? "inline" : "presigned",
|
|
@@ -4777,7 +4844,17 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
|
4777
4844
|
encryptedContent
|
|
4778
4845
|
};
|
|
4779
4846
|
}
|
|
4780
|
-
|
|
4847
|
+
function presignedPutBodyInit(encryptedContent, encryptedTempPath) {
|
|
4848
|
+
if (encryptedTempPath?.trim()) {
|
|
4849
|
+
const body = Readable.toWeb(createReadStream2(encryptedTempPath));
|
|
4850
|
+
return { body, duplex: "half" };
|
|
4851
|
+
}
|
|
4852
|
+
if (encryptedContent) {
|
|
4853
|
+
return { body: new Uint8Array(encryptedContent) };
|
|
4854
|
+
}
|
|
4855
|
+
throw new Error("Cannot upload storage object: missing encrypted payload body.");
|
|
4856
|
+
}
|
|
4857
|
+
async function uploadPresignedObjectIfNeeded(uploadResponse, uploadMode, encryptedContent, encryptedTempPath, fetchImpl = fetch, presignedStreamContentLengthBytes) {
|
|
4781
4858
|
if (!uploadResponse.upload_url) {
|
|
4782
4859
|
if (uploadMode === "presigned") {
|
|
4783
4860
|
throw new Error("Cannot upload storage object: missing presigned upload URL.");
|
|
@@ -4788,24 +4865,39 @@ async function uploadPresignedObjectIfNeeded(uploadResponse, uploadMode, encrypt
|
|
|
4788
4865
|
if (!headers.has("content-type")) {
|
|
4789
4866
|
headers.set("content-type", "application/octet-stream");
|
|
4790
4867
|
}
|
|
4791
|
-
|
|
4792
|
-
|
|
4868
|
+
if (encryptedTempPath?.trim()) {
|
|
4869
|
+
const len = presignedStreamContentLengthBytes ?? (await stat2(encryptedTempPath.trim())).size;
|
|
4870
|
+
if (!headers.has("content-length")) {
|
|
4871
|
+
headers.set("content-length", String(len));
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
const { body, duplex } = presignedPutBodyInit(encryptedContent, encryptedTempPath);
|
|
4875
|
+
const firstInit = {
|
|
4793
4876
|
method: "PUT",
|
|
4794
4877
|
headers,
|
|
4795
|
-
body
|
|
4878
|
+
body,
|
|
4796
4879
|
redirect: "manual"
|
|
4797
|
-
}
|
|
4880
|
+
};
|
|
4881
|
+
if (duplex) {
|
|
4882
|
+
firstInit.duplex = duplex;
|
|
4883
|
+
}
|
|
4884
|
+
const firstAttempt = await fetchImpl(uploadResponse.upload_url, firstInit);
|
|
4798
4885
|
if (firstAttempt.ok) {
|
|
4799
4886
|
return;
|
|
4800
4887
|
}
|
|
4801
4888
|
if ((firstAttempt.status === 307 || firstAttempt.status === 308) && firstAttempt.headers.has("location")) {
|
|
4802
4889
|
const location = firstAttempt.headers.get("location")?.trim();
|
|
4803
4890
|
if (location) {
|
|
4804
|
-
const
|
|
4891
|
+
const retryBody = presignedPutBodyInit(encryptedContent, encryptedTempPath);
|
|
4892
|
+
const retryInit = {
|
|
4805
4893
|
method: "PUT",
|
|
4806
4894
|
headers,
|
|
4807
|
-
body:
|
|
4808
|
-
}
|
|
4895
|
+
body: retryBody.body
|
|
4896
|
+
};
|
|
4897
|
+
if (retryBody.duplex) {
|
|
4898
|
+
retryInit.duplex = retryBody.duplex;
|
|
4899
|
+
}
|
|
4900
|
+
const redirectedAttempt = await fetchImpl(location, retryInit);
|
|
4809
4901
|
if (redirectedAttempt.ok) {
|
|
4810
4902
|
return;
|
|
4811
4903
|
}
|
|
@@ -6218,6 +6310,7 @@ operation-id: ${operationId}`,
|
|
|
6218
6310
|
executionContext.forcedOperationId ?? idempotencyKeyFn(),
|
|
6219
6311
|
executionContext.forcedTraceId
|
|
6220
6312
|
);
|
|
6313
|
+
let preparedPayload;
|
|
6221
6314
|
try {
|
|
6222
6315
|
const loggedQuote = await datastore.findQuoteById(parsed.uploadRequest.quote_id);
|
|
6223
6316
|
if (!loggedQuote) {
|
|
@@ -6289,7 +6382,7 @@ operation-id: ${operationId}`,
|
|
|
6289
6382
|
isError: true
|
|
6290
6383
|
};
|
|
6291
6384
|
}
|
|
6292
|
-
|
|
6385
|
+
preparedPayload = await prepareUploadPayload(
|
|
6293
6386
|
archivePath,
|
|
6294
6387
|
parsed.uploadRequest.wallet_address,
|
|
6295
6388
|
mnemosparkHomeDir
|
|
@@ -6333,7 +6426,9 @@ operation-id: ${operationId}`,
|
|
|
6333
6426
|
uploadResponse,
|
|
6334
6427
|
preparedPayload.payload.mode,
|
|
6335
6428
|
preparedPayload.encryptedContent,
|
|
6336
|
-
|
|
6429
|
+
preparedPayload.encryptedTempPath,
|
|
6430
|
+
fetchImpl,
|
|
6431
|
+
preparedPayload.encryptedTempPath ? preparedPayload.payload.content_length_bytes : void 0
|
|
6337
6432
|
);
|
|
6338
6433
|
let finalizedUploadResponse = uploadResponse;
|
|
6339
6434
|
if (preparedPayload.payload.mode === "presigned" && uploadResponse.confirmation_required === true) {
|
|
@@ -6470,6 +6565,11 @@ operation-id: ${operationId}`,
|
|
|
6470
6565
|
text: uploadErrorMessage ?? "Cannot upload storage object",
|
|
6471
6566
|
isError: true
|
|
6472
6567
|
};
|
|
6568
|
+
} finally {
|
|
6569
|
+
if (preparedPayload?.encryptedTempPath) {
|
|
6570
|
+
await rm(preparedPayload.encryptedTempPath, { force: true }).catch(() => {
|
|
6571
|
+
});
|
|
6572
|
+
}
|
|
6473
6573
|
}
|
|
6474
6574
|
}
|
|
6475
6575
|
if (parsed.mode === "ls") {
|