mnemospark 0.1.21 → 0.2.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/README.md +10 -8
- package/README.md.bak +1 -1
- package/dist/cli.js +1141 -161
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +60 -0
- package/dist/index.js +1149 -166
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
- package/skills/mnemospark/SKILL.md +56 -0
- package/skills/mnemospark/references/commands.md +75 -0
- package/skills/mnemospark/references/state-and-logs.md +80 -0
- package/skills/mnemospark/references/troubleshooting.md +48 -0
- package/skills/mnemospark/scripts/debug-operation.sh +184 -0
package/dist/cli.js
CHANGED
|
@@ -282,6 +282,21 @@ async function createWalletSignatureHeaderValue(method, path, walletAddress, wal
|
|
|
282
282
|
return encodeBase64Json(headerEnvelope);
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
+
// src/cloud-correlation.ts
|
|
286
|
+
var MNEMOSPARK_TRACE_ID_HEADER = "X-Mnemospark-Trace-Id";
|
|
287
|
+
var MNEMOSPARK_OPERATION_ID_HEADER = "X-Mnemospark-Operation-Id";
|
|
288
|
+
function applyCorrelationHeaders(headers, correlation) {
|
|
289
|
+
const traceId = correlation?.traceId?.trim();
|
|
290
|
+
if (traceId) {
|
|
291
|
+
headers[MNEMOSPARK_TRACE_ID_HEADER] = traceId;
|
|
292
|
+
}
|
|
293
|
+
const operationId = correlation?.operationId?.trim();
|
|
294
|
+
if (operationId) {
|
|
295
|
+
headers[MNEMOSPARK_OPERATION_ID_HEADER] = operationId;
|
|
296
|
+
}
|
|
297
|
+
return headers;
|
|
298
|
+
}
|
|
299
|
+
|
|
285
300
|
// src/cloud-utils.ts
|
|
286
301
|
function normalizeBaseUrl(baseUrl) {
|
|
287
302
|
return baseUrl.replace(/\/+$/, "");
|
|
@@ -511,9 +526,12 @@ async function requestPriceStorageViaProxy(request, options = {}) {
|
|
|
511
526
|
);
|
|
512
527
|
const response = await fetchImpl(`${baseUrl}${PRICE_STORAGE_PROXY_PATH}`, {
|
|
513
528
|
method: "POST",
|
|
514
|
-
headers:
|
|
515
|
-
|
|
516
|
-
|
|
529
|
+
headers: applyCorrelationHeaders(
|
|
530
|
+
{
|
|
531
|
+
"Content-Type": "application/json"
|
|
532
|
+
},
|
|
533
|
+
options.correlation
|
|
534
|
+
),
|
|
517
535
|
body: JSON.stringify(request)
|
|
518
536
|
});
|
|
519
537
|
const responseBody = await response.text();
|
|
@@ -538,6 +556,7 @@ async function requestStorageUploadViaProxy(request, options = {}) {
|
|
|
538
556
|
const requestHeaders = {
|
|
539
557
|
"Content-Type": "application/json"
|
|
540
558
|
};
|
|
559
|
+
applyCorrelationHeaders(requestHeaders, options.correlation);
|
|
541
560
|
if (options.idempotencyKey && options.idempotencyKey.trim().length > 0) {
|
|
542
561
|
requestHeaders["Idempotency-Key"] = options.idempotencyKey.trim();
|
|
543
562
|
}
|
|
@@ -619,9 +638,12 @@ async function requestStorageUploadConfirmViaProxy(request, options = {}) {
|
|
|
619
638
|
);
|
|
620
639
|
const response = await fetchImpl(`${baseUrl}${UPLOAD_CONFIRM_PROXY_PATH}`, {
|
|
621
640
|
method: "POST",
|
|
622
|
-
headers:
|
|
623
|
-
|
|
624
|
-
|
|
641
|
+
headers: applyCorrelationHeaders(
|
|
642
|
+
{
|
|
643
|
+
"Content-Type": "application/json"
|
|
644
|
+
},
|
|
645
|
+
options.correlation
|
|
646
|
+
),
|
|
625
647
|
body: JSON.stringify(request)
|
|
626
648
|
});
|
|
627
649
|
const responseBody = await response.text();
|
|
@@ -736,7 +758,7 @@ async function requestPaymentSettleViaProxy(quoteId, walletAddress, options = {}
|
|
|
736
758
|
}
|
|
737
759
|
const response = await fetchImpl(targetUrl, {
|
|
738
760
|
method: "POST",
|
|
739
|
-
headers: { "Content-Type": "application/json" },
|
|
761
|
+
headers: applyCorrelationHeaders({ "Content-Type": "application/json" }, options.correlation),
|
|
740
762
|
body: JSON.stringify(requestBody)
|
|
741
763
|
});
|
|
742
764
|
return {
|
|
@@ -1220,9 +1242,12 @@ async function requestJsonViaProxy(proxyPath, request, parser, options = {}) {
|
|
|
1220
1242
|
);
|
|
1221
1243
|
const response = await fetchImpl(`${baseUrl}${proxyPath}`, {
|
|
1222
1244
|
method: "POST",
|
|
1223
|
-
headers:
|
|
1224
|
-
|
|
1225
|
-
|
|
1245
|
+
headers: applyCorrelationHeaders(
|
|
1246
|
+
{
|
|
1247
|
+
"Content-Type": "application/json"
|
|
1248
|
+
},
|
|
1249
|
+
options.correlation
|
|
1250
|
+
),
|
|
1226
1251
|
body: JSON.stringify(request)
|
|
1227
1252
|
});
|
|
1228
1253
|
const bodyText = await response.text();
|
|
@@ -1546,6 +1571,20 @@ function emitProxyEvent(eventType, status, correlation, details = {}) {
|
|
|
1546
1571
|
details
|
|
1547
1572
|
}).catch(() => void 0);
|
|
1548
1573
|
}
|
|
1574
|
+
function createProxyCorrelation(headers) {
|
|
1575
|
+
const traceId = readHeaderValue(headers[MNEMOSPARK_TRACE_ID_HEADER.toLowerCase()]) ?? randomUUID();
|
|
1576
|
+
const operationId = readHeaderValue(
|
|
1577
|
+
headers[MNEMOSPARK_OPERATION_ID_HEADER.toLowerCase()] ?? headers["idempotency-key"]
|
|
1578
|
+
) ?? randomUUID();
|
|
1579
|
+
return { trace_id: traceId, operation_id: operationId };
|
|
1580
|
+
}
|
|
1581
|
+
function emitProxyTerminalFromStatus(correlation, statusCode, details = {}) {
|
|
1582
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
1583
|
+
emitProxyEvent("terminal.success", "success", correlation, { status: statusCode, ...details });
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
emitProxyEvent("terminal.failure", "failure", correlation, { status: statusCode, ...details });
|
|
1587
|
+
}
|
|
1549
1588
|
function isAlreadySettledConflict(status, bodyText) {
|
|
1550
1589
|
if (status !== 409) {
|
|
1551
1590
|
return false;
|
|
@@ -1670,10 +1709,7 @@ async function startProxy(options) {
|
|
|
1670
1709
|
console.error(`[mnemospark] Response stream error: ${err.message}`);
|
|
1671
1710
|
});
|
|
1672
1711
|
if (req.method === "POST" && matchesProxyPath(req.url, PRICE_STORAGE_PROXY_PATH)) {
|
|
1673
|
-
const correlation =
|
|
1674
|
-
trace_id: randomUUID(),
|
|
1675
|
-
operation_id: randomUUID()
|
|
1676
|
-
};
|
|
1712
|
+
const correlation = createProxyCorrelation(req.headers);
|
|
1677
1713
|
logProxyEvent("info", "proxy_price_storage_received");
|
|
1678
1714
|
emitProxyEvent("request.received", "start", correlation, { path: PRICE_STORAGE_PROXY_PATH });
|
|
1679
1715
|
try {
|
|
@@ -1682,15 +1718,17 @@ async function startProxy(options) {
|
|
|
1682
1718
|
payload = await readProxyJsonBody(req);
|
|
1683
1719
|
} catch {
|
|
1684
1720
|
logProxyEvent("warn", "proxy_price_storage_invalid_json");
|
|
1721
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
1685
1722
|
sendJson(res, 400, {
|
|
1686
1723
|
error: "Bad request",
|
|
1687
|
-
message: "Invalid JSON body for /
|
|
1724
|
+
message: "Invalid JSON body for /mnemospark_cloud price-storage"
|
|
1688
1725
|
});
|
|
1689
1726
|
return;
|
|
1690
1727
|
}
|
|
1691
1728
|
const requestPayload = parsePriceStorageQuoteRequest(payload);
|
|
1692
1729
|
if (!requestPayload) {
|
|
1693
1730
|
logProxyEvent("warn", "proxy_price_storage_missing_fields");
|
|
1731
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
1694
1732
|
sendJson(res, 400, {
|
|
1695
1733
|
error: "Bad request",
|
|
1696
1734
|
message: "Missing required fields: wallet_address, object_id, object_id_hash, gb, provider, region"
|
|
@@ -1709,6 +1747,7 @@ async function startProxy(options) {
|
|
|
1709
1747
|
logProxyEvent("warn", "proxy_price_storage_wallet_signature_missing");
|
|
1710
1748
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1711
1749
|
res.end(createWalletRequiredBody());
|
|
1750
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
|
|
1712
1751
|
return;
|
|
1713
1752
|
}
|
|
1714
1753
|
const backendResponse = await forwardPriceStorageToBackend(requestPayload, {
|
|
@@ -1734,14 +1773,13 @@ async function startProxy(options) {
|
|
|
1734
1773
|
});
|
|
1735
1774
|
res.writeHead(authFailure.status, responseHeaders2);
|
|
1736
1775
|
res.end(authFailure.bodyText);
|
|
1776
|
+
emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
|
|
1737
1777
|
return;
|
|
1738
1778
|
}
|
|
1739
1779
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
1740
1780
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1741
1781
|
res.end(backendResponse.bodyText);
|
|
1742
|
-
|
|
1743
|
-
status: backendResponse.status
|
|
1744
|
-
});
|
|
1782
|
+
emitProxyTerminalFromStatus(correlation, backendResponse.status);
|
|
1745
1783
|
} catch (err) {
|
|
1746
1784
|
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
1747
1785
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -1751,16 +1789,13 @@ async function startProxy(options) {
|
|
|
1751
1789
|
});
|
|
1752
1790
|
sendJson(res, 502, {
|
|
1753
1791
|
error: "proxy_error",
|
|
1754
|
-
message: `Failed to forward /
|
|
1792
|
+
message: `Failed to forward /mnemospark_cloud price-storage: ${err instanceof Error ? err.message : String(err)}`
|
|
1755
1793
|
});
|
|
1756
1794
|
}
|
|
1757
1795
|
return;
|
|
1758
1796
|
}
|
|
1759
1797
|
if (req.method === "POST" && matchesProxyPath(req.url, PAYMENT_SETTLE_PROXY_PATH)) {
|
|
1760
|
-
const correlation =
|
|
1761
|
-
trace_id: randomUUID(),
|
|
1762
|
-
operation_id: randomUUID()
|
|
1763
|
-
};
|
|
1798
|
+
const correlation = createProxyCorrelation(req.headers);
|
|
1764
1799
|
logProxyEvent("info", "proxy_payment_settle_received");
|
|
1765
1800
|
emitProxyEvent("request.received", "start", correlation, { path: PAYMENT_SETTLE_PROXY_PATH });
|
|
1766
1801
|
try {
|
|
@@ -1769,6 +1804,7 @@ async function startProxy(options) {
|
|
|
1769
1804
|
payload = await readProxyJsonBody(req);
|
|
1770
1805
|
} catch {
|
|
1771
1806
|
logProxyEvent("warn", "proxy_payment_settle_invalid_json");
|
|
1807
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
1772
1808
|
sendJson(res, 400, {
|
|
1773
1809
|
error: "Bad request",
|
|
1774
1810
|
message: "Invalid JSON body for /mnemospark/payment/settle"
|
|
@@ -1782,6 +1818,7 @@ async function startProxy(options) {
|
|
|
1782
1818
|
const inlinePaymentAuthorization = record?.payment_authorization;
|
|
1783
1819
|
if (!quoteId || !walletAddress) {
|
|
1784
1820
|
logProxyEvent("warn", "proxy_payment_settle_missing_fields");
|
|
1821
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
1785
1822
|
sendJson(res, 400, {
|
|
1786
1823
|
error: "Bad request",
|
|
1787
1824
|
message: "Missing required fields: quote_id, wallet_address"
|
|
@@ -1790,6 +1827,7 @@ async function startProxy(options) {
|
|
|
1790
1827
|
}
|
|
1791
1828
|
if (inlinePayment !== void 0 && (inlinePayment === null || typeof inlinePayment !== "object" || Array.isArray(inlinePayment))) {
|
|
1792
1829
|
logProxyEvent("warn", "proxy_payment_settle_invalid_payment_shape");
|
|
1830
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_payment_shape" });
|
|
1793
1831
|
sendJson(res, 400, {
|
|
1794
1832
|
error: "Bad request",
|
|
1795
1833
|
message: "Invalid field: payment must be an object when provided"
|
|
@@ -1798,6 +1836,9 @@ async function startProxy(options) {
|
|
|
1798
1836
|
}
|
|
1799
1837
|
if (inlinePaymentAuthorization !== void 0 && !(typeof inlinePaymentAuthorization === "string" || inlinePaymentAuthorization !== null && typeof inlinePaymentAuthorization === "object" && !Array.isArray(inlinePaymentAuthorization))) {
|
|
1800
1838
|
logProxyEvent("warn", "proxy_payment_settle_invalid_payment_authorization_shape");
|
|
1839
|
+
emitProxyTerminalFromStatus(correlation, 400, {
|
|
1840
|
+
reason: "invalid_payment_authorization_shape"
|
|
1841
|
+
});
|
|
1801
1842
|
sendJson(res, 400, {
|
|
1802
1843
|
error: "Bad request",
|
|
1803
1844
|
message: "Invalid field: payment_authorization must be an object or string when provided"
|
|
@@ -1809,6 +1850,7 @@ async function startProxy(options) {
|
|
|
1809
1850
|
request_wallet: walletAddress,
|
|
1810
1851
|
proxy_wallet: account.address
|
|
1811
1852
|
});
|
|
1853
|
+
emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
|
|
1812
1854
|
sendJson(res, 403, {
|
|
1813
1855
|
error: "wallet_proof_invalid",
|
|
1814
1856
|
message: "wallet proof invalid"
|
|
@@ -1824,6 +1866,7 @@ async function startProxy(options) {
|
|
|
1824
1866
|
logProxyEvent("warn", "proxy_payment_settle_wallet_signature_missing");
|
|
1825
1867
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1826
1868
|
res.end(createWalletRequiredBody());
|
|
1869
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
|
|
1827
1870
|
return;
|
|
1828
1871
|
}
|
|
1829
1872
|
correlation.quote_id = quoteId;
|
|
@@ -1858,14 +1901,13 @@ async function startProxy(options) {
|
|
|
1858
1901
|
});
|
|
1859
1902
|
res.writeHead(authFailure.status, responseHeaders2);
|
|
1860
1903
|
res.end(authFailure.bodyText);
|
|
1904
|
+
emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
|
|
1861
1905
|
return;
|
|
1862
1906
|
}
|
|
1863
1907
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
1864
1908
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1865
1909
|
res.end(backendResponse.bodyText);
|
|
1866
|
-
|
|
1867
|
-
status: backendResponse.status
|
|
1868
|
-
});
|
|
1910
|
+
emitProxyTerminalFromStatus(correlation, backendResponse.status);
|
|
1869
1911
|
} catch (err) {
|
|
1870
1912
|
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
1871
1913
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -1881,10 +1923,7 @@ async function startProxy(options) {
|
|
|
1881
1923
|
return;
|
|
1882
1924
|
}
|
|
1883
1925
|
if (req.method === "POST" && matchesProxyPath(req.url, UPLOAD_PROXY_PATH)) {
|
|
1884
|
-
const correlation =
|
|
1885
|
-
trace_id: randomUUID(),
|
|
1886
|
-
operation_id: randomUUID()
|
|
1887
|
-
};
|
|
1926
|
+
const correlation = createProxyCorrelation(req.headers);
|
|
1888
1927
|
logProxyEvent("info", "proxy_upload_received");
|
|
1889
1928
|
emitProxyEvent("request.received", "start", correlation, { path: UPLOAD_PROXY_PATH });
|
|
1890
1929
|
try {
|
|
@@ -1893,15 +1932,17 @@ async function startProxy(options) {
|
|
|
1893
1932
|
payload = await readProxyJsonBody(req);
|
|
1894
1933
|
} catch {
|
|
1895
1934
|
logProxyEvent("warn", "proxy_upload_invalid_json");
|
|
1935
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
1896
1936
|
sendJson(res, 400, {
|
|
1897
1937
|
error: "Bad request",
|
|
1898
|
-
message: "Invalid JSON body for /
|
|
1938
|
+
message: "Invalid JSON body for /mnemospark_cloud upload"
|
|
1899
1939
|
});
|
|
1900
1940
|
return;
|
|
1901
1941
|
}
|
|
1902
1942
|
const requestPayload = parseStorageUploadRequest(payload);
|
|
1903
1943
|
if (!requestPayload) {
|
|
1904
1944
|
logProxyEvent("warn", "proxy_upload_missing_fields");
|
|
1945
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
1905
1946
|
sendJson(res, 400, {
|
|
1906
1947
|
error: "Bad request",
|
|
1907
1948
|
message: "Missing required fields: quote_id, wallet_address, object_id, object_id_hash, quoted_storage_price, payload"
|
|
@@ -1911,12 +1952,12 @@ async function startProxy(options) {
|
|
|
1911
1952
|
correlation.quote_id = requestPayload.quote_id;
|
|
1912
1953
|
correlation.wallet_address = requestPayload.wallet_address;
|
|
1913
1954
|
correlation.object_id = requestPayload.object_id;
|
|
1914
|
-
emitProxyEvent("storage.call", "start", correlation, { target: "storage/upload" });
|
|
1915
1955
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
1916
1956
|
logProxyEvent("warn", "proxy_upload_wallet_mismatch", {
|
|
1917
1957
|
request_wallet: requestPayload.wallet_address,
|
|
1918
1958
|
proxy_wallet: account.address
|
|
1919
1959
|
});
|
|
1960
|
+
emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
|
|
1920
1961
|
sendJson(res, 403, {
|
|
1921
1962
|
error: "wallet_proof_invalid",
|
|
1922
1963
|
message: "wallet proof invalid"
|
|
@@ -1932,6 +1973,7 @@ async function startProxy(options) {
|
|
|
1932
1973
|
logProxyEvent("warn", "proxy_upload_wallet_signature_missing");
|
|
1933
1974
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1934
1975
|
res.end(createWalletRequiredBody());
|
|
1976
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
|
|
1935
1977
|
return;
|
|
1936
1978
|
}
|
|
1937
1979
|
const requiredMicros = BigInt(
|
|
@@ -1951,11 +1993,12 @@ async function startProxy(options) {
|
|
|
1951
1993
|
requiredUSD,
|
|
1952
1994
|
walletAddress: requestPayload.wallet_address
|
|
1953
1995
|
});
|
|
1996
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "insufficient_balance" });
|
|
1954
1997
|
sendJson(res, 400, {
|
|
1955
1998
|
error: "insufficient_balance",
|
|
1956
1999
|
message: `Insufficient USDC balance. Current: ${sufficiency.info.balanceUSD}, Required: ${requiredUSD}`,
|
|
1957
2000
|
wallet: requestPayload.wallet_address,
|
|
1958
|
-
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /
|
|
2001
|
+
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /mnemospark_cloud upload`
|
|
1959
2002
|
});
|
|
1960
2003
|
return;
|
|
1961
2004
|
}
|
|
@@ -1978,6 +2021,7 @@ async function startProxy(options) {
|
|
|
1978
2021
|
logProxyEvent("warn", "proxy_upload_settle_signature_missing");
|
|
1979
2022
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1980
2023
|
res.end(createWalletRequiredBody());
|
|
2024
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "settle_signature_missing" });
|
|
1981
2025
|
return;
|
|
1982
2026
|
}
|
|
1983
2027
|
const uploadPaymentFetch = createPaymentFetch(walletPrivateKey).fetch;
|
|
@@ -2007,6 +2051,9 @@ async function startProxy(options) {
|
|
|
2007
2051
|
});
|
|
2008
2052
|
res.writeHead(settleResponse.status, responseHeaders2);
|
|
2009
2053
|
res.end(settleResponse.bodyText);
|
|
2054
|
+
emitProxyTerminalFromStatus(correlation, settleResponse.status, {
|
|
2055
|
+
reason: "settle_failed"
|
|
2056
|
+
});
|
|
2010
2057
|
return;
|
|
2011
2058
|
}
|
|
2012
2059
|
if (settledAlready) {
|
|
@@ -2018,6 +2065,7 @@ async function startProxy(options) {
|
|
|
2018
2065
|
status: settleResponse.status
|
|
2019
2066
|
});
|
|
2020
2067
|
}
|
|
2068
|
+
emitProxyEvent("storage.call", "start", correlation, { target: "storage/upload" });
|
|
2021
2069
|
const backendResponse = await forwardStorageUploadToBackend(requestPayload, {
|
|
2022
2070
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
2023
2071
|
walletSignature,
|
|
@@ -2042,14 +2090,13 @@ async function startProxy(options) {
|
|
|
2042
2090
|
});
|
|
2043
2091
|
res.writeHead(authFailure.status, responseHeaders2);
|
|
2044
2092
|
res.end(authFailure.bodyText);
|
|
2093
|
+
emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
|
|
2045
2094
|
return;
|
|
2046
2095
|
}
|
|
2047
2096
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
2048
2097
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
2049
2098
|
res.end(backendResponse.bodyText);
|
|
2050
|
-
|
|
2051
|
-
status: backendResponse.status
|
|
2052
|
-
});
|
|
2099
|
+
emitProxyTerminalFromStatus(correlation, backendResponse.status);
|
|
2053
2100
|
} catch (err) {
|
|
2054
2101
|
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2055
2102
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2059,39 +2106,47 @@ async function startProxy(options) {
|
|
|
2059
2106
|
});
|
|
2060
2107
|
sendJson(res, 502, {
|
|
2061
2108
|
error: "proxy_error",
|
|
2062
|
-
message: `Failed to forward /
|
|
2109
|
+
message: `Failed to forward /mnemospark_cloud upload: ${err instanceof Error ? err.message : String(err)}`
|
|
2063
2110
|
});
|
|
2064
2111
|
}
|
|
2065
2112
|
return;
|
|
2066
2113
|
}
|
|
2067
2114
|
if (req.method === "POST" && matchesProxyPath(req.url, UPLOAD_CONFIRM_PROXY_PATH)) {
|
|
2115
|
+
const correlation = createProxyCorrelation(req.headers);
|
|
2068
2116
|
logProxyEvent("info", "proxy_upload_confirm_received");
|
|
2117
|
+
emitProxyEvent("request.received", "start", correlation, { path: UPLOAD_CONFIRM_PROXY_PATH });
|
|
2069
2118
|
try {
|
|
2070
2119
|
let payload;
|
|
2071
2120
|
try {
|
|
2072
2121
|
payload = await readProxyJsonBody(req);
|
|
2073
2122
|
} catch {
|
|
2074
2123
|
logProxyEvent("warn", "proxy_upload_confirm_invalid_json");
|
|
2124
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2075
2125
|
sendJson(res, 400, {
|
|
2076
2126
|
error: "Bad request",
|
|
2077
|
-
message: "Invalid JSON body for /
|
|
2127
|
+
message: "Invalid JSON body for /mnemospark_cloud upload/confirm"
|
|
2078
2128
|
});
|
|
2079
2129
|
return;
|
|
2080
2130
|
}
|
|
2081
2131
|
const requestPayload = parseStorageUploadConfirmRequest(payload);
|
|
2082
2132
|
if (!requestPayload) {
|
|
2083
2133
|
logProxyEvent("warn", "proxy_upload_confirm_missing_fields");
|
|
2134
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
2084
2135
|
sendJson(res, 400, {
|
|
2085
2136
|
error: "Bad request",
|
|
2086
2137
|
message: "Missing required fields: quote_id, wallet_address, object_key, idempotency_key"
|
|
2087
2138
|
});
|
|
2088
2139
|
return;
|
|
2089
2140
|
}
|
|
2141
|
+
correlation.quote_id = requestPayload.quote_id;
|
|
2142
|
+
correlation.wallet_address = requestPayload.wallet_address;
|
|
2143
|
+
correlation.object_key = requestPayload.object_key;
|
|
2090
2144
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
2091
2145
|
logProxyEvent("warn", "proxy_upload_confirm_wallet_mismatch", {
|
|
2092
2146
|
request_wallet: requestPayload.wallet_address,
|
|
2093
2147
|
proxy_wallet: account.address
|
|
2094
2148
|
});
|
|
2149
|
+
emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
|
|
2095
2150
|
sendJson(res, 403, {
|
|
2096
2151
|
error: "wallet_proof_invalid",
|
|
2097
2152
|
message: "wallet proof invalid"
|
|
@@ -2107,8 +2162,10 @@ async function startProxy(options) {
|
|
|
2107
2162
|
logProxyEvent("warn", "proxy_upload_confirm_wallet_signature_missing");
|
|
2108
2163
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2109
2164
|
res.end(createWalletRequiredBody());
|
|
2165
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
|
|
2110
2166
|
return;
|
|
2111
2167
|
}
|
|
2168
|
+
emitProxyEvent("storage.call", "start", correlation, { target: "storage/upload/confirm" });
|
|
2112
2169
|
const backendResponse = await forwardStorageUploadConfirmToBackend(requestPayload, {
|
|
2113
2170
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
2114
2171
|
walletSignature
|
|
@@ -2116,6 +2173,7 @@ async function startProxy(options) {
|
|
|
2116
2173
|
logProxyEvent("info", "proxy_upload_confirm_backend_response", {
|
|
2117
2174
|
status: backendResponse.status
|
|
2118
2175
|
});
|
|
2176
|
+
emitProxyEvent("storage.call", "result", correlation, { status: backendResponse.status });
|
|
2119
2177
|
const authFailure = normalizeBackendAuthFailure(
|
|
2120
2178
|
backendResponse.status,
|
|
2121
2179
|
backendResponse.bodyText
|
|
@@ -2131,27 +2189,29 @@ async function startProxy(options) {
|
|
|
2131
2189
|
});
|
|
2132
2190
|
res.writeHead(authFailure.status, responseHeaders2);
|
|
2133
2191
|
res.end(authFailure.bodyText);
|
|
2192
|
+
emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
|
|
2134
2193
|
return;
|
|
2135
2194
|
}
|
|
2136
2195
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
2137
2196
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
2138
2197
|
res.end(backendResponse.bodyText);
|
|
2198
|
+
emitProxyTerminalFromStatus(correlation, backendResponse.status);
|
|
2139
2199
|
} catch (err) {
|
|
2200
|
+
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2201
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2202
|
+
});
|
|
2140
2203
|
logProxyEvent("error", "proxy_upload_confirm_forward_failed", {
|
|
2141
2204
|
error: err instanceof Error ? err.message : String(err)
|
|
2142
2205
|
});
|
|
2143
2206
|
sendJson(res, 502, {
|
|
2144
2207
|
error: "proxy_error",
|
|
2145
|
-
message: `Failed to forward /
|
|
2208
|
+
message: `Failed to forward /mnemospark_cloud upload/confirm: ${err instanceof Error ? err.message : String(err)}`
|
|
2146
2209
|
});
|
|
2147
2210
|
}
|
|
2148
2211
|
return;
|
|
2149
2212
|
}
|
|
2150
2213
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_LS_PROXY_PATH)) {
|
|
2151
|
-
const correlation =
|
|
2152
|
-
trace_id: randomUUID(),
|
|
2153
|
-
operation_id: randomUUID()
|
|
2154
|
-
};
|
|
2214
|
+
const correlation = createProxyCorrelation(req.headers);
|
|
2155
2215
|
logProxyEvent("info", "proxy_ls_received");
|
|
2156
2216
|
emitProxyEvent("request.received", "start", correlation, { path: STORAGE_LS_PROXY_PATH });
|
|
2157
2217
|
try {
|
|
@@ -2160,15 +2220,17 @@ async function startProxy(options) {
|
|
|
2160
2220
|
payload = await readProxyJsonBody(req);
|
|
2161
2221
|
} catch {
|
|
2162
2222
|
logProxyEvent("warn", "proxy_ls_invalid_json");
|
|
2223
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2163
2224
|
sendJson(res, 400, {
|
|
2164
2225
|
error: "Bad request",
|
|
2165
|
-
message: "Invalid JSON body for /
|
|
2226
|
+
message: "Invalid JSON body for /mnemospark_cloud ls"
|
|
2166
2227
|
});
|
|
2167
2228
|
return;
|
|
2168
2229
|
}
|
|
2169
2230
|
const requestPayload = parseStorageObjectRequest(payload);
|
|
2170
2231
|
if (!requestPayload) {
|
|
2171
2232
|
logProxyEvent("warn", "proxy_ls_missing_fields");
|
|
2233
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
2172
2234
|
sendJson(res, 400, {
|
|
2173
2235
|
error: "Bad request",
|
|
2174
2236
|
message: "Missing required fields: wallet_address, object_key"
|
|
@@ -2179,6 +2241,7 @@ async function startProxy(options) {
|
|
|
2179
2241
|
correlation.object_key = requestPayload.object_key;
|
|
2180
2242
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
2181
2243
|
logProxyEvent("warn", "proxy_ls_wallet_mismatch");
|
|
2244
|
+
emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
|
|
2182
2245
|
sendJson(res, 403, {
|
|
2183
2246
|
error: "wallet_proof_invalid",
|
|
2184
2247
|
message: "wallet proof invalid"
|
|
@@ -2194,6 +2257,7 @@ async function startProxy(options) {
|
|
|
2194
2257
|
logProxyEvent("warn", "proxy_ls_wallet_signature_missing");
|
|
2195
2258
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2196
2259
|
res.end(createWalletRequiredBody());
|
|
2260
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
|
|
2197
2261
|
return;
|
|
2198
2262
|
}
|
|
2199
2263
|
emitProxyEvent("storage.call", "start", correlation, { target: "storage/ls" });
|
|
@@ -2216,14 +2280,13 @@ async function startProxy(options) {
|
|
|
2216
2280
|
});
|
|
2217
2281
|
res.writeHead(authFailure.status, responseHeaders2);
|
|
2218
2282
|
res.end(authFailure.bodyText);
|
|
2283
|
+
emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
|
|
2219
2284
|
return;
|
|
2220
2285
|
}
|
|
2221
2286
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
2222
2287
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
2223
2288
|
res.end(backendResponse.bodyText);
|
|
2224
|
-
|
|
2225
|
-
status: backendResponse.status
|
|
2226
|
-
});
|
|
2289
|
+
emitProxyTerminalFromStatus(correlation, backendResponse.status);
|
|
2227
2290
|
} catch (err) {
|
|
2228
2291
|
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2229
2292
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2233,16 +2296,13 @@ async function startProxy(options) {
|
|
|
2233
2296
|
});
|
|
2234
2297
|
sendJson(res, 502, {
|
|
2235
2298
|
error: "proxy_error",
|
|
2236
|
-
message: `Failed to forward /
|
|
2299
|
+
message: `Failed to forward /mnemospark_cloud ls: ${err instanceof Error ? err.message : String(err)}`
|
|
2237
2300
|
});
|
|
2238
2301
|
}
|
|
2239
2302
|
return;
|
|
2240
2303
|
}
|
|
2241
2304
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_DOWNLOAD_PROXY_PATH)) {
|
|
2242
|
-
const correlation =
|
|
2243
|
-
trace_id: randomUUID(),
|
|
2244
|
-
operation_id: randomUUID()
|
|
2245
|
-
};
|
|
2305
|
+
const correlation = createProxyCorrelation(req.headers);
|
|
2246
2306
|
logProxyEvent("info", "proxy_download_received");
|
|
2247
2307
|
emitProxyEvent("request.received", "start", correlation, {
|
|
2248
2308
|
path: STORAGE_DOWNLOAD_PROXY_PATH
|
|
@@ -2253,15 +2313,17 @@ async function startProxy(options) {
|
|
|
2253
2313
|
payload = await readProxyJsonBody(req);
|
|
2254
2314
|
} catch {
|
|
2255
2315
|
logProxyEvent("warn", "proxy_download_invalid_json");
|
|
2316
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2256
2317
|
sendJson(res, 400, {
|
|
2257
2318
|
error: "Bad request",
|
|
2258
|
-
message: "Invalid JSON body for /
|
|
2319
|
+
message: "Invalid JSON body for /mnemospark_cloud download"
|
|
2259
2320
|
});
|
|
2260
2321
|
return;
|
|
2261
2322
|
}
|
|
2262
2323
|
const requestPayload = parseStorageObjectRequest(payload);
|
|
2263
2324
|
if (!requestPayload) {
|
|
2264
2325
|
logProxyEvent("warn", "proxy_download_missing_fields");
|
|
2326
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
2265
2327
|
sendJson(res, 400, {
|
|
2266
2328
|
error: "Bad request",
|
|
2267
2329
|
message: "Missing required fields: wallet_address, object_key"
|
|
@@ -2272,6 +2334,7 @@ async function startProxy(options) {
|
|
|
2272
2334
|
correlation.object_key = requestPayload.object_key;
|
|
2273
2335
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
2274
2336
|
logProxyEvent("warn", "proxy_download_wallet_mismatch");
|
|
2337
|
+
emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
|
|
2275
2338
|
sendJson(res, 403, {
|
|
2276
2339
|
error: "wallet_proof_invalid",
|
|
2277
2340
|
message: "wallet proof invalid"
|
|
@@ -2287,6 +2350,7 @@ async function startProxy(options) {
|
|
|
2287
2350
|
logProxyEvent("warn", "proxy_download_wallet_signature_missing");
|
|
2288
2351
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2289
2352
|
res.end(createWalletRequiredBody());
|
|
2353
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
|
|
2290
2354
|
return;
|
|
2291
2355
|
}
|
|
2292
2356
|
emitProxyEvent("storage.call", "start", correlation, { target: "storage/download" });
|
|
@@ -2313,6 +2377,7 @@ async function startProxy(options) {
|
|
|
2313
2377
|
});
|
|
2314
2378
|
res.writeHead(authFailure.status, responseHeaders);
|
|
2315
2379
|
res.end(authFailure.bodyText);
|
|
2380
|
+
emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
|
|
2316
2381
|
return;
|
|
2317
2382
|
}
|
|
2318
2383
|
if (backendResponse.status < 200 || backendResponse.status >= 300) {
|
|
@@ -2322,6 +2387,7 @@ async function startProxy(options) {
|
|
|
2322
2387
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
2323
2388
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
2324
2389
|
res.end(backendResponse.bodyText);
|
|
2390
|
+
emitProxyTerminalFromStatus(correlation, backendResponse.status);
|
|
2325
2391
|
return;
|
|
2326
2392
|
}
|
|
2327
2393
|
const downloadResult = await downloadStorageToDisk(requestPayload, backendResponse, {
|
|
@@ -2338,9 +2404,7 @@ async function startProxy(options) {
|
|
|
2338
2404
|
file_path: downloadResult.filePath,
|
|
2339
2405
|
bytes_written: downloadResult.bytesWritten
|
|
2340
2406
|
});
|
|
2341
|
-
|
|
2342
|
-
status: 200
|
|
2343
|
-
});
|
|
2407
|
+
emitProxyTerminalFromStatus(correlation, 200);
|
|
2344
2408
|
} catch (err) {
|
|
2345
2409
|
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2346
2410
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2350,16 +2414,13 @@ async function startProxy(options) {
|
|
|
2350
2414
|
});
|
|
2351
2415
|
sendJson(res, 502, {
|
|
2352
2416
|
error: "proxy_error",
|
|
2353
|
-
message: `Failed to forward /
|
|
2417
|
+
message: `Failed to forward /mnemospark_cloud download: ${err instanceof Error ? err.message : String(err)}`
|
|
2354
2418
|
});
|
|
2355
2419
|
}
|
|
2356
2420
|
return;
|
|
2357
2421
|
}
|
|
2358
2422
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_DELETE_PROXY_PATH)) {
|
|
2359
|
-
const correlation =
|
|
2360
|
-
trace_id: randomUUID(),
|
|
2361
|
-
operation_id: randomUUID()
|
|
2362
|
-
};
|
|
2423
|
+
const correlation = createProxyCorrelation(req.headers);
|
|
2363
2424
|
logProxyEvent("info", "proxy_delete_received");
|
|
2364
2425
|
emitProxyEvent("request.received", "start", correlation, { path: STORAGE_DELETE_PROXY_PATH });
|
|
2365
2426
|
try {
|
|
@@ -2368,15 +2429,17 @@ async function startProxy(options) {
|
|
|
2368
2429
|
payload = await readProxyJsonBody(req);
|
|
2369
2430
|
} catch {
|
|
2370
2431
|
logProxyEvent("warn", "proxy_delete_invalid_json");
|
|
2432
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2371
2433
|
sendJson(res, 400, {
|
|
2372
2434
|
error: "Bad request",
|
|
2373
|
-
message: "Invalid JSON body for /
|
|
2435
|
+
message: "Invalid JSON body for /mnemospark_cloud delete"
|
|
2374
2436
|
});
|
|
2375
2437
|
return;
|
|
2376
2438
|
}
|
|
2377
2439
|
const requestPayload = parseStorageObjectRequest(payload);
|
|
2378
2440
|
if (!requestPayload) {
|
|
2379
2441
|
logProxyEvent("warn", "proxy_delete_missing_fields");
|
|
2442
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
|
|
2380
2443
|
sendJson(res, 400, {
|
|
2381
2444
|
error: "Bad request",
|
|
2382
2445
|
message: "Missing required fields: wallet_address, object_key"
|
|
@@ -2387,6 +2450,7 @@ async function startProxy(options) {
|
|
|
2387
2450
|
correlation.object_key = requestPayload.object_key;
|
|
2388
2451
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
2389
2452
|
logProxyEvent("warn", "proxy_delete_wallet_mismatch");
|
|
2453
|
+
emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
|
|
2390
2454
|
sendJson(res, 403, {
|
|
2391
2455
|
error: "wallet_proof_invalid",
|
|
2392
2456
|
message: "wallet proof invalid"
|
|
@@ -2402,6 +2466,7 @@ async function startProxy(options) {
|
|
|
2402
2466
|
logProxyEvent("warn", "proxy_delete_wallet_signature_missing");
|
|
2403
2467
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2404
2468
|
res.end(createWalletRequiredBody());
|
|
2469
|
+
emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
|
|
2405
2470
|
return;
|
|
2406
2471
|
}
|
|
2407
2472
|
emitProxyEvent("storage.call", "start", correlation, { target: "storage/delete" });
|
|
@@ -2428,14 +2493,13 @@ async function startProxy(options) {
|
|
|
2428
2493
|
});
|
|
2429
2494
|
res.writeHead(authFailure.status, responseHeaders2);
|
|
2430
2495
|
res.end(authFailure.bodyText);
|
|
2496
|
+
emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
|
|
2431
2497
|
return;
|
|
2432
2498
|
}
|
|
2433
2499
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
2434
2500
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
2435
2501
|
res.end(backendResponse.bodyText);
|
|
2436
|
-
|
|
2437
|
-
status: backendResponse.status
|
|
2438
|
-
});
|
|
2502
|
+
emitProxyTerminalFromStatus(correlation, backendResponse.status);
|
|
2439
2503
|
} catch (err) {
|
|
2440
2504
|
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2441
2505
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2445,7 +2509,7 @@ async function startProxy(options) {
|
|
|
2445
2509
|
});
|
|
2446
2510
|
sendJson(res, 502, {
|
|
2447
2511
|
error: "proxy_error",
|
|
2448
|
-
message: `Failed to forward /
|
|
2512
|
+
message: `Failed to forward /mnemospark_cloud delete: ${err instanceof Error ? err.message : String(err)}`
|
|
2449
2513
|
});
|
|
2450
2514
|
}
|
|
2451
2515
|
return;
|
|
@@ -2691,7 +2755,7 @@ import { mkdir as mkdir4 } from "fs/promises";
|
|
|
2691
2755
|
import { homedir as homedir5 } from "os";
|
|
2692
2756
|
import { dirname as dirname4, join as join7 } from "path";
|
|
2693
2757
|
var DB_SUBPATH = join7(".openclaw", "mnemospark", "state.db");
|
|
2694
|
-
var SCHEMA_VERSION =
|
|
2758
|
+
var SCHEMA_VERSION = 3;
|
|
2695
2759
|
function resolveDbPath(homeDir) {
|
|
2696
2760
|
return join7(homeDir ?? homedir5(), DB_SUBPATH);
|
|
2697
2761
|
}
|
|
@@ -2797,6 +2861,21 @@ async function createCloudDatastore(homeDir) {
|
|
|
2797
2861
|
CREATE INDEX IF NOT EXISTS idx_friendly_names_wallet ON friendly_names(wallet_address);
|
|
2798
2862
|
CREATE INDEX IF NOT EXISTS idx_friendly_names_created_at ON friendly_names(created_at);
|
|
2799
2863
|
`);
|
|
2864
|
+
const addOperationsColumn = (columnName, sqlType) => {
|
|
2865
|
+
try {
|
|
2866
|
+
nextDb.exec(`ALTER TABLE operations ADD COLUMN ${columnName} ${sqlType}`);
|
|
2867
|
+
} catch (error) {
|
|
2868
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error);
|
|
2869
|
+
if (!message.includes("duplicate column name")) {
|
|
2870
|
+
throw error;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
};
|
|
2874
|
+
addOperationsColumn("trace_id", "TEXT");
|
|
2875
|
+
addOperationsColumn("orchestrator", "TEXT");
|
|
2876
|
+
addOperationsColumn("subagent_session_id", "TEXT");
|
|
2877
|
+
addOperationsColumn("timeout_seconds", "INTEGER");
|
|
2878
|
+
addOperationsColumn("cancel_requested_at", "TEXT");
|
|
2800
2879
|
nextDb.prepare(
|
|
2801
2880
|
`INSERT INTO schema_migrations(version, applied_at)
|
|
2802
2881
|
VALUES(?, ?)
|
|
@@ -2808,7 +2887,10 @@ async function createCloudDatastore(homeDir) {
|
|
|
2808
2887
|
try {
|
|
2809
2888
|
await ensureReady();
|
|
2810
2889
|
return fn();
|
|
2811
|
-
} catch {
|
|
2890
|
+
} catch (error) {
|
|
2891
|
+
if (process.env.MNEMOSPARK_SQLITE_STRICT === "1") {
|
|
2892
|
+
throw error;
|
|
2893
|
+
}
|
|
2812
2894
|
return fallback;
|
|
2813
2895
|
}
|
|
2814
2896
|
};
|
|
@@ -2933,13 +3015,19 @@ async function createCloudDatastore(homeDir) {
|
|
|
2933
3015
|
upsertOperation: async (row) => {
|
|
2934
3016
|
await safe(() => {
|
|
2935
3017
|
const ts = nowIso();
|
|
3018
|
+
const terminalStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "timed_out"]);
|
|
2936
3019
|
db.prepare(
|
|
2937
|
-
`INSERT INTO operations(operation_id, type, object_id, quote_id, status, error_code, error_message, started_at, finished_at, created_at, updated_at)
|
|
2938
|
-
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3020
|
+
`INSERT INTO operations(operation_id, type, object_id, quote_id, trace_id, orchestrator, subagent_session_id, timeout_seconds, cancel_requested_at, status, error_code, error_message, started_at, finished_at, created_at, updated_at)
|
|
3021
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2939
3022
|
ON CONFLICT(operation_id) DO UPDATE SET
|
|
2940
3023
|
type=excluded.type,
|
|
2941
|
-
object_id=excluded.object_id,
|
|
2942
|
-
quote_id=excluded.quote_id,
|
|
3024
|
+
object_id=COALESCE(excluded.object_id, operations.object_id),
|
|
3025
|
+
quote_id=COALESCE(excluded.quote_id, operations.quote_id),
|
|
3026
|
+
trace_id=COALESCE(excluded.trace_id, operations.trace_id),
|
|
3027
|
+
orchestrator=COALESCE(excluded.orchestrator, operations.orchestrator),
|
|
3028
|
+
subagent_session_id=COALESCE(excluded.subagent_session_id, operations.subagent_session_id),
|
|
3029
|
+
timeout_seconds=COALESCE(excluded.timeout_seconds, operations.timeout_seconds),
|
|
3030
|
+
cancel_requested_at=COALESCE(excluded.cancel_requested_at, operations.cancel_requested_at),
|
|
2943
3031
|
status=excluded.status,
|
|
2944
3032
|
error_code=excluded.error_code,
|
|
2945
3033
|
error_message=excluded.error_message,
|
|
@@ -2951,11 +3039,16 @@ async function createCloudDatastore(homeDir) {
|
|
|
2951
3039
|
row.type,
|
|
2952
3040
|
row.object_id,
|
|
2953
3041
|
row.quote_id,
|
|
3042
|
+
row.trace_id ?? null,
|
|
3043
|
+
row.orchestrator ?? null,
|
|
3044
|
+
row.subagent_session_id ?? null,
|
|
3045
|
+
row.timeout_seconds ?? null,
|
|
3046
|
+
row.cancel_requested_at ?? null,
|
|
2954
3047
|
row.status,
|
|
2955
3048
|
row.error_code,
|
|
2956
3049
|
row.error_message,
|
|
2957
3050
|
row.status === "started" ? ts : null,
|
|
2958
|
-
|
|
3051
|
+
terminalStatuses.has(row.status) ? ts : null,
|
|
2959
3052
|
ts,
|
|
2960
3053
|
ts
|
|
2961
3054
|
);
|
|
@@ -2963,7 +3056,7 @@ async function createCloudDatastore(homeDir) {
|
|
|
2963
3056
|
},
|
|
2964
3057
|
findOperationById: async (operationId) => safe(() => {
|
|
2965
3058
|
const row = db.prepare(
|
|
2966
|
-
`SELECT operation_id, type, status, error_code, error_message, started_at, finished_at, updated_at
|
|
3059
|
+
`SELECT operation_id, type, object_id, quote_id, trace_id, orchestrator, subagent_session_id, timeout_seconds, cancel_requested_at, status, error_code, error_message, started_at, finished_at, updated_at
|
|
2967
3060
|
FROM operations
|
|
2968
3061
|
WHERE operation_id = ?
|
|
2969
3062
|
LIMIT 1`
|
|
@@ -3065,7 +3158,9 @@ var REQUIRED_UPLOAD = "--quote-id, --wallet-address, --object-id, --object-id-ha
|
|
|
3065
3158
|
var REQUIRED_STORAGE_OBJECT = "--wallet-address and one of (--object-key | --name [--latest|--at])";
|
|
3066
3159
|
var BOOLEAN_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["latest"]);
|
|
3067
3160
|
var BOOLEAN_ASYNC_FLAGS = /* @__PURE__ */ new Set(["async"]);
|
|
3161
|
+
var BOOLEAN_OP_STATUS_FLAGS = /* @__PURE__ */ new Set(["cancel"]);
|
|
3068
3162
|
var BOOLEAN_SELECTOR_AND_ASYNC_FLAGS = /* @__PURE__ */ new Set(["latest", "async"]);
|
|
3163
|
+
var ORCHESTRATOR_MODES = /* @__PURE__ */ new Set(["inline", "subagent"]);
|
|
3069
3164
|
function expandTilde(path) {
|
|
3070
3165
|
const trimmed = path.trim();
|
|
3071
3166
|
if (trimmed === "~") {
|
|
@@ -3079,29 +3174,54 @@ function expandTilde(path) {
|
|
|
3079
3174
|
var CLOUD_HELP_TEXT = [
|
|
3080
3175
|
"\u2601\uFE0F **mnemospark Cloud Commands**",
|
|
3081
3176
|
"",
|
|
3082
|
-
"\u2022 `/
|
|
3177
|
+
"\u2022 `/mnemospark_cloud` or `/mnemospark_cloud help` \u2014 show this message",
|
|
3083
3178
|
"",
|
|
3084
|
-
"\u2022 `/
|
|
3085
|
-
"
|
|
3179
|
+
"\u2022 `/mnemospark_cloud backup <file|directory> [--name <friendly-name>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3180
|
+
" Purpose: create a local tar+gzip backup object and index it for later upload.",
|
|
3181
|
+
" Required: <file|directory>",
|
|
3086
3182
|
"",
|
|
3087
|
-
"\u2022 `/
|
|
3183
|
+
"\u2022 `/mnemospark_cloud price-storage --wallet-address <addr> --object-id <id> --object-id-hash <hash> --gb <gb> --provider <provider> --region <region>`",
|
|
3184
|
+
" Purpose: request a storage quote before upload.",
|
|
3088
3185
|
" Required: " + REQUIRED_PRICE_STORAGE,
|
|
3089
3186
|
"",
|
|
3090
|
-
"\u2022 `/
|
|
3187
|
+
"\u2022 `/mnemospark_cloud upload --quote-id <quote-id> --wallet-address <addr> --object-id <id> --object-id-hash <hash> [--name <friendly-name>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3188
|
+
" Purpose: upload an encrypted object using a valid quote-id.",
|
|
3091
3189
|
" Required: " + REQUIRED_UPLOAD,
|
|
3092
3190
|
"",
|
|
3093
|
-
"\u2022 `/
|
|
3191
|
+
"\u2022 `/mnemospark_cloud ls --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
3192
|
+
" Purpose: look up remote object metadata.",
|
|
3094
3193
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3095
3194
|
"",
|
|
3096
|
-
"\u2022 `/
|
|
3195
|
+
"\u2022 `/mnemospark_cloud download --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3196
|
+
" Purpose: fetch an object to local disk.",
|
|
3097
3197
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3098
3198
|
"",
|
|
3099
|
-
"\u2022 `/
|
|
3199
|
+
"\u2022 `/mnemospark_cloud delete --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
3200
|
+
" Purpose: remove a remote object and local cron tracking when present.",
|
|
3100
3201
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3101
3202
|
"",
|
|
3102
|
-
"\u2022 `/
|
|
3203
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id> [--cancel]`",
|
|
3204
|
+
" Purpose: inspect async operation status, or request cancellation for subagent runs.",
|
|
3103
3205
|
" Required: --operation-id",
|
|
3104
3206
|
"",
|
|
3207
|
+
"Async orchestration flags (`backup`, `upload`, `download` only):",
|
|
3208
|
+
"\u2022 `--async`",
|
|
3209
|
+
" Start operation in background and return quickly with operation-id.",
|
|
3210
|
+
"\u2022 `--orchestrator <inline|subagent>`",
|
|
3211
|
+
" Choose async engine. Default when omitted is `inline`.",
|
|
3212
|
+
" Use `subagent` for explicit subagent session tracking and cancellation.",
|
|
3213
|
+
"\u2022 `--timeout-seconds <n>`",
|
|
3214
|
+
" Optional per-operation timeout. Valid only with `--async --orchestrator subagent`.",
|
|
3215
|
+
" `n` must be a positive integer (seconds).",
|
|
3216
|
+
"\u2022 `op-status --cancel`",
|
|
3217
|
+
" Cancel a subagent-orchestrated operation by operation-id (idempotent).",
|
|
3218
|
+
"",
|
|
3219
|
+
"Examples:",
|
|
3220
|
+
"\u2022 `/mnemospark_cloud upload ... --async --orchestrator subagent`",
|
|
3221
|
+
"\u2022 `/mnemospark_cloud download ... --async --orchestrator subagent --timeout-seconds 900`",
|
|
3222
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id>`",
|
|
3223
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id> --cancel`",
|
|
3224
|
+
"",
|
|
3105
3225
|
"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. All storage commands (price-storage, upload, ls, download, delete) require --wallet-address."
|
|
3106
3226
|
].join("\n");
|
|
3107
3227
|
var UnsupportedBackupPlatformError = class extends Error {
|
|
@@ -3191,9 +3311,73 @@ function parseStorageObjectRequestInput(flags, selector) {
|
|
|
3191
3311
|
location
|
|
3192
3312
|
});
|
|
3193
3313
|
}
|
|
3194
|
-
function
|
|
3314
|
+
function parseOrchestratorMode(value) {
|
|
3315
|
+
if (!value) {
|
|
3316
|
+
return void 0;
|
|
3317
|
+
}
|
|
3318
|
+
const normalized = value.trim().toLowerCase();
|
|
3319
|
+
if (!normalized) {
|
|
3320
|
+
return null;
|
|
3321
|
+
}
|
|
3322
|
+
if (!ORCHESTRATOR_MODES.has(normalized)) {
|
|
3323
|
+
return null;
|
|
3324
|
+
}
|
|
3325
|
+
return normalized;
|
|
3326
|
+
}
|
|
3327
|
+
function parseTimeoutSeconds(value) {
|
|
3328
|
+
if (!value) {
|
|
3329
|
+
return void 0;
|
|
3330
|
+
}
|
|
3331
|
+
const trimmed = value.trim();
|
|
3332
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
3333
|
+
return null;
|
|
3334
|
+
}
|
|
3335
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
3336
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
3337
|
+
return null;
|
|
3338
|
+
}
|
|
3339
|
+
return parsed;
|
|
3340
|
+
}
|
|
3341
|
+
function parseAsyncOperationArgs(flags) {
|
|
3342
|
+
const asyncRequested = flags.async === "true";
|
|
3343
|
+
const hasOrchestratorFlag = typeof flags.orchestrator === "string";
|
|
3344
|
+
const hasTimeoutFlag = typeof flags["timeout-seconds"] === "string";
|
|
3345
|
+
if (!asyncRequested && (hasOrchestratorFlag || hasTimeoutFlag)) {
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3348
|
+
const parsedOrchestrator = parseOrchestratorMode(flags.orchestrator);
|
|
3349
|
+
if (parsedOrchestrator === null) {
|
|
3350
|
+
return null;
|
|
3351
|
+
}
|
|
3352
|
+
const parsedTimeoutSeconds = parseTimeoutSeconds(flags["timeout-seconds"]);
|
|
3353
|
+
if (parsedTimeoutSeconds === null) {
|
|
3354
|
+
return null;
|
|
3355
|
+
}
|
|
3356
|
+
if (typeof parsedTimeoutSeconds === "number" && (parsedOrchestrator ?? "inline") !== "subagent") {
|
|
3357
|
+
return null;
|
|
3358
|
+
}
|
|
3359
|
+
return {
|
|
3360
|
+
async: asyncRequested,
|
|
3361
|
+
orchestrator: parsedOrchestrator === void 0 ? void 0 : parsedOrchestrator,
|
|
3362
|
+
timeoutSeconds: parsedTimeoutSeconds === void 0 ? void 0 : parsedTimeoutSeconds
|
|
3363
|
+
};
|
|
3364
|
+
}
|
|
3365
|
+
var INVALID_ASYNC_FLAGS_MESSAGE = "invalid async flags. `--orchestrator`/`--timeout-seconds` require `--async`, and `--timeout-seconds` is only valid with `--orchestrator subagent`.";
|
|
3366
|
+
function stripAsyncControlFlags(args) {
|
|
3195
3367
|
const tokens = tokenizeArgsRaw(args ?? "");
|
|
3196
|
-
const filtered =
|
|
3368
|
+
const filtered = [];
|
|
3369
|
+
for (let idx = 0; idx < tokens.length; idx += 1) {
|
|
3370
|
+
const token = tokens[idx];
|
|
3371
|
+
const lowerToken = token.toLowerCase();
|
|
3372
|
+
if (lowerToken === "--async") {
|
|
3373
|
+
continue;
|
|
3374
|
+
}
|
|
3375
|
+
if (lowerToken === "--orchestrator" || lowerToken === "--timeout-seconds") {
|
|
3376
|
+
idx += 1;
|
|
3377
|
+
continue;
|
|
3378
|
+
}
|
|
3379
|
+
filtered.push(token);
|
|
3380
|
+
}
|
|
3197
3381
|
return filtered.join(" ");
|
|
3198
3382
|
}
|
|
3199
3383
|
function parseCloudArgs(args) {
|
|
@@ -3216,8 +3400,21 @@ function parseCloudArgs(args) {
|
|
|
3216
3400
|
if (!backupTarget) {
|
|
3217
3401
|
return { mode: "unknown" };
|
|
3218
3402
|
}
|
|
3219
|
-
const
|
|
3220
|
-
|
|
3403
|
+
const remainingTokens = tokens.slice(1);
|
|
3404
|
+
const flags = remainingTokens.length === 0 ? {} : parseNamedFlagsTokens(remainingTokens, BOOLEAN_ASYNC_FLAGS);
|
|
3405
|
+
if (!flags) {
|
|
3406
|
+
return { mode: "backup-invalid" };
|
|
3407
|
+
}
|
|
3408
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3409
|
+
if (!asyncArgs) {
|
|
3410
|
+
return { mode: "backup-invalid-async" };
|
|
3411
|
+
}
|
|
3412
|
+
return {
|
|
3413
|
+
mode: "backup",
|
|
3414
|
+
backupTarget,
|
|
3415
|
+
friendlyName: flags.name?.trim() || void 0,
|
|
3416
|
+
...asyncArgs
|
|
3417
|
+
};
|
|
3221
3418
|
}
|
|
3222
3419
|
if (subcommand === "price-storage") {
|
|
3223
3420
|
const flags = parseNamedFlags(rest);
|
|
@@ -3243,6 +3440,10 @@ function parseCloudArgs(args) {
|
|
|
3243
3440
|
if (!flags) {
|
|
3244
3441
|
return { mode: "upload-invalid" };
|
|
3245
3442
|
}
|
|
3443
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3444
|
+
if (!asyncArgs) {
|
|
3445
|
+
return { mode: "upload-invalid-async" };
|
|
3446
|
+
}
|
|
3246
3447
|
const quoteId = flags["quote-id"]?.trim();
|
|
3247
3448
|
const walletAddress = flags["wallet-address"]?.trim();
|
|
3248
3449
|
const objectId = flags["object-id"]?.trim();
|
|
@@ -3253,7 +3454,7 @@ function parseCloudArgs(args) {
|
|
|
3253
3454
|
return {
|
|
3254
3455
|
mode: "upload",
|
|
3255
3456
|
friendlyName: flags.name?.trim() || void 0,
|
|
3256
|
-
|
|
3457
|
+
...asyncArgs,
|
|
3257
3458
|
uploadRequest: {
|
|
3258
3459
|
quote_id: quoteId,
|
|
3259
3460
|
wallet_address: walletAddress,
|
|
@@ -3282,6 +3483,10 @@ function parseCloudArgs(args) {
|
|
|
3282
3483
|
if (!flags) {
|
|
3283
3484
|
return { mode: "download-invalid" };
|
|
3284
3485
|
}
|
|
3486
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3487
|
+
if (!asyncArgs) {
|
|
3488
|
+
return { mode: "download-invalid-async" };
|
|
3489
|
+
}
|
|
3285
3490
|
const selector = parseObjectSelector(flags);
|
|
3286
3491
|
if (!selector) {
|
|
3287
3492
|
return { mode: "download-invalid" };
|
|
@@ -3294,7 +3499,7 @@ function parseCloudArgs(args) {
|
|
|
3294
3499
|
mode: "download",
|
|
3295
3500
|
storageObjectRequest: request,
|
|
3296
3501
|
nameSelector: selector.nameSelector,
|
|
3297
|
-
|
|
3502
|
+
...asyncArgs
|
|
3298
3503
|
};
|
|
3299
3504
|
}
|
|
3300
3505
|
if (subcommand === "delete") {
|
|
@@ -3313,12 +3518,12 @@ function parseCloudArgs(args) {
|
|
|
3313
3518
|
return { mode: "delete", storageObjectRequest: request, nameSelector: selector.nameSelector };
|
|
3314
3519
|
}
|
|
3315
3520
|
if (subcommand === "op-status") {
|
|
3316
|
-
const flags = parseNamedFlags(rest);
|
|
3521
|
+
const flags = parseNamedFlags(rest, BOOLEAN_OP_STATUS_FLAGS);
|
|
3317
3522
|
const operationId = flags?.["operation-id"]?.trim();
|
|
3318
3523
|
if (!operationId) {
|
|
3319
3524
|
return { mode: "op-status-invalid" };
|
|
3320
3525
|
}
|
|
3321
|
-
return { mode: "op-status", operationId };
|
|
3526
|
+
return { mode: "op-status", operationId, cancel: flags?.cancel === "true" };
|
|
3322
3527
|
}
|
|
3323
3528
|
return { mode: "unknown" };
|
|
3324
3529
|
}
|
|
@@ -3809,17 +4014,37 @@ async function uploadPresignedObjectIfNeeded(uploadResponse, uploadMode, encrypt
|
|
|
3809
4014
|
if (!headers.has("content-type")) {
|
|
3810
4015
|
headers.set("content-type", "application/octet-stream");
|
|
3811
4016
|
}
|
|
3812
|
-
const
|
|
4017
|
+
const putBody = new Uint8Array(encryptedContent);
|
|
4018
|
+
const firstAttempt = await fetchImpl(uploadResponse.upload_url, {
|
|
3813
4019
|
method: "PUT",
|
|
3814
4020
|
headers,
|
|
3815
|
-
body:
|
|
4021
|
+
body: putBody,
|
|
4022
|
+
redirect: "manual"
|
|
3816
4023
|
});
|
|
3817
|
-
if (
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
);
|
|
4024
|
+
if (firstAttempt.ok) {
|
|
4025
|
+
return;
|
|
4026
|
+
}
|
|
4027
|
+
if ((firstAttempt.status === 307 || firstAttempt.status === 308) && firstAttempt.headers.has("location")) {
|
|
4028
|
+
const location = firstAttempt.headers.get("location")?.trim();
|
|
4029
|
+
if (location) {
|
|
4030
|
+
const redirectedAttempt = await fetchImpl(location, {
|
|
4031
|
+
method: "PUT",
|
|
4032
|
+
headers,
|
|
4033
|
+
body: putBody
|
|
4034
|
+
});
|
|
4035
|
+
if (redirectedAttempt.ok) {
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
4038
|
+
const redirectedDetails = (await redirectedAttempt.text()).trim();
|
|
4039
|
+
throw new Error(
|
|
4040
|
+
`Presigned upload failed after redirect with status ${redirectedAttempt.status}${redirectedDetails ? `: ${redirectedDetails}` : ""}`
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
3822
4043
|
}
|
|
4044
|
+
const details = (await firstAttempt.text()).trim();
|
|
4045
|
+
throw new Error(
|
|
4046
|
+
`Presigned upload failed with status ${firstAttempt.status}${details ? `: ${details}` : ""}`
|
|
4047
|
+
);
|
|
3823
4048
|
}
|
|
3824
4049
|
async function appendStorageUploadLog(upload, homeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
|
|
3825
4050
|
return appendObjectLogLine(
|
|
@@ -3890,16 +4115,118 @@ function extractUploadErrorMessage(error) {
|
|
|
3890
4115
|
function formatPriceStorageUserMessage(quote) {
|
|
3891
4116
|
return [
|
|
3892
4117
|
`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}\``,
|
|
3893
|
-
`If you accept this quote run the command /
|
|
4118
|
+
`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}\``
|
|
3894
4119
|
].join("\n");
|
|
3895
4120
|
}
|
|
3896
4121
|
function formatStorageLsUserMessage(result, requestedObjectKey) {
|
|
3897
4122
|
const objectId = result.object_id ?? result.key;
|
|
3898
4123
|
return `${objectId} with ${requestedObjectKey} is ${result.size_bytes} in ${result.bucket}`;
|
|
3899
4124
|
}
|
|
4125
|
+
function createInProcessSubagentOrchestrator() {
|
|
4126
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
4127
|
+
const completeSession = async (sessionId, handler) => {
|
|
4128
|
+
const session = sessions.get(sessionId);
|
|
4129
|
+
if (!session || session.terminal) {
|
|
4130
|
+
return false;
|
|
4131
|
+
}
|
|
4132
|
+
session.terminal = true;
|
|
4133
|
+
if (session.timeoutHandle) {
|
|
4134
|
+
clearTimeout(session.timeoutHandle);
|
|
4135
|
+
}
|
|
4136
|
+
sessions.delete(sessionId);
|
|
4137
|
+
await handler(session.hooks);
|
|
4138
|
+
return true;
|
|
4139
|
+
};
|
|
4140
|
+
return {
|
|
4141
|
+
dispatch: async (input) => {
|
|
4142
|
+
const sessionId = `agent:mnemospark:subagent:${randomUUID3()}`;
|
|
4143
|
+
const state = {
|
|
4144
|
+
terminal: false,
|
|
4145
|
+
cancelRequested: false,
|
|
4146
|
+
hooks: input.hooks
|
|
4147
|
+
};
|
|
4148
|
+
sessions.set(sessionId, state);
|
|
4149
|
+
if (typeof input.timeoutSeconds === "number" && input.timeoutSeconds > 0) {
|
|
4150
|
+
state.timeoutHandle = setTimeout(() => {
|
|
4151
|
+
void completeSession(sessionId, async (hooks) => {
|
|
4152
|
+
await hooks?.onTimedOut?.(sessionId);
|
|
4153
|
+
});
|
|
4154
|
+
}, input.timeoutSeconds * 1e3);
|
|
4155
|
+
}
|
|
4156
|
+
setTimeout(() => {
|
|
4157
|
+
void (async () => {
|
|
4158
|
+
try {
|
|
4159
|
+
await input.hooks?.onRunning?.(sessionId);
|
|
4160
|
+
await input.hooks?.onProgress?.(sessionId, "subagent execution started");
|
|
4161
|
+
const result = await input.runTask();
|
|
4162
|
+
const session = sessions.get(sessionId);
|
|
4163
|
+
if (!session || session.terminal) {
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
if (session.cancelRequested) {
|
|
4167
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4168
|
+
await hooks?.onCancelled?.(sessionId, "cancel requested");
|
|
4169
|
+
});
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
if (result.isError) {
|
|
4173
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4174
|
+
await hooks?.onFailed?.(sessionId, {
|
|
4175
|
+
code: "ASYNC_FAILED",
|
|
4176
|
+
message: result.text
|
|
4177
|
+
});
|
|
4178
|
+
});
|
|
4179
|
+
return;
|
|
4180
|
+
}
|
|
4181
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4182
|
+
await hooks?.onCompleted?.(sessionId, result);
|
|
4183
|
+
});
|
|
4184
|
+
} catch (error) {
|
|
4185
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4186
|
+
const session = sessions.get(sessionId);
|
|
4187
|
+
if (!session || session.terminal) {
|
|
4188
|
+
return;
|
|
4189
|
+
}
|
|
4190
|
+
if (session.cancelRequested) {
|
|
4191
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4192
|
+
await hooks?.onCancelled?.(sessionId, "cancel requested");
|
|
4193
|
+
});
|
|
4194
|
+
return;
|
|
4195
|
+
}
|
|
4196
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4197
|
+
await hooks?.onFailed?.(sessionId, {
|
|
4198
|
+
code: "ASYNC_EXCEPTION",
|
|
4199
|
+
message
|
|
4200
|
+
});
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
4203
|
+
})();
|
|
4204
|
+
}, 0);
|
|
4205
|
+
return { sessionId };
|
|
4206
|
+
},
|
|
4207
|
+
cancel: async (sessionId, reason) => {
|
|
4208
|
+
const session = sessions.get(sessionId);
|
|
4209
|
+
if (!session) {
|
|
4210
|
+
return { accepted: false };
|
|
4211
|
+
}
|
|
4212
|
+
if (session.terminal) {
|
|
4213
|
+
return { accepted: false, alreadyTerminal: true };
|
|
4214
|
+
}
|
|
4215
|
+
session.cancelRequested = true;
|
|
4216
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4217
|
+
await hooks?.onCancelled?.(sessionId, reason ?? "cancel requested");
|
|
4218
|
+
});
|
|
4219
|
+
return { accepted: true };
|
|
4220
|
+
}
|
|
4221
|
+
};
|
|
4222
|
+
}
|
|
3900
4223
|
function createCloudCommand(options = {}) {
|
|
4224
|
+
const subagentOrchestrator = options.subagentOrchestrator ?? createInProcessSubagentOrchestrator();
|
|
3901
4225
|
return {
|
|
3902
|
-
name: "
|
|
4226
|
+
name: "mnemospark_cloud",
|
|
4227
|
+
nativeNames: {
|
|
4228
|
+
default: "mnemospark_cloud"
|
|
4229
|
+
},
|
|
3903
4230
|
description: "Manage mnemospark cloud storage workflow commands",
|
|
3904
4231
|
acceptsArgs: true,
|
|
3905
4232
|
requireAuth: true,
|
|
@@ -3923,6 +4250,7 @@ function createCloudCommand(options = {}) {
|
|
|
3923
4250
|
proxyQuoteOptions: options.proxyQuoteOptions,
|
|
3924
4251
|
proxyUploadOptions: options.proxyUploadOptions,
|
|
3925
4252
|
proxyUploadConfirmOptions: options.proxyUploadConfirmOptions,
|
|
4253
|
+
subagentOrchestrator,
|
|
3926
4254
|
proxyStorageOptions: options.proxyStorageOptions
|
|
3927
4255
|
});
|
|
3928
4256
|
} catch (outerError) {
|
|
@@ -3932,7 +4260,42 @@ function createCloudCommand(options = {}) {
|
|
|
3932
4260
|
}
|
|
3933
4261
|
};
|
|
3934
4262
|
}
|
|
3935
|
-
async function
|
|
4263
|
+
async function resolveFriendlyNameFromManifest(params, homeDir) {
|
|
4264
|
+
const manifestPath = join8(homeDir ?? homedir6(), ".openclaw", "mnemospark", "manifest.jsonl");
|
|
4265
|
+
let manifestRaw;
|
|
4266
|
+
try {
|
|
4267
|
+
manifestRaw = await readFile3(manifestPath, "utf-8");
|
|
4268
|
+
} catch {
|
|
4269
|
+
return { objectKey: null, matchCount: 0 };
|
|
4270
|
+
}
|
|
4271
|
+
const wallet = params.walletAddress.trim();
|
|
4272
|
+
const name = params.friendlyName.trim();
|
|
4273
|
+
const atMs = params.at ? Date.parse(params.at) : Number.NaN;
|
|
4274
|
+
const hasAt = Number.isFinite(atMs);
|
|
4275
|
+
const rows = manifestRaw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
4276
|
+
try {
|
|
4277
|
+
return JSON.parse(line);
|
|
4278
|
+
} catch {
|
|
4279
|
+
return null;
|
|
4280
|
+
}
|
|
4281
|
+
}).filter((row) => Boolean(row)).filter((row) => {
|
|
4282
|
+
if (!row.object_key || !row.friendly_name || !row.wallet_address || !row.created_at)
|
|
4283
|
+
return false;
|
|
4284
|
+
if (row.friendly_name !== name) return false;
|
|
4285
|
+
if (row.wallet_address.trim() !== wallet) return false;
|
|
4286
|
+
if (params.latest || !hasAt) return true;
|
|
4287
|
+
const createdAtMs = Date.parse(row.created_at);
|
|
4288
|
+
return Number.isFinite(createdAtMs) && createdAtMs <= atMs;
|
|
4289
|
+
}).sort((a, b) => Date.parse(b.created_at ?? "") - Date.parse(a.created_at ?? ""));
|
|
4290
|
+
if (rows.length === 0) {
|
|
4291
|
+
return { objectKey: null, matchCount: 0 };
|
|
4292
|
+
}
|
|
4293
|
+
if (!params.latest && !hasAt && rows.length > 1) {
|
|
4294
|
+
return { objectKey: null, matchCount: rows.length };
|
|
4295
|
+
}
|
|
4296
|
+
return { objectKey: rows[0].object_key ?? null, matchCount: rows.length };
|
|
4297
|
+
}
|
|
4298
|
+
async function resolveNameSelectorIfNeeded(datastore, request, selector, homeDir) {
|
|
3936
4299
|
if (!selector) {
|
|
3937
4300
|
const parsedRequest2 = parseStorageObjectRequest(request);
|
|
3938
4301
|
if (!parsedRequest2) {
|
|
@@ -3940,6 +4303,12 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector) {
|
|
|
3940
4303
|
}
|
|
3941
4304
|
return { request: parsedRequest2 };
|
|
3942
4305
|
}
|
|
4306
|
+
let sqliteUnavailable = false;
|
|
4307
|
+
try {
|
|
4308
|
+
await datastore.ensureReady();
|
|
4309
|
+
} catch {
|
|
4310
|
+
sqliteUnavailable = true;
|
|
4311
|
+
}
|
|
3943
4312
|
const matches = await datastore.countFriendlyNameMatches(request.wallet_address, selector.name);
|
|
3944
4313
|
if (matches > 1 && !selector.latest && !selector.at) {
|
|
3945
4314
|
return {
|
|
@@ -3952,18 +4321,41 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector) {
|
|
|
3952
4321
|
latest: selector.latest,
|
|
3953
4322
|
at: selector.at
|
|
3954
4323
|
});
|
|
3955
|
-
|
|
4324
|
+
let resolvedObjectKey = resolved?.objectKey ?? null;
|
|
4325
|
+
let degradedWarning;
|
|
4326
|
+
if (!resolvedObjectKey && sqliteUnavailable) {
|
|
4327
|
+
const manifestResolved = await resolveFriendlyNameFromManifest(
|
|
4328
|
+
{
|
|
4329
|
+
walletAddress: request.wallet_address,
|
|
4330
|
+
friendlyName: selector.name,
|
|
4331
|
+
latest: selector.latest,
|
|
4332
|
+
at: selector.at
|
|
4333
|
+
},
|
|
4334
|
+
homeDir
|
|
4335
|
+
);
|
|
4336
|
+
if (manifestResolved.matchCount > 1 && !selector.latest && !selector.at) {
|
|
4337
|
+
return {
|
|
4338
|
+
error: `Multiple objects match --name ${selector.name}. Add --latest or --at <timestamp>.`
|
|
4339
|
+
};
|
|
4340
|
+
}
|
|
4341
|
+
resolvedObjectKey = manifestResolved.objectKey;
|
|
4342
|
+
if (resolvedObjectKey) {
|
|
4343
|
+
degradedWarning = "SQLite friendly-name index unavailable; resolved --name via manifest.jsonl fallback.";
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
if (!resolvedObjectKey) {
|
|
3956
4347
|
return { error: `No object found for --name ${selector.name}.` };
|
|
3957
4348
|
}
|
|
3958
4349
|
const parsedRequest = parseStorageObjectRequest({
|
|
3959
4350
|
...request,
|
|
3960
|
-
object_key:
|
|
4351
|
+
object_key: resolvedObjectKey
|
|
3961
4352
|
});
|
|
3962
4353
|
if (!parsedRequest) {
|
|
3963
4354
|
return { error: "Cannot resolve storage object request." };
|
|
3964
4355
|
}
|
|
3965
4356
|
return {
|
|
3966
|
-
request: parsedRequest
|
|
4357
|
+
request: parsedRequest,
|
|
4358
|
+
degradedWarning
|
|
3967
4359
|
};
|
|
3968
4360
|
}
|
|
3969
4361
|
async function emitCloudEvent(eventType, details, homeDir) {
|
|
@@ -3983,7 +4375,44 @@ async function emitCloudEventBestEffort(eventType, details, homeDir) {
|
|
|
3983
4375
|
} catch {
|
|
3984
4376
|
}
|
|
3985
4377
|
}
|
|
3986
|
-
|
|
4378
|
+
function toOperationEventPayload(eventType, context) {
|
|
4379
|
+
return {
|
|
4380
|
+
operation_id: context.operationId,
|
|
4381
|
+
trace_id: context.traceId,
|
|
4382
|
+
event_type: eventType,
|
|
4383
|
+
status: context.status,
|
|
4384
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4385
|
+
wallet_address: context.walletAddress ?? void 0,
|
|
4386
|
+
object_id: context.objectId ?? void 0,
|
|
4387
|
+
object_key: context.objectKey ?? void 0,
|
|
4388
|
+
quote_id: context.quoteId ?? void 0,
|
|
4389
|
+
orchestrator: context.orchestrator ?? void 0,
|
|
4390
|
+
"subagent-session-id": context.subagentSessionId ?? void 0,
|
|
4391
|
+
"timeout-seconds": context.timeoutSeconds ?? void 0,
|
|
4392
|
+
"error-code": context.errorCode ?? void 0,
|
|
4393
|
+
"error-message": context.errorMessage ?? void 0,
|
|
4394
|
+
progress: context.progressMessage ?? void 0
|
|
4395
|
+
};
|
|
4396
|
+
}
|
|
4397
|
+
async function emitOperationEvent(eventType, context, homeDir) {
|
|
4398
|
+
const payload = toOperationEventPayload(eventType, context);
|
|
4399
|
+
await Promise.all([
|
|
4400
|
+
appendJsonlEvent("events.jsonl", payload, homeDir),
|
|
4401
|
+
appendJsonlEvent("proxy-events.jsonl", payload, homeDir)
|
|
4402
|
+
]);
|
|
4403
|
+
}
|
|
4404
|
+
async function emitOperationEventBestEffort(eventType, context, homeDir) {
|
|
4405
|
+
try {
|
|
4406
|
+
await emitOperationEvent(eventType, context, homeDir);
|
|
4407
|
+
} catch {
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
|
|
4411
|
+
const operationId = forcedOperationId?.trim() || randomUUID3();
|
|
4412
|
+
const traceId = forcedTraceId?.trim() || randomUUID3();
|
|
4413
|
+
return { operationId, traceId };
|
|
4414
|
+
}
|
|
4415
|
+
async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
3987
4416
|
const parsed = parseCloudArgs(ctx.args);
|
|
3988
4417
|
const objectLogHomeDir = options.objectLogHomeDir;
|
|
3989
4418
|
const backupBuilder = options.buildBackupObjectFn;
|
|
@@ -3998,6 +4427,7 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3998
4427
|
const requestStorageLs = options.requestStorageLsFn;
|
|
3999
4428
|
const requestStorageDownload = options.requestStorageDownloadFn;
|
|
4000
4429
|
const requestStorageDelete = options.requestStorageDeleteFn;
|
|
4430
|
+
const subagentOrchestrator = options.subagentOrchestrator;
|
|
4001
4431
|
if (parsed.mode === "help" || parsed.mode === "unknown") {
|
|
4002
4432
|
return {
|
|
4003
4433
|
text: CLOUD_HELP_TEXT,
|
|
@@ -4010,12 +4440,30 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
4010
4440
|
isError: true
|
|
4011
4441
|
};
|
|
4012
4442
|
}
|
|
4443
|
+
if (parsed.mode === "backup-invalid") {
|
|
4444
|
+
return {
|
|
4445
|
+
text: "Cannot build storage object",
|
|
4446
|
+
isError: true
|
|
4447
|
+
};
|
|
4448
|
+
}
|
|
4449
|
+
if (parsed.mode === "backup-invalid-async") {
|
|
4450
|
+
return {
|
|
4451
|
+
text: `Cannot build storage object: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4452
|
+
isError: true
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4013
4455
|
if (parsed.mode === "upload-invalid") {
|
|
4014
4456
|
return {
|
|
4015
4457
|
text: `Cannot upload storage object: required arguments are ${REQUIRED_UPLOAD}.`,
|
|
4016
4458
|
isError: true
|
|
4017
4459
|
};
|
|
4018
4460
|
}
|
|
4461
|
+
if (parsed.mode === "upload-invalid-async") {
|
|
4462
|
+
return {
|
|
4463
|
+
text: `Cannot upload storage object: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4464
|
+
isError: true
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4019
4467
|
if (parsed.mode === "ls-invalid") {
|
|
4020
4468
|
return {
|
|
4021
4469
|
text: `Cannot list storage object: required arguments are ${REQUIRED_STORAGE_OBJECT}.`,
|
|
@@ -4028,6 +4476,12 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
4028
4476
|
isError: true
|
|
4029
4477
|
};
|
|
4030
4478
|
}
|
|
4479
|
+
if (parsed.mode === "download-invalid-async") {
|
|
4480
|
+
return {
|
|
4481
|
+
text: `Cannot download file: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4482
|
+
isError: true
|
|
4483
|
+
};
|
|
4484
|
+
}
|
|
4031
4485
|
if (parsed.mode === "delete-invalid") {
|
|
4032
4486
|
return {
|
|
4033
4487
|
text: `Cannot delete file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`,
|
|
@@ -4041,66 +4495,450 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
4041
4495
|
};
|
|
4042
4496
|
}
|
|
4043
4497
|
const datastore = await createCloudDatastore(objectLogHomeDir);
|
|
4498
|
+
const terminalOperationStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "timed_out"]);
|
|
4499
|
+
const isTerminalOperationStatus = (status) => terminalOperationStatuses.has(status);
|
|
4500
|
+
const formatOperationStatus = (operation) => ({
|
|
4501
|
+
text: [
|
|
4502
|
+
`operation-id: ${operation.operation_id}`,
|
|
4503
|
+
`type: ${operation.type}`,
|
|
4504
|
+
`status: ${operation.status}`,
|
|
4505
|
+
`started-at: ${operation.started_at ?? "n/a"}`,
|
|
4506
|
+
`finished-at: ${operation.finished_at ?? "n/a"}`,
|
|
4507
|
+
operation.orchestrator ? `orchestrator: ${operation.orchestrator}` : null,
|
|
4508
|
+
operation.subagent_session_id ? `subagent-session-id: ${operation.subagent_session_id}` : null,
|
|
4509
|
+
operation.timeout_seconds ? `timeout-seconds: ${operation.timeout_seconds}` : null,
|
|
4510
|
+
operation.error_code ? `error-code: ${operation.error_code}` : null,
|
|
4511
|
+
operation.error_message ? `error-message: ${operation.error_message}` : null
|
|
4512
|
+
].filter((v) => Boolean(v)).join("\n"),
|
|
4513
|
+
isError: operation.status === "failed" || operation.status === "cancelled" || operation.status === "timed_out"
|
|
4514
|
+
});
|
|
4044
4515
|
if (parsed.mode === "op-status") {
|
|
4045
|
-
|
|
4516
|
+
let operation = await datastore.findOperationById(parsed.operationId);
|
|
4046
4517
|
if (!operation) {
|
|
4047
4518
|
return {
|
|
4048
4519
|
text: `Operation not found: ${parsed.operationId}`,
|
|
4049
4520
|
isError: true
|
|
4050
4521
|
};
|
|
4051
4522
|
}
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4523
|
+
if (parsed.cancel) {
|
|
4524
|
+
if (operation.orchestrator !== "subagent" || !operation.subagent_session_id) {
|
|
4525
|
+
return {
|
|
4526
|
+
text: "Cancellation is only supported for subagent-orchestrated operations.",
|
|
4527
|
+
isError: true
|
|
4528
|
+
};
|
|
4529
|
+
}
|
|
4530
|
+
if (!isTerminalOperationStatus(operation.status)) {
|
|
4531
|
+
const traceId = operation.trace_id ?? randomUUID3();
|
|
4532
|
+
const cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4533
|
+
await datastore.upsertOperation({
|
|
4534
|
+
operation_id: operation.operation_id,
|
|
4535
|
+
type: operation.type,
|
|
4536
|
+
object_id: operation.object_id,
|
|
4537
|
+
quote_id: operation.quote_id,
|
|
4538
|
+
trace_id: traceId,
|
|
4539
|
+
orchestrator: "subagent",
|
|
4540
|
+
subagent_session_id: operation.subagent_session_id,
|
|
4541
|
+
timeout_seconds: operation.timeout_seconds,
|
|
4542
|
+
cancel_requested_at: cancelRequestedAt,
|
|
4543
|
+
status: "running",
|
|
4544
|
+
error_code: null,
|
|
4545
|
+
error_message: null
|
|
4546
|
+
});
|
|
4547
|
+
await emitOperationEventBestEffort(
|
|
4548
|
+
"operation.cancel.requested",
|
|
4549
|
+
{
|
|
4550
|
+
operationId: operation.operation_id,
|
|
4551
|
+
traceId,
|
|
4552
|
+
status: "running",
|
|
4553
|
+
objectId: operation.object_id,
|
|
4554
|
+
quoteId: operation.quote_id,
|
|
4555
|
+
orchestrator: "subagent",
|
|
4556
|
+
subagentSessionId: operation.subagent_session_id,
|
|
4557
|
+
timeoutSeconds: operation.timeout_seconds
|
|
4558
|
+
},
|
|
4559
|
+
objectLogHomeDir
|
|
4560
|
+
);
|
|
4561
|
+
const cancelResult = await subagentOrchestrator.cancel(
|
|
4562
|
+
operation.subagent_session_id,
|
|
4563
|
+
"cancel requested by op-status"
|
|
4564
|
+
);
|
|
4565
|
+
if (cancelResult.accepted || cancelResult.alreadyTerminal) {
|
|
4566
|
+
const afterCancel = await datastore.findOperationById(parsed.operationId);
|
|
4567
|
+
if (afterCancel && !isTerminalOperationStatus(afterCancel.status)) {
|
|
4568
|
+
await datastore.upsertOperation({
|
|
4569
|
+
operation_id: operation.operation_id,
|
|
4570
|
+
type: operation.type,
|
|
4571
|
+
object_id: operation.object_id,
|
|
4572
|
+
quote_id: operation.quote_id,
|
|
4573
|
+
trace_id: traceId,
|
|
4574
|
+
orchestrator: "subagent",
|
|
4575
|
+
subagent_session_id: operation.subagent_session_id,
|
|
4576
|
+
timeout_seconds: operation.timeout_seconds,
|
|
4577
|
+
cancel_requested_at: cancelRequestedAt,
|
|
4578
|
+
status: "cancelled",
|
|
4579
|
+
error_code: "ASYNC_CANCELLED",
|
|
4580
|
+
error_message: "Operation cancelled by user request."
|
|
4581
|
+
});
|
|
4582
|
+
await emitOperationEventBestEffort(
|
|
4583
|
+
"operation.cancelled",
|
|
4584
|
+
{
|
|
4585
|
+
operationId: operation.operation_id,
|
|
4586
|
+
traceId,
|
|
4587
|
+
status: "cancelled",
|
|
4588
|
+
objectId: operation.object_id,
|
|
4589
|
+
quoteId: operation.quote_id,
|
|
4590
|
+
orchestrator: "subagent",
|
|
4591
|
+
subagentSessionId: operation.subagent_session_id,
|
|
4592
|
+
timeoutSeconds: operation.timeout_seconds,
|
|
4593
|
+
errorCode: "ASYNC_CANCELLED",
|
|
4594
|
+
errorMessage: "Operation cancelled by user request."
|
|
4595
|
+
},
|
|
4596
|
+
objectLogHomeDir
|
|
4597
|
+
);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
operation = await datastore.findOperationById(parsed.operationId);
|
|
4602
|
+
if (!operation) {
|
|
4603
|
+
return {
|
|
4604
|
+
text: `Operation not found: ${parsed.operationId}`,
|
|
4605
|
+
isError: true
|
|
4606
|
+
};
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
return formatOperationStatus(operation);
|
|
4064
4610
|
}
|
|
4065
|
-
if ((parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4066
|
-
const
|
|
4611
|
+
if ((parsed.mode === "backup" || parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4612
|
+
const asyncCorrelation = buildRequestCorrelation();
|
|
4613
|
+
const operationId = asyncCorrelation.operationId;
|
|
4067
4614
|
const opType = parsed.mode;
|
|
4068
|
-
const opObject = parsed.mode === "upload" ? parsed.uploadRequest.object_id :
|
|
4615
|
+
const opObject = parsed.mode === "upload" ? parsed.uploadRequest.object_id : null;
|
|
4069
4616
|
const opQuote = parsed.mode === "upload" ? parsed.uploadRequest.quote_id : null;
|
|
4617
|
+
const orchestratorMode = parsed.orchestrator ?? "inline";
|
|
4618
|
+
const timeoutSeconds = orchestratorMode === "subagent" ? parsed.timeoutSeconds ?? null : null;
|
|
4619
|
+
const eventContextBase = {
|
|
4620
|
+
operationId,
|
|
4621
|
+
traceId: asyncCorrelation.traceId,
|
|
4622
|
+
walletAddress: parsed.mode === "upload" ? parsed.uploadRequest.wallet_address : parsed.mode === "download" ? parsed.storageObjectRequest.wallet_address : null,
|
|
4623
|
+
objectId: opObject,
|
|
4624
|
+
objectKey: parsed.mode === "download" ? parsed.storageObjectRequest.object_key ?? null : null,
|
|
4625
|
+
quoteId: opQuote,
|
|
4626
|
+
orchestrator: orchestratorMode,
|
|
4627
|
+
timeoutSeconds
|
|
4628
|
+
};
|
|
4070
4629
|
await datastore.upsertOperation({
|
|
4071
4630
|
operation_id: operationId,
|
|
4072
4631
|
type: opType,
|
|
4073
4632
|
object_id: opObject,
|
|
4074
4633
|
quote_id: opQuote,
|
|
4634
|
+
trace_id: asyncCorrelation.traceId,
|
|
4635
|
+
orchestrator: orchestratorMode,
|
|
4636
|
+
timeout_seconds: timeoutSeconds,
|
|
4075
4637
|
status: "started",
|
|
4076
4638
|
error_code: null,
|
|
4077
4639
|
error_message: null
|
|
4078
4640
|
});
|
|
4079
|
-
|
|
4080
|
-
|
|
4641
|
+
await emitOperationEventBestEffort(
|
|
4642
|
+
"operation.dispatched",
|
|
4643
|
+
{ ...eventContextBase, status: "started" },
|
|
4644
|
+
objectLogHomeDir
|
|
4645
|
+
);
|
|
4646
|
+
const syncArgs = stripAsyncControlFlags(ctx.args);
|
|
4647
|
+
if (orchestratorMode === "subagent") {
|
|
4648
|
+
const subagentTask = {
|
|
4649
|
+
schema: "mnemospark.subagent-task.v1",
|
|
4650
|
+
operationId,
|
|
4651
|
+
traceId: asyncCorrelation.traceId,
|
|
4652
|
+
command: parsed.mode,
|
|
4653
|
+
args: syncArgs,
|
|
4654
|
+
timeoutSeconds: parsed.timeoutSeconds,
|
|
4655
|
+
requestedBy: {
|
|
4656
|
+
pluginCommand: "mnemospark_cloud",
|
|
4657
|
+
chatId: ctx.channel,
|
|
4658
|
+
senderId: ctx.senderId
|
|
4659
|
+
}
|
|
4660
|
+
};
|
|
4661
|
+
try {
|
|
4662
|
+
const dispatchResult = await subagentOrchestrator.dispatch({
|
|
4663
|
+
task: subagentTask,
|
|
4664
|
+
timeoutSeconds: parsed.timeoutSeconds,
|
|
4665
|
+
runTask: async () => runCloudCommandHandler(
|
|
4666
|
+
{ args: syncArgs, channel: ctx.channel, senderId: ctx.senderId },
|
|
4667
|
+
options,
|
|
4668
|
+
{
|
|
4669
|
+
forcedOperationId: asyncCorrelation.operationId,
|
|
4670
|
+
forcedTraceId: asyncCorrelation.traceId
|
|
4671
|
+
}
|
|
4672
|
+
),
|
|
4673
|
+
hooks: {
|
|
4674
|
+
onRunning: async (sessionId) => {
|
|
4675
|
+
await datastore.upsertOperation({
|
|
4676
|
+
operation_id: operationId,
|
|
4677
|
+
type: opType,
|
|
4678
|
+
object_id: opObject,
|
|
4679
|
+
quote_id: opQuote,
|
|
4680
|
+
trace_id: asyncCorrelation.traceId,
|
|
4681
|
+
orchestrator: "subagent",
|
|
4682
|
+
subagent_session_id: sessionId,
|
|
4683
|
+
timeout_seconds: timeoutSeconds,
|
|
4684
|
+
status: "running",
|
|
4685
|
+
error_code: null,
|
|
4686
|
+
error_message: null
|
|
4687
|
+
});
|
|
4688
|
+
await emitOperationEventBestEffort(
|
|
4689
|
+
"operation.progress",
|
|
4690
|
+
{
|
|
4691
|
+
...eventContextBase,
|
|
4692
|
+
status: "running",
|
|
4693
|
+
subagentSessionId: sessionId,
|
|
4694
|
+
progressMessage: "subagent running"
|
|
4695
|
+
},
|
|
4696
|
+
objectLogHomeDir
|
|
4697
|
+
);
|
|
4698
|
+
},
|
|
4699
|
+
onProgress: async (sessionId, message) => {
|
|
4700
|
+
await emitOperationEventBestEffort(
|
|
4701
|
+
"operation.progress",
|
|
4702
|
+
{
|
|
4703
|
+
...eventContextBase,
|
|
4704
|
+
status: "running",
|
|
4705
|
+
subagentSessionId: sessionId,
|
|
4706
|
+
progressMessage: message
|
|
4707
|
+
},
|
|
4708
|
+
objectLogHomeDir
|
|
4709
|
+
);
|
|
4710
|
+
},
|
|
4711
|
+
onCompleted: async (sessionId) => {
|
|
4712
|
+
await datastore.upsertOperation({
|
|
4713
|
+
operation_id: operationId,
|
|
4714
|
+
type: opType,
|
|
4715
|
+
object_id: opObject,
|
|
4716
|
+
quote_id: opQuote,
|
|
4717
|
+
trace_id: asyncCorrelation.traceId,
|
|
4718
|
+
orchestrator: "subagent",
|
|
4719
|
+
subagent_session_id: sessionId,
|
|
4720
|
+
timeout_seconds: timeoutSeconds,
|
|
4721
|
+
status: "succeeded",
|
|
4722
|
+
error_code: null,
|
|
4723
|
+
error_message: null
|
|
4724
|
+
});
|
|
4725
|
+
await emitOperationEventBestEffort(
|
|
4726
|
+
"operation.completed",
|
|
4727
|
+
{
|
|
4728
|
+
...eventContextBase,
|
|
4729
|
+
status: "succeeded",
|
|
4730
|
+
subagentSessionId: sessionId
|
|
4731
|
+
},
|
|
4732
|
+
objectLogHomeDir
|
|
4733
|
+
);
|
|
4734
|
+
},
|
|
4735
|
+
onFailed: async (sessionId, details) => {
|
|
4736
|
+
await datastore.upsertOperation({
|
|
4737
|
+
operation_id: operationId,
|
|
4738
|
+
type: opType,
|
|
4739
|
+
object_id: opObject,
|
|
4740
|
+
quote_id: opQuote,
|
|
4741
|
+
trace_id: asyncCorrelation.traceId,
|
|
4742
|
+
orchestrator: "subagent",
|
|
4743
|
+
subagent_session_id: sessionId,
|
|
4744
|
+
timeout_seconds: timeoutSeconds,
|
|
4745
|
+
status: "failed",
|
|
4746
|
+
error_code: details.code,
|
|
4747
|
+
error_message: details.message
|
|
4748
|
+
});
|
|
4749
|
+
await emitOperationEventBestEffort(
|
|
4750
|
+
"operation.completed",
|
|
4751
|
+
{
|
|
4752
|
+
...eventContextBase,
|
|
4753
|
+
status: "failed",
|
|
4754
|
+
subagentSessionId: sessionId,
|
|
4755
|
+
errorCode: details.code,
|
|
4756
|
+
errorMessage: details.message
|
|
4757
|
+
},
|
|
4758
|
+
objectLogHomeDir
|
|
4759
|
+
);
|
|
4760
|
+
},
|
|
4761
|
+
onCancelled: async (sessionId, reason) => {
|
|
4762
|
+
await datastore.upsertOperation({
|
|
4763
|
+
operation_id: operationId,
|
|
4764
|
+
type: opType,
|
|
4765
|
+
object_id: opObject,
|
|
4766
|
+
quote_id: opQuote,
|
|
4767
|
+
trace_id: asyncCorrelation.traceId,
|
|
4768
|
+
orchestrator: "subagent",
|
|
4769
|
+
subagent_session_id: sessionId,
|
|
4770
|
+
timeout_seconds: timeoutSeconds,
|
|
4771
|
+
cancel_requested_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4772
|
+
status: "cancelled",
|
|
4773
|
+
error_code: "ASYNC_CANCELLED",
|
|
4774
|
+
error_message: reason ?? "Operation cancelled."
|
|
4775
|
+
});
|
|
4776
|
+
await emitOperationEventBestEffort(
|
|
4777
|
+
"operation.cancelled",
|
|
4778
|
+
{
|
|
4779
|
+
...eventContextBase,
|
|
4780
|
+
status: "cancelled",
|
|
4781
|
+
subagentSessionId: sessionId,
|
|
4782
|
+
errorCode: "ASYNC_CANCELLED",
|
|
4783
|
+
errorMessage: reason ?? "Operation cancelled."
|
|
4784
|
+
},
|
|
4785
|
+
objectLogHomeDir
|
|
4786
|
+
);
|
|
4787
|
+
},
|
|
4788
|
+
onTimedOut: async (sessionId) => {
|
|
4789
|
+
await datastore.upsertOperation({
|
|
4790
|
+
operation_id: operationId,
|
|
4791
|
+
type: opType,
|
|
4792
|
+
object_id: opObject,
|
|
4793
|
+
quote_id: opQuote,
|
|
4794
|
+
trace_id: asyncCorrelation.traceId,
|
|
4795
|
+
orchestrator: "subagent",
|
|
4796
|
+
subagent_session_id: sessionId,
|
|
4797
|
+
timeout_seconds: timeoutSeconds,
|
|
4798
|
+
status: "timed_out",
|
|
4799
|
+
error_code: "ASYNC_TIMEOUT",
|
|
4800
|
+
error_message: "Operation timed out."
|
|
4801
|
+
});
|
|
4802
|
+
await emitOperationEventBestEffort(
|
|
4803
|
+
"operation.timed_out",
|
|
4804
|
+
{
|
|
4805
|
+
...eventContextBase,
|
|
4806
|
+
status: "timed_out",
|
|
4807
|
+
subagentSessionId: sessionId,
|
|
4808
|
+
errorCode: "ASYNC_TIMEOUT",
|
|
4809
|
+
errorMessage: "Operation timed out."
|
|
4810
|
+
},
|
|
4811
|
+
objectLogHomeDir
|
|
4812
|
+
);
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
});
|
|
4816
|
+
const operationAfterDispatch = await datastore.findOperationById(operationId);
|
|
4817
|
+
if (operationAfterDispatch?.subagent_session_id !== dispatchResult.sessionId) {
|
|
4818
|
+
await datastore.upsertOperation({
|
|
4819
|
+
operation_id: operationId,
|
|
4820
|
+
type: opType,
|
|
4821
|
+
object_id: opObject,
|
|
4822
|
+
quote_id: opQuote,
|
|
4823
|
+
trace_id: asyncCorrelation.traceId,
|
|
4824
|
+
orchestrator: "subagent",
|
|
4825
|
+
subagent_session_id: dispatchResult.sessionId,
|
|
4826
|
+
timeout_seconds: timeoutSeconds,
|
|
4827
|
+
status: operationAfterDispatch?.status ?? "started",
|
|
4828
|
+
error_code: operationAfterDispatch?.error_code ?? null,
|
|
4829
|
+
error_message: operationAfterDispatch?.error_message ?? null
|
|
4830
|
+
});
|
|
4831
|
+
}
|
|
4832
|
+
return {
|
|
4833
|
+
text: [
|
|
4834
|
+
`Operation started in background. operation-id: ${operationId}`,
|
|
4835
|
+
`orchestrator: subagent`,
|
|
4836
|
+
`subagent-session-id: ${dispatchResult.sessionId}`,
|
|
4837
|
+
timeoutSeconds ? `timeout-seconds: ${timeoutSeconds}` : null,
|
|
4838
|
+
`Use /mnemospark_cloud op-status --operation-id ${operationId}`
|
|
4839
|
+
].filter((line) => Boolean(line)).join("\n")
|
|
4840
|
+
};
|
|
4841
|
+
} catch (dispatchError) {
|
|
4842
|
+
const dispatchMessage = dispatchError instanceof Error ? dispatchError.message : String(dispatchError);
|
|
4843
|
+
await datastore.upsertOperation({
|
|
4844
|
+
operation_id: operationId,
|
|
4845
|
+
type: opType,
|
|
4846
|
+
object_id: opObject,
|
|
4847
|
+
quote_id: opQuote,
|
|
4848
|
+
trace_id: asyncCorrelation.traceId,
|
|
4849
|
+
orchestrator: "subagent",
|
|
4850
|
+
timeout_seconds: timeoutSeconds,
|
|
4851
|
+
status: "failed",
|
|
4852
|
+
error_code: "ASYNC_DISPATCH_FAILED",
|
|
4853
|
+
error_message: dispatchMessage
|
|
4854
|
+
});
|
|
4855
|
+
await emitOperationEventBestEffort(
|
|
4856
|
+
"operation.completed",
|
|
4857
|
+
{
|
|
4858
|
+
...eventContextBase,
|
|
4859
|
+
status: "failed",
|
|
4860
|
+
errorCode: "ASYNC_DISPATCH_FAILED",
|
|
4861
|
+
errorMessage: dispatchMessage
|
|
4862
|
+
},
|
|
4863
|
+
objectLogHomeDir
|
|
4864
|
+
);
|
|
4865
|
+
return {
|
|
4866
|
+
text: `Cannot dispatch subagent operation: ${dispatchMessage}
|
|
4867
|
+
operation-id: ${operationId}`,
|
|
4868
|
+
isError: true
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
await datastore.upsertOperation({
|
|
4873
|
+
operation_id: operationId,
|
|
4874
|
+
type: opType,
|
|
4875
|
+
object_id: opObject,
|
|
4876
|
+
quote_id: opQuote,
|
|
4877
|
+
trace_id: asyncCorrelation.traceId,
|
|
4878
|
+
orchestrator: "inline",
|
|
4879
|
+
status: "running",
|
|
4880
|
+
error_code: null,
|
|
4881
|
+
error_message: null
|
|
4882
|
+
});
|
|
4883
|
+
void runCloudCommandHandler(
|
|
4884
|
+
{ args: syncArgs, channel: ctx.channel, senderId: ctx.senderId },
|
|
4885
|
+
options,
|
|
4886
|
+
{
|
|
4887
|
+
forcedOperationId: asyncCorrelation.operationId,
|
|
4888
|
+
forcedTraceId: asyncCorrelation.traceId
|
|
4889
|
+
}
|
|
4890
|
+
).then(async (result) => {
|
|
4081
4891
|
await datastore.upsertOperation({
|
|
4082
4892
|
operation_id: operationId,
|
|
4083
4893
|
type: opType,
|
|
4084
4894
|
object_id: opObject,
|
|
4085
4895
|
quote_id: opQuote,
|
|
4896
|
+
trace_id: asyncCorrelation.traceId,
|
|
4897
|
+
orchestrator: "inline",
|
|
4086
4898
|
status: result.isError ? "failed" : "succeeded",
|
|
4087
4899
|
error_code: result.isError ? "ASYNC_FAILED" : null,
|
|
4088
4900
|
error_message: result.isError ? result.text : null
|
|
4089
4901
|
});
|
|
4902
|
+
await emitOperationEventBestEffort(
|
|
4903
|
+
"operation.completed",
|
|
4904
|
+
{
|
|
4905
|
+
...eventContextBase,
|
|
4906
|
+
status: result.isError ? "failed" : "succeeded",
|
|
4907
|
+
errorCode: result.isError ? "ASYNC_FAILED" : null,
|
|
4908
|
+
errorMessage: result.isError ? result.text : null
|
|
4909
|
+
},
|
|
4910
|
+
objectLogHomeDir
|
|
4911
|
+
);
|
|
4090
4912
|
}).catch(async (err) => {
|
|
4913
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
4091
4914
|
await datastore.upsertOperation({
|
|
4092
4915
|
operation_id: operationId,
|
|
4093
4916
|
type: opType,
|
|
4094
4917
|
object_id: opObject,
|
|
4095
4918
|
quote_id: opQuote,
|
|
4919
|
+
trace_id: asyncCorrelation.traceId,
|
|
4920
|
+
orchestrator: "inline",
|
|
4096
4921
|
status: "failed",
|
|
4097
4922
|
error_code: "ASYNC_EXCEPTION",
|
|
4098
|
-
error_message:
|
|
4923
|
+
error_message: errorMessage
|
|
4099
4924
|
});
|
|
4925
|
+
await emitOperationEventBestEffort(
|
|
4926
|
+
"operation.completed",
|
|
4927
|
+
{
|
|
4928
|
+
...eventContextBase,
|
|
4929
|
+
status: "failed",
|
|
4930
|
+
errorCode: "ASYNC_EXCEPTION",
|
|
4931
|
+
errorMessage
|
|
4932
|
+
},
|
|
4933
|
+
objectLogHomeDir
|
|
4934
|
+
);
|
|
4100
4935
|
});
|
|
4101
4936
|
return {
|
|
4102
|
-
text:
|
|
4103
|
-
|
|
4937
|
+
text: [
|
|
4938
|
+
`Operation started in background. operation-id: ${operationId}`,
|
|
4939
|
+
`orchestrator: inline`,
|
|
4940
|
+
`Use /mnemospark_cloud op-status --operation-id ${operationId}`
|
|
4941
|
+
].join("\n")
|
|
4104
4942
|
};
|
|
4105
4943
|
}
|
|
4106
4944
|
if (parsed.mode === "backup") {
|
|
@@ -4158,11 +4996,12 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4158
4996
|
}
|
|
4159
4997
|
}
|
|
4160
4998
|
if (parsed.mode === "price-storage") {
|
|
4999
|
+
const correlation = buildRequestCorrelation();
|
|
4161
5000
|
try {
|
|
4162
|
-
const quote = await requestPriceStorageQuote(
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
);
|
|
5001
|
+
const quote = await requestPriceStorageQuote(parsed.priceStorageRequest, {
|
|
5002
|
+
...options.proxyQuoteOptions,
|
|
5003
|
+
correlation
|
|
5004
|
+
});
|
|
4166
5005
|
await appendPriceStorageQuoteLog(quote, objectLogHomeDir);
|
|
4167
5006
|
await datastore.upsertObject({
|
|
4168
5007
|
object_id: quote.object_id,
|
|
@@ -4183,10 +5022,33 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4183
5022
|
network: null,
|
|
4184
5023
|
status: "quoted"
|
|
4185
5024
|
});
|
|
5025
|
+
await emitCloudEventBestEffort(
|
|
5026
|
+
"price-storage.completed",
|
|
5027
|
+
{
|
|
5028
|
+
operation_id: correlation.operationId,
|
|
5029
|
+
trace_id: correlation.traceId,
|
|
5030
|
+
wallet_address: quote.addr,
|
|
5031
|
+
object_id: quote.object_id,
|
|
5032
|
+
quote_id: quote.quote_id,
|
|
5033
|
+
status: "succeeded"
|
|
5034
|
+
},
|
|
5035
|
+
objectLogHomeDir
|
|
5036
|
+
);
|
|
4186
5037
|
return {
|
|
4187
5038
|
text: formatPriceStorageUserMessage(quote)
|
|
4188
5039
|
};
|
|
4189
5040
|
} catch (err) {
|
|
5041
|
+
await emitCloudEventBestEffort(
|
|
5042
|
+
"price-storage.completed",
|
|
5043
|
+
{
|
|
5044
|
+
operation_id: correlation.operationId,
|
|
5045
|
+
trace_id: correlation.traceId,
|
|
5046
|
+
wallet_address: parsed.priceStorageRequest.wallet_address,
|
|
5047
|
+
object_id: parsed.priceStorageRequest.object_id,
|
|
5048
|
+
status: "failed"
|
|
5049
|
+
},
|
|
5050
|
+
objectLogHomeDir
|
|
5051
|
+
);
|
|
4190
5052
|
const message = err instanceof Error ? err.message : typeof err === "string" ? err : String(err);
|
|
4191
5053
|
return {
|
|
4192
5054
|
text: message ? `Cannot price storage: ${message}` : "Cannot price storage",
|
|
@@ -4195,11 +5057,15 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4195
5057
|
}
|
|
4196
5058
|
}
|
|
4197
5059
|
if (parsed.mode === "upload") {
|
|
5060
|
+
const uploadCorrelation = buildRequestCorrelation(
|
|
5061
|
+
executionContext.forcedOperationId ?? idempotencyKeyFn(),
|
|
5062
|
+
executionContext.forcedTraceId
|
|
5063
|
+
);
|
|
4198
5064
|
try {
|
|
4199
5065
|
const loggedQuote = await datastore.findQuoteById(parsed.uploadRequest.quote_id) ?? await findLoggedPriceStorageQuote(parsed.uploadRequest.quote_id, objectLogHomeDir);
|
|
4200
5066
|
if (!loggedQuote) {
|
|
4201
5067
|
return {
|
|
4202
|
-
text: "Cannot upload storage object: quote-id not found in object.log. Run /
|
|
5068
|
+
text: "Cannot upload storage object: quote-id not found in object.log. Run /mnemospark_cloud price-storage first.",
|
|
4203
5069
|
isError: true
|
|
4204
5070
|
};
|
|
4205
5071
|
}
|
|
@@ -4218,7 +5084,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4218
5084
|
archiveStats = await stat2(archivePath);
|
|
4219
5085
|
} catch {
|
|
4220
5086
|
return {
|
|
4221
|
-
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /
|
|
5087
|
+
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /mnemospark_cloud backup first.`,
|
|
4222
5088
|
isError: true
|
|
4223
5089
|
};
|
|
4224
5090
|
}
|
|
@@ -4248,7 +5114,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4248
5114
|
parsed.uploadRequest.wallet_address,
|
|
4249
5115
|
objectLogHomeDir
|
|
4250
5116
|
);
|
|
4251
|
-
const idempotencyKey =
|
|
5117
|
+
const idempotencyKey = uploadCorrelation.operationId;
|
|
4252
5118
|
const shouldSettleBeforeUpload = requestStorageUpload !== requestStorageUploadViaProxy;
|
|
4253
5119
|
if (shouldSettleBeforeUpload) {
|
|
4254
5120
|
const paymentFetch = createPayment(walletKey).fetch;
|
|
@@ -4257,6 +5123,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4257
5123
|
parsed.uploadRequest.wallet_address,
|
|
4258
5124
|
{
|
|
4259
5125
|
...options.proxyUploadOptions,
|
|
5126
|
+
correlation: uploadCorrelation,
|
|
4260
5127
|
fetchImpl: (input, init) => paymentFetch(input, init)
|
|
4261
5128
|
}
|
|
4262
5129
|
);
|
|
@@ -4278,6 +5145,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4278
5145
|
{
|
|
4279
5146
|
...options.proxyUploadOptions,
|
|
4280
5147
|
idempotencyKey,
|
|
5148
|
+
correlation: uploadCorrelation,
|
|
4281
5149
|
fetchImpl: uploadFetchImpl
|
|
4282
5150
|
}
|
|
4283
5151
|
);
|
|
@@ -4297,7 +5165,10 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4297
5165
|
object_key: uploadResponse.object_key,
|
|
4298
5166
|
idempotency_key: idempotencyKey
|
|
4299
5167
|
},
|
|
4300
|
-
|
|
5168
|
+
{
|
|
5169
|
+
...options.proxyUploadConfirmOptions,
|
|
5170
|
+
correlation: uploadCorrelation
|
|
5171
|
+
}
|
|
4301
5172
|
);
|
|
4302
5173
|
} catch (confirmError) {
|
|
4303
5174
|
const transId = uploadResponse.trans_id ?? "unknown";
|
|
@@ -4346,18 +5217,50 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4346
5217
|
status: "active"
|
|
4347
5218
|
});
|
|
4348
5219
|
if (parsed.friendlyName?.trim()) {
|
|
5220
|
+
const normalizedFriendlyName = parsed.friendlyName.trim();
|
|
4349
5221
|
await datastore.upsertFriendlyName({
|
|
4350
|
-
friendly_name:
|
|
5222
|
+
friendly_name: normalizedFriendlyName,
|
|
4351
5223
|
object_id: finalizedUploadResponse.object_id,
|
|
4352
5224
|
object_key: finalizedUploadResponse.object_key,
|
|
4353
5225
|
quote_id: finalizedUploadResponse.quote_id,
|
|
4354
5226
|
wallet_address: finalizedUploadResponse.addr
|
|
4355
5227
|
});
|
|
5228
|
+
let friendlyNameVerified = false;
|
|
5229
|
+
try {
|
|
5230
|
+
const readBack = await datastore.resolveFriendlyName({
|
|
5231
|
+
walletAddress: finalizedUploadResponse.addr,
|
|
5232
|
+
friendlyName: normalizedFriendlyName,
|
|
5233
|
+
latest: true
|
|
5234
|
+
});
|
|
5235
|
+
friendlyNameVerified = Boolean(readBack?.objectKey) && readBack?.objectKey === finalizedUploadResponse.object_key;
|
|
5236
|
+
} catch {
|
|
5237
|
+
friendlyNameVerified = false;
|
|
5238
|
+
}
|
|
5239
|
+
if (!friendlyNameVerified) {
|
|
5240
|
+
const warning = "SQLite friendly-name write verification failed; manifest fallback may be required for --name lookups.";
|
|
5241
|
+
await emitCloudEventBestEffort(
|
|
5242
|
+
"friendly_name.write_verification_failed",
|
|
5243
|
+
{
|
|
5244
|
+
operation_id: uploadCorrelation.operationId,
|
|
5245
|
+
trace_id: uploadCorrelation.traceId,
|
|
5246
|
+
wallet_address: finalizedUploadResponse.addr,
|
|
5247
|
+
object_id: finalizedUploadResponse.object_id,
|
|
5248
|
+
object_key: finalizedUploadResponse.object_key,
|
|
5249
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
5250
|
+
friendly_name: normalizedFriendlyName,
|
|
5251
|
+
warning
|
|
5252
|
+
},
|
|
5253
|
+
objectLogHomeDir
|
|
5254
|
+
);
|
|
5255
|
+
if (process.env.MNEMOSPARK_SQLITE_STRICT === "1") {
|
|
5256
|
+
throw new Error(warning);
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
4356
5259
|
try {
|
|
4357
5260
|
await appendJsonlEvent(
|
|
4358
5261
|
"manifest.jsonl",
|
|
4359
5262
|
{
|
|
4360
|
-
friendly_name:
|
|
5263
|
+
friendly_name: normalizedFriendlyName,
|
|
4361
5264
|
object_id: finalizedUploadResponse.object_id,
|
|
4362
5265
|
object_key: finalizedUploadResponse.object_key,
|
|
4363
5266
|
quote_id: finalizedUploadResponse.quote_id,
|
|
@@ -4373,6 +5276,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4373
5276
|
"upload.completed",
|
|
4374
5277
|
{
|
|
4375
5278
|
operation_id: idempotencyKey,
|
|
5279
|
+
trace_id: uploadCorrelation.traceId,
|
|
4376
5280
|
wallet_address: finalizedUploadResponse.addr,
|
|
4377
5281
|
object_id: finalizedUploadResponse.object_id,
|
|
4378
5282
|
object_key: finalizedUploadResponse.object_key,
|
|
@@ -4386,6 +5290,18 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4386
5290
|
text: formatStorageUploadUserMessage(finalizedUploadResponse, cronJob.cronId)
|
|
4387
5291
|
};
|
|
4388
5292
|
} catch (error) {
|
|
5293
|
+
await emitCloudEventBestEffort(
|
|
5294
|
+
"upload.completed",
|
|
5295
|
+
{
|
|
5296
|
+
operation_id: uploadCorrelation.operationId,
|
|
5297
|
+
trace_id: uploadCorrelation.traceId,
|
|
5298
|
+
wallet_address: parsed.uploadRequest.wallet_address,
|
|
5299
|
+
object_id: parsed.uploadRequest.object_id,
|
|
5300
|
+
quote_id: parsed.uploadRequest.quote_id,
|
|
5301
|
+
status: "failed"
|
|
5302
|
+
},
|
|
5303
|
+
objectLogHomeDir
|
|
5304
|
+
);
|
|
4389
5305
|
const uploadErrorMessage = extractUploadErrorMessage(error);
|
|
4390
5306
|
return {
|
|
4391
5307
|
text: uploadErrorMessage ?? "Cannot upload storage object",
|
|
@@ -4397,31 +5313,49 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4397
5313
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4398
5314
|
datastore,
|
|
4399
5315
|
parsed.storageObjectRequest,
|
|
4400
|
-
parsed.nameSelector
|
|
5316
|
+
parsed.nameSelector,
|
|
5317
|
+
objectLogHomeDir
|
|
4401
5318
|
);
|
|
4402
5319
|
if (resolved.error || !resolved.request) {
|
|
4403
5320
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4404
5321
|
}
|
|
4405
5322
|
const resolvedRequest = resolved.request;
|
|
4406
|
-
|
|
5323
|
+
if (resolved.degradedWarning) {
|
|
5324
|
+
await emitCloudEventBestEffort(
|
|
5325
|
+
"name_resolution.degraded",
|
|
5326
|
+
{
|
|
5327
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5328
|
+
object_key: resolvedRequest.object_key,
|
|
5329
|
+
warning: resolved.degradedWarning
|
|
5330
|
+
},
|
|
5331
|
+
objectLogHomeDir
|
|
5332
|
+
);
|
|
5333
|
+
}
|
|
5334
|
+
const correlation = buildRequestCorrelation();
|
|
5335
|
+
const operationId = correlation.operationId;
|
|
5336
|
+
const knownObject = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
5337
|
+
const operationObjectId = knownObject?.object_id ?? null;
|
|
4407
5338
|
await datastore.upsertOperation({
|
|
4408
5339
|
operation_id: operationId,
|
|
4409
5340
|
type: "ls",
|
|
4410
|
-
object_id:
|
|
5341
|
+
object_id: operationObjectId,
|
|
4411
5342
|
quote_id: null,
|
|
4412
5343
|
status: "started",
|
|
4413
5344
|
error_code: null,
|
|
4414
5345
|
error_message: null
|
|
4415
5346
|
});
|
|
4416
5347
|
try {
|
|
4417
|
-
const lsResult = await requestStorageLs(resolvedRequest,
|
|
5348
|
+
const lsResult = await requestStorageLs(resolvedRequest, {
|
|
5349
|
+
...options.proxyStorageOptions,
|
|
5350
|
+
correlation
|
|
5351
|
+
});
|
|
4418
5352
|
if (!lsResult.success) {
|
|
4419
5353
|
throw new Error("ls failed");
|
|
4420
5354
|
}
|
|
4421
5355
|
await datastore.upsertOperation({
|
|
4422
5356
|
operation_id: operationId,
|
|
4423
5357
|
type: "ls",
|
|
4424
|
-
object_id:
|
|
5358
|
+
object_id: operationObjectId,
|
|
4425
5359
|
quote_id: null,
|
|
4426
5360
|
status: "succeeded",
|
|
4427
5361
|
error_code: null,
|
|
@@ -4431,20 +5365,23 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4431
5365
|
"ls.completed",
|
|
4432
5366
|
{
|
|
4433
5367
|
operation_id: operationId,
|
|
5368
|
+
trace_id: correlation.traceId,
|
|
4434
5369
|
wallet_address: resolvedRequest.wallet_address,
|
|
4435
5370
|
object_key: resolvedRequest.object_key,
|
|
4436
5371
|
status: "succeeded"
|
|
4437
5372
|
},
|
|
4438
5373
|
objectLogHomeDir
|
|
4439
5374
|
);
|
|
5375
|
+
const lsText = formatStorageLsUserMessage(lsResult, resolvedRequest.object_key);
|
|
4440
5376
|
return {
|
|
4441
|
-
text:
|
|
5377
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5378
|
+
${lsText}` : lsText
|
|
4442
5379
|
};
|
|
4443
5380
|
} catch {
|
|
4444
5381
|
await datastore.upsertOperation({
|
|
4445
5382
|
operation_id: operationId,
|
|
4446
5383
|
type: "ls",
|
|
4447
|
-
object_id:
|
|
5384
|
+
object_id: operationObjectId,
|
|
4448
5385
|
quote_id: null,
|
|
4449
5386
|
status: "failed",
|
|
4450
5387
|
error_code: "LS_FAILED",
|
|
@@ -4454,6 +5391,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4454
5391
|
"ls.completed",
|
|
4455
5392
|
{
|
|
4456
5393
|
operation_id: operationId,
|
|
5394
|
+
trace_id: correlation.traceId,
|
|
4457
5395
|
wallet_address: resolvedRequest.wallet_address,
|
|
4458
5396
|
object_key: resolvedRequest.object_key,
|
|
4459
5397
|
status: "failed"
|
|
@@ -4470,34 +5408,52 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4470
5408
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4471
5409
|
datastore,
|
|
4472
5410
|
parsed.storageObjectRequest,
|
|
4473
|
-
parsed.nameSelector
|
|
5411
|
+
parsed.nameSelector,
|
|
5412
|
+
objectLogHomeDir
|
|
4474
5413
|
);
|
|
4475
5414
|
if (resolved.error || !resolved.request) {
|
|
4476
5415
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4477
5416
|
}
|
|
4478
5417
|
const resolvedRequest = resolved.request;
|
|
4479
|
-
|
|
5418
|
+
if (resolved.degradedWarning) {
|
|
5419
|
+
await emitCloudEventBestEffort(
|
|
5420
|
+
"name_resolution.degraded",
|
|
5421
|
+
{
|
|
5422
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5423
|
+
object_key: resolvedRequest.object_key,
|
|
5424
|
+
warning: resolved.degradedWarning
|
|
5425
|
+
},
|
|
5426
|
+
objectLogHomeDir
|
|
5427
|
+
);
|
|
5428
|
+
}
|
|
5429
|
+
const correlation = buildRequestCorrelation(
|
|
5430
|
+
executionContext.forcedOperationId,
|
|
5431
|
+
executionContext.forcedTraceId
|
|
5432
|
+
);
|
|
5433
|
+
const operationId = correlation.operationId;
|
|
5434
|
+
const knownObject = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
5435
|
+
const operationObjectId = knownObject?.object_id ?? null;
|
|
4480
5436
|
await datastore.upsertOperation({
|
|
4481
5437
|
operation_id: operationId,
|
|
4482
5438
|
type: "download",
|
|
4483
|
-
object_id:
|
|
5439
|
+
object_id: operationObjectId,
|
|
4484
5440
|
quote_id: null,
|
|
4485
5441
|
status: "started",
|
|
4486
5442
|
error_code: null,
|
|
4487
5443
|
error_message: null
|
|
4488
5444
|
});
|
|
4489
5445
|
try {
|
|
4490
|
-
const downloadResult = await requestStorageDownload(
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
);
|
|
5446
|
+
const downloadResult = await requestStorageDownload(resolvedRequest, {
|
|
5447
|
+
...options.proxyStorageOptions,
|
|
5448
|
+
correlation
|
|
5449
|
+
});
|
|
4494
5450
|
if (!downloadResult.success) {
|
|
4495
5451
|
throw new Error("download failed");
|
|
4496
5452
|
}
|
|
4497
5453
|
await datastore.upsertOperation({
|
|
4498
5454
|
operation_id: operationId,
|
|
4499
5455
|
type: "download",
|
|
4500
|
-
object_id:
|
|
5456
|
+
object_id: operationObjectId,
|
|
4501
5457
|
quote_id: null,
|
|
4502
5458
|
status: "succeeded",
|
|
4503
5459
|
error_code: null,
|
|
@@ -4507,20 +5463,23 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4507
5463
|
"download.completed",
|
|
4508
5464
|
{
|
|
4509
5465
|
operation_id: operationId,
|
|
5466
|
+
trace_id: correlation.traceId,
|
|
4510
5467
|
wallet_address: resolvedRequest.wallet_address,
|
|
4511
5468
|
object_key: resolvedRequest.object_key,
|
|
4512
5469
|
status: "succeeded"
|
|
4513
5470
|
},
|
|
4514
5471
|
objectLogHomeDir
|
|
4515
5472
|
);
|
|
5473
|
+
const downloadText = `File ${resolvedRequest.object_key} downloaded to ${downloadResult.file_path}`;
|
|
4516
5474
|
return {
|
|
4517
|
-
text:
|
|
5475
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5476
|
+
${downloadText}` : downloadText
|
|
4518
5477
|
};
|
|
4519
5478
|
} catch {
|
|
4520
5479
|
await datastore.upsertOperation({
|
|
4521
5480
|
operation_id: operationId,
|
|
4522
5481
|
type: "download",
|
|
4523
|
-
object_id:
|
|
5482
|
+
object_id: operationObjectId,
|
|
4524
5483
|
quote_id: null,
|
|
4525
5484
|
status: "failed",
|
|
4526
5485
|
error_code: "DOWNLOAD_FAILED",
|
|
@@ -4530,6 +5489,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4530
5489
|
"download.completed",
|
|
4531
5490
|
{
|
|
4532
5491
|
operation_id: operationId,
|
|
5492
|
+
trace_id: correlation.traceId,
|
|
4533
5493
|
wallet_address: resolvedRequest.wallet_address,
|
|
4534
5494
|
object_key: resolvedRequest.object_key,
|
|
4535
5495
|
status: "failed"
|
|
@@ -4546,16 +5506,32 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4546
5506
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4547
5507
|
datastore,
|
|
4548
5508
|
parsed.storageObjectRequest,
|
|
4549
|
-
parsed.nameSelector
|
|
5509
|
+
parsed.nameSelector,
|
|
5510
|
+
objectLogHomeDir
|
|
4550
5511
|
);
|
|
4551
5512
|
if (resolved.error || !resolved.request) {
|
|
4552
5513
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4553
5514
|
}
|
|
4554
5515
|
const resolvedRequest = resolved.request;
|
|
4555
|
-
|
|
5516
|
+
if (resolved.degradedWarning) {
|
|
5517
|
+
await emitCloudEventBestEffort(
|
|
5518
|
+
"name_resolution.degraded",
|
|
5519
|
+
{
|
|
5520
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5521
|
+
object_key: resolvedRequest.object_key,
|
|
5522
|
+
warning: resolved.degradedWarning
|
|
5523
|
+
},
|
|
5524
|
+
objectLogHomeDir
|
|
5525
|
+
);
|
|
5526
|
+
}
|
|
5527
|
+
const correlation = buildRequestCorrelation();
|
|
5528
|
+
const operationId = correlation.operationId;
|
|
4556
5529
|
const existingObjectByKey = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
4557
5530
|
try {
|
|
4558
|
-
const deleteResult = await requestStorageDelete(resolvedRequest,
|
|
5531
|
+
const deleteResult = await requestStorageDelete(resolvedRequest, {
|
|
5532
|
+
...options.proxyStorageOptions,
|
|
5533
|
+
correlation
|
|
5534
|
+
});
|
|
4559
5535
|
if (!deleteResult.success) {
|
|
4560
5536
|
throw new Error("delete failed");
|
|
4561
5537
|
}
|
|
@@ -4564,6 +5540,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4564
5540
|
"delete.completed",
|
|
4565
5541
|
{
|
|
4566
5542
|
operation_id: operationId,
|
|
5543
|
+
trace_id: correlation.traceId,
|
|
4567
5544
|
wallet_address: resolvedRequest.wallet_address,
|
|
4568
5545
|
object_key: resolvedRequest.object_key,
|
|
4569
5546
|
status: "failed"
|
|
@@ -4621,18 +5598,21 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4621
5598
|
"delete.completed",
|
|
4622
5599
|
{
|
|
4623
5600
|
operation_id: operationId,
|
|
5601
|
+
trace_id: correlation.traceId,
|
|
4624
5602
|
wallet_address: resolvedRequest.wallet_address,
|
|
4625
5603
|
object_key: resolvedRequest.object_key,
|
|
4626
5604
|
status: "succeeded"
|
|
4627
5605
|
},
|
|
4628
5606
|
objectLogHomeDir
|
|
4629
5607
|
);
|
|
5608
|
+
const deleteText = formatStorageDeleteUserMessage(
|
|
5609
|
+
resolvedRequest.object_key,
|
|
5610
|
+
cronEntry?.cronId ?? null,
|
|
5611
|
+
cronDeleted
|
|
5612
|
+
);
|
|
4630
5613
|
return {
|
|
4631
|
-
text:
|
|
4632
|
-
|
|
4633
|
-
cronEntry?.cronId ?? null,
|
|
4634
|
-
cronDeleted
|
|
4635
|
-
)
|
|
5614
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5615
|
+
${deleteText}` : deleteText
|
|
4636
5616
|
};
|
|
4637
5617
|
}
|
|
4638
5618
|
return {
|