mnemospark 0.3.0 → 0.5.0
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 +925 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +47 -2
- package/dist/index.js +925 -42
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
- package/skills/mnemospark/SKILL.md +2 -1
- package/skills/mnemospark/references/commands.md +7 -0
- package/skills/mnemospark/references/state-and-logs.md +8 -0
package/dist/index.js
CHANGED
|
@@ -377,6 +377,34 @@ function normalizePaymentRequired(headers) {
|
|
|
377
377
|
function normalizePaymentResponse(headers) {
|
|
378
378
|
return headers.get("PAYMENT-RESPONSE") ?? headers.get("x-payment-response") ?? void 0;
|
|
379
379
|
}
|
|
380
|
+
var BYTE_SI_UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
381
|
+
var BYTE_SI_BASE = 1e3;
|
|
382
|
+
function formatBytesForDisplay(bytes) {
|
|
383
|
+
if (!Number.isInteger(bytes) || bytes < 0 || !Number.isFinite(bytes)) {
|
|
384
|
+
throw new Error("formatBytesForDisplay expects a non-negative integer");
|
|
385
|
+
}
|
|
386
|
+
if (bytes === 0) {
|
|
387
|
+
return "0 B";
|
|
388
|
+
}
|
|
389
|
+
let value = bytes;
|
|
390
|
+
let unitIndex = 0;
|
|
391
|
+
while (value >= BYTE_SI_BASE && unitIndex < BYTE_SI_UNITS.length - 1) {
|
|
392
|
+
value /= BYTE_SI_BASE;
|
|
393
|
+
unitIndex += 1;
|
|
394
|
+
}
|
|
395
|
+
if (unitIndex === 0) {
|
|
396
|
+
return `${bytes} B`;
|
|
397
|
+
}
|
|
398
|
+
const nearestInt = Math.round(value);
|
|
399
|
+
const pickInt = nearestInt > 0 && Math.abs(value - nearestInt) / Math.max(value, 1e-9) <= 0.01;
|
|
400
|
+
let rounded = pickInt ? nearestInt : Math.round(value * 10) / 10;
|
|
401
|
+
if (rounded >= BYTE_SI_BASE && unitIndex < BYTE_SI_UNITS.length - 1) {
|
|
402
|
+
rounded = Math.round(rounded / BYTE_SI_BASE * 10) / 10;
|
|
403
|
+
unitIndex += 1;
|
|
404
|
+
}
|
|
405
|
+
const text = Number.isInteger(rounded) ? String(rounded) : String(rounded).replace(/\.0$/, "");
|
|
406
|
+
return `${text} ${BYTE_SI_UNITS[unitIndex]}`;
|
|
407
|
+
}
|
|
380
408
|
|
|
381
409
|
// src/wallet-signature.ts
|
|
382
410
|
function normalizeWalletSignature(value) {
|
|
@@ -1193,6 +1221,9 @@ function parseStoredAes256Key(raw, errorMessage = "Invalid key file format") {
|
|
|
1193
1221
|
var STORAGE_LS_PROXY_PATH = "/mnemospark/storage/ls";
|
|
1194
1222
|
var STORAGE_DOWNLOAD_PROXY_PATH = "/mnemospark/storage/download";
|
|
1195
1223
|
var STORAGE_DELETE_PROXY_PATH = "/mnemospark/storage/delete";
|
|
1224
|
+
function isStorageLsListResponse(r) {
|
|
1225
|
+
return r.mode === "list";
|
|
1226
|
+
}
|
|
1196
1227
|
var AES_GCM_TAG_BYTES = 16;
|
|
1197
1228
|
function asBooleanOrDefault(value, defaultValue) {
|
|
1198
1229
|
if (typeof value === "boolean") {
|
|
@@ -1280,7 +1311,7 @@ async function decryptDownloadBytes(encryptedBytes, wrappedDekBase64, walletAddr
|
|
|
1280
1311
|
}
|
|
1281
1312
|
return decryptAesGcm(encryptedBytes, dek);
|
|
1282
1313
|
}
|
|
1283
|
-
async function requestJsonViaProxy(proxyPath,
|
|
1314
|
+
async function requestJsonViaProxy(proxyPath, jsonBody, parser, options = {}) {
|
|
1284
1315
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1285
1316
|
const baseUrl = normalizeBaseUrl(
|
|
1286
1317
|
options.proxyBaseUrl ?? `http://127.0.0.1:${PROXY_PORT.toString()}`
|
|
@@ -1293,7 +1324,7 @@ async function requestJsonViaProxy(proxyPath, request, parser, options = {}) {
|
|
|
1293
1324
|
},
|
|
1294
1325
|
options.correlation
|
|
1295
1326
|
),
|
|
1296
|
-
body: JSON.stringify(
|
|
1327
|
+
body: JSON.stringify(jsonBody)
|
|
1297
1328
|
});
|
|
1298
1329
|
const bodyText = await response.text();
|
|
1299
1330
|
if (!response.ok) {
|
|
@@ -1307,7 +1338,7 @@ async function requestJsonViaProxy(proxyPath, request, parser, options = {}) {
|
|
|
1307
1338
|
}
|
|
1308
1339
|
return parser(payload);
|
|
1309
1340
|
}
|
|
1310
|
-
async function forwardStorageToBackend(path, method,
|
|
1341
|
+
async function forwardStorageToBackend(path, method, jsonBody, options = {}) {
|
|
1311
1342
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1312
1343
|
const backendBaseUrl = (options.backendBaseUrl ?? "").trim();
|
|
1313
1344
|
const walletSignature = normalizeWalletSignature(options.walletSignature);
|
|
@@ -1326,7 +1357,7 @@ async function forwardStorageToBackend(path, method, request, options = {}) {
|
|
|
1326
1357
|
"Content-Type": "application/json",
|
|
1327
1358
|
"X-Wallet-Signature": walletSignature
|
|
1328
1359
|
},
|
|
1329
|
-
body: JSON.stringify(
|
|
1360
|
+
body: JSON.stringify(jsonBody)
|
|
1330
1361
|
});
|
|
1331
1362
|
const bodyBuffer = Buffer.from(await response.arrayBuffer());
|
|
1332
1363
|
return {
|
|
@@ -1356,11 +1387,97 @@ function parseStorageObjectRequest(payload) {
|
|
|
1356
1387
|
location
|
|
1357
1388
|
};
|
|
1358
1389
|
}
|
|
1390
|
+
function jsonBodyForObjectRequest(request) {
|
|
1391
|
+
const o = {
|
|
1392
|
+
wallet_address: request.wallet_address,
|
|
1393
|
+
object_key: request.object_key
|
|
1394
|
+
};
|
|
1395
|
+
if (request.location) {
|
|
1396
|
+
o.location = request.location;
|
|
1397
|
+
}
|
|
1398
|
+
return o;
|
|
1399
|
+
}
|
|
1400
|
+
function jsonBodyForLsRequest(request) {
|
|
1401
|
+
const o = { wallet_address: request.wallet_address };
|
|
1402
|
+
if (request.object_key) {
|
|
1403
|
+
o.object_key = request.object_key;
|
|
1404
|
+
}
|
|
1405
|
+
if (request.location) {
|
|
1406
|
+
o.location = request.location;
|
|
1407
|
+
}
|
|
1408
|
+
if (request.continuation_token) {
|
|
1409
|
+
o.continuation_token = request.continuation_token;
|
|
1410
|
+
}
|
|
1411
|
+
if (typeof request.max_keys === "number") {
|
|
1412
|
+
o.max_keys = request.max_keys;
|
|
1413
|
+
}
|
|
1414
|
+
if (request.prefix) {
|
|
1415
|
+
o.prefix = request.prefix;
|
|
1416
|
+
}
|
|
1417
|
+
return o;
|
|
1418
|
+
}
|
|
1419
|
+
function parseStorageLsRequestPayload(payload) {
|
|
1420
|
+
const record = asRecord(payload);
|
|
1421
|
+
if (!record) {
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
const walletAddress = asNonEmptyString(record.wallet_address);
|
|
1425
|
+
if (!walletAddress) {
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
const objectKey = asNonEmptyString(record.object_key) ?? void 0;
|
|
1429
|
+
const location = asNonEmptyString(record.location) ?? void 0;
|
|
1430
|
+
const continuation_token = asNonEmptyString(record.continuation_token) ?? void 0;
|
|
1431
|
+
const maxRaw = asNumber(record.max_keys);
|
|
1432
|
+
const max_keys = maxRaw !== null && Number.isInteger(maxRaw) && maxRaw >= 1 ? maxRaw : void 0;
|
|
1433
|
+
const prefix = asNonEmptyString(record.prefix) ?? void 0;
|
|
1434
|
+
return {
|
|
1435
|
+
wallet_address: walletAddress,
|
|
1436
|
+
...objectKey ? { object_key: objectKey } : {},
|
|
1437
|
+
...location ? { location } : {},
|
|
1438
|
+
...continuation_token ? { continuation_token } : {},
|
|
1439
|
+
...typeof max_keys === "number" ? { max_keys } : {},
|
|
1440
|
+
...prefix ? { prefix } : {}
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1359
1443
|
function parseStorageLsResponse(payload) {
|
|
1360
1444
|
const record = asRecord(payload);
|
|
1361
1445
|
if (!record) {
|
|
1362
1446
|
throw new Error("Invalid ls response payload");
|
|
1363
1447
|
}
|
|
1448
|
+
if (record.list_mode === true) {
|
|
1449
|
+
const bucket2 = asNonEmptyString(record.bucket) ?? asNonEmptyString(record.bucket_name);
|
|
1450
|
+
const rawObjects = record.objects;
|
|
1451
|
+
if (!bucket2 || !Array.isArray(rawObjects)) {
|
|
1452
|
+
throw new Error("ls list response is missing required fields");
|
|
1453
|
+
}
|
|
1454
|
+
const objects = [];
|
|
1455
|
+
for (const item of rawObjects) {
|
|
1456
|
+
const row = asRecord(item);
|
|
1457
|
+
if (!row) {
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
const key2 = asNonEmptyString(row.key);
|
|
1461
|
+
const sizeBytes2 = asNumber(row.size_bytes);
|
|
1462
|
+
if (!key2 || sizeBytes2 === null || !Number.isInteger(sizeBytes2) || sizeBytes2 < 0) {
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
const last_modified = asNonEmptyString(row.last_modified) ?? void 0;
|
|
1466
|
+
objects.push({ key: key2, size_bytes: sizeBytes2, last_modified });
|
|
1467
|
+
}
|
|
1468
|
+
const is_truncated = asBooleanOrDefault(record.is_truncated, false);
|
|
1469
|
+
const nextRaw = record.next_continuation_token;
|
|
1470
|
+
const next_continuation_token = nextRaw === void 0 || nextRaw === null ? null : String(nextRaw);
|
|
1471
|
+
return {
|
|
1472
|
+
mode: "list",
|
|
1473
|
+
success: asBooleanOrDefault(record.success, true),
|
|
1474
|
+
list_mode: true,
|
|
1475
|
+
bucket: bucket2,
|
|
1476
|
+
objects,
|
|
1477
|
+
is_truncated,
|
|
1478
|
+
next_continuation_token
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1364
1481
|
const key = asNonEmptyString(record.key) ?? asNonEmptyString(record.object_key);
|
|
1365
1482
|
const sizeBytes = asNumber(record.size_bytes);
|
|
1366
1483
|
const bucket = asNonEmptyString(record.bucket) ?? asNonEmptyString(record.bucket_name);
|
|
@@ -1368,7 +1485,11 @@ function parseStorageLsResponse(payload) {
|
|
|
1368
1485
|
if (!key || sizeBytes === null || !bucket) {
|
|
1369
1486
|
throw new Error("ls response is missing required fields");
|
|
1370
1487
|
}
|
|
1488
|
+
if (!Number.isInteger(sizeBytes) || sizeBytes < 0) {
|
|
1489
|
+
throw new Error("ls response has invalid size_bytes; expected non-negative integer");
|
|
1490
|
+
}
|
|
1371
1491
|
return {
|
|
1492
|
+
mode: "stat",
|
|
1372
1493
|
success: asBooleanOrDefault(record.success, true),
|
|
1373
1494
|
key,
|
|
1374
1495
|
size_bytes: sizeBytes,
|
|
@@ -1411,12 +1532,17 @@ function parseStorageDownloadProxyResponse(payload) {
|
|
|
1411
1532
|
};
|
|
1412
1533
|
}
|
|
1413
1534
|
async function requestStorageLsViaProxy(request, options = {}) {
|
|
1414
|
-
return requestJsonViaProxy(
|
|
1535
|
+
return requestJsonViaProxy(
|
|
1536
|
+
STORAGE_LS_PROXY_PATH,
|
|
1537
|
+
jsonBodyForLsRequest(request),
|
|
1538
|
+
parseStorageLsResponse,
|
|
1539
|
+
options
|
|
1540
|
+
);
|
|
1415
1541
|
}
|
|
1416
1542
|
async function requestStorageDownloadViaProxy(request, options = {}) {
|
|
1417
1543
|
return requestJsonViaProxy(
|
|
1418
1544
|
STORAGE_DOWNLOAD_PROXY_PATH,
|
|
1419
|
-
request,
|
|
1545
|
+
jsonBodyForObjectRequest(request),
|
|
1420
1546
|
parseStorageDownloadProxyResponse,
|
|
1421
1547
|
options
|
|
1422
1548
|
);
|
|
@@ -1424,19 +1550,29 @@ async function requestStorageDownloadViaProxy(request, options = {}) {
|
|
|
1424
1550
|
async function requestStorageDeleteViaProxy(request, options = {}) {
|
|
1425
1551
|
return requestJsonViaProxy(
|
|
1426
1552
|
STORAGE_DELETE_PROXY_PATH,
|
|
1427
|
-
request,
|
|
1553
|
+
jsonBodyForObjectRequest(request),
|
|
1428
1554
|
parseStorageDeleteResponse,
|
|
1429
1555
|
options
|
|
1430
1556
|
);
|
|
1431
1557
|
}
|
|
1432
1558
|
async function forwardStorageLsToBackend(request, options = {}) {
|
|
1433
|
-
return forwardStorageToBackend("/storage/ls", "POST", request, options);
|
|
1559
|
+
return forwardStorageToBackend("/storage/ls", "POST", jsonBodyForLsRequest(request), options);
|
|
1434
1560
|
}
|
|
1435
1561
|
async function forwardStorageDownloadToBackend(request, options = {}) {
|
|
1436
|
-
return forwardStorageToBackend(
|
|
1562
|
+
return forwardStorageToBackend(
|
|
1563
|
+
"/storage/download",
|
|
1564
|
+
"POST",
|
|
1565
|
+
jsonBodyForObjectRequest(request),
|
|
1566
|
+
options
|
|
1567
|
+
);
|
|
1437
1568
|
}
|
|
1438
1569
|
async function forwardStorageDeleteToBackend(request, options = {}) {
|
|
1439
|
-
return forwardStorageToBackend(
|
|
1570
|
+
return forwardStorageToBackend(
|
|
1571
|
+
"/storage/delete",
|
|
1572
|
+
"POST",
|
|
1573
|
+
jsonBodyForObjectRequest(request),
|
|
1574
|
+
options
|
|
1575
|
+
);
|
|
1440
1576
|
}
|
|
1441
1577
|
async function downloadStorageToDisk(request, backendResponse, options = {}) {
|
|
1442
1578
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -2272,13 +2408,13 @@ async function startProxy(options) {
|
|
|
2272
2408
|
});
|
|
2273
2409
|
return;
|
|
2274
2410
|
}
|
|
2275
|
-
const requestPayload =
|
|
2411
|
+
const requestPayload = parseStorageLsRequestPayload(payload);
|
|
2276
2412
|
if (!requestPayload) {
|
|
2277
2413
|
logProxyEvent("warn", "proxy_ls_missing_fields");
|
|
2278
2414
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
2279
2415
|
sendJson(res, 400, {
|
|
2280
2416
|
error: "Bad request",
|
|
2281
|
-
message: "Missing required
|
|
2417
|
+
message: "Missing required field: wallet_address"
|
|
2282
2418
|
});
|
|
2283
2419
|
return;
|
|
2284
2420
|
}
|
|
@@ -2800,6 +2936,259 @@ import { homedir as homedir6 } from "os";
|
|
|
2800
2936
|
import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
|
|
2801
2937
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
2802
2938
|
|
|
2939
|
+
// src/cloud-ls-format.ts
|
|
2940
|
+
import { CronExpressionParser } from "cron-parser";
|
|
2941
|
+
var LS_NAME_DISPLAY_MAX = 72;
|
|
2942
|
+
var LS_PAY_DISPLAY_MAX = 28;
|
|
2943
|
+
var LS_CRON_ID_MAX = 14;
|
|
2944
|
+
var LS_TIME_FIELD_WIDTH = 12;
|
|
2945
|
+
var MONTHS_SHORT = [
|
|
2946
|
+
"Jan",
|
|
2947
|
+
"Feb",
|
|
2948
|
+
"Mar",
|
|
2949
|
+
"Apr",
|
|
2950
|
+
"May",
|
|
2951
|
+
"Jun",
|
|
2952
|
+
"Jul",
|
|
2953
|
+
"Aug",
|
|
2954
|
+
"Sep",
|
|
2955
|
+
"Oct",
|
|
2956
|
+
"Nov",
|
|
2957
|
+
"Dec"
|
|
2958
|
+
];
|
|
2959
|
+
function formatLsTimeFieldUtc(iso, now) {
|
|
2960
|
+
const placeholder = " - ".slice(0, LS_TIME_FIELD_WIDTH);
|
|
2961
|
+
if (!iso) {
|
|
2962
|
+
return placeholder.padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2963
|
+
}
|
|
2964
|
+
const d = new Date(iso);
|
|
2965
|
+
if (Number.isNaN(d.getTime())) {
|
|
2966
|
+
return placeholder.padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2967
|
+
}
|
|
2968
|
+
const mon = MONTHS_SHORT[d.getUTCMonth()] ?? "???";
|
|
2969
|
+
const day = String(d.getUTCDate()).padStart(2, " ");
|
|
2970
|
+
const y = d.getUTCFullYear();
|
|
2971
|
+
const nowY = now.getUTCFullYear();
|
|
2972
|
+
let core;
|
|
2973
|
+
if (y === nowY) {
|
|
2974
|
+
const hh = String(d.getUTCHours()).padStart(2, "0");
|
|
2975
|
+
const mm = String(d.getUTCMinutes()).padStart(2, "0");
|
|
2976
|
+
core = `${mon} ${day} ${hh}:${mm}`;
|
|
2977
|
+
} else {
|
|
2978
|
+
core = `${mon} ${day} ${y}`;
|
|
2979
|
+
}
|
|
2980
|
+
return core.padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2981
|
+
}
|
|
2982
|
+
function truncateEnd(value, max) {
|
|
2983
|
+
if (value.length <= max) {
|
|
2984
|
+
return value;
|
|
2985
|
+
}
|
|
2986
|
+
if (max <= 1) {
|
|
2987
|
+
return "\u2026";
|
|
2988
|
+
}
|
|
2989
|
+
return `${value.slice(0, max - 1)}\u2026`;
|
|
2990
|
+
}
|
|
2991
|
+
function truncateMiddle(value, max, suffixMin) {
|
|
2992
|
+
if (value.length <= max) {
|
|
2993
|
+
return value;
|
|
2994
|
+
}
|
|
2995
|
+
if (max < suffixMin + 5) {
|
|
2996
|
+
return truncateEnd(value, max);
|
|
2997
|
+
}
|
|
2998
|
+
const suffixLen = Math.min(suffixMin, max - 5);
|
|
2999
|
+
const prefixLen = max - 3 - suffixLen;
|
|
3000
|
+
return `${value.slice(0, prefixLen)} \u2026 ${value.slice(-suffixLen)}`;
|
|
3001
|
+
}
|
|
3002
|
+
function formatCronIdCell(cronId, width) {
|
|
3003
|
+
if (!cronId) {
|
|
3004
|
+
return " - ".slice(0, width).padStart(width, " ");
|
|
3005
|
+
}
|
|
3006
|
+
const t = truncateEnd(cronId, width);
|
|
3007
|
+
return t.padStart(width, " ");
|
|
3008
|
+
}
|
|
3009
|
+
function formatPaymentCell(amount, network, maxWidth) {
|
|
3010
|
+
if (amount === null) {
|
|
3011
|
+
return " - ".slice(0, maxWidth).padStart(maxWidth, " ");
|
|
3012
|
+
}
|
|
3013
|
+
let s = amount.toFixed(6).replace(/\.?0+$/, "");
|
|
3014
|
+
if (network && network.trim()) {
|
|
3015
|
+
s = `${s} (${network.trim()})`;
|
|
3016
|
+
}
|
|
3017
|
+
return truncateEnd(s, maxWidth).padStart(maxWidth, " ");
|
|
3018
|
+
}
|
|
3019
|
+
function formatNextCronUtc(schedule, cronStatus, now) {
|
|
3020
|
+
const blank = " - ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
3021
|
+
if (cronStatus !== "active") {
|
|
3022
|
+
return blank;
|
|
3023
|
+
}
|
|
3024
|
+
try {
|
|
3025
|
+
const expr = CronExpressionParser.parse(schedule, { tz: "UTC", currentDate: now });
|
|
3026
|
+
const next = expr.next().toDate();
|
|
3027
|
+
return formatLsTimeFieldUtc(next.toISOString(), now);
|
|
3028
|
+
} catch {
|
|
3029
|
+
return "?".padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
async function prepareRows(objects, walletAddress, datastore, now) {
|
|
3033
|
+
const sorted = [...objects].sort((a, b) => {
|
|
3034
|
+
const ta = a.last_modified ? Date.parse(a.last_modified) : Number.NaN;
|
|
3035
|
+
const tb = b.last_modified ? Date.parse(b.last_modified) : Number.NaN;
|
|
3036
|
+
const aOk = Number.isFinite(ta);
|
|
3037
|
+
const bOk = Number.isFinite(tb);
|
|
3038
|
+
if (aOk && bOk && tb !== ta) {
|
|
3039
|
+
return tb - ta;
|
|
3040
|
+
}
|
|
3041
|
+
if (aOk && !bOk) {
|
|
3042
|
+
return -1;
|
|
3043
|
+
}
|
|
3044
|
+
if (!aOk && bOk) {
|
|
3045
|
+
return 1;
|
|
3046
|
+
}
|
|
3047
|
+
return a.key.localeCompare(b.key);
|
|
3048
|
+
});
|
|
3049
|
+
const rows = [];
|
|
3050
|
+
for (const obj of sorted) {
|
|
3051
|
+
const friendly = await datastore.findLatestFriendlyNameForObjectKey(walletAddress, obj.key);
|
|
3052
|
+
const cp = await datastore.findCronAndPaymentForObjectKey(walletAddress, obj.key);
|
|
3053
|
+
const sizeStr = formatBytesForDisplay(obj.size_bytes);
|
|
3054
|
+
const s3time = formatLsTimeFieldUtc(obj.last_modified, now);
|
|
3055
|
+
let cronIdDisp = null;
|
|
3056
|
+
let nextRun = " - ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
3057
|
+
let payCell = "";
|
|
3058
|
+
if (cp) {
|
|
3059
|
+
cronIdDisp = cp.cronId;
|
|
3060
|
+
nextRun = formatNextCronUtc(cp.schedule, cp.cronStatus, now);
|
|
3061
|
+
payCell = formatPaymentCell(cp.amount, cp.network, LS_PAY_DISPLAY_MAX);
|
|
3062
|
+
} else {
|
|
3063
|
+
payCell = formatPaymentCell(null, null, LS_PAY_DISPLAY_MAX);
|
|
3064
|
+
}
|
|
3065
|
+
const nameRaw = friendly ? `${friendly} (${obj.key})` : obj.key;
|
|
3066
|
+
rows.push({
|
|
3067
|
+
perm: "----------",
|
|
3068
|
+
ln: " 1",
|
|
3069
|
+
user: "- ",
|
|
3070
|
+
grp: "- ",
|
|
3071
|
+
sizeStr,
|
|
3072
|
+
s3time,
|
|
3073
|
+
cronIdRaw: cronIdDisp,
|
|
3074
|
+
nextRun,
|
|
3075
|
+
payRaw: payCell,
|
|
3076
|
+
nameRaw: truncateMiddle(nameRaw, LS_NAME_DISPLAY_MAX, 8)
|
|
3077
|
+
});
|
|
3078
|
+
}
|
|
3079
|
+
return rows;
|
|
3080
|
+
}
|
|
3081
|
+
function columnWidths(rows) {
|
|
3082
|
+
let sizeW = 4;
|
|
3083
|
+
let cronW = 4;
|
|
3084
|
+
let payW = 3;
|
|
3085
|
+
for (const r of rows) {
|
|
3086
|
+
sizeW = Math.max(sizeW, r.sizeStr.length);
|
|
3087
|
+
const cid = r.cronIdRaw ? truncateEnd(r.cronIdRaw, LS_CRON_ID_MAX) : "";
|
|
3088
|
+
cronW = Math.max(cronW, cid.length || 1);
|
|
3089
|
+
payW = Math.max(payW, r.payRaw.length);
|
|
3090
|
+
}
|
|
3091
|
+
cronW = Math.min(Math.max(cronW, 4), LS_CRON_ID_MAX);
|
|
3092
|
+
payW = Math.min(Math.max(payW, 3), LS_PAY_DISPLAY_MAX);
|
|
3093
|
+
return { sizeW, cronW, payW };
|
|
3094
|
+
}
|
|
3095
|
+
function renderRow(r, w) {
|
|
3096
|
+
const cronPadded = formatCronIdCell(r.cronIdRaw, w.cronW);
|
|
3097
|
+
const sizePadded = r.sizeStr.padStart(w.sizeW, " ");
|
|
3098
|
+
const payPadded = r.payRaw.padStart(w.payW, " ");
|
|
3099
|
+
return [
|
|
3100
|
+
r.perm,
|
|
3101
|
+
r.ln,
|
|
3102
|
+
r.user,
|
|
3103
|
+
r.grp,
|
|
3104
|
+
sizePadded,
|
|
3105
|
+
r.s3time,
|
|
3106
|
+
cronPadded,
|
|
3107
|
+
r.nextRun,
|
|
3108
|
+
payPadded,
|
|
3109
|
+
r.nameRaw
|
|
3110
|
+
].join(" ");
|
|
3111
|
+
}
|
|
3112
|
+
function renderHeader(w) {
|
|
3113
|
+
return [
|
|
3114
|
+
"PERM ",
|
|
3115
|
+
"LN",
|
|
3116
|
+
"USER ",
|
|
3117
|
+
"GRP ",
|
|
3118
|
+
"SIZE".padStart(w.sizeW, " "),
|
|
3119
|
+
"S3_TIME ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " "),
|
|
3120
|
+
"CRON".padStart(w.cronW, " "),
|
|
3121
|
+
"NEXT ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " "),
|
|
3122
|
+
"PAY".padStart(w.payW, " "),
|
|
3123
|
+
"NAME"
|
|
3124
|
+
].join(" ");
|
|
3125
|
+
}
|
|
3126
|
+
async function buildMnemosparkLsMessage(result, ctx) {
|
|
3127
|
+
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
3128
|
+
if (isStorageLsListResponse(result)) {
|
|
3129
|
+
const disclaimer2 = "Names, cron, and payment columns come from this machine's SQLite catalog when available; `-` means unknown locally. S3 is authoritative for which keys exist.";
|
|
3130
|
+
const legend2 = "Legend: S3_TIME and NEXT are UTC. NEXT is the next cron fire from the stored expression.";
|
|
3131
|
+
const bucketLine = `bucket: ${result.bucket}`;
|
|
3132
|
+
const sortLine = "sorted by: S3 last_modified descending (missing dates last), then key ascending.";
|
|
3133
|
+
if (result.objects.length === 0) {
|
|
3134
|
+
const lines = [disclaimer2, "", bucketLine, "", "No objects in this bucket."];
|
|
3135
|
+
return lines.join("\n");
|
|
3136
|
+
}
|
|
3137
|
+
const rows = await prepareRows(result.objects, ctx.walletAddress, ctx.datastore, now);
|
|
3138
|
+
const w2 = columnWidths(rows);
|
|
3139
|
+
const header2 = renderHeader(w2);
|
|
3140
|
+
const bodyLines = rows.map((r) => renderRow(r, w2));
|
|
3141
|
+
const totalLine = `total ${String(result.objects.length)}`;
|
|
3142
|
+
const truncLine = result.is_truncated ? "List truncated; more objects in bucket." : null;
|
|
3143
|
+
const prose2 = [disclaimer2, legend2, bucketLine, sortLine, totalLine, truncLine].filter((x) => Boolean(x)).join("\n");
|
|
3144
|
+
const fence2 = ["```", [header2, ...bodyLines].join("\n"), "```"].join("\n");
|
|
3145
|
+
return `${prose2}
|
|
3146
|
+
|
|
3147
|
+
${fence2}`;
|
|
3148
|
+
}
|
|
3149
|
+
const friendly = await ctx.datastore.findLatestFriendlyNameForObjectKey(
|
|
3150
|
+
ctx.walletAddress,
|
|
3151
|
+
result.key
|
|
3152
|
+
);
|
|
3153
|
+
const cp = await ctx.datastore.findCronAndPaymentForObjectKey(ctx.walletAddress, result.key);
|
|
3154
|
+
const sizeStr = formatBytesForDisplay(result.size_bytes);
|
|
3155
|
+
const s3time = formatLsTimeFieldUtc(void 0, now);
|
|
3156
|
+
let payCell = formatPaymentCell(null, null, LS_PAY_DISPLAY_MAX);
|
|
3157
|
+
let cronIdDisp = null;
|
|
3158
|
+
let nextRun = " - ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
3159
|
+
if (cp) {
|
|
3160
|
+
cronIdDisp = cp.cronId;
|
|
3161
|
+
nextRun = formatNextCronUtc(cp.schedule, cp.cronStatus, now);
|
|
3162
|
+
payCell = formatPaymentCell(cp.amount, cp.network, LS_PAY_DISPLAY_MAX);
|
|
3163
|
+
}
|
|
3164
|
+
const nameShown = truncateMiddle(
|
|
3165
|
+
friendly ? `${friendly} (${result.key})` : result.key,
|
|
3166
|
+
LS_NAME_DISPLAY_MAX,
|
|
3167
|
+
8
|
|
3168
|
+
);
|
|
3169
|
+
const prep = {
|
|
3170
|
+
perm: "----------",
|
|
3171
|
+
ln: " 1",
|
|
3172
|
+
user: "- ",
|
|
3173
|
+
grp: "- ",
|
|
3174
|
+
sizeStr,
|
|
3175
|
+
s3time,
|
|
3176
|
+
cronIdRaw: cronIdDisp,
|
|
3177
|
+
nextRun,
|
|
3178
|
+
payRaw: payCell,
|
|
3179
|
+
nameRaw: nameShown
|
|
3180
|
+
};
|
|
3181
|
+
const w = columnWidths([prep]);
|
|
3182
|
+
const header = renderHeader(w);
|
|
3183
|
+
const line = renderRow(prep, w);
|
|
3184
|
+
const disclaimer = "Names, cron, and payment columns come from this machine's SQLite catalog when available; `-` means unknown locally.";
|
|
3185
|
+
const legend = "Legend: S3_TIME and NEXT are UTC.";
|
|
3186
|
+
const prose = [disclaimer, legend, `bucket: ${result.bucket}`, ""].join("\n");
|
|
3187
|
+
const fence = ["```", [header, line].join("\n"), "```"].join("\n");
|
|
3188
|
+
return `${prose}
|
|
3189
|
+
${fence}`;
|
|
3190
|
+
}
|
|
3191
|
+
|
|
2803
3192
|
// src/cloud-datastore.ts
|
|
2804
3193
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2805
3194
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
@@ -2835,6 +3224,7 @@ async function createCloudDatastore(homeDir) {
|
|
|
2835
3224
|
const nextDb = new DatabaseSyncCtor(dbPath);
|
|
2836
3225
|
nextDb.exec("PRAGMA journal_mode=WAL;");
|
|
2837
3226
|
nextDb.exec("PRAGMA foreign_keys=ON;");
|
|
3227
|
+
nextDb.exec("PRAGMA busy_timeout=5000;");
|
|
2838
3228
|
nextDb.exec(`
|
|
2839
3229
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
2840
3230
|
version INTEGER PRIMARY KEY,
|
|
@@ -2883,6 +3273,7 @@ async function createCloudDatastore(homeDir) {
|
|
|
2883
3273
|
updated_at TEXT NOT NULL
|
|
2884
3274
|
);
|
|
2885
3275
|
CREATE INDEX IF NOT EXISTS idx_cron_jobs_object_key ON cron_jobs(object_key);
|
|
3276
|
+
CREATE INDEX IF NOT EXISTS idx_cron_jobs_quote_id ON cron_jobs(quote_id);
|
|
2886
3277
|
|
|
2887
3278
|
CREATE TABLE IF NOT EXISTS operations (
|
|
2888
3279
|
operation_id TEXT PRIMARY KEY,
|
|
@@ -3083,6 +3474,38 @@ async function createCloudDatastore(homeDir) {
|
|
|
3083
3474
|
if (!row) return null;
|
|
3084
3475
|
return { cronId: row.cron_id, objectId: row.object_id };
|
|
3085
3476
|
}, null),
|
|
3477
|
+
findCronByQuoteId: async (quoteId) => safe(() => {
|
|
3478
|
+
const row = db.prepare(
|
|
3479
|
+
`SELECT cron_id, object_id, object_key, quote_id, schedule, command, status
|
|
3480
|
+
FROM cron_jobs WHERE quote_id = ? ORDER BY updated_at DESC LIMIT 1`
|
|
3481
|
+
).get(quoteId);
|
|
3482
|
+
if (!row) return null;
|
|
3483
|
+
return {
|
|
3484
|
+
cron_id: row.cron_id,
|
|
3485
|
+
object_id: row.object_id,
|
|
3486
|
+
object_key: row.object_key,
|
|
3487
|
+
quote_id: row.quote_id,
|
|
3488
|
+
schedule: row.schedule,
|
|
3489
|
+
command: row.command,
|
|
3490
|
+
status: row.status
|
|
3491
|
+
};
|
|
3492
|
+
}, null),
|
|
3493
|
+
findPaymentByQuoteId: async (quoteId) => safe(() => {
|
|
3494
|
+
const row = db.prepare(
|
|
3495
|
+
`SELECT quote_id, wallet_address, trans_id, amount, network, status, settled_at
|
|
3496
|
+
FROM payments WHERE quote_id = ? LIMIT 1`
|
|
3497
|
+
).get(quoteId);
|
|
3498
|
+
if (!row) return null;
|
|
3499
|
+
return {
|
|
3500
|
+
quote_id: row.quote_id,
|
|
3501
|
+
wallet_address: row.wallet_address,
|
|
3502
|
+
trans_id: row.trans_id,
|
|
3503
|
+
amount: row.amount,
|
|
3504
|
+
network: row.network,
|
|
3505
|
+
status: row.status,
|
|
3506
|
+
settled_at: row.settled_at
|
|
3507
|
+
};
|
|
3508
|
+
}, null),
|
|
3086
3509
|
upsertOperation: async (row) => {
|
|
3087
3510
|
await safe(() => {
|
|
3088
3511
|
const ts = nowIso();
|
|
@@ -3209,7 +3632,63 @@ async function createCloudDatastore(homeDir) {
|
|
|
3209
3632
|
WHERE wallet_address = ? AND friendly_name = ? AND is_active = 1`
|
|
3210
3633
|
).get(normalizedWalletAddress, friendlyName);
|
|
3211
3634
|
return Number(row?.cnt ?? 0);
|
|
3212
|
-
}, 0)
|
|
3635
|
+
}, 0),
|
|
3636
|
+
findLatestFriendlyNameForObjectKey: async (walletAddress, objectKey) => safe(() => {
|
|
3637
|
+
const w = normalizeWalletAddress(walletAddress);
|
|
3638
|
+
const byKey = db.prepare(
|
|
3639
|
+
`SELECT friendly_name
|
|
3640
|
+
FROM friendly_names
|
|
3641
|
+
WHERE wallet_address = ? AND object_key = ? AND is_active = 1
|
|
3642
|
+
ORDER BY created_at DESC
|
|
3643
|
+
LIMIT 1`
|
|
3644
|
+
).get(w, objectKey);
|
|
3645
|
+
if (byKey) {
|
|
3646
|
+
return byKey.friendly_name;
|
|
3647
|
+
}
|
|
3648
|
+
const obj = db.prepare(
|
|
3649
|
+
`SELECT object_id FROM objects WHERE wallet_address = ? AND object_key = ? LIMIT 1`
|
|
3650
|
+
).get(w, objectKey);
|
|
3651
|
+
if (!obj) {
|
|
3652
|
+
return null;
|
|
3653
|
+
}
|
|
3654
|
+
const byObj = db.prepare(
|
|
3655
|
+
`SELECT friendly_name
|
|
3656
|
+
FROM friendly_names
|
|
3657
|
+
WHERE wallet_address = ? AND object_id = ? AND is_active = 1
|
|
3658
|
+
ORDER BY created_at DESC
|
|
3659
|
+
LIMIT 1`
|
|
3660
|
+
).get(w, obj.object_id);
|
|
3661
|
+
return byObj?.friendly_name ?? null;
|
|
3662
|
+
}, null),
|
|
3663
|
+
findCronAndPaymentForObjectKey: async (walletAddress, objectKey) => safe(() => {
|
|
3664
|
+
const w = normalizeWalletAddress(walletAddress);
|
|
3665
|
+
const cron = db.prepare(
|
|
3666
|
+
`SELECT c.cron_id, c.quote_id, c.schedule, c.status
|
|
3667
|
+
FROM cron_jobs c
|
|
3668
|
+
INNER JOIN objects o ON o.object_id = c.object_id
|
|
3669
|
+
WHERE c.object_key = ? AND o.wallet_address = ?
|
|
3670
|
+
ORDER BY c.updated_at DESC
|
|
3671
|
+
LIMIT 1`
|
|
3672
|
+
).get(objectKey, w);
|
|
3673
|
+
if (!cron) {
|
|
3674
|
+
return null;
|
|
3675
|
+
}
|
|
3676
|
+
const pay = db.prepare(
|
|
3677
|
+
`SELECT amount, network, status
|
|
3678
|
+
FROM payments
|
|
3679
|
+
WHERE quote_id = ? AND wallet_address = ?
|
|
3680
|
+
LIMIT 1`
|
|
3681
|
+
).get(cron.quote_id, w);
|
|
3682
|
+
return {
|
|
3683
|
+
cronId: cron.cron_id,
|
|
3684
|
+
schedule: cron.schedule,
|
|
3685
|
+
quoteId: cron.quote_id,
|
|
3686
|
+
cronStatus: cron.status,
|
|
3687
|
+
amount: pay?.amount ?? null,
|
|
3688
|
+
network: pay?.network ?? null,
|
|
3689
|
+
paymentStatus: pay?.status ?? null
|
|
3690
|
+
};
|
|
3691
|
+
}, null)
|
|
3213
3692
|
};
|
|
3214
3693
|
}
|
|
3215
3694
|
|
|
@@ -3229,7 +3708,16 @@ var CRON_LOG_ROW_PREFIX = "cron";
|
|
|
3229
3708
|
var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
|
|
3230
3709
|
var REQUIRED_PRICE_STORAGE = "--wallet-address, --object-id, --object-id-hash, --gb, --provider, --region";
|
|
3231
3710
|
var REQUIRED_UPLOAD = "--quote-id, --wallet-address, --object-id, --object-id-hash";
|
|
3711
|
+
var REQUIRED_PAYMENT_SETTLE = "--quote-id and --wallet-address";
|
|
3232
3712
|
var REQUIRED_STORAGE_OBJECT = "--wallet-address and one of (--object-key | --name [--latest|--at])";
|
|
3713
|
+
var REQUIRED_LS = "--wallet-address (for one object add --object-key or --name [--latest|--at]; omit both to list the bucket)";
|
|
3714
|
+
var PAYMENT_SETTLE_FLAG_NAMES = /* @__PURE__ */ new Set([
|
|
3715
|
+
"quote-id",
|
|
3716
|
+
"wallet-address",
|
|
3717
|
+
"object-id",
|
|
3718
|
+
"object-key",
|
|
3719
|
+
"storage-price"
|
|
3720
|
+
]);
|
|
3233
3721
|
var BOOLEAN_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["latest"]);
|
|
3234
3722
|
var BOOLEAN_ASYNC_FLAGS = /* @__PURE__ */ new Set(["async"]);
|
|
3235
3723
|
var BOOLEAN_OP_STATUS_FLAGS = /* @__PURE__ */ new Set(["cancel"]);
|
|
@@ -3262,9 +3750,13 @@ var CLOUD_HELP_TEXT = [
|
|
|
3262
3750
|
" Purpose: upload an encrypted object using a valid quote-id.",
|
|
3263
3751
|
" Required: " + REQUIRED_UPLOAD,
|
|
3264
3752
|
"",
|
|
3265
|
-
"\u2022 `/mnemospark_cloud
|
|
3266
|
-
" Purpose:
|
|
3267
|
-
" Required:
|
|
3753
|
+
"\u2022 `/mnemospark_cloud payment-settle --quote-id <quote-id> --wallet-address <addr> [--object-id <id>] [--object-key <key>] [--storage-price <n>]`",
|
|
3754
|
+
" Purpose: settle storage payment for a quote (e.g. monthly cron). Uses the same proxy + x402 path as upload pre-settlement.",
|
|
3755
|
+
" Required: --quote-id, --wallet-address (wallet private key must match the address).",
|
|
3756
|
+
"",
|
|
3757
|
+
"\u2022 `/mnemospark_cloud ls --wallet-address <addr> [--object-key <key> | --name <friendly-name> | omit both to list bucket] [--latest|--at <timestamp>]`",
|
|
3758
|
+
" Purpose: stat one object or list all keys in the wallet bucket (S3).",
|
|
3759
|
+
" Required: " + REQUIRED_LS,
|
|
3268
3760
|
"",
|
|
3269
3761
|
"\u2022 `/mnemospark_cloud download --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3270
3762
|
" Purpose: fetch an object to local disk.",
|
|
@@ -3296,7 +3788,7 @@ var CLOUD_HELP_TEXT = [
|
|
|
3296
3788
|
"\u2022 `/mnemospark_cloud op-status --operation-id <id>`",
|
|
3297
3789
|
"\u2022 `/mnemospark_cloud op-status --operation-id <id> --cancel`",
|
|
3298
3790
|
"",
|
|
3299
|
-
"Backup creates a tar+gzip object in ~/.openclaw/mnemospark/backup and appends object metadata to ~/.openclaw/mnemospark/object.log. Upload appends storage rows and cron-tracking rows to object.log, and keeps job entries in ~/.openclaw/mnemospark/crontab.txt.
|
|
3791
|
+
"Backup creates a tar+gzip object in ~/.openclaw/mnemospark/backup and appends object metadata to ~/.openclaw/mnemospark/object.log. Upload appends storage rows and cron-tracking rows to object.log, and keeps job entries in ~/.openclaw/mnemospark/crontab.txt. Commands price-storage, upload, ls, download, delete, and payment-settle require --wallet-address."
|
|
3300
3792
|
].join("\n");
|
|
3301
3793
|
var UnsupportedBackupPlatformError = class extends Error {
|
|
3302
3794
|
constructor(platform) {
|
|
@@ -3367,6 +3859,18 @@ function parseObjectSelector(flags) {
|
|
|
3367
3859
|
if (objectKey) return { objectKey };
|
|
3368
3860
|
return { nameSelector: { name, latest, at } };
|
|
3369
3861
|
}
|
|
3862
|
+
function parseLsObjectSelector(flags) {
|
|
3863
|
+
const objectKey = flags["object-key"]?.trim();
|
|
3864
|
+
const name = flags.name?.trim();
|
|
3865
|
+
const latest = flags.latest === "true";
|
|
3866
|
+
const at = flags.at?.trim();
|
|
3867
|
+
if (objectKey && name) return null;
|
|
3868
|
+
if (latest && at) return null;
|
|
3869
|
+
if (!objectKey && !name && (latest || at)) return null;
|
|
3870
|
+
if (objectKey) return { objectKey };
|
|
3871
|
+
if (name) return { nameSelector: { name, latest, at } };
|
|
3872
|
+
return {};
|
|
3873
|
+
}
|
|
3370
3874
|
function parseStorageObjectRequestInput(flags, selector) {
|
|
3371
3875
|
const walletAddress = flags["wallet-address"]?.trim();
|
|
3372
3876
|
if (!walletAddress) {
|
|
@@ -3537,20 +4041,76 @@ function parseCloudArgs(args) {
|
|
|
3537
4041
|
}
|
|
3538
4042
|
};
|
|
3539
4043
|
}
|
|
4044
|
+
if (subcommand === "payment-settle") {
|
|
4045
|
+
const flags = parseNamedFlags(rest);
|
|
4046
|
+
if (!flags) {
|
|
4047
|
+
return { mode: "payment-settle-invalid" };
|
|
4048
|
+
}
|
|
4049
|
+
for (const key of Object.keys(flags)) {
|
|
4050
|
+
if (!PAYMENT_SETTLE_FLAG_NAMES.has(key)) {
|
|
4051
|
+
return { mode: "payment-settle-invalid" };
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
const quoteId = flags["quote-id"]?.trim();
|
|
4055
|
+
const walletAddress = flags["wallet-address"]?.trim();
|
|
4056
|
+
if (!quoteId || !walletAddress) {
|
|
4057
|
+
return { mode: "payment-settle-invalid" };
|
|
4058
|
+
}
|
|
4059
|
+
let storagePrice;
|
|
4060
|
+
if (flags["storage-price"] !== void 0 && flags["storage-price"] !== "") {
|
|
4061
|
+
const raw = flags["storage-price"]?.trim() ?? "";
|
|
4062
|
+
const n = Number.parseFloat(raw);
|
|
4063
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
4064
|
+
return { mode: "payment-settle-invalid" };
|
|
4065
|
+
}
|
|
4066
|
+
storagePrice = n;
|
|
4067
|
+
}
|
|
4068
|
+
return {
|
|
4069
|
+
mode: "payment-settle",
|
|
4070
|
+
paymentSettleRequest: {
|
|
4071
|
+
quote_id: quoteId,
|
|
4072
|
+
wallet_address: walletAddress,
|
|
4073
|
+
object_id: flags["object-id"]?.trim() || void 0,
|
|
4074
|
+
object_key: flags["object-key"]?.trim() || void 0,
|
|
4075
|
+
storage_price: storagePrice
|
|
4076
|
+
}
|
|
4077
|
+
};
|
|
4078
|
+
}
|
|
3540
4079
|
if (subcommand === "ls") {
|
|
3541
4080
|
const flags = parseNamedFlags(rest, BOOLEAN_SELECTOR_FLAGS);
|
|
3542
4081
|
if (!flags) {
|
|
3543
4082
|
return { mode: "ls-invalid" };
|
|
3544
4083
|
}
|
|
3545
|
-
const
|
|
3546
|
-
if (!
|
|
4084
|
+
const walletAddress = flags["wallet-address"]?.trim() ?? flags["wallet_address"]?.trim() ?? "";
|
|
4085
|
+
if (!walletAddress) {
|
|
3547
4086
|
return { mode: "ls-invalid" };
|
|
3548
4087
|
}
|
|
3549
|
-
const
|
|
3550
|
-
if (!
|
|
4088
|
+
const selector = parseLsObjectSelector(flags);
|
|
4089
|
+
if (!selector) {
|
|
3551
4090
|
return { mode: "ls-invalid" };
|
|
3552
4091
|
}
|
|
3553
|
-
|
|
4092
|
+
const location = flags.location?.trim() || flags.region?.trim() || void 0;
|
|
4093
|
+
if (selector.nameSelector) {
|
|
4094
|
+
return {
|
|
4095
|
+
mode: "ls",
|
|
4096
|
+
storageObjectRequest: { wallet_address: walletAddress, location },
|
|
4097
|
+
nameSelector: selector.nameSelector
|
|
4098
|
+
};
|
|
4099
|
+
}
|
|
4100
|
+
if (selector.objectKey) {
|
|
4101
|
+
return {
|
|
4102
|
+
mode: "ls",
|
|
4103
|
+
storageObjectRequest: {
|
|
4104
|
+
wallet_address: walletAddress,
|
|
4105
|
+
object_key: selector.objectKey,
|
|
4106
|
+
location
|
|
4107
|
+
}
|
|
4108
|
+
};
|
|
4109
|
+
}
|
|
4110
|
+
return {
|
|
4111
|
+
mode: "ls",
|
|
4112
|
+
storageObjectRequest: { wallet_address: walletAddress, location }
|
|
4113
|
+
};
|
|
3554
4114
|
}
|
|
3555
4115
|
if (subcommand === "download") {
|
|
3556
4116
|
const flags = parseNamedFlags(rest, BOOLEAN_SELECTOR_AND_ASYNC_FLAGS);
|
|
@@ -3897,7 +4457,8 @@ function quoteCronArgument(value) {
|
|
|
3897
4457
|
}
|
|
3898
4458
|
function buildStoragePaymentCronCommand(job) {
|
|
3899
4459
|
return [
|
|
3900
|
-
"
|
|
4460
|
+
"/mnemospark_cloud",
|
|
4461
|
+
"payment-settle",
|
|
3901
4462
|
"--quote-id",
|
|
3902
4463
|
quoteCronArgument(job.quoteId),
|
|
3903
4464
|
"--wallet-address",
|
|
@@ -4186,16 +4747,28 @@ function extractUploadErrorMessage(error) {
|
|
|
4186
4747
|
}
|
|
4187
4748
|
return message;
|
|
4188
4749
|
}
|
|
4750
|
+
function extractLsErrorMessage(error) {
|
|
4751
|
+
if (!(error instanceof Error)) {
|
|
4752
|
+
return null;
|
|
4753
|
+
}
|
|
4754
|
+
const message = error.message.trim();
|
|
4755
|
+
if (!message) {
|
|
4756
|
+
return null;
|
|
4757
|
+
}
|
|
4758
|
+
if (message.startsWith("ls response") || message.startsWith("ls list response") || message.startsWith("Invalid ls response payload")) {
|
|
4759
|
+
return `Cannot list storage object: ${message}`;
|
|
4760
|
+
}
|
|
4761
|
+
if (message === "formatBytesForDisplay expects a non-negative integer") {
|
|
4762
|
+
return "Cannot list storage object: ls response has invalid size_bytes; expected non-negative integer";
|
|
4763
|
+
}
|
|
4764
|
+
return null;
|
|
4765
|
+
}
|
|
4189
4766
|
function formatPriceStorageUserMessage(quote) {
|
|
4190
4767
|
return [
|
|
4191
4768
|
`Your storage quote \`${quote.quote_id}\` is valid for 1 hour, the storage price is \`${quote.storage_price}\` for \`${quote.object_id}\` with file size of \`${quote.object_size_gb}\` in \`${quote.provider}\` \`${quote.location}\``,
|
|
4192
4769
|
`If you accept this quote run the command /mnemospark_cloud upload --quote-id \`${quote.quote_id}\` --wallet-address \`${quote.addr}\` --object-id \`${quote.object_id}\` --object-id-hash \`${quote.object_id_hash}\``
|
|
4193
4770
|
].join("\n");
|
|
4194
4771
|
}
|
|
4195
|
-
function formatStorageLsUserMessage(result, requestedObjectKey) {
|
|
4196
|
-
const objectId = result.object_id ?? result.key;
|
|
4197
|
-
return `${objectId} with ${requestedObjectKey} is ${result.size_bytes} in ${result.bucket}`;
|
|
4198
|
-
}
|
|
4199
4772
|
function createInProcessSubagentOrchestrator() {
|
|
4200
4773
|
const sessions = /* @__PURE__ */ new Map();
|
|
4201
4774
|
const completeSession = async (sessionId, handler) => {
|
|
@@ -4319,10 +4892,12 @@ function createCloudCommand(options = {}) {
|
|
|
4319
4892
|
requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
|
|
4320
4893
|
requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
|
|
4321
4894
|
requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
|
|
4895
|
+
requestPaymentSettleViaProxyFn: options.requestPaymentSettleViaProxyFn ?? requestPaymentSettleViaProxy,
|
|
4322
4896
|
objectLogHomeDir: options.objectLogHomeDir ?? options.backupOptions?.homeDir,
|
|
4323
4897
|
backupOptions: options.backupOptions,
|
|
4324
4898
|
proxyQuoteOptions: options.proxyQuoteOptions,
|
|
4325
4899
|
proxyUploadOptions: options.proxyUploadOptions,
|
|
4900
|
+
proxySettleOptions: options.proxySettleOptions,
|
|
4326
4901
|
proxyUploadConfirmOptions: options.proxyUploadConfirmOptions,
|
|
4327
4902
|
subagentOrchestrator,
|
|
4328
4903
|
proxyStorageOptions: options.proxyStorageOptions
|
|
@@ -4371,7 +4946,23 @@ async function resolveFriendlyNameFromManifest(params, homeDir) {
|
|
|
4371
4946
|
}
|
|
4372
4947
|
async function resolveNameSelectorIfNeeded(datastore, request, selector, homeDir) {
|
|
4373
4948
|
if (!selector) {
|
|
4374
|
-
const
|
|
4949
|
+
const walletAddress = request.wallet_address?.trim();
|
|
4950
|
+
if (!walletAddress) {
|
|
4951
|
+
return { error: "Cannot resolve storage object request." };
|
|
4952
|
+
}
|
|
4953
|
+
const objectKey = request.object_key?.trim();
|
|
4954
|
+
if (!objectKey) {
|
|
4955
|
+
const listRequest = { wallet_address: walletAddress };
|
|
4956
|
+
if (request.location) {
|
|
4957
|
+
listRequest.location = request.location;
|
|
4958
|
+
}
|
|
4959
|
+
return { request: listRequest };
|
|
4960
|
+
}
|
|
4961
|
+
const parsedRequest2 = parseStorageObjectRequest({
|
|
4962
|
+
wallet_address: walletAddress,
|
|
4963
|
+
object_key: objectKey,
|
|
4964
|
+
location: request.location
|
|
4965
|
+
});
|
|
4375
4966
|
if (!parsedRequest2) {
|
|
4376
4967
|
return { error: "Cannot resolve storage object request." };
|
|
4377
4968
|
}
|
|
@@ -4432,6 +5023,21 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector, homeDir
|
|
|
4432
5023
|
degradedWarning
|
|
4433
5024
|
};
|
|
4434
5025
|
}
|
|
5026
|
+
function toStorageObjectRequestOrError(request, missingKeyMessage) {
|
|
5027
|
+
const key = request.object_key?.trim();
|
|
5028
|
+
if (!key) {
|
|
5029
|
+
return { ok: false, error: missingKeyMessage };
|
|
5030
|
+
}
|
|
5031
|
+
const parsed = parseStorageObjectRequest({
|
|
5032
|
+
wallet_address: request.wallet_address,
|
|
5033
|
+
object_key: key,
|
|
5034
|
+
location: request.location
|
|
5035
|
+
});
|
|
5036
|
+
if (!parsed) {
|
|
5037
|
+
return { ok: false, error: "Cannot resolve storage object request." };
|
|
5038
|
+
}
|
|
5039
|
+
return { ok: true, request: parsed };
|
|
5040
|
+
}
|
|
4435
5041
|
async function emitCloudEvent(eventType, details, homeDir) {
|
|
4436
5042
|
await appendJsonlEvent(
|
|
4437
5043
|
"events.jsonl",
|
|
@@ -4486,6 +5092,117 @@ function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
|
|
|
4486
5092
|
const traceId = forcedTraceId?.trim() || randomUUID3();
|
|
4487
5093
|
return { operationId, traceId };
|
|
4488
5094
|
}
|
|
5095
|
+
function parseTransIdFromPaymentSettleBody(bodyText) {
|
|
5096
|
+
const trimmed = bodyText.trim();
|
|
5097
|
+
if (!trimmed.startsWith("{")) {
|
|
5098
|
+
return null;
|
|
5099
|
+
}
|
|
5100
|
+
try {
|
|
5101
|
+
const parsed = JSON.parse(trimmed);
|
|
5102
|
+
const tid = parsed.trans_id;
|
|
5103
|
+
return typeof tid === "string" && tid.trim() ? tid.trim() : null;
|
|
5104
|
+
} catch {
|
|
5105
|
+
return null;
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
async function resolveAmountForPaymentSettle(quoteId, storagePriceFromFlag, datastore, homeDir) {
|
|
5109
|
+
if (storagePriceFromFlag !== void 0 && Number.isFinite(storagePriceFromFlag)) {
|
|
5110
|
+
return storagePriceFromFlag;
|
|
5111
|
+
}
|
|
5112
|
+
const quoteLookup = await datastore.findQuoteById(quoteId);
|
|
5113
|
+
if (quoteLookup && Number.isFinite(quoteLookup.storagePrice)) {
|
|
5114
|
+
return quoteLookup.storagePrice;
|
|
5115
|
+
}
|
|
5116
|
+
const logged = await findLoggedPriceStorageQuote(quoteId, homeDir);
|
|
5117
|
+
if (logged && Number.isFinite(logged.storagePrice)) {
|
|
5118
|
+
return logged.storagePrice;
|
|
5119
|
+
}
|
|
5120
|
+
const payment = await datastore.findPaymentByQuoteId(quoteId);
|
|
5121
|
+
if (payment && Number.isFinite(payment.amount)) {
|
|
5122
|
+
return payment.amount;
|
|
5123
|
+
}
|
|
5124
|
+
return 0;
|
|
5125
|
+
}
|
|
5126
|
+
async function emitPaymentSettleClientObservationBestEffort(params) {
|
|
5127
|
+
try {
|
|
5128
|
+
const {
|
|
5129
|
+
phase,
|
|
5130
|
+
correlation,
|
|
5131
|
+
quoteId,
|
|
5132
|
+
walletAddress,
|
|
5133
|
+
objectId,
|
|
5134
|
+
objectKey,
|
|
5135
|
+
httpStatus,
|
|
5136
|
+
outcomeStatus,
|
|
5137
|
+
homeDir
|
|
5138
|
+
} = params;
|
|
5139
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
5140
|
+
if (phase === "start") {
|
|
5141
|
+
await emitCloudEventBestEffort(
|
|
5142
|
+
"payment-settle.started",
|
|
5143
|
+
{
|
|
5144
|
+
operation_id: correlation.operationId,
|
|
5145
|
+
trace_id: correlation.traceId,
|
|
5146
|
+
quote_id: quoteId,
|
|
5147
|
+
wallet_address: walletAddress,
|
|
5148
|
+
object_id: objectId,
|
|
5149
|
+
object_key: objectKey,
|
|
5150
|
+
status: "running"
|
|
5151
|
+
},
|
|
5152
|
+
homeDir
|
|
5153
|
+
);
|
|
5154
|
+
await appendJsonlEvent(
|
|
5155
|
+
"proxy-events.jsonl",
|
|
5156
|
+
{
|
|
5157
|
+
ts,
|
|
5158
|
+
event_type: "payment.settle",
|
|
5159
|
+
status: "start",
|
|
5160
|
+
trace_id: correlation.traceId,
|
|
5161
|
+
operation_id: correlation.operationId,
|
|
5162
|
+
quote_id: quoteId,
|
|
5163
|
+
wallet_address: walletAddress,
|
|
5164
|
+
object_id: objectId ?? null,
|
|
5165
|
+
object_key: objectKey ?? null,
|
|
5166
|
+
details: { source: "client" }
|
|
5167
|
+
},
|
|
5168
|
+
homeDir
|
|
5169
|
+
);
|
|
5170
|
+
return;
|
|
5171
|
+
}
|
|
5172
|
+
const terminal = outcomeStatus ?? "failed";
|
|
5173
|
+
await emitCloudEventBestEffort(
|
|
5174
|
+
"payment-settle.completed",
|
|
5175
|
+
{
|
|
5176
|
+
operation_id: correlation.operationId,
|
|
5177
|
+
trace_id: correlation.traceId,
|
|
5178
|
+
quote_id: quoteId,
|
|
5179
|
+
wallet_address: walletAddress,
|
|
5180
|
+
object_id: objectId,
|
|
5181
|
+
object_key: objectKey,
|
|
5182
|
+
status: terminal === "succeeded" ? "succeeded" : "failed",
|
|
5183
|
+
http_status: httpStatus
|
|
5184
|
+
},
|
|
5185
|
+
homeDir
|
|
5186
|
+
);
|
|
5187
|
+
await appendJsonlEvent(
|
|
5188
|
+
"proxy-events.jsonl",
|
|
5189
|
+
{
|
|
5190
|
+
ts,
|
|
5191
|
+
event_type: "payment.settle",
|
|
5192
|
+
status: "result",
|
|
5193
|
+
trace_id: correlation.traceId,
|
|
5194
|
+
operation_id: correlation.operationId,
|
|
5195
|
+
quote_id: quoteId,
|
|
5196
|
+
wallet_address: walletAddress,
|
|
5197
|
+
object_id: objectId ?? null,
|
|
5198
|
+
object_key: objectKey ?? null,
|
|
5199
|
+
details: { http_status: httpStatus ?? null, source: "client" }
|
|
5200
|
+
},
|
|
5201
|
+
homeDir
|
|
5202
|
+
);
|
|
5203
|
+
} catch {
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
4489
5206
|
async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
4490
5207
|
const parsed = parseCloudArgs(ctx.args);
|
|
4491
5208
|
const objectLogHomeDir = options.objectLogHomeDir;
|
|
@@ -4501,6 +5218,7 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4501
5218
|
const requestStorageLs = options.requestStorageLsFn;
|
|
4502
5219
|
const requestStorageDownload = options.requestStorageDownloadFn;
|
|
4503
5220
|
const requestStorageDelete = options.requestStorageDeleteFn;
|
|
5221
|
+
const requestPaymentSettleViaProxy2 = options.requestPaymentSettleViaProxyFn;
|
|
4504
5222
|
const subagentOrchestrator = options.subagentOrchestrator;
|
|
4505
5223
|
if (parsed.mode === "help" || parsed.mode === "unknown") {
|
|
4506
5224
|
return {
|
|
@@ -4538,9 +5256,15 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4538
5256
|
isError: true
|
|
4539
5257
|
};
|
|
4540
5258
|
}
|
|
5259
|
+
if (parsed.mode === "payment-settle-invalid") {
|
|
5260
|
+
return {
|
|
5261
|
+
text: `Cannot settle payment: required arguments are ${REQUIRED_PAYMENT_SETTLE}. Optional: --object-id, --object-key, --storage-price.`,
|
|
5262
|
+
isError: true
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
4541
5265
|
if (parsed.mode === "ls-invalid") {
|
|
4542
5266
|
return {
|
|
4543
|
-
text: `Cannot list storage object: required arguments are ${
|
|
5267
|
+
text: `Cannot list storage object: required arguments are ${REQUIRED_LS}.`,
|
|
4544
5268
|
isError: true
|
|
4545
5269
|
};
|
|
4546
5270
|
}
|
|
@@ -4682,6 +5406,142 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4682
5406
|
}
|
|
4683
5407
|
return formatOperationStatus(operation);
|
|
4684
5408
|
}
|
|
5409
|
+
if (parsed.mode === "payment-settle") {
|
|
5410
|
+
const req = parsed.paymentSettleRequest;
|
|
5411
|
+
let walletKey;
|
|
5412
|
+
try {
|
|
5413
|
+
walletKey = await resolveWalletKey(objectLogHomeDir);
|
|
5414
|
+
} catch (err) {
|
|
5415
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5416
|
+
return { text: message.trim() || "Cannot resolve wallet key.", isError: true };
|
|
5417
|
+
}
|
|
5418
|
+
const walletAccount = privateKeyToAccount5(walletKey);
|
|
5419
|
+
if (walletAccount.address.toLowerCase() !== req.wallet_address.toLowerCase()) {
|
|
5420
|
+
return {
|
|
5421
|
+
text: `Cannot settle payment: wallet key address ${walletAccount.address} does not match --wallet-address ${req.wallet_address}.`,
|
|
5422
|
+
isError: true
|
|
5423
|
+
};
|
|
5424
|
+
}
|
|
5425
|
+
const correlation = buildRequestCorrelation();
|
|
5426
|
+
const settleFetch = createPayment(walletKey).fetch;
|
|
5427
|
+
const objectId = req.object_id;
|
|
5428
|
+
const objectKey = req.object_key;
|
|
5429
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5430
|
+
phase: "start",
|
|
5431
|
+
correlation,
|
|
5432
|
+
quoteId: req.quote_id,
|
|
5433
|
+
walletAddress: req.wallet_address,
|
|
5434
|
+
objectId,
|
|
5435
|
+
objectKey,
|
|
5436
|
+
homeDir: objectLogHomeDir
|
|
5437
|
+
});
|
|
5438
|
+
let settleResult;
|
|
5439
|
+
try {
|
|
5440
|
+
settleResult = await requestPaymentSettleViaProxy2(req.quote_id, req.wallet_address, {
|
|
5441
|
+
...options.proxySettleOptions,
|
|
5442
|
+
correlation,
|
|
5443
|
+
fetchImpl: (input, init) => settleFetch(input, init)
|
|
5444
|
+
});
|
|
5445
|
+
} catch (err) {
|
|
5446
|
+
const amountErr = await resolveAmountForPaymentSettle(
|
|
5447
|
+
req.quote_id,
|
|
5448
|
+
req.storage_price,
|
|
5449
|
+
datastore,
|
|
5450
|
+
objectLogHomeDir
|
|
5451
|
+
);
|
|
5452
|
+
await datastore.upsertPayment({
|
|
5453
|
+
quote_id: req.quote_id,
|
|
5454
|
+
wallet_address: req.wallet_address,
|
|
5455
|
+
trans_id: null,
|
|
5456
|
+
amount: amountErr,
|
|
5457
|
+
network: null,
|
|
5458
|
+
status: "settle_failed",
|
|
5459
|
+
settled_at: null
|
|
5460
|
+
});
|
|
5461
|
+
const cronErr = await datastore.findCronByQuoteId(req.quote_id);
|
|
5462
|
+
if (cronErr) {
|
|
5463
|
+
await datastore.upsertCronJob({ ...cronErr, status: "active" });
|
|
5464
|
+
}
|
|
5465
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5466
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5467
|
+
phase: "result",
|
|
5468
|
+
correlation,
|
|
5469
|
+
quoteId: req.quote_id,
|
|
5470
|
+
walletAddress: req.wallet_address,
|
|
5471
|
+
objectId,
|
|
5472
|
+
objectKey,
|
|
5473
|
+
outcomeStatus: "failed",
|
|
5474
|
+
homeDir: objectLogHomeDir
|
|
5475
|
+
});
|
|
5476
|
+
return { text: `Payment settle failed: ${msg}`, isError: true };
|
|
5477
|
+
}
|
|
5478
|
+
const amount = await resolveAmountForPaymentSettle(
|
|
5479
|
+
req.quote_id,
|
|
5480
|
+
req.storage_price,
|
|
5481
|
+
datastore,
|
|
5482
|
+
objectLogHomeDir
|
|
5483
|
+
);
|
|
5484
|
+
const transId = settleResult.status === 200 ? parseTransIdFromPaymentSettleBody(settleResult.bodyText ?? "") : null;
|
|
5485
|
+
if (settleResult.status === 200) {
|
|
5486
|
+
await datastore.upsertPayment({
|
|
5487
|
+
quote_id: req.quote_id,
|
|
5488
|
+
wallet_address: req.wallet_address,
|
|
5489
|
+
trans_id: transId,
|
|
5490
|
+
amount,
|
|
5491
|
+
network: null,
|
|
5492
|
+
status: "settled",
|
|
5493
|
+
settled_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5494
|
+
});
|
|
5495
|
+
const cronRow = await datastore.findCronByQuoteId(req.quote_id);
|
|
5496
|
+
if (cronRow) {
|
|
5497
|
+
await datastore.upsertCronJob({ ...cronRow, status: "active" });
|
|
5498
|
+
}
|
|
5499
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5500
|
+
phase: "result",
|
|
5501
|
+
correlation,
|
|
5502
|
+
quoteId: req.quote_id,
|
|
5503
|
+
walletAddress: req.wallet_address,
|
|
5504
|
+
objectId,
|
|
5505
|
+
objectKey,
|
|
5506
|
+
httpStatus: settleResult.status,
|
|
5507
|
+
outcomeStatus: "succeeded",
|
|
5508
|
+
homeDir: objectLogHomeDir
|
|
5509
|
+
});
|
|
5510
|
+
return {
|
|
5511
|
+
text: transId ? `Payment settled for quote ${req.quote_id} (trans_id: ${transId}).` : `Payment settled for quote ${req.quote_id}.`
|
|
5512
|
+
};
|
|
5513
|
+
}
|
|
5514
|
+
await datastore.upsertPayment({
|
|
5515
|
+
quote_id: req.quote_id,
|
|
5516
|
+
wallet_address: req.wallet_address,
|
|
5517
|
+
trans_id: transId,
|
|
5518
|
+
amount,
|
|
5519
|
+
network: null,
|
|
5520
|
+
status: "settle_failed",
|
|
5521
|
+
settled_at: null
|
|
5522
|
+
});
|
|
5523
|
+
const cronRowFailed = await datastore.findCronByQuoteId(req.quote_id);
|
|
5524
|
+
if (cronRowFailed) {
|
|
5525
|
+
await datastore.upsertCronJob({ ...cronRowFailed, status: "active" });
|
|
5526
|
+
}
|
|
5527
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5528
|
+
phase: "result",
|
|
5529
|
+
correlation,
|
|
5530
|
+
quoteId: req.quote_id,
|
|
5531
|
+
walletAddress: req.wallet_address,
|
|
5532
|
+
objectId,
|
|
5533
|
+
objectKey,
|
|
5534
|
+
httpStatus: settleResult.status,
|
|
5535
|
+
outcomeStatus: "failed",
|
|
5536
|
+
homeDir: objectLogHomeDir
|
|
5537
|
+
});
|
|
5538
|
+
const bodySnippet = settleResult.bodyText?.trim();
|
|
5539
|
+
const detail = bodySnippet && bodySnippet.length > 500 ? `${bodySnippet.slice(0, 500)}\u2026` : bodySnippet;
|
|
5540
|
+
return {
|
|
5541
|
+
text: detail ? `Payment settle failed (HTTP ${settleResult.status}): ${detail}` : `Payment settle failed with HTTP ${settleResult.status}.`,
|
|
5542
|
+
isError: true
|
|
5543
|
+
};
|
|
5544
|
+
}
|
|
4685
5545
|
if ((parsed.mode === "backup" || parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4686
5546
|
const asyncCorrelation = buildRequestCorrelation();
|
|
4687
5547
|
const operationId = asyncCorrelation.operationId;
|
|
@@ -5192,7 +6052,7 @@ operation-id: ${operationId}`,
|
|
|
5192
6052
|
const shouldSettleBeforeUpload = requestStorageUpload !== requestStorageUploadViaProxy;
|
|
5193
6053
|
if (shouldSettleBeforeUpload) {
|
|
5194
6054
|
const paymentFetch = createPayment(walletKey).fetch;
|
|
5195
|
-
const settleResult = await
|
|
6055
|
+
const settleResult = await requestPaymentSettleViaProxy2(
|
|
5196
6056
|
parsed.uploadRequest.quote_id,
|
|
5197
6057
|
parsed.uploadRequest.wallet_address,
|
|
5198
6058
|
{
|
|
@@ -5399,15 +6259,17 @@ operation-id: ${operationId}`,
|
|
|
5399
6259
|
"name_resolution.degraded",
|
|
5400
6260
|
{
|
|
5401
6261
|
wallet_address: resolvedRequest.wallet_address,
|
|
5402
|
-
object_key: resolvedRequest.object_key,
|
|
6262
|
+
object_key: resolvedRequest.object_key ?? null,
|
|
5403
6263
|
warning: resolved.degradedWarning
|
|
5404
6264
|
},
|
|
5405
6265
|
objectLogHomeDir
|
|
5406
6266
|
);
|
|
5407
6267
|
}
|
|
6268
|
+
const objectKeyForLs = resolvedRequest.object_key?.trim();
|
|
6269
|
+
const isBucketList = !objectKeyForLs;
|
|
5408
6270
|
const correlation = buildRequestCorrelation();
|
|
5409
6271
|
const operationId = correlation.operationId;
|
|
5410
|
-
const knownObject = await datastore.findObjectByObjectKey(
|
|
6272
|
+
const knownObject = isBucketList ? null : await datastore.findObjectByObjectKey(objectKeyForLs);
|
|
5411
6273
|
const operationObjectId = knownObject?.object_id ?? null;
|
|
5412
6274
|
await datastore.upsertOperation({
|
|
5413
6275
|
operation_id: operationId,
|
|
@@ -5441,17 +6303,23 @@ operation-id: ${operationId}`,
|
|
|
5441
6303
|
operation_id: operationId,
|
|
5442
6304
|
trace_id: correlation.traceId,
|
|
5443
6305
|
wallet_address: resolvedRequest.wallet_address,
|
|
5444
|
-
object_key: resolvedRequest.object_key,
|
|
5445
|
-
status: "succeeded"
|
|
6306
|
+
object_key: resolvedRequest.object_key ?? null,
|
|
6307
|
+
status: "succeeded",
|
|
6308
|
+
list_mode: isBucketList
|
|
5446
6309
|
},
|
|
5447
6310
|
objectLogHomeDir
|
|
5448
6311
|
);
|
|
5449
|
-
const lsText =
|
|
6312
|
+
const lsText = await buildMnemosparkLsMessage(lsResult, {
|
|
6313
|
+
walletAddress: resolvedRequest.wallet_address,
|
|
6314
|
+
datastore
|
|
6315
|
+
});
|
|
5450
6316
|
return {
|
|
5451
6317
|
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
6318
|
+
|
|
5452
6319
|
${lsText}` : lsText
|
|
5453
6320
|
};
|
|
5454
|
-
} catch {
|
|
6321
|
+
} catch (error) {
|
|
6322
|
+
const lsErrorMessage = extractLsErrorMessage(error) ?? "Cannot list storage object";
|
|
5455
6323
|
await datastore.upsertOperation({
|
|
5456
6324
|
operation_id: operationId,
|
|
5457
6325
|
type: "ls",
|
|
@@ -5459,7 +6327,7 @@ ${lsText}` : lsText
|
|
|
5459
6327
|
quote_id: null,
|
|
5460
6328
|
status: "failed",
|
|
5461
6329
|
error_code: "LS_FAILED",
|
|
5462
|
-
error_message:
|
|
6330
|
+
error_message: lsErrorMessage
|
|
5463
6331
|
});
|
|
5464
6332
|
await emitCloudEventBestEffort(
|
|
5465
6333
|
"ls.completed",
|
|
@@ -5467,13 +6335,14 @@ ${lsText}` : lsText
|
|
|
5467
6335
|
operation_id: operationId,
|
|
5468
6336
|
trace_id: correlation.traceId,
|
|
5469
6337
|
wallet_address: resolvedRequest.wallet_address,
|
|
5470
|
-
object_key: resolvedRequest.object_key,
|
|
5471
|
-
status: "failed"
|
|
6338
|
+
object_key: resolvedRequest.object_key ?? null,
|
|
6339
|
+
status: "failed",
|
|
6340
|
+
list_mode: isBucketList
|
|
5472
6341
|
},
|
|
5473
6342
|
objectLogHomeDir
|
|
5474
6343
|
);
|
|
5475
6344
|
return {
|
|
5476
|
-
text:
|
|
6345
|
+
text: lsErrorMessage,
|
|
5477
6346
|
isError: true
|
|
5478
6347
|
};
|
|
5479
6348
|
}
|
|
@@ -5488,7 +6357,14 @@ ${lsText}` : lsText
|
|
|
5488
6357
|
if (resolved.error || !resolved.request) {
|
|
5489
6358
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
5490
6359
|
}
|
|
5491
|
-
const
|
|
6360
|
+
const narrowed = toStorageObjectRequestOrError(
|
|
6361
|
+
resolved.request,
|
|
6362
|
+
`Cannot download file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`
|
|
6363
|
+
);
|
|
6364
|
+
if (!narrowed.ok) {
|
|
6365
|
+
return { text: narrowed.error, isError: true };
|
|
6366
|
+
}
|
|
6367
|
+
const resolvedRequest = narrowed.request;
|
|
5492
6368
|
if (resolved.degradedWarning) {
|
|
5493
6369
|
await emitCloudEventBestEffort(
|
|
5494
6370
|
"name_resolution.degraded",
|
|
@@ -5586,7 +6462,14 @@ ${downloadText}` : downloadText
|
|
|
5586
6462
|
if (resolved.error || !resolved.request) {
|
|
5587
6463
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
5588
6464
|
}
|
|
5589
|
-
const
|
|
6465
|
+
const narrowedDelete = toStorageObjectRequestOrError(
|
|
6466
|
+
resolved.request,
|
|
6467
|
+
`Cannot delete file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`
|
|
6468
|
+
);
|
|
6469
|
+
if (!narrowedDelete.ok) {
|
|
6470
|
+
return { text: narrowedDelete.error, isError: true };
|
|
6471
|
+
}
|
|
6472
|
+
const resolvedRequest = narrowedDelete.request;
|
|
5590
6473
|
if (resolved.degradedWarning) {
|
|
5591
6474
|
await emitCloudEventBestEffort(
|
|
5592
6475
|
"name_resolution.degraded",
|