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 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 response = await fetchImpl(`${baseUrl}${UPLOAD_PROXY_PATH}`, {
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: requestHeaders,
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 cloud price-storage"
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 cloud price-storage: ${err instanceof Error ? err.message : String(err)}`
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 cloud upload"
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 cloud upload`
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 cloud upload: ${err instanceof Error ? err.message : String(err)}`
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 cloud ls"
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 cloud ls: ${err instanceof Error ? err.message : String(err)}`
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 cloud download"
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 cloud download: ${err instanceof Error ? err.message : String(err)}`
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 cloud delete"
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 cloud delete: ${err instanceof Error ? err.message : String(err)}`
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 join2 } from "path";
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 = join2(homedir(), ".openclaw", "blockrun");
1551
- var LEGACY_WALLET_FILE = join2(LEGACY_WALLET_DIR, "wallet.key");
1552
- var WALLET_DIR = join2(homedir(), ".openclaw", "mnemospark", "wallet");
1553
- var WALLET_FILE = join2(WALLET_DIR, "wallet.key");
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 join3 } from "path";
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(join3(__dirname, "..", "package.json"));
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 homedir2 } from "os";
1628
- import { basename, dirname as dirname3, join as join4, resolve as resolve2 } from "path";
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 = join4(".openclaw", "mnemospark", "backup");
1892
- var DEFAULT_BACKUP_DIR = join4(homedir2(), BACKUP_DIR_SUBPATH);
1893
- var OBJECT_LOG_SUBPATH = join4(".openclaw", "mnemospark", "object.log");
1894
- var CRON_TABLE_SUBPATH = join4(".openclaw", "mnemospark", "crontab.txt");
1895
- var BLOCKRUN_WALLET_KEY_SUBPATH = join4(".openclaw", "blockrun", "wallet.key");
1896
- var MNEMOSPARK_WALLET_KEY_SUBPATH = join4(".openclaw", "mnemospark", "wallet", "wallet.key");
1897
- var KEY_STORE_SUBPATH = join4(".openclaw", "mnemospark", "keys");
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 homedir2();
2237
+ return homedir3();
1912
2238
  }
1913
2239
  if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
1914
- return join4(homedir2(), trimmed.slice(2));
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 join4(homeDir ?? homedir2(), OBJECT_LOG_SUBPATH);
2424
+ return join5(homeDir ?? homedir3(), OBJECT_LOG_SUBPATH);
2099
2425
  }
2100
2426
  function resolveCronTablePath(homeDir) {
2101
- return join4(homeDir ?? homedir2(), CRON_TABLE_SUBPATH);
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(join4(targetPath, entry.name));
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 = join4(tmpDir, objectId);
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 ?? homedir2();
2506
- const primaryWalletPath = join4(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
2507
- const fallbackWalletPath = join4(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
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 = join4(
2541
- homeDir ?? homedir2(),
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 deleted from your system.` : `File \`${objectKey}\` has been deleted from the cloud and the cron job \`${cronId}\` was not found in your system.` : `File \`${objectKey}\` has been deleted from the cloud and no matching cron job was found in your system.`;
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 cloud price-storage first.",
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 = join4(
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 cloud backup first.`,
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
- await appendStorageUploadLog(uploadResponse, objectLogHomeDir, nowDateFn);
2864
- const cronStoragePriceCandidate = uploadResponse.storage_price ?? loggedQuote.storagePrice;
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
- uploadResponse,
3214
+ finalizedUploadResponse,
2868
3215
  cronStoragePrice,
2869
3216
  objectLogHomeDir,
2870
3217
  nowDateFn
2871
3218
  );
2872
3219
  await maybeCleanupLocalBackupArchive(archivePath);
2873
3220
  return {
2874
- text: formatStorageUploadUserMessage(uploadResponse, cronJob.cronId)
3221
+ text: formatStorageUploadUserMessage(finalizedUploadResponse, cronJob.cronId)
2875
3222
  };
2876
3223
  } catch (error) {
3224
+ const uploadErrorMessage = extractUploadErrorMessage(error);
2877
3225
  return {
2878
- text: extractUploadErrorMessage(error) ?? "Cannot upload storage object",
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 join5 } from "path";
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 homedir3 } from "os";
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 = join5(PACKAGE_ROOT, "scripts");
3427
+ const scriptsSource = join6(PACKAGE_ROOT, "scripts");
3080
3428
  if (!existsSync(scriptsSource)) return;
3081
- const mnemoScriptsDir = join5(homedir3(), ".openclaw", "mnemospark", "scripts");
3429
+ const mnemoScriptsDir = join6(homedir4(), ".openclaw", "mnemospark", "scripts");
3082
3430
  await ensureDir(mnemoScriptsDir);
3083
- const uninstallSrc = join5(scriptsSource, "uninstall.sh");
3431
+ const uninstallSrc = join6(scriptsSource, "uninstall.sh");
3084
3432
  if (existsSync(uninstallSrc)) {
3085
3433
  const content = await readFile3(uninstallSrc);
3086
- await writeFile4(join5(mnemoScriptsDir, "uninstall.sh"), content, { mode: 493 });
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 ?? join5(homedir3(), ".openclaw");
3101
- return join5(stateDir, "openclaw.json");
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();