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/cli.js
CHANGED
|
@@ -332,6 +332,34 @@ function normalizePaymentRequired(headers) {
|
|
|
332
332
|
function normalizePaymentResponse(headers) {
|
|
333
333
|
return headers.get("PAYMENT-RESPONSE") ?? headers.get("x-payment-response") ?? void 0;
|
|
334
334
|
}
|
|
335
|
+
var BYTE_SI_UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
336
|
+
var BYTE_SI_BASE = 1e3;
|
|
337
|
+
function formatBytesForDisplay(bytes) {
|
|
338
|
+
if (!Number.isInteger(bytes) || bytes < 0 || !Number.isFinite(bytes)) {
|
|
339
|
+
throw new Error("formatBytesForDisplay expects a non-negative integer");
|
|
340
|
+
}
|
|
341
|
+
if (bytes === 0) {
|
|
342
|
+
return "0 B";
|
|
343
|
+
}
|
|
344
|
+
let value = bytes;
|
|
345
|
+
let unitIndex = 0;
|
|
346
|
+
while (value >= BYTE_SI_BASE && unitIndex < BYTE_SI_UNITS.length - 1) {
|
|
347
|
+
value /= BYTE_SI_BASE;
|
|
348
|
+
unitIndex += 1;
|
|
349
|
+
}
|
|
350
|
+
if (unitIndex === 0) {
|
|
351
|
+
return `${bytes} B`;
|
|
352
|
+
}
|
|
353
|
+
const nearestInt = Math.round(value);
|
|
354
|
+
const pickInt = nearestInt > 0 && Math.abs(value - nearestInt) / Math.max(value, 1e-9) <= 0.01;
|
|
355
|
+
let rounded = pickInt ? nearestInt : Math.round(value * 10) / 10;
|
|
356
|
+
if (rounded >= BYTE_SI_BASE && unitIndex < BYTE_SI_UNITS.length - 1) {
|
|
357
|
+
rounded = Math.round(rounded / BYTE_SI_BASE * 10) / 10;
|
|
358
|
+
unitIndex += 1;
|
|
359
|
+
}
|
|
360
|
+
const text = Number.isInteger(rounded) ? String(rounded) : String(rounded).replace(/\.0$/, "");
|
|
361
|
+
return `${text} ${BYTE_SI_UNITS[unitIndex]}`;
|
|
362
|
+
}
|
|
335
363
|
|
|
336
364
|
// src/wallet-signature.ts
|
|
337
365
|
function normalizeWalletSignature(value) {
|
|
@@ -1148,6 +1176,9 @@ function parseStoredAes256Key(raw, errorMessage = "Invalid key file format") {
|
|
|
1148
1176
|
var STORAGE_LS_PROXY_PATH = "/mnemospark/storage/ls";
|
|
1149
1177
|
var STORAGE_DOWNLOAD_PROXY_PATH = "/mnemospark/storage/download";
|
|
1150
1178
|
var STORAGE_DELETE_PROXY_PATH = "/mnemospark/storage/delete";
|
|
1179
|
+
function isStorageLsListResponse(r) {
|
|
1180
|
+
return r.mode === "list";
|
|
1181
|
+
}
|
|
1151
1182
|
var AES_GCM_TAG_BYTES = 16;
|
|
1152
1183
|
function asBooleanOrDefault(value, defaultValue) {
|
|
1153
1184
|
if (typeof value === "boolean") {
|
|
@@ -1235,7 +1266,7 @@ async function decryptDownloadBytes(encryptedBytes, wrappedDekBase64, walletAddr
|
|
|
1235
1266
|
}
|
|
1236
1267
|
return decryptAesGcm(encryptedBytes, dek);
|
|
1237
1268
|
}
|
|
1238
|
-
async function requestJsonViaProxy(proxyPath,
|
|
1269
|
+
async function requestJsonViaProxy(proxyPath, jsonBody, parser, options = {}) {
|
|
1239
1270
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1240
1271
|
const baseUrl = normalizeBaseUrl(
|
|
1241
1272
|
options.proxyBaseUrl ?? `http://127.0.0.1:${PROXY_PORT.toString()}`
|
|
@@ -1248,7 +1279,7 @@ async function requestJsonViaProxy(proxyPath, request, parser, options = {}) {
|
|
|
1248
1279
|
},
|
|
1249
1280
|
options.correlation
|
|
1250
1281
|
),
|
|
1251
|
-
body: JSON.stringify(
|
|
1282
|
+
body: JSON.stringify(jsonBody)
|
|
1252
1283
|
});
|
|
1253
1284
|
const bodyText = await response.text();
|
|
1254
1285
|
if (!response.ok) {
|
|
@@ -1262,7 +1293,7 @@ async function requestJsonViaProxy(proxyPath, request, parser, options = {}) {
|
|
|
1262
1293
|
}
|
|
1263
1294
|
return parser(payload);
|
|
1264
1295
|
}
|
|
1265
|
-
async function forwardStorageToBackend(path, method,
|
|
1296
|
+
async function forwardStorageToBackend(path, method, jsonBody, options = {}) {
|
|
1266
1297
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1267
1298
|
const backendBaseUrl = (options.backendBaseUrl ?? "").trim();
|
|
1268
1299
|
const walletSignature = normalizeWalletSignature(options.walletSignature);
|
|
@@ -1281,7 +1312,7 @@ async function forwardStorageToBackend(path, method, request, options = {}) {
|
|
|
1281
1312
|
"Content-Type": "application/json",
|
|
1282
1313
|
"X-Wallet-Signature": walletSignature
|
|
1283
1314
|
},
|
|
1284
|
-
body: JSON.stringify(
|
|
1315
|
+
body: JSON.stringify(jsonBody)
|
|
1285
1316
|
});
|
|
1286
1317
|
const bodyBuffer = Buffer.from(await response.arrayBuffer());
|
|
1287
1318
|
return {
|
|
@@ -1311,11 +1342,97 @@ function parseStorageObjectRequest(payload) {
|
|
|
1311
1342
|
location
|
|
1312
1343
|
};
|
|
1313
1344
|
}
|
|
1345
|
+
function jsonBodyForObjectRequest(request) {
|
|
1346
|
+
const o = {
|
|
1347
|
+
wallet_address: request.wallet_address,
|
|
1348
|
+
object_key: request.object_key
|
|
1349
|
+
};
|
|
1350
|
+
if (request.location) {
|
|
1351
|
+
o.location = request.location;
|
|
1352
|
+
}
|
|
1353
|
+
return o;
|
|
1354
|
+
}
|
|
1355
|
+
function jsonBodyForLsRequest(request) {
|
|
1356
|
+
const o = { wallet_address: request.wallet_address };
|
|
1357
|
+
if (request.object_key) {
|
|
1358
|
+
o.object_key = request.object_key;
|
|
1359
|
+
}
|
|
1360
|
+
if (request.location) {
|
|
1361
|
+
o.location = request.location;
|
|
1362
|
+
}
|
|
1363
|
+
if (request.continuation_token) {
|
|
1364
|
+
o.continuation_token = request.continuation_token;
|
|
1365
|
+
}
|
|
1366
|
+
if (typeof request.max_keys === "number") {
|
|
1367
|
+
o.max_keys = request.max_keys;
|
|
1368
|
+
}
|
|
1369
|
+
if (request.prefix) {
|
|
1370
|
+
o.prefix = request.prefix;
|
|
1371
|
+
}
|
|
1372
|
+
return o;
|
|
1373
|
+
}
|
|
1374
|
+
function parseStorageLsRequestPayload(payload) {
|
|
1375
|
+
const record = asRecord(payload);
|
|
1376
|
+
if (!record) {
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
const walletAddress = asNonEmptyString(record.wallet_address);
|
|
1380
|
+
if (!walletAddress) {
|
|
1381
|
+
return null;
|
|
1382
|
+
}
|
|
1383
|
+
const objectKey = asNonEmptyString(record.object_key) ?? void 0;
|
|
1384
|
+
const location = asNonEmptyString(record.location) ?? void 0;
|
|
1385
|
+
const continuation_token = asNonEmptyString(record.continuation_token) ?? void 0;
|
|
1386
|
+
const maxRaw = asNumber(record.max_keys);
|
|
1387
|
+
const max_keys = maxRaw !== null && Number.isInteger(maxRaw) && maxRaw >= 1 ? maxRaw : void 0;
|
|
1388
|
+
const prefix = asNonEmptyString(record.prefix) ?? void 0;
|
|
1389
|
+
return {
|
|
1390
|
+
wallet_address: walletAddress,
|
|
1391
|
+
...objectKey ? { object_key: objectKey } : {},
|
|
1392
|
+
...location ? { location } : {},
|
|
1393
|
+
...continuation_token ? { continuation_token } : {},
|
|
1394
|
+
...typeof max_keys === "number" ? { max_keys } : {},
|
|
1395
|
+
...prefix ? { prefix } : {}
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1314
1398
|
function parseStorageLsResponse(payload) {
|
|
1315
1399
|
const record = asRecord(payload);
|
|
1316
1400
|
if (!record) {
|
|
1317
1401
|
throw new Error("Invalid ls response payload");
|
|
1318
1402
|
}
|
|
1403
|
+
if (record.list_mode === true) {
|
|
1404
|
+
const bucket2 = asNonEmptyString(record.bucket) ?? asNonEmptyString(record.bucket_name);
|
|
1405
|
+
const rawObjects = record.objects;
|
|
1406
|
+
if (!bucket2 || !Array.isArray(rawObjects)) {
|
|
1407
|
+
throw new Error("ls list response is missing required fields");
|
|
1408
|
+
}
|
|
1409
|
+
const objects = [];
|
|
1410
|
+
for (const item of rawObjects) {
|
|
1411
|
+
const row = asRecord(item);
|
|
1412
|
+
if (!row) {
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
const key2 = asNonEmptyString(row.key);
|
|
1416
|
+
const sizeBytes2 = asNumber(row.size_bytes);
|
|
1417
|
+
if (!key2 || sizeBytes2 === null || !Number.isInteger(sizeBytes2) || sizeBytes2 < 0) {
|
|
1418
|
+
continue;
|
|
1419
|
+
}
|
|
1420
|
+
const last_modified = asNonEmptyString(row.last_modified) ?? void 0;
|
|
1421
|
+
objects.push({ key: key2, size_bytes: sizeBytes2, last_modified });
|
|
1422
|
+
}
|
|
1423
|
+
const is_truncated = asBooleanOrDefault(record.is_truncated, false);
|
|
1424
|
+
const nextRaw = record.next_continuation_token;
|
|
1425
|
+
const next_continuation_token = nextRaw === void 0 || nextRaw === null ? null : String(nextRaw);
|
|
1426
|
+
return {
|
|
1427
|
+
mode: "list",
|
|
1428
|
+
success: asBooleanOrDefault(record.success, true),
|
|
1429
|
+
list_mode: true,
|
|
1430
|
+
bucket: bucket2,
|
|
1431
|
+
objects,
|
|
1432
|
+
is_truncated,
|
|
1433
|
+
next_continuation_token
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1319
1436
|
const key = asNonEmptyString(record.key) ?? asNonEmptyString(record.object_key);
|
|
1320
1437
|
const sizeBytes = asNumber(record.size_bytes);
|
|
1321
1438
|
const bucket = asNonEmptyString(record.bucket) ?? asNonEmptyString(record.bucket_name);
|
|
@@ -1323,7 +1440,11 @@ function parseStorageLsResponse(payload) {
|
|
|
1323
1440
|
if (!key || sizeBytes === null || !bucket) {
|
|
1324
1441
|
throw new Error("ls response is missing required fields");
|
|
1325
1442
|
}
|
|
1443
|
+
if (!Number.isInteger(sizeBytes) || sizeBytes < 0) {
|
|
1444
|
+
throw new Error("ls response has invalid size_bytes; expected non-negative integer");
|
|
1445
|
+
}
|
|
1326
1446
|
return {
|
|
1447
|
+
mode: "stat",
|
|
1327
1448
|
success: asBooleanOrDefault(record.success, true),
|
|
1328
1449
|
key,
|
|
1329
1450
|
size_bytes: sizeBytes,
|
|
@@ -1366,12 +1487,17 @@ function parseStorageDownloadProxyResponse(payload) {
|
|
|
1366
1487
|
};
|
|
1367
1488
|
}
|
|
1368
1489
|
async function requestStorageLsViaProxy(request, options = {}) {
|
|
1369
|
-
return requestJsonViaProxy(
|
|
1490
|
+
return requestJsonViaProxy(
|
|
1491
|
+
STORAGE_LS_PROXY_PATH,
|
|
1492
|
+
jsonBodyForLsRequest(request),
|
|
1493
|
+
parseStorageLsResponse,
|
|
1494
|
+
options
|
|
1495
|
+
);
|
|
1370
1496
|
}
|
|
1371
1497
|
async function requestStorageDownloadViaProxy(request, options = {}) {
|
|
1372
1498
|
return requestJsonViaProxy(
|
|
1373
1499
|
STORAGE_DOWNLOAD_PROXY_PATH,
|
|
1374
|
-
request,
|
|
1500
|
+
jsonBodyForObjectRequest(request),
|
|
1375
1501
|
parseStorageDownloadProxyResponse,
|
|
1376
1502
|
options
|
|
1377
1503
|
);
|
|
@@ -1379,19 +1505,29 @@ async function requestStorageDownloadViaProxy(request, options = {}) {
|
|
|
1379
1505
|
async function requestStorageDeleteViaProxy(request, options = {}) {
|
|
1380
1506
|
return requestJsonViaProxy(
|
|
1381
1507
|
STORAGE_DELETE_PROXY_PATH,
|
|
1382
|
-
request,
|
|
1508
|
+
jsonBodyForObjectRequest(request),
|
|
1383
1509
|
parseStorageDeleteResponse,
|
|
1384
1510
|
options
|
|
1385
1511
|
);
|
|
1386
1512
|
}
|
|
1387
1513
|
async function forwardStorageLsToBackend(request, options = {}) {
|
|
1388
|
-
return forwardStorageToBackend("/storage/ls", "POST", request, options);
|
|
1514
|
+
return forwardStorageToBackend("/storage/ls", "POST", jsonBodyForLsRequest(request), options);
|
|
1389
1515
|
}
|
|
1390
1516
|
async function forwardStorageDownloadToBackend(request, options = {}) {
|
|
1391
|
-
return forwardStorageToBackend(
|
|
1517
|
+
return forwardStorageToBackend(
|
|
1518
|
+
"/storage/download",
|
|
1519
|
+
"POST",
|
|
1520
|
+
jsonBodyForObjectRequest(request),
|
|
1521
|
+
options
|
|
1522
|
+
);
|
|
1392
1523
|
}
|
|
1393
1524
|
async function forwardStorageDeleteToBackend(request, options = {}) {
|
|
1394
|
-
return forwardStorageToBackend(
|
|
1525
|
+
return forwardStorageToBackend(
|
|
1526
|
+
"/storage/delete",
|
|
1527
|
+
"POST",
|
|
1528
|
+
jsonBodyForObjectRequest(request),
|
|
1529
|
+
options
|
|
1530
|
+
);
|
|
1395
1531
|
}
|
|
1396
1532
|
async function downloadStorageToDisk(request, backendResponse, options = {}) {
|
|
1397
1533
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -2227,13 +2363,13 @@ async function startProxy(options) {
|
|
|
2227
2363
|
});
|
|
2228
2364
|
return;
|
|
2229
2365
|
}
|
|
2230
|
-
const requestPayload =
|
|
2366
|
+
const requestPayload = parseStorageLsRequestPayload(payload);
|
|
2231
2367
|
if (!requestPayload) {
|
|
2232
2368
|
logProxyEvent("warn", "proxy_ls_missing_fields");
|
|
2233
2369
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
2234
2370
|
sendJson(res, 400, {
|
|
2235
2371
|
error: "Bad request",
|
|
2236
|
-
message: "Missing required
|
|
2372
|
+
message: "Missing required field: wallet_address"
|
|
2237
2373
|
});
|
|
2238
2374
|
return;
|
|
2239
2375
|
}
|
|
@@ -2749,6 +2885,259 @@ import { homedir as homedir6 } from "os";
|
|
|
2749
2885
|
import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
|
|
2750
2886
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
2751
2887
|
|
|
2888
|
+
// src/cloud-ls-format.ts
|
|
2889
|
+
import { CronExpressionParser } from "cron-parser";
|
|
2890
|
+
var LS_NAME_DISPLAY_MAX = 72;
|
|
2891
|
+
var LS_PAY_DISPLAY_MAX = 28;
|
|
2892
|
+
var LS_CRON_ID_MAX = 14;
|
|
2893
|
+
var LS_TIME_FIELD_WIDTH = 12;
|
|
2894
|
+
var MONTHS_SHORT = [
|
|
2895
|
+
"Jan",
|
|
2896
|
+
"Feb",
|
|
2897
|
+
"Mar",
|
|
2898
|
+
"Apr",
|
|
2899
|
+
"May",
|
|
2900
|
+
"Jun",
|
|
2901
|
+
"Jul",
|
|
2902
|
+
"Aug",
|
|
2903
|
+
"Sep",
|
|
2904
|
+
"Oct",
|
|
2905
|
+
"Nov",
|
|
2906
|
+
"Dec"
|
|
2907
|
+
];
|
|
2908
|
+
function formatLsTimeFieldUtc(iso, now) {
|
|
2909
|
+
const placeholder = " - ".slice(0, LS_TIME_FIELD_WIDTH);
|
|
2910
|
+
if (!iso) {
|
|
2911
|
+
return placeholder.padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2912
|
+
}
|
|
2913
|
+
const d = new Date(iso);
|
|
2914
|
+
if (Number.isNaN(d.getTime())) {
|
|
2915
|
+
return placeholder.padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2916
|
+
}
|
|
2917
|
+
const mon = MONTHS_SHORT[d.getUTCMonth()] ?? "???";
|
|
2918
|
+
const day = String(d.getUTCDate()).padStart(2, " ");
|
|
2919
|
+
const y = d.getUTCFullYear();
|
|
2920
|
+
const nowY = now.getUTCFullYear();
|
|
2921
|
+
let core;
|
|
2922
|
+
if (y === nowY) {
|
|
2923
|
+
const hh = String(d.getUTCHours()).padStart(2, "0");
|
|
2924
|
+
const mm = String(d.getUTCMinutes()).padStart(2, "0");
|
|
2925
|
+
core = `${mon} ${day} ${hh}:${mm}`;
|
|
2926
|
+
} else {
|
|
2927
|
+
core = `${mon} ${day} ${y}`;
|
|
2928
|
+
}
|
|
2929
|
+
return core.padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2930
|
+
}
|
|
2931
|
+
function truncateEnd(value, max) {
|
|
2932
|
+
if (value.length <= max) {
|
|
2933
|
+
return value;
|
|
2934
|
+
}
|
|
2935
|
+
if (max <= 1) {
|
|
2936
|
+
return "\u2026";
|
|
2937
|
+
}
|
|
2938
|
+
return `${value.slice(0, max - 1)}\u2026`;
|
|
2939
|
+
}
|
|
2940
|
+
function truncateMiddle(value, max, suffixMin) {
|
|
2941
|
+
if (value.length <= max) {
|
|
2942
|
+
return value;
|
|
2943
|
+
}
|
|
2944
|
+
if (max < suffixMin + 5) {
|
|
2945
|
+
return truncateEnd(value, max);
|
|
2946
|
+
}
|
|
2947
|
+
const suffixLen = Math.min(suffixMin, max - 5);
|
|
2948
|
+
const prefixLen = max - 3 - suffixLen;
|
|
2949
|
+
return `${value.slice(0, prefixLen)} \u2026 ${value.slice(-suffixLen)}`;
|
|
2950
|
+
}
|
|
2951
|
+
function formatCronIdCell(cronId, width) {
|
|
2952
|
+
if (!cronId) {
|
|
2953
|
+
return " - ".slice(0, width).padStart(width, " ");
|
|
2954
|
+
}
|
|
2955
|
+
const t = truncateEnd(cronId, width);
|
|
2956
|
+
return t.padStart(width, " ");
|
|
2957
|
+
}
|
|
2958
|
+
function formatPaymentCell(amount, network, maxWidth) {
|
|
2959
|
+
if (amount === null) {
|
|
2960
|
+
return " - ".slice(0, maxWidth).padStart(maxWidth, " ");
|
|
2961
|
+
}
|
|
2962
|
+
let s = amount.toFixed(6).replace(/\.?0+$/, "");
|
|
2963
|
+
if (network && network.trim()) {
|
|
2964
|
+
s = `${s} (${network.trim()})`;
|
|
2965
|
+
}
|
|
2966
|
+
return truncateEnd(s, maxWidth).padStart(maxWidth, " ");
|
|
2967
|
+
}
|
|
2968
|
+
function formatNextCronUtc(schedule, cronStatus, now) {
|
|
2969
|
+
const blank = " - ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2970
|
+
if (cronStatus !== "active") {
|
|
2971
|
+
return blank;
|
|
2972
|
+
}
|
|
2973
|
+
try {
|
|
2974
|
+
const expr = CronExpressionParser.parse(schedule, { tz: "UTC", currentDate: now });
|
|
2975
|
+
const next = expr.next().toDate();
|
|
2976
|
+
return formatLsTimeFieldUtc(next.toISOString(), now);
|
|
2977
|
+
} catch {
|
|
2978
|
+
return "?".padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
async function prepareRows(objects, walletAddress, datastore, now) {
|
|
2982
|
+
const sorted = [...objects].sort((a, b) => {
|
|
2983
|
+
const ta = a.last_modified ? Date.parse(a.last_modified) : Number.NaN;
|
|
2984
|
+
const tb = b.last_modified ? Date.parse(b.last_modified) : Number.NaN;
|
|
2985
|
+
const aOk = Number.isFinite(ta);
|
|
2986
|
+
const bOk = Number.isFinite(tb);
|
|
2987
|
+
if (aOk && bOk && tb !== ta) {
|
|
2988
|
+
return tb - ta;
|
|
2989
|
+
}
|
|
2990
|
+
if (aOk && !bOk) {
|
|
2991
|
+
return -1;
|
|
2992
|
+
}
|
|
2993
|
+
if (!aOk && bOk) {
|
|
2994
|
+
return 1;
|
|
2995
|
+
}
|
|
2996
|
+
return a.key.localeCompare(b.key);
|
|
2997
|
+
});
|
|
2998
|
+
const rows = [];
|
|
2999
|
+
for (const obj of sorted) {
|
|
3000
|
+
const friendly = await datastore.findLatestFriendlyNameForObjectKey(walletAddress, obj.key);
|
|
3001
|
+
const cp = await datastore.findCronAndPaymentForObjectKey(walletAddress, obj.key);
|
|
3002
|
+
const sizeStr = formatBytesForDisplay(obj.size_bytes);
|
|
3003
|
+
const s3time = formatLsTimeFieldUtc(obj.last_modified, now);
|
|
3004
|
+
let cronIdDisp = null;
|
|
3005
|
+
let nextRun = " - ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
3006
|
+
let payCell = "";
|
|
3007
|
+
if (cp) {
|
|
3008
|
+
cronIdDisp = cp.cronId;
|
|
3009
|
+
nextRun = formatNextCronUtc(cp.schedule, cp.cronStatus, now);
|
|
3010
|
+
payCell = formatPaymentCell(cp.amount, cp.network, LS_PAY_DISPLAY_MAX);
|
|
3011
|
+
} else {
|
|
3012
|
+
payCell = formatPaymentCell(null, null, LS_PAY_DISPLAY_MAX);
|
|
3013
|
+
}
|
|
3014
|
+
const nameRaw = friendly ? `${friendly} (${obj.key})` : obj.key;
|
|
3015
|
+
rows.push({
|
|
3016
|
+
perm: "----------",
|
|
3017
|
+
ln: " 1",
|
|
3018
|
+
user: "- ",
|
|
3019
|
+
grp: "- ",
|
|
3020
|
+
sizeStr,
|
|
3021
|
+
s3time,
|
|
3022
|
+
cronIdRaw: cronIdDisp,
|
|
3023
|
+
nextRun,
|
|
3024
|
+
payRaw: payCell,
|
|
3025
|
+
nameRaw: truncateMiddle(nameRaw, LS_NAME_DISPLAY_MAX, 8)
|
|
3026
|
+
});
|
|
3027
|
+
}
|
|
3028
|
+
return rows;
|
|
3029
|
+
}
|
|
3030
|
+
function columnWidths(rows) {
|
|
3031
|
+
let sizeW = 4;
|
|
3032
|
+
let cronW = 4;
|
|
3033
|
+
let payW = 3;
|
|
3034
|
+
for (const r of rows) {
|
|
3035
|
+
sizeW = Math.max(sizeW, r.sizeStr.length);
|
|
3036
|
+
const cid = r.cronIdRaw ? truncateEnd(r.cronIdRaw, LS_CRON_ID_MAX) : "";
|
|
3037
|
+
cronW = Math.max(cronW, cid.length || 1);
|
|
3038
|
+
payW = Math.max(payW, r.payRaw.length);
|
|
3039
|
+
}
|
|
3040
|
+
cronW = Math.min(Math.max(cronW, 4), LS_CRON_ID_MAX);
|
|
3041
|
+
payW = Math.min(Math.max(payW, 3), LS_PAY_DISPLAY_MAX);
|
|
3042
|
+
return { sizeW, cronW, payW };
|
|
3043
|
+
}
|
|
3044
|
+
function renderRow(r, w) {
|
|
3045
|
+
const cronPadded = formatCronIdCell(r.cronIdRaw, w.cronW);
|
|
3046
|
+
const sizePadded = r.sizeStr.padStart(w.sizeW, " ");
|
|
3047
|
+
const payPadded = r.payRaw.padStart(w.payW, " ");
|
|
3048
|
+
return [
|
|
3049
|
+
r.perm,
|
|
3050
|
+
r.ln,
|
|
3051
|
+
r.user,
|
|
3052
|
+
r.grp,
|
|
3053
|
+
sizePadded,
|
|
3054
|
+
r.s3time,
|
|
3055
|
+
cronPadded,
|
|
3056
|
+
r.nextRun,
|
|
3057
|
+
payPadded,
|
|
3058
|
+
r.nameRaw
|
|
3059
|
+
].join(" ");
|
|
3060
|
+
}
|
|
3061
|
+
function renderHeader(w) {
|
|
3062
|
+
return [
|
|
3063
|
+
"PERM ",
|
|
3064
|
+
"LN",
|
|
3065
|
+
"USER ",
|
|
3066
|
+
"GRP ",
|
|
3067
|
+
"SIZE".padStart(w.sizeW, " "),
|
|
3068
|
+
"S3_TIME ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " "),
|
|
3069
|
+
"CRON".padStart(w.cronW, " "),
|
|
3070
|
+
"NEXT ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " "),
|
|
3071
|
+
"PAY".padStart(w.payW, " "),
|
|
3072
|
+
"NAME"
|
|
3073
|
+
].join(" ");
|
|
3074
|
+
}
|
|
3075
|
+
async function buildMnemosparkLsMessage(result, ctx) {
|
|
3076
|
+
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
3077
|
+
if (isStorageLsListResponse(result)) {
|
|
3078
|
+
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.";
|
|
3079
|
+
const legend2 = "Legend: S3_TIME and NEXT are UTC. NEXT is the next cron fire from the stored expression.";
|
|
3080
|
+
const bucketLine = `bucket: ${result.bucket}`;
|
|
3081
|
+
const sortLine = "sorted by: S3 last_modified descending (missing dates last), then key ascending.";
|
|
3082
|
+
if (result.objects.length === 0) {
|
|
3083
|
+
const lines = [disclaimer2, "", bucketLine, "", "No objects in this bucket."];
|
|
3084
|
+
return lines.join("\n");
|
|
3085
|
+
}
|
|
3086
|
+
const rows = await prepareRows(result.objects, ctx.walletAddress, ctx.datastore, now);
|
|
3087
|
+
const w2 = columnWidths(rows);
|
|
3088
|
+
const header2 = renderHeader(w2);
|
|
3089
|
+
const bodyLines = rows.map((r) => renderRow(r, w2));
|
|
3090
|
+
const totalLine = `total ${String(result.objects.length)}`;
|
|
3091
|
+
const truncLine = result.is_truncated ? "List truncated; more objects in bucket." : null;
|
|
3092
|
+
const prose2 = [disclaimer2, legend2, bucketLine, sortLine, totalLine, truncLine].filter((x) => Boolean(x)).join("\n");
|
|
3093
|
+
const fence2 = ["```", [header2, ...bodyLines].join("\n"), "```"].join("\n");
|
|
3094
|
+
return `${prose2}
|
|
3095
|
+
|
|
3096
|
+
${fence2}`;
|
|
3097
|
+
}
|
|
3098
|
+
const friendly = await ctx.datastore.findLatestFriendlyNameForObjectKey(
|
|
3099
|
+
ctx.walletAddress,
|
|
3100
|
+
result.key
|
|
3101
|
+
);
|
|
3102
|
+
const cp = await ctx.datastore.findCronAndPaymentForObjectKey(ctx.walletAddress, result.key);
|
|
3103
|
+
const sizeStr = formatBytesForDisplay(result.size_bytes);
|
|
3104
|
+
const s3time = formatLsTimeFieldUtc(void 0, now);
|
|
3105
|
+
let payCell = formatPaymentCell(null, null, LS_PAY_DISPLAY_MAX);
|
|
3106
|
+
let cronIdDisp = null;
|
|
3107
|
+
let nextRun = " - ".slice(0, LS_TIME_FIELD_WIDTH).padEnd(LS_TIME_FIELD_WIDTH, " ");
|
|
3108
|
+
if (cp) {
|
|
3109
|
+
cronIdDisp = cp.cronId;
|
|
3110
|
+
nextRun = formatNextCronUtc(cp.schedule, cp.cronStatus, now);
|
|
3111
|
+
payCell = formatPaymentCell(cp.amount, cp.network, LS_PAY_DISPLAY_MAX);
|
|
3112
|
+
}
|
|
3113
|
+
const nameShown = truncateMiddle(
|
|
3114
|
+
friendly ? `${friendly} (${result.key})` : result.key,
|
|
3115
|
+
LS_NAME_DISPLAY_MAX,
|
|
3116
|
+
8
|
|
3117
|
+
);
|
|
3118
|
+
const prep = {
|
|
3119
|
+
perm: "----------",
|
|
3120
|
+
ln: " 1",
|
|
3121
|
+
user: "- ",
|
|
3122
|
+
grp: "- ",
|
|
3123
|
+
sizeStr,
|
|
3124
|
+
s3time,
|
|
3125
|
+
cronIdRaw: cronIdDisp,
|
|
3126
|
+
nextRun,
|
|
3127
|
+
payRaw: payCell,
|
|
3128
|
+
nameRaw: nameShown
|
|
3129
|
+
};
|
|
3130
|
+
const w = columnWidths([prep]);
|
|
3131
|
+
const header = renderHeader(w);
|
|
3132
|
+
const line = renderRow(prep, w);
|
|
3133
|
+
const disclaimer = "Names, cron, and payment columns come from this machine's SQLite catalog when available; `-` means unknown locally.";
|
|
3134
|
+
const legend = "Legend: S3_TIME and NEXT are UTC.";
|
|
3135
|
+
const prose = [disclaimer, legend, `bucket: ${result.bucket}`, ""].join("\n");
|
|
3136
|
+
const fence = ["```", [header, line].join("\n"), "```"].join("\n");
|
|
3137
|
+
return `${prose}
|
|
3138
|
+
${fence}`;
|
|
3139
|
+
}
|
|
3140
|
+
|
|
2752
3141
|
// src/cloud-datastore.ts
|
|
2753
3142
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2754
3143
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
@@ -2784,6 +3173,7 @@ async function createCloudDatastore(homeDir) {
|
|
|
2784
3173
|
const nextDb = new DatabaseSyncCtor(dbPath);
|
|
2785
3174
|
nextDb.exec("PRAGMA journal_mode=WAL;");
|
|
2786
3175
|
nextDb.exec("PRAGMA foreign_keys=ON;");
|
|
3176
|
+
nextDb.exec("PRAGMA busy_timeout=5000;");
|
|
2787
3177
|
nextDb.exec(`
|
|
2788
3178
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
2789
3179
|
version INTEGER PRIMARY KEY,
|
|
@@ -2832,6 +3222,7 @@ async function createCloudDatastore(homeDir) {
|
|
|
2832
3222
|
updated_at TEXT NOT NULL
|
|
2833
3223
|
);
|
|
2834
3224
|
CREATE INDEX IF NOT EXISTS idx_cron_jobs_object_key ON cron_jobs(object_key);
|
|
3225
|
+
CREATE INDEX IF NOT EXISTS idx_cron_jobs_quote_id ON cron_jobs(quote_id);
|
|
2835
3226
|
|
|
2836
3227
|
CREATE TABLE IF NOT EXISTS operations (
|
|
2837
3228
|
operation_id TEXT PRIMARY KEY,
|
|
@@ -3032,6 +3423,38 @@ async function createCloudDatastore(homeDir) {
|
|
|
3032
3423
|
if (!row) return null;
|
|
3033
3424
|
return { cronId: row.cron_id, objectId: row.object_id };
|
|
3034
3425
|
}, null),
|
|
3426
|
+
findCronByQuoteId: async (quoteId) => safe(() => {
|
|
3427
|
+
const row = db.prepare(
|
|
3428
|
+
`SELECT cron_id, object_id, object_key, quote_id, schedule, command, status
|
|
3429
|
+
FROM cron_jobs WHERE quote_id = ? ORDER BY updated_at DESC LIMIT 1`
|
|
3430
|
+
).get(quoteId);
|
|
3431
|
+
if (!row) return null;
|
|
3432
|
+
return {
|
|
3433
|
+
cron_id: row.cron_id,
|
|
3434
|
+
object_id: row.object_id,
|
|
3435
|
+
object_key: row.object_key,
|
|
3436
|
+
quote_id: row.quote_id,
|
|
3437
|
+
schedule: row.schedule,
|
|
3438
|
+
command: row.command,
|
|
3439
|
+
status: row.status
|
|
3440
|
+
};
|
|
3441
|
+
}, null),
|
|
3442
|
+
findPaymentByQuoteId: async (quoteId) => safe(() => {
|
|
3443
|
+
const row = db.prepare(
|
|
3444
|
+
`SELECT quote_id, wallet_address, trans_id, amount, network, status, settled_at
|
|
3445
|
+
FROM payments WHERE quote_id = ? LIMIT 1`
|
|
3446
|
+
).get(quoteId);
|
|
3447
|
+
if (!row) return null;
|
|
3448
|
+
return {
|
|
3449
|
+
quote_id: row.quote_id,
|
|
3450
|
+
wallet_address: row.wallet_address,
|
|
3451
|
+
trans_id: row.trans_id,
|
|
3452
|
+
amount: row.amount,
|
|
3453
|
+
network: row.network,
|
|
3454
|
+
status: row.status,
|
|
3455
|
+
settled_at: row.settled_at
|
|
3456
|
+
};
|
|
3457
|
+
}, null),
|
|
3035
3458
|
upsertOperation: async (row) => {
|
|
3036
3459
|
await safe(() => {
|
|
3037
3460
|
const ts = nowIso();
|
|
@@ -3158,7 +3581,63 @@ async function createCloudDatastore(homeDir) {
|
|
|
3158
3581
|
WHERE wallet_address = ? AND friendly_name = ? AND is_active = 1`
|
|
3159
3582
|
).get(normalizedWalletAddress, friendlyName);
|
|
3160
3583
|
return Number(row?.cnt ?? 0);
|
|
3161
|
-
}, 0)
|
|
3584
|
+
}, 0),
|
|
3585
|
+
findLatestFriendlyNameForObjectKey: async (walletAddress, objectKey) => safe(() => {
|
|
3586
|
+
const w = normalizeWalletAddress(walletAddress);
|
|
3587
|
+
const byKey = db.prepare(
|
|
3588
|
+
`SELECT friendly_name
|
|
3589
|
+
FROM friendly_names
|
|
3590
|
+
WHERE wallet_address = ? AND object_key = ? AND is_active = 1
|
|
3591
|
+
ORDER BY created_at DESC
|
|
3592
|
+
LIMIT 1`
|
|
3593
|
+
).get(w, objectKey);
|
|
3594
|
+
if (byKey) {
|
|
3595
|
+
return byKey.friendly_name;
|
|
3596
|
+
}
|
|
3597
|
+
const obj = db.prepare(
|
|
3598
|
+
`SELECT object_id FROM objects WHERE wallet_address = ? AND object_key = ? LIMIT 1`
|
|
3599
|
+
).get(w, objectKey);
|
|
3600
|
+
if (!obj) {
|
|
3601
|
+
return null;
|
|
3602
|
+
}
|
|
3603
|
+
const byObj = db.prepare(
|
|
3604
|
+
`SELECT friendly_name
|
|
3605
|
+
FROM friendly_names
|
|
3606
|
+
WHERE wallet_address = ? AND object_id = ? AND is_active = 1
|
|
3607
|
+
ORDER BY created_at DESC
|
|
3608
|
+
LIMIT 1`
|
|
3609
|
+
).get(w, obj.object_id);
|
|
3610
|
+
return byObj?.friendly_name ?? null;
|
|
3611
|
+
}, null),
|
|
3612
|
+
findCronAndPaymentForObjectKey: async (walletAddress, objectKey) => safe(() => {
|
|
3613
|
+
const w = normalizeWalletAddress(walletAddress);
|
|
3614
|
+
const cron = db.prepare(
|
|
3615
|
+
`SELECT c.cron_id, c.quote_id, c.schedule, c.status
|
|
3616
|
+
FROM cron_jobs c
|
|
3617
|
+
INNER JOIN objects o ON o.object_id = c.object_id
|
|
3618
|
+
WHERE c.object_key = ? AND o.wallet_address = ?
|
|
3619
|
+
ORDER BY c.updated_at DESC
|
|
3620
|
+
LIMIT 1`
|
|
3621
|
+
).get(objectKey, w);
|
|
3622
|
+
if (!cron) {
|
|
3623
|
+
return null;
|
|
3624
|
+
}
|
|
3625
|
+
const pay = db.prepare(
|
|
3626
|
+
`SELECT amount, network, status
|
|
3627
|
+
FROM payments
|
|
3628
|
+
WHERE quote_id = ? AND wallet_address = ?
|
|
3629
|
+
LIMIT 1`
|
|
3630
|
+
).get(cron.quote_id, w);
|
|
3631
|
+
return {
|
|
3632
|
+
cronId: cron.cron_id,
|
|
3633
|
+
schedule: cron.schedule,
|
|
3634
|
+
quoteId: cron.quote_id,
|
|
3635
|
+
cronStatus: cron.status,
|
|
3636
|
+
amount: pay?.amount ?? null,
|
|
3637
|
+
network: pay?.network ?? null,
|
|
3638
|
+
paymentStatus: pay?.status ?? null
|
|
3639
|
+
};
|
|
3640
|
+
}, null)
|
|
3162
3641
|
};
|
|
3163
3642
|
}
|
|
3164
3643
|
|
|
@@ -3178,7 +3657,16 @@ var CRON_LOG_ROW_PREFIX = "cron";
|
|
|
3178
3657
|
var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
|
|
3179
3658
|
var REQUIRED_PRICE_STORAGE = "--wallet-address, --object-id, --object-id-hash, --gb, --provider, --region";
|
|
3180
3659
|
var REQUIRED_UPLOAD = "--quote-id, --wallet-address, --object-id, --object-id-hash";
|
|
3660
|
+
var REQUIRED_PAYMENT_SETTLE = "--quote-id and --wallet-address";
|
|
3181
3661
|
var REQUIRED_STORAGE_OBJECT = "--wallet-address and one of (--object-key | --name [--latest|--at])";
|
|
3662
|
+
var REQUIRED_LS = "--wallet-address (for one object add --object-key or --name [--latest|--at]; omit both to list the bucket)";
|
|
3663
|
+
var PAYMENT_SETTLE_FLAG_NAMES = /* @__PURE__ */ new Set([
|
|
3664
|
+
"quote-id",
|
|
3665
|
+
"wallet-address",
|
|
3666
|
+
"object-id",
|
|
3667
|
+
"object-key",
|
|
3668
|
+
"storage-price"
|
|
3669
|
+
]);
|
|
3182
3670
|
var BOOLEAN_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["latest"]);
|
|
3183
3671
|
var BOOLEAN_ASYNC_FLAGS = /* @__PURE__ */ new Set(["async"]);
|
|
3184
3672
|
var BOOLEAN_OP_STATUS_FLAGS = /* @__PURE__ */ new Set(["cancel"]);
|
|
@@ -3211,9 +3699,13 @@ var CLOUD_HELP_TEXT = [
|
|
|
3211
3699
|
" Purpose: upload an encrypted object using a valid quote-id.",
|
|
3212
3700
|
" Required: " + REQUIRED_UPLOAD,
|
|
3213
3701
|
"",
|
|
3214
|
-
"\u2022 `/mnemospark_cloud
|
|
3215
|
-
" Purpose:
|
|
3216
|
-
" Required:
|
|
3702
|
+
"\u2022 `/mnemospark_cloud payment-settle --quote-id <quote-id> --wallet-address <addr> [--object-id <id>] [--object-key <key>] [--storage-price <n>]`",
|
|
3703
|
+
" Purpose: settle storage payment for a quote (e.g. monthly cron). Uses the same proxy + x402 path as upload pre-settlement.",
|
|
3704
|
+
" Required: --quote-id, --wallet-address (wallet private key must match the address).",
|
|
3705
|
+
"",
|
|
3706
|
+
"\u2022 `/mnemospark_cloud ls --wallet-address <addr> [--object-key <key> | --name <friendly-name> | omit both to list bucket] [--latest|--at <timestamp>]`",
|
|
3707
|
+
" Purpose: stat one object or list all keys in the wallet bucket (S3).",
|
|
3708
|
+
" Required: " + REQUIRED_LS,
|
|
3217
3709
|
"",
|
|
3218
3710
|
"\u2022 `/mnemospark_cloud download --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3219
3711
|
" Purpose: fetch an object to local disk.",
|
|
@@ -3245,7 +3737,7 @@ var CLOUD_HELP_TEXT = [
|
|
|
3245
3737
|
"\u2022 `/mnemospark_cloud op-status --operation-id <id>`",
|
|
3246
3738
|
"\u2022 `/mnemospark_cloud op-status --operation-id <id> --cancel`",
|
|
3247
3739
|
"",
|
|
3248
|
-
"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.
|
|
3740
|
+
"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."
|
|
3249
3741
|
].join("\n");
|
|
3250
3742
|
var UnsupportedBackupPlatformError = class extends Error {
|
|
3251
3743
|
constructor(platform) {
|
|
@@ -3316,6 +3808,18 @@ function parseObjectSelector(flags) {
|
|
|
3316
3808
|
if (objectKey) return { objectKey };
|
|
3317
3809
|
return { nameSelector: { name, latest, at } };
|
|
3318
3810
|
}
|
|
3811
|
+
function parseLsObjectSelector(flags) {
|
|
3812
|
+
const objectKey = flags["object-key"]?.trim();
|
|
3813
|
+
const name = flags.name?.trim();
|
|
3814
|
+
const latest = flags.latest === "true";
|
|
3815
|
+
const at = flags.at?.trim();
|
|
3816
|
+
if (objectKey && name) return null;
|
|
3817
|
+
if (latest && at) return null;
|
|
3818
|
+
if (!objectKey && !name && (latest || at)) return null;
|
|
3819
|
+
if (objectKey) return { objectKey };
|
|
3820
|
+
if (name) return { nameSelector: { name, latest, at } };
|
|
3821
|
+
return {};
|
|
3822
|
+
}
|
|
3319
3823
|
function parseStorageObjectRequestInput(flags, selector) {
|
|
3320
3824
|
const walletAddress = flags["wallet-address"]?.trim();
|
|
3321
3825
|
if (!walletAddress) {
|
|
@@ -3486,20 +3990,76 @@ function parseCloudArgs(args) {
|
|
|
3486
3990
|
}
|
|
3487
3991
|
};
|
|
3488
3992
|
}
|
|
3993
|
+
if (subcommand === "payment-settle") {
|
|
3994
|
+
const flags = parseNamedFlags(rest);
|
|
3995
|
+
if (!flags) {
|
|
3996
|
+
return { mode: "payment-settle-invalid" };
|
|
3997
|
+
}
|
|
3998
|
+
for (const key of Object.keys(flags)) {
|
|
3999
|
+
if (!PAYMENT_SETTLE_FLAG_NAMES.has(key)) {
|
|
4000
|
+
return { mode: "payment-settle-invalid" };
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
const quoteId = flags["quote-id"]?.trim();
|
|
4004
|
+
const walletAddress = flags["wallet-address"]?.trim();
|
|
4005
|
+
if (!quoteId || !walletAddress) {
|
|
4006
|
+
return { mode: "payment-settle-invalid" };
|
|
4007
|
+
}
|
|
4008
|
+
let storagePrice;
|
|
4009
|
+
if (flags["storage-price"] !== void 0 && flags["storage-price"] !== "") {
|
|
4010
|
+
const raw = flags["storage-price"]?.trim() ?? "";
|
|
4011
|
+
const n = Number.parseFloat(raw);
|
|
4012
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
4013
|
+
return { mode: "payment-settle-invalid" };
|
|
4014
|
+
}
|
|
4015
|
+
storagePrice = n;
|
|
4016
|
+
}
|
|
4017
|
+
return {
|
|
4018
|
+
mode: "payment-settle",
|
|
4019
|
+
paymentSettleRequest: {
|
|
4020
|
+
quote_id: quoteId,
|
|
4021
|
+
wallet_address: walletAddress,
|
|
4022
|
+
object_id: flags["object-id"]?.trim() || void 0,
|
|
4023
|
+
object_key: flags["object-key"]?.trim() || void 0,
|
|
4024
|
+
storage_price: storagePrice
|
|
4025
|
+
}
|
|
4026
|
+
};
|
|
4027
|
+
}
|
|
3489
4028
|
if (subcommand === "ls") {
|
|
3490
4029
|
const flags = parseNamedFlags(rest, BOOLEAN_SELECTOR_FLAGS);
|
|
3491
4030
|
if (!flags) {
|
|
3492
4031
|
return { mode: "ls-invalid" };
|
|
3493
4032
|
}
|
|
3494
|
-
const
|
|
3495
|
-
if (!
|
|
4033
|
+
const walletAddress = flags["wallet-address"]?.trim() ?? flags["wallet_address"]?.trim() ?? "";
|
|
4034
|
+
if (!walletAddress) {
|
|
3496
4035
|
return { mode: "ls-invalid" };
|
|
3497
4036
|
}
|
|
3498
|
-
const
|
|
3499
|
-
if (!
|
|
4037
|
+
const selector = parseLsObjectSelector(flags);
|
|
4038
|
+
if (!selector) {
|
|
3500
4039
|
return { mode: "ls-invalid" };
|
|
3501
4040
|
}
|
|
3502
|
-
|
|
4041
|
+
const location = flags.location?.trim() || flags.region?.trim() || void 0;
|
|
4042
|
+
if (selector.nameSelector) {
|
|
4043
|
+
return {
|
|
4044
|
+
mode: "ls",
|
|
4045
|
+
storageObjectRequest: { wallet_address: walletAddress, location },
|
|
4046
|
+
nameSelector: selector.nameSelector
|
|
4047
|
+
};
|
|
4048
|
+
}
|
|
4049
|
+
if (selector.objectKey) {
|
|
4050
|
+
return {
|
|
4051
|
+
mode: "ls",
|
|
4052
|
+
storageObjectRequest: {
|
|
4053
|
+
wallet_address: walletAddress,
|
|
4054
|
+
object_key: selector.objectKey,
|
|
4055
|
+
location
|
|
4056
|
+
}
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
4059
|
+
return {
|
|
4060
|
+
mode: "ls",
|
|
4061
|
+
storageObjectRequest: { wallet_address: walletAddress, location }
|
|
4062
|
+
};
|
|
3503
4063
|
}
|
|
3504
4064
|
if (subcommand === "download") {
|
|
3505
4065
|
const flags = parseNamedFlags(rest, BOOLEAN_SELECTOR_AND_ASYNC_FLAGS);
|
|
@@ -3846,7 +4406,8 @@ function quoteCronArgument(value) {
|
|
|
3846
4406
|
}
|
|
3847
4407
|
function buildStoragePaymentCronCommand(job) {
|
|
3848
4408
|
return [
|
|
3849
|
-
"
|
|
4409
|
+
"/mnemospark_cloud",
|
|
4410
|
+
"payment-settle",
|
|
3850
4411
|
"--quote-id",
|
|
3851
4412
|
quoteCronArgument(job.quoteId),
|
|
3852
4413
|
"--wallet-address",
|
|
@@ -4135,16 +4696,28 @@ function extractUploadErrorMessage(error) {
|
|
|
4135
4696
|
}
|
|
4136
4697
|
return message;
|
|
4137
4698
|
}
|
|
4699
|
+
function extractLsErrorMessage(error) {
|
|
4700
|
+
if (!(error instanceof Error)) {
|
|
4701
|
+
return null;
|
|
4702
|
+
}
|
|
4703
|
+
const message = error.message.trim();
|
|
4704
|
+
if (!message) {
|
|
4705
|
+
return null;
|
|
4706
|
+
}
|
|
4707
|
+
if (message.startsWith("ls response") || message.startsWith("ls list response") || message.startsWith("Invalid ls response payload")) {
|
|
4708
|
+
return `Cannot list storage object: ${message}`;
|
|
4709
|
+
}
|
|
4710
|
+
if (message === "formatBytesForDisplay expects a non-negative integer") {
|
|
4711
|
+
return "Cannot list storage object: ls response has invalid size_bytes; expected non-negative integer";
|
|
4712
|
+
}
|
|
4713
|
+
return null;
|
|
4714
|
+
}
|
|
4138
4715
|
function formatPriceStorageUserMessage(quote) {
|
|
4139
4716
|
return [
|
|
4140
4717
|
`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}\``,
|
|
4141
4718
|
`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}\``
|
|
4142
4719
|
].join("\n");
|
|
4143
4720
|
}
|
|
4144
|
-
function formatStorageLsUserMessage(result, requestedObjectKey) {
|
|
4145
|
-
const objectId = result.object_id ?? result.key;
|
|
4146
|
-
return `${objectId} with ${requestedObjectKey} is ${result.size_bytes} in ${result.bucket}`;
|
|
4147
|
-
}
|
|
4148
4721
|
function createInProcessSubagentOrchestrator() {
|
|
4149
4722
|
const sessions = /* @__PURE__ */ new Map();
|
|
4150
4723
|
const completeSession = async (sessionId, handler) => {
|
|
@@ -4268,10 +4841,12 @@ function createCloudCommand(options = {}) {
|
|
|
4268
4841
|
requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
|
|
4269
4842
|
requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
|
|
4270
4843
|
requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
|
|
4844
|
+
requestPaymentSettleViaProxyFn: options.requestPaymentSettleViaProxyFn ?? requestPaymentSettleViaProxy,
|
|
4271
4845
|
objectLogHomeDir: options.objectLogHomeDir ?? options.backupOptions?.homeDir,
|
|
4272
4846
|
backupOptions: options.backupOptions,
|
|
4273
4847
|
proxyQuoteOptions: options.proxyQuoteOptions,
|
|
4274
4848
|
proxyUploadOptions: options.proxyUploadOptions,
|
|
4849
|
+
proxySettleOptions: options.proxySettleOptions,
|
|
4275
4850
|
proxyUploadConfirmOptions: options.proxyUploadConfirmOptions,
|
|
4276
4851
|
subagentOrchestrator,
|
|
4277
4852
|
proxyStorageOptions: options.proxyStorageOptions
|
|
@@ -4320,7 +4895,23 @@ async function resolveFriendlyNameFromManifest(params, homeDir) {
|
|
|
4320
4895
|
}
|
|
4321
4896
|
async function resolveNameSelectorIfNeeded(datastore, request, selector, homeDir) {
|
|
4322
4897
|
if (!selector) {
|
|
4323
|
-
const
|
|
4898
|
+
const walletAddress = request.wallet_address?.trim();
|
|
4899
|
+
if (!walletAddress) {
|
|
4900
|
+
return { error: "Cannot resolve storage object request." };
|
|
4901
|
+
}
|
|
4902
|
+
const objectKey = request.object_key?.trim();
|
|
4903
|
+
if (!objectKey) {
|
|
4904
|
+
const listRequest = { wallet_address: walletAddress };
|
|
4905
|
+
if (request.location) {
|
|
4906
|
+
listRequest.location = request.location;
|
|
4907
|
+
}
|
|
4908
|
+
return { request: listRequest };
|
|
4909
|
+
}
|
|
4910
|
+
const parsedRequest2 = parseStorageObjectRequest({
|
|
4911
|
+
wallet_address: walletAddress,
|
|
4912
|
+
object_key: objectKey,
|
|
4913
|
+
location: request.location
|
|
4914
|
+
});
|
|
4324
4915
|
if (!parsedRequest2) {
|
|
4325
4916
|
return { error: "Cannot resolve storage object request." };
|
|
4326
4917
|
}
|
|
@@ -4381,6 +4972,21 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector, homeDir
|
|
|
4381
4972
|
degradedWarning
|
|
4382
4973
|
};
|
|
4383
4974
|
}
|
|
4975
|
+
function toStorageObjectRequestOrError(request, missingKeyMessage) {
|
|
4976
|
+
const key = request.object_key?.trim();
|
|
4977
|
+
if (!key) {
|
|
4978
|
+
return { ok: false, error: missingKeyMessage };
|
|
4979
|
+
}
|
|
4980
|
+
const parsed = parseStorageObjectRequest({
|
|
4981
|
+
wallet_address: request.wallet_address,
|
|
4982
|
+
object_key: key,
|
|
4983
|
+
location: request.location
|
|
4984
|
+
});
|
|
4985
|
+
if (!parsed) {
|
|
4986
|
+
return { ok: false, error: "Cannot resolve storage object request." };
|
|
4987
|
+
}
|
|
4988
|
+
return { ok: true, request: parsed };
|
|
4989
|
+
}
|
|
4384
4990
|
async function emitCloudEvent(eventType, details, homeDir) {
|
|
4385
4991
|
await appendJsonlEvent(
|
|
4386
4992
|
"events.jsonl",
|
|
@@ -4435,6 +5041,117 @@ function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
|
|
|
4435
5041
|
const traceId = forcedTraceId?.trim() || randomUUID3();
|
|
4436
5042
|
return { operationId, traceId };
|
|
4437
5043
|
}
|
|
5044
|
+
function parseTransIdFromPaymentSettleBody(bodyText) {
|
|
5045
|
+
const trimmed = bodyText.trim();
|
|
5046
|
+
if (!trimmed.startsWith("{")) {
|
|
5047
|
+
return null;
|
|
5048
|
+
}
|
|
5049
|
+
try {
|
|
5050
|
+
const parsed = JSON.parse(trimmed);
|
|
5051
|
+
const tid = parsed.trans_id;
|
|
5052
|
+
return typeof tid === "string" && tid.trim() ? tid.trim() : null;
|
|
5053
|
+
} catch {
|
|
5054
|
+
return null;
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
async function resolveAmountForPaymentSettle(quoteId, storagePriceFromFlag, datastore, homeDir) {
|
|
5058
|
+
if (storagePriceFromFlag !== void 0 && Number.isFinite(storagePriceFromFlag)) {
|
|
5059
|
+
return storagePriceFromFlag;
|
|
5060
|
+
}
|
|
5061
|
+
const quoteLookup = await datastore.findQuoteById(quoteId);
|
|
5062
|
+
if (quoteLookup && Number.isFinite(quoteLookup.storagePrice)) {
|
|
5063
|
+
return quoteLookup.storagePrice;
|
|
5064
|
+
}
|
|
5065
|
+
const logged = await findLoggedPriceStorageQuote(quoteId, homeDir);
|
|
5066
|
+
if (logged && Number.isFinite(logged.storagePrice)) {
|
|
5067
|
+
return logged.storagePrice;
|
|
5068
|
+
}
|
|
5069
|
+
const payment = await datastore.findPaymentByQuoteId(quoteId);
|
|
5070
|
+
if (payment && Number.isFinite(payment.amount)) {
|
|
5071
|
+
return payment.amount;
|
|
5072
|
+
}
|
|
5073
|
+
return 0;
|
|
5074
|
+
}
|
|
5075
|
+
async function emitPaymentSettleClientObservationBestEffort(params) {
|
|
5076
|
+
try {
|
|
5077
|
+
const {
|
|
5078
|
+
phase,
|
|
5079
|
+
correlation,
|
|
5080
|
+
quoteId,
|
|
5081
|
+
walletAddress,
|
|
5082
|
+
objectId,
|
|
5083
|
+
objectKey,
|
|
5084
|
+
httpStatus,
|
|
5085
|
+
outcomeStatus,
|
|
5086
|
+
homeDir
|
|
5087
|
+
} = params;
|
|
5088
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
5089
|
+
if (phase === "start") {
|
|
5090
|
+
await emitCloudEventBestEffort(
|
|
5091
|
+
"payment-settle.started",
|
|
5092
|
+
{
|
|
5093
|
+
operation_id: correlation.operationId,
|
|
5094
|
+
trace_id: correlation.traceId,
|
|
5095
|
+
quote_id: quoteId,
|
|
5096
|
+
wallet_address: walletAddress,
|
|
5097
|
+
object_id: objectId,
|
|
5098
|
+
object_key: objectKey,
|
|
5099
|
+
status: "running"
|
|
5100
|
+
},
|
|
5101
|
+
homeDir
|
|
5102
|
+
);
|
|
5103
|
+
await appendJsonlEvent(
|
|
5104
|
+
"proxy-events.jsonl",
|
|
5105
|
+
{
|
|
5106
|
+
ts,
|
|
5107
|
+
event_type: "payment.settle",
|
|
5108
|
+
status: "start",
|
|
5109
|
+
trace_id: correlation.traceId,
|
|
5110
|
+
operation_id: correlation.operationId,
|
|
5111
|
+
quote_id: quoteId,
|
|
5112
|
+
wallet_address: walletAddress,
|
|
5113
|
+
object_id: objectId ?? null,
|
|
5114
|
+
object_key: objectKey ?? null,
|
|
5115
|
+
details: { source: "client" }
|
|
5116
|
+
},
|
|
5117
|
+
homeDir
|
|
5118
|
+
);
|
|
5119
|
+
return;
|
|
5120
|
+
}
|
|
5121
|
+
const terminal = outcomeStatus ?? "failed";
|
|
5122
|
+
await emitCloudEventBestEffort(
|
|
5123
|
+
"payment-settle.completed",
|
|
5124
|
+
{
|
|
5125
|
+
operation_id: correlation.operationId,
|
|
5126
|
+
trace_id: correlation.traceId,
|
|
5127
|
+
quote_id: quoteId,
|
|
5128
|
+
wallet_address: walletAddress,
|
|
5129
|
+
object_id: objectId,
|
|
5130
|
+
object_key: objectKey,
|
|
5131
|
+
status: terminal === "succeeded" ? "succeeded" : "failed",
|
|
5132
|
+
http_status: httpStatus
|
|
5133
|
+
},
|
|
5134
|
+
homeDir
|
|
5135
|
+
);
|
|
5136
|
+
await appendJsonlEvent(
|
|
5137
|
+
"proxy-events.jsonl",
|
|
5138
|
+
{
|
|
5139
|
+
ts,
|
|
5140
|
+
event_type: "payment.settle",
|
|
5141
|
+
status: "result",
|
|
5142
|
+
trace_id: correlation.traceId,
|
|
5143
|
+
operation_id: correlation.operationId,
|
|
5144
|
+
quote_id: quoteId,
|
|
5145
|
+
wallet_address: walletAddress,
|
|
5146
|
+
object_id: objectId ?? null,
|
|
5147
|
+
object_key: objectKey ?? null,
|
|
5148
|
+
details: { http_status: httpStatus ?? null, source: "client" }
|
|
5149
|
+
},
|
|
5150
|
+
homeDir
|
|
5151
|
+
);
|
|
5152
|
+
} catch {
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
4438
5155
|
async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
4439
5156
|
const parsed = parseCloudArgs(ctx.args);
|
|
4440
5157
|
const objectLogHomeDir = options.objectLogHomeDir;
|
|
@@ -4450,6 +5167,7 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4450
5167
|
const requestStorageLs = options.requestStorageLsFn;
|
|
4451
5168
|
const requestStorageDownload = options.requestStorageDownloadFn;
|
|
4452
5169
|
const requestStorageDelete = options.requestStorageDeleteFn;
|
|
5170
|
+
const requestPaymentSettleViaProxy2 = options.requestPaymentSettleViaProxyFn;
|
|
4453
5171
|
const subagentOrchestrator = options.subagentOrchestrator;
|
|
4454
5172
|
if (parsed.mode === "help" || parsed.mode === "unknown") {
|
|
4455
5173
|
return {
|
|
@@ -4487,9 +5205,15 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4487
5205
|
isError: true
|
|
4488
5206
|
};
|
|
4489
5207
|
}
|
|
5208
|
+
if (parsed.mode === "payment-settle-invalid") {
|
|
5209
|
+
return {
|
|
5210
|
+
text: `Cannot settle payment: required arguments are ${REQUIRED_PAYMENT_SETTLE}. Optional: --object-id, --object-key, --storage-price.`,
|
|
5211
|
+
isError: true
|
|
5212
|
+
};
|
|
5213
|
+
}
|
|
4490
5214
|
if (parsed.mode === "ls-invalid") {
|
|
4491
5215
|
return {
|
|
4492
|
-
text: `Cannot list storage object: required arguments are ${
|
|
5216
|
+
text: `Cannot list storage object: required arguments are ${REQUIRED_LS}.`,
|
|
4493
5217
|
isError: true
|
|
4494
5218
|
};
|
|
4495
5219
|
}
|
|
@@ -4631,6 +5355,142 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4631
5355
|
}
|
|
4632
5356
|
return formatOperationStatus(operation);
|
|
4633
5357
|
}
|
|
5358
|
+
if (parsed.mode === "payment-settle") {
|
|
5359
|
+
const req = parsed.paymentSettleRequest;
|
|
5360
|
+
let walletKey;
|
|
5361
|
+
try {
|
|
5362
|
+
walletKey = await resolveWalletKey(objectLogHomeDir);
|
|
5363
|
+
} catch (err) {
|
|
5364
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5365
|
+
return { text: message.trim() || "Cannot resolve wallet key.", isError: true };
|
|
5366
|
+
}
|
|
5367
|
+
const walletAccount = privateKeyToAccount5(walletKey);
|
|
5368
|
+
if (walletAccount.address.toLowerCase() !== req.wallet_address.toLowerCase()) {
|
|
5369
|
+
return {
|
|
5370
|
+
text: `Cannot settle payment: wallet key address ${walletAccount.address} does not match --wallet-address ${req.wallet_address}.`,
|
|
5371
|
+
isError: true
|
|
5372
|
+
};
|
|
5373
|
+
}
|
|
5374
|
+
const correlation = buildRequestCorrelation();
|
|
5375
|
+
const settleFetch = createPayment(walletKey).fetch;
|
|
5376
|
+
const objectId = req.object_id;
|
|
5377
|
+
const objectKey = req.object_key;
|
|
5378
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5379
|
+
phase: "start",
|
|
5380
|
+
correlation,
|
|
5381
|
+
quoteId: req.quote_id,
|
|
5382
|
+
walletAddress: req.wallet_address,
|
|
5383
|
+
objectId,
|
|
5384
|
+
objectKey,
|
|
5385
|
+
homeDir: objectLogHomeDir
|
|
5386
|
+
});
|
|
5387
|
+
let settleResult;
|
|
5388
|
+
try {
|
|
5389
|
+
settleResult = await requestPaymentSettleViaProxy2(req.quote_id, req.wallet_address, {
|
|
5390
|
+
...options.proxySettleOptions,
|
|
5391
|
+
correlation,
|
|
5392
|
+
fetchImpl: (input, init) => settleFetch(input, init)
|
|
5393
|
+
});
|
|
5394
|
+
} catch (err) {
|
|
5395
|
+
const amountErr = await resolveAmountForPaymentSettle(
|
|
5396
|
+
req.quote_id,
|
|
5397
|
+
req.storage_price,
|
|
5398
|
+
datastore,
|
|
5399
|
+
objectLogHomeDir
|
|
5400
|
+
);
|
|
5401
|
+
await datastore.upsertPayment({
|
|
5402
|
+
quote_id: req.quote_id,
|
|
5403
|
+
wallet_address: req.wallet_address,
|
|
5404
|
+
trans_id: null,
|
|
5405
|
+
amount: amountErr,
|
|
5406
|
+
network: null,
|
|
5407
|
+
status: "settle_failed",
|
|
5408
|
+
settled_at: null
|
|
5409
|
+
});
|
|
5410
|
+
const cronErr = await datastore.findCronByQuoteId(req.quote_id);
|
|
5411
|
+
if (cronErr) {
|
|
5412
|
+
await datastore.upsertCronJob({ ...cronErr, status: "active" });
|
|
5413
|
+
}
|
|
5414
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5415
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5416
|
+
phase: "result",
|
|
5417
|
+
correlation,
|
|
5418
|
+
quoteId: req.quote_id,
|
|
5419
|
+
walletAddress: req.wallet_address,
|
|
5420
|
+
objectId,
|
|
5421
|
+
objectKey,
|
|
5422
|
+
outcomeStatus: "failed",
|
|
5423
|
+
homeDir: objectLogHomeDir
|
|
5424
|
+
});
|
|
5425
|
+
return { text: `Payment settle failed: ${msg}`, isError: true };
|
|
5426
|
+
}
|
|
5427
|
+
const amount = await resolveAmountForPaymentSettle(
|
|
5428
|
+
req.quote_id,
|
|
5429
|
+
req.storage_price,
|
|
5430
|
+
datastore,
|
|
5431
|
+
objectLogHomeDir
|
|
5432
|
+
);
|
|
5433
|
+
const transId = settleResult.status === 200 ? parseTransIdFromPaymentSettleBody(settleResult.bodyText ?? "") : null;
|
|
5434
|
+
if (settleResult.status === 200) {
|
|
5435
|
+
await datastore.upsertPayment({
|
|
5436
|
+
quote_id: req.quote_id,
|
|
5437
|
+
wallet_address: req.wallet_address,
|
|
5438
|
+
trans_id: transId,
|
|
5439
|
+
amount,
|
|
5440
|
+
network: null,
|
|
5441
|
+
status: "settled",
|
|
5442
|
+
settled_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5443
|
+
});
|
|
5444
|
+
const cronRow = await datastore.findCronByQuoteId(req.quote_id);
|
|
5445
|
+
if (cronRow) {
|
|
5446
|
+
await datastore.upsertCronJob({ ...cronRow, status: "active" });
|
|
5447
|
+
}
|
|
5448
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5449
|
+
phase: "result",
|
|
5450
|
+
correlation,
|
|
5451
|
+
quoteId: req.quote_id,
|
|
5452
|
+
walletAddress: req.wallet_address,
|
|
5453
|
+
objectId,
|
|
5454
|
+
objectKey,
|
|
5455
|
+
httpStatus: settleResult.status,
|
|
5456
|
+
outcomeStatus: "succeeded",
|
|
5457
|
+
homeDir: objectLogHomeDir
|
|
5458
|
+
});
|
|
5459
|
+
return {
|
|
5460
|
+
text: transId ? `Payment settled for quote ${req.quote_id} (trans_id: ${transId}).` : `Payment settled for quote ${req.quote_id}.`
|
|
5461
|
+
};
|
|
5462
|
+
}
|
|
5463
|
+
await datastore.upsertPayment({
|
|
5464
|
+
quote_id: req.quote_id,
|
|
5465
|
+
wallet_address: req.wallet_address,
|
|
5466
|
+
trans_id: transId,
|
|
5467
|
+
amount,
|
|
5468
|
+
network: null,
|
|
5469
|
+
status: "settle_failed",
|
|
5470
|
+
settled_at: null
|
|
5471
|
+
});
|
|
5472
|
+
const cronRowFailed = await datastore.findCronByQuoteId(req.quote_id);
|
|
5473
|
+
if (cronRowFailed) {
|
|
5474
|
+
await datastore.upsertCronJob({ ...cronRowFailed, status: "active" });
|
|
5475
|
+
}
|
|
5476
|
+
await emitPaymentSettleClientObservationBestEffort({
|
|
5477
|
+
phase: "result",
|
|
5478
|
+
correlation,
|
|
5479
|
+
quoteId: req.quote_id,
|
|
5480
|
+
walletAddress: req.wallet_address,
|
|
5481
|
+
objectId,
|
|
5482
|
+
objectKey,
|
|
5483
|
+
httpStatus: settleResult.status,
|
|
5484
|
+
outcomeStatus: "failed",
|
|
5485
|
+
homeDir: objectLogHomeDir
|
|
5486
|
+
});
|
|
5487
|
+
const bodySnippet = settleResult.bodyText?.trim();
|
|
5488
|
+
const detail = bodySnippet && bodySnippet.length > 500 ? `${bodySnippet.slice(0, 500)}\u2026` : bodySnippet;
|
|
5489
|
+
return {
|
|
5490
|
+
text: detail ? `Payment settle failed (HTTP ${settleResult.status}): ${detail}` : `Payment settle failed with HTTP ${settleResult.status}.`,
|
|
5491
|
+
isError: true
|
|
5492
|
+
};
|
|
5493
|
+
}
|
|
4634
5494
|
if ((parsed.mode === "backup" || parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4635
5495
|
const asyncCorrelation = buildRequestCorrelation();
|
|
4636
5496
|
const operationId = asyncCorrelation.operationId;
|
|
@@ -5141,7 +6001,7 @@ operation-id: ${operationId}`,
|
|
|
5141
6001
|
const shouldSettleBeforeUpload = requestStorageUpload !== requestStorageUploadViaProxy;
|
|
5142
6002
|
if (shouldSettleBeforeUpload) {
|
|
5143
6003
|
const paymentFetch = createPayment(walletKey).fetch;
|
|
5144
|
-
const settleResult = await
|
|
6004
|
+
const settleResult = await requestPaymentSettleViaProxy2(
|
|
5145
6005
|
parsed.uploadRequest.quote_id,
|
|
5146
6006
|
parsed.uploadRequest.wallet_address,
|
|
5147
6007
|
{
|
|
@@ -5348,15 +6208,17 @@ operation-id: ${operationId}`,
|
|
|
5348
6208
|
"name_resolution.degraded",
|
|
5349
6209
|
{
|
|
5350
6210
|
wallet_address: resolvedRequest.wallet_address,
|
|
5351
|
-
object_key: resolvedRequest.object_key,
|
|
6211
|
+
object_key: resolvedRequest.object_key ?? null,
|
|
5352
6212
|
warning: resolved.degradedWarning
|
|
5353
6213
|
},
|
|
5354
6214
|
objectLogHomeDir
|
|
5355
6215
|
);
|
|
5356
6216
|
}
|
|
6217
|
+
const objectKeyForLs = resolvedRequest.object_key?.trim();
|
|
6218
|
+
const isBucketList = !objectKeyForLs;
|
|
5357
6219
|
const correlation = buildRequestCorrelation();
|
|
5358
6220
|
const operationId = correlation.operationId;
|
|
5359
|
-
const knownObject = await datastore.findObjectByObjectKey(
|
|
6221
|
+
const knownObject = isBucketList ? null : await datastore.findObjectByObjectKey(objectKeyForLs);
|
|
5360
6222
|
const operationObjectId = knownObject?.object_id ?? null;
|
|
5361
6223
|
await datastore.upsertOperation({
|
|
5362
6224
|
operation_id: operationId,
|
|
@@ -5390,17 +6252,23 @@ operation-id: ${operationId}`,
|
|
|
5390
6252
|
operation_id: operationId,
|
|
5391
6253
|
trace_id: correlation.traceId,
|
|
5392
6254
|
wallet_address: resolvedRequest.wallet_address,
|
|
5393
|
-
object_key: resolvedRequest.object_key,
|
|
5394
|
-
status: "succeeded"
|
|
6255
|
+
object_key: resolvedRequest.object_key ?? null,
|
|
6256
|
+
status: "succeeded",
|
|
6257
|
+
list_mode: isBucketList
|
|
5395
6258
|
},
|
|
5396
6259
|
objectLogHomeDir
|
|
5397
6260
|
);
|
|
5398
|
-
const lsText =
|
|
6261
|
+
const lsText = await buildMnemosparkLsMessage(lsResult, {
|
|
6262
|
+
walletAddress: resolvedRequest.wallet_address,
|
|
6263
|
+
datastore
|
|
6264
|
+
});
|
|
5399
6265
|
return {
|
|
5400
6266
|
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
6267
|
+
|
|
5401
6268
|
${lsText}` : lsText
|
|
5402
6269
|
};
|
|
5403
|
-
} catch {
|
|
6270
|
+
} catch (error) {
|
|
6271
|
+
const lsErrorMessage = extractLsErrorMessage(error) ?? "Cannot list storage object";
|
|
5404
6272
|
await datastore.upsertOperation({
|
|
5405
6273
|
operation_id: operationId,
|
|
5406
6274
|
type: "ls",
|
|
@@ -5408,7 +6276,7 @@ ${lsText}` : lsText
|
|
|
5408
6276
|
quote_id: null,
|
|
5409
6277
|
status: "failed",
|
|
5410
6278
|
error_code: "LS_FAILED",
|
|
5411
|
-
error_message:
|
|
6279
|
+
error_message: lsErrorMessage
|
|
5412
6280
|
});
|
|
5413
6281
|
await emitCloudEventBestEffort(
|
|
5414
6282
|
"ls.completed",
|
|
@@ -5416,13 +6284,14 @@ ${lsText}` : lsText
|
|
|
5416
6284
|
operation_id: operationId,
|
|
5417
6285
|
trace_id: correlation.traceId,
|
|
5418
6286
|
wallet_address: resolvedRequest.wallet_address,
|
|
5419
|
-
object_key: resolvedRequest.object_key,
|
|
5420
|
-
status: "failed"
|
|
6287
|
+
object_key: resolvedRequest.object_key ?? null,
|
|
6288
|
+
status: "failed",
|
|
6289
|
+
list_mode: isBucketList
|
|
5421
6290
|
},
|
|
5422
6291
|
objectLogHomeDir
|
|
5423
6292
|
);
|
|
5424
6293
|
return {
|
|
5425
|
-
text:
|
|
6294
|
+
text: lsErrorMessage,
|
|
5426
6295
|
isError: true
|
|
5427
6296
|
};
|
|
5428
6297
|
}
|
|
@@ -5437,7 +6306,14 @@ ${lsText}` : lsText
|
|
|
5437
6306
|
if (resolved.error || !resolved.request) {
|
|
5438
6307
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
5439
6308
|
}
|
|
5440
|
-
const
|
|
6309
|
+
const narrowed = toStorageObjectRequestOrError(
|
|
6310
|
+
resolved.request,
|
|
6311
|
+
`Cannot download file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`
|
|
6312
|
+
);
|
|
6313
|
+
if (!narrowed.ok) {
|
|
6314
|
+
return { text: narrowed.error, isError: true };
|
|
6315
|
+
}
|
|
6316
|
+
const resolvedRequest = narrowed.request;
|
|
5441
6317
|
if (resolved.degradedWarning) {
|
|
5442
6318
|
await emitCloudEventBestEffort(
|
|
5443
6319
|
"name_resolution.degraded",
|
|
@@ -5535,7 +6411,14 @@ ${downloadText}` : downloadText
|
|
|
5535
6411
|
if (resolved.error || !resolved.request) {
|
|
5536
6412
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
5537
6413
|
}
|
|
5538
|
-
const
|
|
6414
|
+
const narrowedDelete = toStorageObjectRequestOrError(
|
|
6415
|
+
resolved.request,
|
|
6416
|
+
`Cannot delete file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`
|
|
6417
|
+
);
|
|
6418
|
+
if (!narrowedDelete.ok) {
|
|
6419
|
+
return { text: narrowedDelete.error, isError: true };
|
|
6420
|
+
}
|
|
6421
|
+
const resolvedRequest = narrowedDelete.request;
|
|
5539
6422
|
if (resolved.degradedWarning) {
|
|
5540
6423
|
await emitCloudEventBestEffort(
|
|
5541
6424
|
"name_resolution.degraded",
|