mnemospark 0.1.15 → 0.1.17
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 +411 -63
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.js +403 -55
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/proxy.ts
|
|
4
4
|
import { createServer } from "http";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join as join2 } from "path";
|
|
5
7
|
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
6
8
|
|
|
7
9
|
// src/balance.ts
|
|
@@ -324,6 +326,7 @@ function normalizeWalletSignature(value) {
|
|
|
324
326
|
// src/cloud-price-storage.ts
|
|
325
327
|
var PRICE_STORAGE_PROXY_PATH = "/mnemospark/price-storage";
|
|
326
328
|
var UPLOAD_PROXY_PATH = "/mnemospark/upload";
|
|
329
|
+
var UPLOAD_CONFIRM_PROXY_PATH = "/mnemospark/upload/confirm";
|
|
327
330
|
function asStringRecord(value) {
|
|
328
331
|
const record = asRecord(value);
|
|
329
332
|
if (!record) {
|
|
@@ -437,6 +440,25 @@ function parseStorageUploadRequest(payload) {
|
|
|
437
440
|
}
|
|
438
441
|
};
|
|
439
442
|
}
|
|
443
|
+
function parseStorageUploadConfirmRequest(payload) {
|
|
444
|
+
const record = asRecord(payload);
|
|
445
|
+
if (!record) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
const quoteId = asNonEmptyString(record.quote_id);
|
|
449
|
+
const walletAddress = asNonEmptyString(record.wallet_address);
|
|
450
|
+
const objectKey = asNonEmptyString(record.object_key);
|
|
451
|
+
const idempotencyKey = asNonEmptyString(record.idempotency_key);
|
|
452
|
+
if (!quoteId || !walletAddress || !objectKey || !idempotencyKey) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
quote_id: quoteId,
|
|
457
|
+
wallet_address: walletAddress,
|
|
458
|
+
object_key: objectKey,
|
|
459
|
+
idempotency_key: idempotencyKey
|
|
460
|
+
};
|
|
461
|
+
}
|
|
440
462
|
function parseStorageUploadResponse(payload) {
|
|
441
463
|
const record = asRecord(payload);
|
|
442
464
|
if (!record) {
|
|
@@ -454,12 +476,16 @@ function parseStorageUploadResponse(payload) {
|
|
|
454
476
|
const location = asNonEmptyString(record.location);
|
|
455
477
|
const uploadUrl = asNonEmptyString(record.upload_url);
|
|
456
478
|
const uploadHeaders = record.upload_headers === void 0 ? void 0 : asStringRecord(record.upload_headers);
|
|
479
|
+
const confirmationRequired = typeof record.confirmation_required === "boolean" ? record.confirmation_required : void 0;
|
|
457
480
|
if (!quoteId || !addr || !objectId || !objectKey || !provider || !bucketName || !location) {
|
|
458
481
|
throw new Error("Upload response is missing required fields");
|
|
459
482
|
}
|
|
460
483
|
if (record.upload_headers !== void 0 && !uploadHeaders) {
|
|
461
484
|
throw new Error("Upload response has invalid upload_headers");
|
|
462
485
|
}
|
|
486
|
+
if (record.confirmation_required !== void 0 && confirmationRequired === void 0) {
|
|
487
|
+
throw new Error("Upload response has invalid confirmation_required");
|
|
488
|
+
}
|
|
463
489
|
return {
|
|
464
490
|
quote_id: quoteId,
|
|
465
491
|
addr,
|
|
@@ -472,7 +498,8 @@ function parseStorageUploadResponse(payload) {
|
|
|
472
498
|
bucket_name: bucketName,
|
|
473
499
|
location,
|
|
474
500
|
upload_url: uploadUrl ?? void 0,
|
|
475
|
-
upload_headers: uploadHeaders ?? void 0
|
|
501
|
+
upload_headers: uploadHeaders ?? void 0,
|
|
502
|
+
confirmation_required: confirmationRequired
|
|
476
503
|
};
|
|
477
504
|
}
|
|
478
505
|
async function requestPriceStorageViaProxy(request, options = {}) {
|
|
@@ -501,29 +528,109 @@ async function requestPriceStorageViaProxy(request, options = {}) {
|
|
|
501
528
|
}
|
|
502
529
|
async function requestStorageUploadViaProxy(request, options = {}) {
|
|
503
530
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
531
|
+
const maxRetries = typeof options.maxRetries === "number" && Number.isFinite(options.maxRetries) && options.maxRetries >= 0 ? Math.floor(options.maxRetries) : 2;
|
|
504
532
|
const baseUrl = normalizeBaseUrl(
|
|
505
533
|
options.proxyBaseUrl ?? `http://127.0.0.1:${PROXY_PORT.toString()}`
|
|
506
534
|
);
|
|
535
|
+
const targetUrl = `${baseUrl}${UPLOAD_PROXY_PATH}`;
|
|
507
536
|
const requestHeaders = {
|
|
508
537
|
"Content-Type": "application/json"
|
|
509
538
|
};
|
|
510
539
|
if (options.idempotencyKey && options.idempotencyKey.trim().length > 0) {
|
|
511
540
|
requestHeaders["Idempotency-Key"] = options.idempotencyKey.trim();
|
|
512
541
|
}
|
|
513
|
-
const
|
|
542
|
+
const requestBody = JSON.stringify(request);
|
|
543
|
+
const sendUploadRequest = async () => {
|
|
544
|
+
const response2 = await fetchImpl(targetUrl, {
|
|
545
|
+
method: "POST",
|
|
546
|
+
headers: requestHeaders,
|
|
547
|
+
body: requestBody
|
|
548
|
+
});
|
|
549
|
+
return { response: response2, bodyText: await response2.text() };
|
|
550
|
+
};
|
|
551
|
+
let { response, bodyText: responseBody } = await sendUploadRequest();
|
|
552
|
+
if (response.status === 207) {
|
|
553
|
+
let parsed207;
|
|
554
|
+
try {
|
|
555
|
+
parsed207 = JSON.parse(responseBody);
|
|
556
|
+
} catch {
|
|
557
|
+
parsed207 = null;
|
|
558
|
+
}
|
|
559
|
+
const retryablePayload = asRecord(parsed207);
|
|
560
|
+
if (retryablePayload?.upload_failed === true) {
|
|
561
|
+
let transId = asNonEmptyString(retryablePayload.trans_id) ?? "unknown";
|
|
562
|
+
let exhaustedRetryableFailures = true;
|
|
563
|
+
for (let attempt = 0; attempt < maxRetries; attempt += 1) {
|
|
564
|
+
await new Promise((resolve3) => setTimeout(resolve3, 1e3));
|
|
565
|
+
try {
|
|
566
|
+
({ response, bodyText: responseBody } = await sendUploadRequest());
|
|
567
|
+
} catch {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (response.ok && response.status !== 207) {
|
|
571
|
+
let retryPayload;
|
|
572
|
+
try {
|
|
573
|
+
retryPayload = JSON.parse(responseBody);
|
|
574
|
+
} catch {
|
|
575
|
+
throw new Error("Upload proxy returned invalid JSON");
|
|
576
|
+
}
|
|
577
|
+
return parseStorageUploadResponse(retryPayload);
|
|
578
|
+
}
|
|
579
|
+
if (response.status === 207) {
|
|
580
|
+
let retryParsed207;
|
|
581
|
+
try {
|
|
582
|
+
retryParsed207 = JSON.parse(responseBody);
|
|
583
|
+
} catch {
|
|
584
|
+
retryParsed207 = null;
|
|
585
|
+
}
|
|
586
|
+
const retryableRetryPayload = asRecord(retryParsed207);
|
|
587
|
+
if (retryableRetryPayload?.upload_failed === true) {
|
|
588
|
+
transId = asNonEmptyString(retryableRetryPayload.trans_id) ?? transId;
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
exhaustedRetryableFailures = false;
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (exhaustedRetryableFailures) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
`Payment confirmed (trans_id: ${transId}) but file storage failed after ${maxRetries} ${maxRetries === 1 ? "retry" : "retries"}. Contact support with your trans_id.`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (!response.ok) {
|
|
603
|
+
throw new Error(responseBody || `Upload proxy failed with status ${response.status}`);
|
|
604
|
+
}
|
|
605
|
+
let payload;
|
|
606
|
+
try {
|
|
607
|
+
payload = JSON.parse(responseBody);
|
|
608
|
+
} catch {
|
|
609
|
+
throw new Error("Upload proxy returned invalid JSON");
|
|
610
|
+
}
|
|
611
|
+
return parseStorageUploadResponse(payload);
|
|
612
|
+
}
|
|
613
|
+
async function requestStorageUploadConfirmViaProxy(request, options = {}) {
|
|
614
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
615
|
+
const baseUrl = normalizeBaseUrl(
|
|
616
|
+
options.proxyBaseUrl ?? `http://127.0.0.1:${PROXY_PORT.toString()}`
|
|
617
|
+
);
|
|
618
|
+
const response = await fetchImpl(`${baseUrl}${UPLOAD_CONFIRM_PROXY_PATH}`, {
|
|
514
619
|
method: "POST",
|
|
515
|
-
headers:
|
|
620
|
+
headers: {
|
|
621
|
+
"Content-Type": "application/json"
|
|
622
|
+
},
|
|
516
623
|
body: JSON.stringify(request)
|
|
517
624
|
});
|
|
518
625
|
const responseBody = await response.text();
|
|
519
626
|
if (!response.ok) {
|
|
520
|
-
throw new Error(responseBody || `Upload proxy failed with status ${response.status}`);
|
|
627
|
+
throw new Error(responseBody || `Upload confirm proxy failed with status ${response.status}`);
|
|
521
628
|
}
|
|
522
629
|
let payload;
|
|
523
630
|
try {
|
|
524
631
|
payload = JSON.parse(responseBody);
|
|
525
632
|
} catch {
|
|
526
|
-
throw new Error("Upload proxy returned invalid JSON");
|
|
633
|
+
throw new Error("Upload confirm proxy returned invalid JSON");
|
|
527
634
|
}
|
|
528
635
|
return parseStorageUploadResponse(payload);
|
|
529
636
|
}
|
|
@@ -623,6 +730,35 @@ async function forwardStorageUploadToBackend(request, options = {}) {
|
|
|
623
730
|
paymentResponse: normalizePaymentResponse(response.headers)
|
|
624
731
|
};
|
|
625
732
|
}
|
|
733
|
+
async function forwardStorageUploadConfirmToBackend(request, options = {}) {
|
|
734
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
735
|
+
const backendBaseUrl = (options.backendBaseUrl ?? "").trim();
|
|
736
|
+
const walletSignature = normalizeWalletSignature(options.walletSignature);
|
|
737
|
+
if (!backendBaseUrl) {
|
|
738
|
+
throw new Error("MNEMOSPARK_BACKEND_API_BASE_URL is not configured");
|
|
739
|
+
}
|
|
740
|
+
if (!walletSignature) {
|
|
741
|
+
throw new Error(
|
|
742
|
+
"Wallet required for storage endpoints: wallet key must be present to sign requests."
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
const targetUrl = `${normalizeBaseUrl(backendBaseUrl)}/storage/upload/confirm`;
|
|
746
|
+
const response = await fetchImpl(targetUrl, {
|
|
747
|
+
method: "POST",
|
|
748
|
+
headers: {
|
|
749
|
+
"Content-Type": "application/json",
|
|
750
|
+
"X-Wallet-Signature": walletSignature
|
|
751
|
+
},
|
|
752
|
+
body: JSON.stringify(request)
|
|
753
|
+
});
|
|
754
|
+
return {
|
|
755
|
+
status: response.status,
|
|
756
|
+
bodyText: await response.text(),
|
|
757
|
+
contentType: response.headers.get("content-type") ?? "application/json",
|
|
758
|
+
paymentRequired: normalizePaymentRequired(response.headers),
|
|
759
|
+
paymentResponse: normalizePaymentResponse(response.headers)
|
|
760
|
+
};
|
|
761
|
+
}
|
|
626
762
|
|
|
627
763
|
// src/cloud-storage.ts
|
|
628
764
|
import { mkdir, writeFile } from "fs/promises";
|
|
@@ -893,6 +1029,14 @@ async function downloadStorageToDisk(request, backendResponse, options = {}) {
|
|
|
893
1029
|
var HEALTH_CHECK_TIMEOUT_MS = 2e3;
|
|
894
1030
|
var PORT_RETRY_ATTEMPTS = 5;
|
|
895
1031
|
var PORT_RETRY_DELAY_MS = 1e3;
|
|
1032
|
+
var DEFAULT_DOWNLOAD_OUTPUT_DIR = join2(homedir(), ".openclaw", "mnemospark", "downloads");
|
|
1033
|
+
function resolveDownloadOutputDir() {
|
|
1034
|
+
const configuredOutputDir = process.env.MNEMOSPARK_DOWNLOAD_DIR?.trim();
|
|
1035
|
+
if (configuredOutputDir && configuredOutputDir.length > 0) {
|
|
1036
|
+
return configuredOutputDir;
|
|
1037
|
+
}
|
|
1038
|
+
return DEFAULT_DOWNLOAD_OUTPUT_DIR;
|
|
1039
|
+
}
|
|
896
1040
|
function matchesProxyPath(url, path) {
|
|
897
1041
|
return url === path || url?.startsWith(`${path}?`) === true;
|
|
898
1042
|
}
|
|
@@ -926,6 +1070,19 @@ function sendJson(res, status, body) {
|
|
|
926
1070
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
927
1071
|
res.end(JSON.stringify(body));
|
|
928
1072
|
}
|
|
1073
|
+
function logProxyEvent(level, event, fields = {}) {
|
|
1074
|
+
const payload = JSON.stringify({ event, ...fields });
|
|
1075
|
+
const message = `[mnemospark] ${payload}`;
|
|
1076
|
+
if (level === "error") {
|
|
1077
|
+
console.error(message);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
if (level === "warn") {
|
|
1081
|
+
console.warn(message);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
console.info(message);
|
|
1085
|
+
}
|
|
929
1086
|
function createBackendForwardHeaders(response) {
|
|
930
1087
|
const responseHeaders = {
|
|
931
1088
|
"Content-Type": response.contentType
|
|
@@ -1037,19 +1194,22 @@ async function startProxy(options) {
|
|
|
1037
1194
|
console.error(`[mnemospark] Response stream error: ${err.message}`);
|
|
1038
1195
|
});
|
|
1039
1196
|
if (req.method === "POST" && matchesProxyPath(req.url, PRICE_STORAGE_PROXY_PATH)) {
|
|
1197
|
+
logProxyEvent("info", "proxy_price_storage_received");
|
|
1040
1198
|
try {
|
|
1041
1199
|
let payload;
|
|
1042
1200
|
try {
|
|
1043
1201
|
payload = await readProxyJsonBody(req);
|
|
1044
1202
|
} catch {
|
|
1203
|
+
logProxyEvent("warn", "proxy_price_storage_invalid_json");
|
|
1045
1204
|
sendJson(res, 400, {
|
|
1046
1205
|
error: "Bad request",
|
|
1047
|
-
message: "Invalid JSON body for /mnemospark
|
|
1206
|
+
message: "Invalid JSON body for /mnemospark-cloud price-storage"
|
|
1048
1207
|
});
|
|
1049
1208
|
return;
|
|
1050
1209
|
}
|
|
1051
1210
|
const requestPayload = parsePriceStorageQuoteRequest(payload);
|
|
1052
1211
|
if (!requestPayload) {
|
|
1212
|
+
logProxyEvent("warn", "proxy_price_storage_missing_fields");
|
|
1053
1213
|
sendJson(res, 400, {
|
|
1054
1214
|
error: "Bad request",
|
|
1055
1215
|
message: "Missing required fields: wallet_address, object_id, object_id_hash, gb, provider, region"
|
|
@@ -1065,11 +1225,17 @@ async function startProxy(options) {
|
|
|
1065
1225
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
1066
1226
|
walletSignature
|
|
1067
1227
|
});
|
|
1228
|
+
logProxyEvent("info", "proxy_price_storage_backend_response", {
|
|
1229
|
+
status: backendResponse.status
|
|
1230
|
+
});
|
|
1068
1231
|
const authFailure = normalizeBackendAuthFailure(
|
|
1069
1232
|
backendResponse.status,
|
|
1070
1233
|
backendResponse.bodyText
|
|
1071
1234
|
);
|
|
1072
1235
|
if (authFailure) {
|
|
1236
|
+
logProxyEvent("warn", "proxy_price_storage_auth_failure", {
|
|
1237
|
+
status: authFailure.status
|
|
1238
|
+
});
|
|
1073
1239
|
const responseHeaders2 = createBackendForwardHeaders({
|
|
1074
1240
|
contentType: authFailure.contentType,
|
|
1075
1241
|
paymentRequired: backendResponse.paymentRequired,
|
|
@@ -1083,27 +1249,33 @@ async function startProxy(options) {
|
|
|
1083
1249
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1084
1250
|
res.end(backendResponse.bodyText);
|
|
1085
1251
|
} catch (err) {
|
|
1252
|
+
logProxyEvent("error", "proxy_price_storage_forward_failed", {
|
|
1253
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1254
|
+
});
|
|
1086
1255
|
sendJson(res, 502, {
|
|
1087
1256
|
error: "proxy_error",
|
|
1088
|
-
message: `Failed to forward /mnemospark
|
|
1257
|
+
message: `Failed to forward /mnemospark-cloud price-storage: ${err instanceof Error ? err.message : String(err)}`
|
|
1089
1258
|
});
|
|
1090
1259
|
}
|
|
1091
1260
|
return;
|
|
1092
1261
|
}
|
|
1093
1262
|
if (req.method === "POST" && matchesProxyPath(req.url, UPLOAD_PROXY_PATH)) {
|
|
1263
|
+
logProxyEvent("info", "proxy_upload_received");
|
|
1094
1264
|
try {
|
|
1095
1265
|
let payload;
|
|
1096
1266
|
try {
|
|
1097
1267
|
payload = await readProxyJsonBody(req);
|
|
1098
1268
|
} catch {
|
|
1269
|
+
logProxyEvent("warn", "proxy_upload_invalid_json");
|
|
1099
1270
|
sendJson(res, 400, {
|
|
1100
1271
|
error: "Bad request",
|
|
1101
|
-
message: "Invalid JSON body for /mnemospark
|
|
1272
|
+
message: "Invalid JSON body for /mnemospark-cloud upload"
|
|
1102
1273
|
});
|
|
1103
1274
|
return;
|
|
1104
1275
|
}
|
|
1105
1276
|
const requestPayload = parseStorageUploadRequest(payload);
|
|
1106
1277
|
if (!requestPayload) {
|
|
1278
|
+
logProxyEvent("warn", "proxy_upload_missing_fields");
|
|
1107
1279
|
sendJson(res, 400, {
|
|
1108
1280
|
error: "Bad request",
|
|
1109
1281
|
message: "Missing required fields: quote_id, wallet_address, object_id, object_id_hash, quoted_storage_price, payload"
|
|
@@ -1111,6 +1283,10 @@ async function startProxy(options) {
|
|
|
1111
1283
|
return;
|
|
1112
1284
|
}
|
|
1113
1285
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
1286
|
+
logProxyEvent("warn", "proxy_upload_wallet_mismatch", {
|
|
1287
|
+
request_wallet: requestPayload.wallet_address,
|
|
1288
|
+
proxy_wallet: account.address
|
|
1289
|
+
});
|
|
1114
1290
|
sendJson(res, 403, {
|
|
1115
1291
|
error: "wallet_proof_invalid",
|
|
1116
1292
|
message: "wallet proof invalid"
|
|
@@ -1123,6 +1299,7 @@ async function startProxy(options) {
|
|
|
1123
1299
|
requestPayload.wallet_address
|
|
1124
1300
|
);
|
|
1125
1301
|
if (!walletSignature) {
|
|
1302
|
+
logProxyEvent("warn", "proxy_upload_wallet_signature_missing");
|
|
1126
1303
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1127
1304
|
res.end(createWalletRequiredBody());
|
|
1128
1305
|
return;
|
|
@@ -1134,6 +1311,11 @@ async function startProxy(options) {
|
|
|
1134
1311
|
const sufficiency = await uploadBalanceMonitor.checkSufficient(requiredMicros);
|
|
1135
1312
|
const requiredUSD = uploadBalanceMonitor.formatUSDC(requiredMicros);
|
|
1136
1313
|
if (!sufficiency.sufficient) {
|
|
1314
|
+
logProxyEvent("warn", "proxy_upload_insufficient_balance", {
|
|
1315
|
+
wallet_address: requestPayload.wallet_address,
|
|
1316
|
+
balance_usd: sufficiency.info.balanceUSD,
|
|
1317
|
+
required_usd: requiredUSD
|
|
1318
|
+
});
|
|
1137
1319
|
options.onInsufficientFunds?.({
|
|
1138
1320
|
balanceUSD: sufficiency.info.balanceUSD,
|
|
1139
1321
|
requiredUSD,
|
|
@@ -1143,11 +1325,15 @@ async function startProxy(options) {
|
|
|
1143
1325
|
error: "insufficient_balance",
|
|
1144
1326
|
message: `Insufficient USDC balance. Current: ${sufficiency.info.balanceUSD}, Required: ${requiredUSD}`,
|
|
1145
1327
|
wallet: requestPayload.wallet_address,
|
|
1146
|
-
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /mnemospark
|
|
1328
|
+
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /mnemospark-cloud upload`
|
|
1147
1329
|
});
|
|
1148
1330
|
return;
|
|
1149
1331
|
}
|
|
1150
1332
|
if (sufficiency.info.isLow) {
|
|
1333
|
+
logProxyEvent("warn", "proxy_upload_low_balance", {
|
|
1334
|
+
wallet_address: requestPayload.wallet_address,
|
|
1335
|
+
balance_usd: sufficiency.info.balanceUSD
|
|
1336
|
+
});
|
|
1151
1337
|
options.onLowBalance?.({
|
|
1152
1338
|
balanceUSD: sufficiency.info.balanceUSD,
|
|
1153
1339
|
walletAddress: requestPayload.wallet_address
|
|
@@ -1160,11 +1346,100 @@ async function startProxy(options) {
|
|
|
1160
1346
|
legacyPayment: readHeaderValue(req.headers["x-payment"]),
|
|
1161
1347
|
idempotencyKey: readHeaderValue(req.headers["idempotency-key"])
|
|
1162
1348
|
});
|
|
1349
|
+
logProxyEvent("info", "proxy_upload_backend_response", {
|
|
1350
|
+
status: backendResponse.status
|
|
1351
|
+
});
|
|
1352
|
+
const authFailure = normalizeBackendAuthFailure(
|
|
1353
|
+
backendResponse.status,
|
|
1354
|
+
backendResponse.bodyText
|
|
1355
|
+
);
|
|
1356
|
+
if (authFailure) {
|
|
1357
|
+
logProxyEvent("warn", "proxy_upload_auth_failure", {
|
|
1358
|
+
status: authFailure.status
|
|
1359
|
+
});
|
|
1360
|
+
const responseHeaders2 = createBackendForwardHeaders({
|
|
1361
|
+
contentType: authFailure.contentType,
|
|
1362
|
+
paymentRequired: backendResponse.paymentRequired,
|
|
1363
|
+
paymentResponse: backendResponse.paymentResponse
|
|
1364
|
+
});
|
|
1365
|
+
res.writeHead(authFailure.status, responseHeaders2);
|
|
1366
|
+
res.end(authFailure.bodyText);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
1370
|
+
res.writeHead(backendResponse.status, responseHeaders);
|
|
1371
|
+
res.end(backendResponse.bodyText);
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
logProxyEvent("error", "proxy_upload_forward_failed", {
|
|
1374
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1375
|
+
});
|
|
1376
|
+
sendJson(res, 502, {
|
|
1377
|
+
error: "proxy_error",
|
|
1378
|
+
message: `Failed to forward /mnemospark-cloud upload: ${err instanceof Error ? err.message : String(err)}`
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
if (req.method === "POST" && matchesProxyPath(req.url, UPLOAD_CONFIRM_PROXY_PATH)) {
|
|
1384
|
+
logProxyEvent("info", "proxy_upload_confirm_received");
|
|
1385
|
+
try {
|
|
1386
|
+
let payload;
|
|
1387
|
+
try {
|
|
1388
|
+
payload = await readProxyJsonBody(req);
|
|
1389
|
+
} catch {
|
|
1390
|
+
logProxyEvent("warn", "proxy_upload_confirm_invalid_json");
|
|
1391
|
+
sendJson(res, 400, {
|
|
1392
|
+
error: "Bad request",
|
|
1393
|
+
message: "Invalid JSON body for /mnemospark-cloud upload/confirm"
|
|
1394
|
+
});
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const requestPayload = parseStorageUploadConfirmRequest(payload);
|
|
1398
|
+
if (!requestPayload) {
|
|
1399
|
+
logProxyEvent("warn", "proxy_upload_confirm_missing_fields");
|
|
1400
|
+
sendJson(res, 400, {
|
|
1401
|
+
error: "Bad request",
|
|
1402
|
+
message: "Missing required fields: quote_id, wallet_address, object_key, idempotency_key"
|
|
1403
|
+
});
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
1407
|
+
logProxyEvent("warn", "proxy_upload_confirm_wallet_mismatch", {
|
|
1408
|
+
request_wallet: requestPayload.wallet_address,
|
|
1409
|
+
proxy_wallet: account.address
|
|
1410
|
+
});
|
|
1411
|
+
sendJson(res, 403, {
|
|
1412
|
+
error: "wallet_proof_invalid",
|
|
1413
|
+
message: "wallet proof invalid"
|
|
1414
|
+
});
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
const walletSignature = await createBackendWalletSignature(
|
|
1418
|
+
"POST",
|
|
1419
|
+
"/storage/upload/confirm",
|
|
1420
|
+
requestPayload.wallet_address
|
|
1421
|
+
);
|
|
1422
|
+
if (!walletSignature) {
|
|
1423
|
+
logProxyEvent("warn", "proxy_upload_confirm_wallet_signature_missing");
|
|
1424
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1425
|
+
res.end(createWalletRequiredBody());
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
const backendResponse = await forwardStorageUploadConfirmToBackend(requestPayload, {
|
|
1429
|
+
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
1430
|
+
walletSignature
|
|
1431
|
+
});
|
|
1432
|
+
logProxyEvent("info", "proxy_upload_confirm_backend_response", {
|
|
1433
|
+
status: backendResponse.status
|
|
1434
|
+
});
|
|
1163
1435
|
const authFailure = normalizeBackendAuthFailure(
|
|
1164
1436
|
backendResponse.status,
|
|
1165
1437
|
backendResponse.bodyText
|
|
1166
1438
|
);
|
|
1167
1439
|
if (authFailure) {
|
|
1440
|
+
logProxyEvent("warn", "proxy_upload_confirm_auth_failure", {
|
|
1441
|
+
status: authFailure.status
|
|
1442
|
+
});
|
|
1168
1443
|
const responseHeaders2 = createBackendForwardHeaders({
|
|
1169
1444
|
contentType: authFailure.contentType,
|
|
1170
1445
|
paymentRequired: backendResponse.paymentRequired,
|
|
@@ -1178,27 +1453,33 @@ async function startProxy(options) {
|
|
|
1178
1453
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1179
1454
|
res.end(backendResponse.bodyText);
|
|
1180
1455
|
} catch (err) {
|
|
1456
|
+
logProxyEvent("error", "proxy_upload_confirm_forward_failed", {
|
|
1457
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1458
|
+
});
|
|
1181
1459
|
sendJson(res, 502, {
|
|
1182
1460
|
error: "proxy_error",
|
|
1183
|
-
message: `Failed to forward /mnemospark
|
|
1461
|
+
message: `Failed to forward /mnemospark-cloud upload/confirm: ${err instanceof Error ? err.message : String(err)}`
|
|
1184
1462
|
});
|
|
1185
1463
|
}
|
|
1186
1464
|
return;
|
|
1187
1465
|
}
|
|
1188
1466
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_LS_PROXY_PATH)) {
|
|
1467
|
+
logProxyEvent("info", "proxy_ls_received");
|
|
1189
1468
|
try {
|
|
1190
1469
|
let payload;
|
|
1191
1470
|
try {
|
|
1192
1471
|
payload = await readProxyJsonBody(req);
|
|
1193
1472
|
} catch {
|
|
1473
|
+
logProxyEvent("warn", "proxy_ls_invalid_json");
|
|
1194
1474
|
sendJson(res, 400, {
|
|
1195
1475
|
error: "Bad request",
|
|
1196
|
-
message: "Invalid JSON body for /mnemospark
|
|
1476
|
+
message: "Invalid JSON body for /mnemospark-cloud ls"
|
|
1197
1477
|
});
|
|
1198
1478
|
return;
|
|
1199
1479
|
}
|
|
1200
1480
|
const requestPayload = parseStorageObjectRequest(payload);
|
|
1201
1481
|
if (!requestPayload) {
|
|
1482
|
+
logProxyEvent("warn", "proxy_ls_missing_fields");
|
|
1202
1483
|
sendJson(res, 400, {
|
|
1203
1484
|
error: "Bad request",
|
|
1204
1485
|
message: "Missing required fields: wallet_address, object_key"
|
|
@@ -1206,6 +1487,7 @@ async function startProxy(options) {
|
|
|
1206
1487
|
return;
|
|
1207
1488
|
}
|
|
1208
1489
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
1490
|
+
logProxyEvent("warn", "proxy_ls_wallet_mismatch");
|
|
1209
1491
|
sendJson(res, 403, {
|
|
1210
1492
|
error: "wallet_proof_invalid",
|
|
1211
1493
|
message: "wallet proof invalid"
|
|
@@ -1218,6 +1500,7 @@ async function startProxy(options) {
|
|
|
1218
1500
|
requestPayload.wallet_address
|
|
1219
1501
|
);
|
|
1220
1502
|
if (!walletSignature) {
|
|
1503
|
+
logProxyEvent("warn", "proxy_ls_wallet_signature_missing");
|
|
1221
1504
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1222
1505
|
res.end(createWalletRequiredBody());
|
|
1223
1506
|
return;
|
|
@@ -1226,11 +1509,13 @@ async function startProxy(options) {
|
|
|
1226
1509
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
1227
1510
|
walletSignature
|
|
1228
1511
|
});
|
|
1512
|
+
logProxyEvent("info", "proxy_ls_backend_response", { status: backendResponse.status });
|
|
1229
1513
|
const authFailure = normalizeBackendAuthFailure(
|
|
1230
1514
|
backendResponse.status,
|
|
1231
1515
|
backendResponse.bodyText
|
|
1232
1516
|
);
|
|
1233
1517
|
if (authFailure) {
|
|
1518
|
+
logProxyEvent("warn", "proxy_ls_auth_failure", { status: authFailure.status });
|
|
1234
1519
|
const responseHeaders2 = createBackendForwardHeaders({
|
|
1235
1520
|
contentType: authFailure.contentType,
|
|
1236
1521
|
paymentRequired: backendResponse.paymentRequired,
|
|
@@ -1244,27 +1529,33 @@ async function startProxy(options) {
|
|
|
1244
1529
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1245
1530
|
res.end(backendResponse.bodyText);
|
|
1246
1531
|
} catch (err) {
|
|
1532
|
+
logProxyEvent("error", "proxy_ls_forward_failed", {
|
|
1533
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1534
|
+
});
|
|
1247
1535
|
sendJson(res, 502, {
|
|
1248
1536
|
error: "proxy_error",
|
|
1249
|
-
message: `Failed to forward /mnemospark
|
|
1537
|
+
message: `Failed to forward /mnemospark-cloud ls: ${err instanceof Error ? err.message : String(err)}`
|
|
1250
1538
|
});
|
|
1251
1539
|
}
|
|
1252
1540
|
return;
|
|
1253
1541
|
}
|
|
1254
1542
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_DOWNLOAD_PROXY_PATH)) {
|
|
1543
|
+
logProxyEvent("info", "proxy_download_received");
|
|
1255
1544
|
try {
|
|
1256
1545
|
let payload;
|
|
1257
1546
|
try {
|
|
1258
1547
|
payload = await readProxyJsonBody(req);
|
|
1259
1548
|
} catch {
|
|
1549
|
+
logProxyEvent("warn", "proxy_download_invalid_json");
|
|
1260
1550
|
sendJson(res, 400, {
|
|
1261
1551
|
error: "Bad request",
|
|
1262
|
-
message: "Invalid JSON body for /mnemospark
|
|
1552
|
+
message: "Invalid JSON body for /mnemospark-cloud download"
|
|
1263
1553
|
});
|
|
1264
1554
|
return;
|
|
1265
1555
|
}
|
|
1266
1556
|
const requestPayload = parseStorageObjectRequest(payload);
|
|
1267
1557
|
if (!requestPayload) {
|
|
1558
|
+
logProxyEvent("warn", "proxy_download_missing_fields");
|
|
1268
1559
|
sendJson(res, 400, {
|
|
1269
1560
|
error: "Bad request",
|
|
1270
1561
|
message: "Missing required fields: wallet_address, object_key"
|
|
@@ -1272,6 +1563,7 @@ async function startProxy(options) {
|
|
|
1272
1563
|
return;
|
|
1273
1564
|
}
|
|
1274
1565
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
1566
|
+
logProxyEvent("warn", "proxy_download_wallet_mismatch");
|
|
1275
1567
|
sendJson(res, 403, {
|
|
1276
1568
|
error: "wallet_proof_invalid",
|
|
1277
1569
|
message: "wallet proof invalid"
|
|
@@ -1284,6 +1576,7 @@ async function startProxy(options) {
|
|
|
1284
1576
|
requestPayload.wallet_address
|
|
1285
1577
|
);
|
|
1286
1578
|
if (!walletSignature) {
|
|
1579
|
+
logProxyEvent("warn", "proxy_download_wallet_signature_missing");
|
|
1287
1580
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1288
1581
|
res.end(createWalletRequiredBody());
|
|
1289
1582
|
return;
|
|
@@ -1292,11 +1585,17 @@ async function startProxy(options) {
|
|
|
1292
1585
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
1293
1586
|
walletSignature
|
|
1294
1587
|
});
|
|
1588
|
+
logProxyEvent("info", "proxy_download_backend_response", {
|
|
1589
|
+
status: backendResponse.status
|
|
1590
|
+
});
|
|
1295
1591
|
const authFailure = normalizeBackendAuthFailure(
|
|
1296
1592
|
backendResponse.status,
|
|
1297
1593
|
backendResponse.bodyText
|
|
1298
1594
|
);
|
|
1299
1595
|
if (authFailure) {
|
|
1596
|
+
logProxyEvent("warn", "proxy_download_auth_failure", {
|
|
1597
|
+
status: authFailure.status
|
|
1598
|
+
});
|
|
1300
1599
|
const responseHeaders = createBackendForwardHeaders({
|
|
1301
1600
|
contentType: authFailure.contentType,
|
|
1302
1601
|
paymentRequired: backendResponse.paymentRequired,
|
|
@@ -1307,12 +1606,22 @@ async function startProxy(options) {
|
|
|
1307
1606
|
return;
|
|
1308
1607
|
}
|
|
1309
1608
|
if (backendResponse.status < 200 || backendResponse.status >= 300) {
|
|
1609
|
+
logProxyEvent("warn", "proxy_download_backend_non_success", {
|
|
1610
|
+
status: backendResponse.status
|
|
1611
|
+
});
|
|
1310
1612
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
1311
1613
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1312
1614
|
res.end(backendResponse.bodyText);
|
|
1313
1615
|
return;
|
|
1314
1616
|
}
|
|
1315
|
-
const downloadResult = await downloadStorageToDisk(requestPayload, backendResponse
|
|
1617
|
+
const downloadResult = await downloadStorageToDisk(requestPayload, backendResponse, {
|
|
1618
|
+
outputDir: resolveDownloadOutputDir()
|
|
1619
|
+
});
|
|
1620
|
+
logProxyEvent("info", "proxy_download_written_to_disk", {
|
|
1621
|
+
key: downloadResult.key,
|
|
1622
|
+
file_path: downloadResult.filePath,
|
|
1623
|
+
bytes_written: downloadResult.bytesWritten
|
|
1624
|
+
});
|
|
1316
1625
|
sendJson(res, 200, {
|
|
1317
1626
|
success: true,
|
|
1318
1627
|
key: downloadResult.key,
|
|
@@ -1320,27 +1629,33 @@ async function startProxy(options) {
|
|
|
1320
1629
|
bytes_written: downloadResult.bytesWritten
|
|
1321
1630
|
});
|
|
1322
1631
|
} catch (err) {
|
|
1632
|
+
logProxyEvent("error", "proxy_download_forward_failed", {
|
|
1633
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1634
|
+
});
|
|
1323
1635
|
sendJson(res, 502, {
|
|
1324
1636
|
error: "proxy_error",
|
|
1325
|
-
message: `Failed to forward /mnemospark
|
|
1637
|
+
message: `Failed to forward /mnemospark-cloud download: ${err instanceof Error ? err.message : String(err)}`
|
|
1326
1638
|
});
|
|
1327
1639
|
}
|
|
1328
1640
|
return;
|
|
1329
1641
|
}
|
|
1330
1642
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_DELETE_PROXY_PATH)) {
|
|
1643
|
+
logProxyEvent("info", "proxy_delete_received");
|
|
1331
1644
|
try {
|
|
1332
1645
|
let payload;
|
|
1333
1646
|
try {
|
|
1334
1647
|
payload = await readProxyJsonBody(req);
|
|
1335
1648
|
} catch {
|
|
1649
|
+
logProxyEvent("warn", "proxy_delete_invalid_json");
|
|
1336
1650
|
sendJson(res, 400, {
|
|
1337
1651
|
error: "Bad request",
|
|
1338
|
-
message: "Invalid JSON body for /mnemospark
|
|
1652
|
+
message: "Invalid JSON body for /mnemospark-cloud delete"
|
|
1339
1653
|
});
|
|
1340
1654
|
return;
|
|
1341
1655
|
}
|
|
1342
1656
|
const requestPayload = parseStorageObjectRequest(payload);
|
|
1343
1657
|
if (!requestPayload) {
|
|
1658
|
+
logProxyEvent("warn", "proxy_delete_missing_fields");
|
|
1344
1659
|
sendJson(res, 400, {
|
|
1345
1660
|
error: "Bad request",
|
|
1346
1661
|
message: "Missing required fields: wallet_address, object_key"
|
|
@@ -1348,6 +1663,7 @@ async function startProxy(options) {
|
|
|
1348
1663
|
return;
|
|
1349
1664
|
}
|
|
1350
1665
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
1666
|
+
logProxyEvent("warn", "proxy_delete_wallet_mismatch");
|
|
1351
1667
|
sendJson(res, 403, {
|
|
1352
1668
|
error: "wallet_proof_invalid",
|
|
1353
1669
|
message: "wallet proof invalid"
|
|
@@ -1360,6 +1676,7 @@ async function startProxy(options) {
|
|
|
1360
1676
|
requestPayload.wallet_address
|
|
1361
1677
|
);
|
|
1362
1678
|
if (!walletSignature) {
|
|
1679
|
+
logProxyEvent("warn", "proxy_delete_wallet_signature_missing");
|
|
1363
1680
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1364
1681
|
res.end(createWalletRequiredBody());
|
|
1365
1682
|
return;
|
|
@@ -1368,11 +1685,17 @@ async function startProxy(options) {
|
|
|
1368
1685
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
1369
1686
|
walletSignature
|
|
1370
1687
|
});
|
|
1688
|
+
logProxyEvent("info", "proxy_delete_backend_response", {
|
|
1689
|
+
status: backendResponse.status
|
|
1690
|
+
});
|
|
1371
1691
|
const authFailure = normalizeBackendAuthFailure(
|
|
1372
1692
|
backendResponse.status,
|
|
1373
1693
|
backendResponse.bodyText
|
|
1374
1694
|
);
|
|
1375
1695
|
if (authFailure) {
|
|
1696
|
+
logProxyEvent("warn", "proxy_delete_auth_failure", {
|
|
1697
|
+
status: authFailure.status
|
|
1698
|
+
});
|
|
1376
1699
|
const responseHeaders2 = createBackendForwardHeaders({
|
|
1377
1700
|
contentType: authFailure.contentType,
|
|
1378
1701
|
paymentRequired: backendResponse.paymentRequired,
|
|
@@ -1386,9 +1709,12 @@ async function startProxy(options) {
|
|
|
1386
1709
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1387
1710
|
res.end(backendResponse.bodyText);
|
|
1388
1711
|
} catch (err) {
|
|
1712
|
+
logProxyEvent("error", "proxy_delete_forward_failed", {
|
|
1713
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1714
|
+
});
|
|
1389
1715
|
sendJson(res, 502, {
|
|
1390
1716
|
error: "proxy_error",
|
|
1391
|
-
message: `Failed to forward /mnemospark
|
|
1717
|
+
message: `Failed to forward /mnemospark-cloud delete: ${err instanceof Error ? err.message : String(err)}`
|
|
1392
1718
|
});
|
|
1393
1719
|
}
|
|
1394
1720
|
return;
|
|
@@ -1537,8 +1863,8 @@ async function startProxy(options) {
|
|
|
1537
1863
|
|
|
1538
1864
|
// src/auth.ts
|
|
1539
1865
|
import { writeFile as writeFile2, readFile, mkdir as mkdir2 } from "fs/promises";
|
|
1540
|
-
import { join as
|
|
1541
|
-
import { homedir } from "os";
|
|
1866
|
+
import { join as join3 } from "path";
|
|
1867
|
+
import { homedir as homedir2 } from "os";
|
|
1542
1868
|
import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
1543
1869
|
|
|
1544
1870
|
// src/wallet-key.ts
|
|
@@ -1547,10 +1873,10 @@ function isValidWalletPrivateKey(value) {
|
|
|
1547
1873
|
}
|
|
1548
1874
|
|
|
1549
1875
|
// src/auth.ts
|
|
1550
|
-
var LEGACY_WALLET_DIR =
|
|
1551
|
-
var LEGACY_WALLET_FILE =
|
|
1552
|
-
var WALLET_DIR =
|
|
1553
|
-
var WALLET_FILE =
|
|
1876
|
+
var LEGACY_WALLET_DIR = join3(homedir2(), ".openclaw", "blockrun");
|
|
1877
|
+
var LEGACY_WALLET_FILE = join3(LEGACY_WALLET_DIR, "wallet.key");
|
|
1878
|
+
var WALLET_DIR = join3(homedir2(), ".openclaw", "mnemospark", "wallet");
|
|
1879
|
+
var WALLET_FILE = join3(WALLET_DIR, "wallet.key");
|
|
1554
1880
|
async function loadSavedWallet() {
|
|
1555
1881
|
for (const path of [WALLET_FILE, LEGACY_WALLET_FILE]) {
|
|
1556
1882
|
try {
|
|
@@ -1606,11 +1932,11 @@ async function resolveOrGenerateWalletKey() {
|
|
|
1606
1932
|
// src/version.ts
|
|
1607
1933
|
import { createRequire } from "module";
|
|
1608
1934
|
import { fileURLToPath } from "url";
|
|
1609
|
-
import { dirname as dirname2, join as
|
|
1935
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
1610
1936
|
var __filename = fileURLToPath(import.meta.url);
|
|
1611
1937
|
var __dirname = dirname2(__filename);
|
|
1612
1938
|
var require2 = createRequire(import.meta.url);
|
|
1613
|
-
var pkg = require2(
|
|
1939
|
+
var pkg = require2(join4(__dirname, "..", "package.json"));
|
|
1614
1940
|
var VERSION = pkg.version;
|
|
1615
1941
|
var USER_AGENT = `mnemospark/${VERSION}`;
|
|
1616
1942
|
|
|
@@ -1624,8 +1950,8 @@ import {
|
|
|
1624
1950
|
} from "crypto";
|
|
1625
1951
|
import { createReadStream, statfsSync } from "fs";
|
|
1626
1952
|
import { appendFile, lstat, mkdir as mkdir3, readFile as readFile2, readdir, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
1627
|
-
import { homedir as
|
|
1628
|
-
import { basename, dirname as dirname3, join as
|
|
1953
|
+
import { homedir as homedir3 } from "os";
|
|
1954
|
+
import { basename, dirname as dirname3, join as join5, resolve as resolve2 } from "path";
|
|
1629
1955
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
1630
1956
|
|
|
1631
1957
|
// src/x402.ts
|
|
@@ -1888,13 +2214,13 @@ function createPaymentFetch(privateKey) {
|
|
|
1888
2214
|
|
|
1889
2215
|
// src/cloud-command.ts
|
|
1890
2216
|
var SUPPORTED_BACKUP_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux"]);
|
|
1891
|
-
var BACKUP_DIR_SUBPATH =
|
|
1892
|
-
var DEFAULT_BACKUP_DIR =
|
|
1893
|
-
var OBJECT_LOG_SUBPATH =
|
|
1894
|
-
var CRON_TABLE_SUBPATH =
|
|
1895
|
-
var BLOCKRUN_WALLET_KEY_SUBPATH =
|
|
1896
|
-
var MNEMOSPARK_WALLET_KEY_SUBPATH =
|
|
1897
|
-
var KEY_STORE_SUBPATH =
|
|
2217
|
+
var BACKUP_DIR_SUBPATH = join5(".openclaw", "mnemospark", "backup");
|
|
2218
|
+
var DEFAULT_BACKUP_DIR = join5(homedir3(), BACKUP_DIR_SUBPATH);
|
|
2219
|
+
var OBJECT_LOG_SUBPATH = join5(".openclaw", "mnemospark", "object.log");
|
|
2220
|
+
var CRON_TABLE_SUBPATH = join5(".openclaw", "mnemospark", "crontab.txt");
|
|
2221
|
+
var BLOCKRUN_WALLET_KEY_SUBPATH = join5(".openclaw", "blockrun", "wallet.key");
|
|
2222
|
+
var MNEMOSPARK_WALLET_KEY_SUBPATH = join5(".openclaw", "mnemospark", "wallet", "wallet.key");
|
|
2223
|
+
var KEY_STORE_SUBPATH = join5(".openclaw", "mnemospark", "keys");
|
|
1898
2224
|
var INLINE_UPLOAD_MAX_BYTES = 45e5;
|
|
1899
2225
|
var AES_GCM_NONCE_BYTES = 12;
|
|
1900
2226
|
var PAYMENT_REMINDER_INTERVAL_DAYS = 30;
|
|
@@ -1908,10 +2234,10 @@ var REQUIRED_STORAGE_OBJECT = "--wallet-address, --object-key";
|
|
|
1908
2234
|
function expandTilde(path) {
|
|
1909
2235
|
const trimmed = path.trim();
|
|
1910
2236
|
if (trimmed === "~") {
|
|
1911
|
-
return
|
|
2237
|
+
return homedir3();
|
|
1912
2238
|
}
|
|
1913
2239
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
1914
|
-
return
|
|
2240
|
+
return join5(homedir3(), trimmed.slice(2));
|
|
1915
2241
|
}
|
|
1916
2242
|
return path;
|
|
1917
2243
|
}
|
|
@@ -2095,10 +2421,10 @@ function parseCloudArgs(args) {
|
|
|
2095
2421
|
return { mode: "unknown" };
|
|
2096
2422
|
}
|
|
2097
2423
|
function resolveObjectLogPath(homeDir) {
|
|
2098
|
-
return
|
|
2424
|
+
return join5(homeDir ?? homedir3(), OBJECT_LOG_SUBPATH);
|
|
2099
2425
|
}
|
|
2100
2426
|
function resolveCronTablePath(homeDir) {
|
|
2101
|
-
return
|
|
2427
|
+
return join5(homeDir ?? homedir3(), CRON_TABLE_SUBPATH);
|
|
2102
2428
|
}
|
|
2103
2429
|
async function appendObjectLogLine(line, homeDir) {
|
|
2104
2430
|
const objectLogPath = resolveObjectLogPath(homeDir);
|
|
@@ -2118,7 +2444,7 @@ async function calculateInputSizeBytes(targetPath) {
|
|
|
2118
2444
|
let total = 0;
|
|
2119
2445
|
const entries = await readdir(targetPath, { withFileTypes: true });
|
|
2120
2446
|
for (const entry of entries) {
|
|
2121
|
-
total += await calculateInputSizeBytes(
|
|
2447
|
+
total += await calculateInputSizeBytes(join5(targetPath, entry.name));
|
|
2122
2448
|
}
|
|
2123
2449
|
return total;
|
|
2124
2450
|
}
|
|
@@ -2197,7 +2523,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
|
|
|
2197
2523
|
throw new Error("Insufficient disk space for backup object");
|
|
2198
2524
|
}
|
|
2199
2525
|
const objectId = createObjectId(options);
|
|
2200
|
-
const archivePath =
|
|
2526
|
+
const archivePath = join5(tmpDir, objectId);
|
|
2201
2527
|
try {
|
|
2202
2528
|
await runTarGzip(archivePath, targetPath);
|
|
2203
2529
|
const archiveStats = await stat(archivePath);
|
|
@@ -2502,9 +2828,9 @@ async function resolveWalletPrivateKey(homeDir) {
|
|
|
2502
2828
|
if (isValidWalletPrivateKey(envKey)) {
|
|
2503
2829
|
return envKey;
|
|
2504
2830
|
}
|
|
2505
|
-
const baseHome = homeDir ??
|
|
2506
|
-
const primaryWalletPath =
|
|
2507
|
-
const fallbackWalletPath =
|
|
2831
|
+
const baseHome = homeDir ?? homedir3();
|
|
2832
|
+
const primaryWalletPath = join5(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
|
|
2833
|
+
const fallbackWalletPath = join5(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
|
|
2508
2834
|
const fromPrimary = await readWalletKeyIfPresent(primaryWalletPath);
|
|
2509
2835
|
if (fromPrimary) {
|
|
2510
2836
|
return fromPrimary;
|
|
@@ -2537,8 +2863,8 @@ function encryptAesGcm(plaintext, key, randomFn = randomBytesNode) {
|
|
|
2537
2863
|
return Buffer.concat([nonce, ciphertext, tag]);
|
|
2538
2864
|
}
|
|
2539
2865
|
async function loadOrCreateKek(walletAddress, homeDir) {
|
|
2540
|
-
const keyPath =
|
|
2541
|
-
homeDir ??
|
|
2866
|
+
const keyPath = join5(
|
|
2867
|
+
homeDir ?? homedir3(),
|
|
2542
2868
|
KEY_STORE_SUBPATH,
|
|
2543
2869
|
`${walletShortHash(walletAddress)}.key`
|
|
2544
2870
|
);
|
|
@@ -2647,7 +2973,7 @@ function formatStorageUploadUserMessage(upload, cronJobId) {
|
|
|
2647
2973
|
].join("\n");
|
|
2648
2974
|
}
|
|
2649
2975
|
function formatStorageDeleteUserMessage(objectKey, cronId, cronDeleted) {
|
|
2650
|
-
const statusLine = cronId ? cronDeleted ? `File \`${objectKey}\` has been deleted from the cloud and the cron job \`${cronId}\` has been
|
|
2976
|
+
const statusLine = cronId ? cronDeleted ? `File \`${objectKey}\` has been deleted from the cloud and the cron job \`${cronId}\` has been removed from local mnemospark cron tracking.` : `File \`${objectKey}\` has been deleted from the cloud and the cron job \`${cronId}\` was not found in local mnemospark cron tracking.` : `File \`${objectKey}\` has been deleted from the cloud and no matching cron job was found in local mnemospark cron tracking.`;
|
|
2651
2977
|
return [statusLine, "Thank you for using mnemospark!"].join("\n");
|
|
2652
2978
|
}
|
|
2653
2979
|
function extractUploadErrorMessage(error) {
|
|
@@ -2687,6 +3013,7 @@ function createCloudCommand(options = {}) {
|
|
|
2687
3013
|
const backupBuilder = options.buildBackupObjectFn ?? buildBackupObject;
|
|
2688
3014
|
const requestPriceStorageQuote = options.requestPriceStorageQuoteFn ?? requestPriceStorageViaProxy;
|
|
2689
3015
|
const requestStorageUpload = options.requestStorageUploadFn ?? requestStorageUploadViaProxy;
|
|
3016
|
+
const requestStorageUploadConfirm = options.requestStorageUploadConfirmFn ?? requestStorageUploadConfirmViaProxy;
|
|
2690
3017
|
const resolveWalletKey = options.resolveWalletPrivateKeyFn ?? resolveWalletPrivateKey;
|
|
2691
3018
|
const createPayment = options.createPaymentFetchFn ?? createPaymentFetch;
|
|
2692
3019
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -2788,7 +3115,7 @@ function createCloudCommand(options = {}) {
|
|
|
2788
3115
|
);
|
|
2789
3116
|
if (!loggedQuote) {
|
|
2790
3117
|
return {
|
|
2791
|
-
text: "Cannot upload storage object: quote-id not found in object.log. Run /mnemospark
|
|
3118
|
+
text: "Cannot upload storage object: quote-id not found in object.log. Run /mnemospark-cloud price-storage first.",
|
|
2792
3119
|
isError: true
|
|
2793
3120
|
};
|
|
2794
3121
|
}
|
|
@@ -2798,7 +3125,7 @@ function createCloudCommand(options = {}) {
|
|
|
2798
3125
|
isError: true
|
|
2799
3126
|
};
|
|
2800
3127
|
}
|
|
2801
|
-
const archivePath =
|
|
3128
|
+
const archivePath = join5(
|
|
2802
3129
|
options.backupOptions?.tmpDir ?? DEFAULT_BACKUP_DIR,
|
|
2803
3130
|
parsed.uploadRequest.object_id
|
|
2804
3131
|
);
|
|
@@ -2807,7 +3134,7 @@ function createCloudCommand(options = {}) {
|
|
|
2807
3134
|
archiveStats = await stat(archivePath);
|
|
2808
3135
|
} catch {
|
|
2809
3136
|
return {
|
|
2810
|
-
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /mnemospark
|
|
3137
|
+
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /mnemospark-cloud backup first.`,
|
|
2811
3138
|
isError: true
|
|
2812
3139
|
};
|
|
2813
3140
|
}
|
|
@@ -2860,22 +3187,43 @@ function createCloudCommand(options = {}) {
|
|
|
2860
3187
|
preparedPayload.encryptedContent,
|
|
2861
3188
|
fetchImpl
|
|
2862
3189
|
);
|
|
2863
|
-
|
|
2864
|
-
|
|
3190
|
+
let finalizedUploadResponse = uploadResponse;
|
|
3191
|
+
if (preparedPayload.payload.mode === "presigned" && uploadResponse.confirmation_required === true) {
|
|
3192
|
+
try {
|
|
3193
|
+
finalizedUploadResponse = await requestStorageUploadConfirm(
|
|
3194
|
+
{
|
|
3195
|
+
quote_id: uploadResponse.quote_id,
|
|
3196
|
+
wallet_address: parsed.uploadRequest.wallet_address,
|
|
3197
|
+
object_key: uploadResponse.object_key,
|
|
3198
|
+
idempotency_key: idempotencyKey
|
|
3199
|
+
},
|
|
3200
|
+
options.proxyUploadConfirmOptions
|
|
3201
|
+
);
|
|
3202
|
+
} catch (confirmError) {
|
|
3203
|
+
const transId = uploadResponse.trans_id ?? "unknown";
|
|
3204
|
+
const confirmMessage = extractUploadErrorMessage(confirmError) ?? "Upload confirmation request failed";
|
|
3205
|
+
throw new Error(
|
|
3206
|
+
`Upload to S3 succeeded, but backend confirmation failed (trans_id: ${transId}, idempotency_key: ${idempotencyKey}). ${confirmMessage}`
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
await appendStorageUploadLog(finalizedUploadResponse, objectLogHomeDir, nowDateFn);
|
|
3211
|
+
const cronStoragePriceCandidate = finalizedUploadResponse.storage_price ?? loggedQuote.storagePrice;
|
|
2865
3212
|
const cronStoragePrice = Number.isFinite(cronStoragePriceCandidate) && cronStoragePriceCandidate > 0 ? cronStoragePriceCandidate : loggedQuote.storagePrice;
|
|
2866
3213
|
const cronJob = await createStoragePaymentCronJob(
|
|
2867
|
-
|
|
3214
|
+
finalizedUploadResponse,
|
|
2868
3215
|
cronStoragePrice,
|
|
2869
3216
|
objectLogHomeDir,
|
|
2870
3217
|
nowDateFn
|
|
2871
3218
|
);
|
|
2872
3219
|
await maybeCleanupLocalBackupArchive(archivePath);
|
|
2873
3220
|
return {
|
|
2874
|
-
text: formatStorageUploadUserMessage(
|
|
3221
|
+
text: formatStorageUploadUserMessage(finalizedUploadResponse, cronJob.cronId)
|
|
2875
3222
|
};
|
|
2876
3223
|
} catch (error) {
|
|
3224
|
+
const uploadErrorMessage = extractUploadErrorMessage(error);
|
|
2877
3225
|
return {
|
|
2878
|
-
text:
|
|
3226
|
+
text: uploadErrorMessage ?? "Cannot upload storage object",
|
|
2879
3227
|
isError: true
|
|
2880
3228
|
};
|
|
2881
3229
|
}
|
|
@@ -2909,7 +3257,7 @@ function createCloudCommand(options = {}) {
|
|
|
2909
3257
|
throw new Error("download failed");
|
|
2910
3258
|
}
|
|
2911
3259
|
return {
|
|
2912
|
-
text: `File ${parsed.storageObjectRequest.object_key} downloaded`
|
|
3260
|
+
text: `File ${parsed.storageObjectRequest.object_key} downloaded to ${downloadResult.file_path}`
|
|
2913
3261
|
};
|
|
2914
3262
|
} catch {
|
|
2915
3263
|
return {
|
|
@@ -2961,11 +3309,11 @@ function createCloudCommand(options = {}) {
|
|
|
2961
3309
|
|
|
2962
3310
|
// src/cli.ts
|
|
2963
3311
|
import { spawn as spawn2 } from "child_process";
|
|
2964
|
-
import { dirname as dirname4, join as
|
|
3312
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
2965
3313
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2966
3314
|
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2967
3315
|
import { existsSync } from "fs";
|
|
2968
|
-
import { homedir as
|
|
3316
|
+
import { homedir as homedir4 } from "os";
|
|
2969
3317
|
function isHexPrivateKey(value) {
|
|
2970
3318
|
return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value.trim());
|
|
2971
3319
|
}
|
|
@@ -3076,14 +3424,14 @@ async function ensureDir(path) {
|
|
|
3076
3424
|
await mkdir4(path, { recursive: true });
|
|
3077
3425
|
}
|
|
3078
3426
|
async function deployExtensionFiles() {
|
|
3079
|
-
const scriptsSource =
|
|
3427
|
+
const scriptsSource = join6(PACKAGE_ROOT, "scripts");
|
|
3080
3428
|
if (!existsSync(scriptsSource)) return;
|
|
3081
|
-
const mnemoScriptsDir =
|
|
3429
|
+
const mnemoScriptsDir = join6(homedir4(), ".openclaw", "mnemospark", "scripts");
|
|
3082
3430
|
await ensureDir(mnemoScriptsDir);
|
|
3083
|
-
const uninstallSrc =
|
|
3431
|
+
const uninstallSrc = join6(scriptsSource, "uninstall.sh");
|
|
3084
3432
|
if (existsSync(uninstallSrc)) {
|
|
3085
3433
|
const content = await readFile3(uninstallSrc);
|
|
3086
|
-
await writeFile4(
|
|
3434
|
+
await writeFile4(join6(mnemoScriptsDir, "uninstall.sh"), content, { mode: 493 });
|
|
3087
3435
|
}
|
|
3088
3436
|
}
|
|
3089
3437
|
function isOpenClawAvailable() {
|
|
@@ -3097,8 +3445,8 @@ function isOpenClawAvailable() {
|
|
|
3097
3445
|
});
|
|
3098
3446
|
}
|
|
3099
3447
|
function getOpenClawConfigPath() {
|
|
3100
|
-
const stateDir = process.env.OPENCLAW_STATE_DIR ??
|
|
3101
|
-
return
|
|
3448
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR ?? join6(homedir4(), ".openclaw");
|
|
3449
|
+
return join6(stateDir, "openclaw.json");
|
|
3102
3450
|
}
|
|
3103
3451
|
async function ensureMnemosparkInPluginsAllow() {
|
|
3104
3452
|
const configPath = getOpenClawConfigPath();
|