mnemospark 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1111 -120
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1098 -107
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/proxy.ts
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
4
5
|
import { createServer } from "http";
|
|
5
|
-
import { homedir as
|
|
6
|
-
import { join as
|
|
6
|
+
import { homedir as homedir3 } from "os";
|
|
7
|
+
import { join as join4 } from "path";
|
|
7
8
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
8
9
|
|
|
9
10
|
// src/balance.ts
|
|
@@ -1432,11 +1433,52 @@ async function downloadStorageToDisk(request, backendResponse, options = {}) {
|
|
|
1432
1433
|
};
|
|
1433
1434
|
}
|
|
1434
1435
|
|
|
1436
|
+
// src/cloud-jsonl.ts
|
|
1437
|
+
import { createGzip } from "zlib";
|
|
1438
|
+
import { createReadStream, createWriteStream } from "fs";
|
|
1439
|
+
import { appendFile, mkdir as mkdir2, readdir, rename, stat, unlink } from "fs/promises";
|
|
1440
|
+
import { homedir as homedir2 } from "os";
|
|
1441
|
+
import { basename, dirname as dirname2, join as join3 } from "path";
|
|
1442
|
+
import { pipeline } from "stream/promises";
|
|
1443
|
+
var BASE_DIR = join3(homedir2(), ".openclaw", "mnemospark");
|
|
1444
|
+
var MAX_BYTES = 10 * 1024 * 1024;
|
|
1445
|
+
var KEEP_ROTATED = 10;
|
|
1446
|
+
function resolvePath(fileName, homeDir) {
|
|
1447
|
+
const baseDir = homeDir ? join3(homeDir, ".openclaw", "mnemospark") : BASE_DIR;
|
|
1448
|
+
return join3(baseDir, fileName);
|
|
1449
|
+
}
|
|
1450
|
+
async function rotateIfNeeded(path) {
|
|
1451
|
+
let fileStat;
|
|
1452
|
+
try {
|
|
1453
|
+
fileStat = await stat(path);
|
|
1454
|
+
} catch {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
if (fileStat.size < MAX_BYTES) return;
|
|
1458
|
+
const rotated = `${path}.${Date.now()}.1`;
|
|
1459
|
+
await rename(path, rotated);
|
|
1460
|
+
const gzPath = `${rotated}.gz`;
|
|
1461
|
+
await pipeline(createReadStream(rotated), createGzip(), createWriteStream(gzPath));
|
|
1462
|
+
await unlink(rotated).catch(() => void 0);
|
|
1463
|
+
const dir = dirname2(path);
|
|
1464
|
+
const base2 = basename(path) || "events.jsonl";
|
|
1465
|
+
const all = (await readdir(dir)).filter((name) => name.startsWith(`${base2}.`) && name.endsWith(".gz")).sort().reverse();
|
|
1466
|
+
const stale = all.slice(KEEP_ROTATED);
|
|
1467
|
+
await Promise.all(stale.map((name) => unlink(join3(dir, name)).catch(() => void 0)));
|
|
1468
|
+
}
|
|
1469
|
+
async function appendJsonlEvent(fileName, event, homeDir) {
|
|
1470
|
+
const filePath = resolvePath(fileName, homeDir);
|
|
1471
|
+
await mkdir2(dirname2(filePath), { recursive: true });
|
|
1472
|
+
await appendFile(filePath, `${JSON.stringify(event)}
|
|
1473
|
+
`, "utf-8");
|
|
1474
|
+
await rotateIfNeeded(filePath);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1435
1477
|
// src/proxy.ts
|
|
1436
1478
|
var HEALTH_CHECK_TIMEOUT_MS = 2e3;
|
|
1437
1479
|
var PORT_RETRY_ATTEMPTS = 5;
|
|
1438
1480
|
var PORT_RETRY_DELAY_MS = 1e3;
|
|
1439
|
-
var DEFAULT_DOWNLOAD_OUTPUT_DIR =
|
|
1481
|
+
var DEFAULT_DOWNLOAD_OUTPUT_DIR = join4(homedir3(), ".openclaw", "mnemospark", "downloads");
|
|
1440
1482
|
function resolveDownloadOutputDir() {
|
|
1441
1483
|
const configuredOutputDir = process.env.MNEMOSPARK_DOWNLOAD_DIR?.trim();
|
|
1442
1484
|
if (configuredOutputDir && configuredOutputDir.length > 0) {
|
|
@@ -1490,6 +1532,20 @@ function logProxyEvent(level, event, fields = {}) {
|
|
|
1490
1532
|
}
|
|
1491
1533
|
console.info(message);
|
|
1492
1534
|
}
|
|
1535
|
+
function emitProxyEvent(eventType, status, correlation, details = {}) {
|
|
1536
|
+
void appendJsonlEvent("proxy-events.jsonl", {
|
|
1537
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1538
|
+
event_type: eventType,
|
|
1539
|
+
status,
|
|
1540
|
+
trace_id: correlation.trace_id,
|
|
1541
|
+
operation_id: correlation.operation_id,
|
|
1542
|
+
quote_id: correlation.quote_id ?? null,
|
|
1543
|
+
wallet_address: correlation.wallet_address ?? null,
|
|
1544
|
+
object_id: correlation.object_id ?? null,
|
|
1545
|
+
object_key: correlation.object_key ?? null,
|
|
1546
|
+
details
|
|
1547
|
+
}).catch(() => void 0);
|
|
1548
|
+
}
|
|
1493
1549
|
function isAlreadySettledConflict(status, bodyText) {
|
|
1494
1550
|
if (status !== 409) {
|
|
1495
1551
|
return false;
|
|
@@ -1614,7 +1670,12 @@ async function startProxy(options) {
|
|
|
1614
1670
|
console.error(`[mnemospark] Response stream error: ${err.message}`);
|
|
1615
1671
|
});
|
|
1616
1672
|
if (req.method === "POST" && matchesProxyPath(req.url, PRICE_STORAGE_PROXY_PATH)) {
|
|
1673
|
+
const correlation = {
|
|
1674
|
+
trace_id: randomUUID(),
|
|
1675
|
+
operation_id: randomUUID()
|
|
1676
|
+
};
|
|
1617
1677
|
logProxyEvent("info", "proxy_price_storage_received");
|
|
1678
|
+
emitProxyEvent("request.received", "start", correlation, { path: PRICE_STORAGE_PROXY_PATH });
|
|
1618
1679
|
try {
|
|
1619
1680
|
let payload;
|
|
1620
1681
|
try {
|
|
@@ -1636,6 +1697,9 @@ async function startProxy(options) {
|
|
|
1636
1697
|
});
|
|
1637
1698
|
return;
|
|
1638
1699
|
}
|
|
1700
|
+
correlation.wallet_address = requestPayload.wallet_address;
|
|
1701
|
+
correlation.object_id = requestPayload.object_id;
|
|
1702
|
+
emitProxyEvent("storage.call", "start", correlation, { target: "price-storage" });
|
|
1639
1703
|
const walletSignature = await createBackendWalletSignature(
|
|
1640
1704
|
"POST",
|
|
1641
1705
|
"/price-storage",
|
|
@@ -1654,6 +1718,7 @@ async function startProxy(options) {
|
|
|
1654
1718
|
logProxyEvent("info", "proxy_price_storage_backend_response", {
|
|
1655
1719
|
status: backendResponse.status
|
|
1656
1720
|
});
|
|
1721
|
+
emitProxyEvent("storage.call", "result", correlation, { status: backendResponse.status });
|
|
1657
1722
|
const authFailure = normalizeBackendAuthFailure(
|
|
1658
1723
|
backendResponse.status,
|
|
1659
1724
|
backendResponse.bodyText
|
|
@@ -1674,7 +1739,13 @@ async function startProxy(options) {
|
|
|
1674
1739
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
1675
1740
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1676
1741
|
res.end(backendResponse.bodyText);
|
|
1742
|
+
emitProxyEvent("terminal.success", "success", correlation, {
|
|
1743
|
+
status: backendResponse.status
|
|
1744
|
+
});
|
|
1677
1745
|
} catch (err) {
|
|
1746
|
+
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
1747
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1748
|
+
});
|
|
1678
1749
|
logProxyEvent("error", "proxy_price_storage_forward_failed", {
|
|
1679
1750
|
error: err instanceof Error ? err.message : String(err)
|
|
1680
1751
|
});
|
|
@@ -1686,7 +1757,12 @@ async function startProxy(options) {
|
|
|
1686
1757
|
return;
|
|
1687
1758
|
}
|
|
1688
1759
|
if (req.method === "POST" && matchesProxyPath(req.url, PAYMENT_SETTLE_PROXY_PATH)) {
|
|
1760
|
+
const correlation = {
|
|
1761
|
+
trace_id: randomUUID(),
|
|
1762
|
+
operation_id: randomUUID()
|
|
1763
|
+
};
|
|
1689
1764
|
logProxyEvent("info", "proxy_payment_settle_received");
|
|
1765
|
+
emitProxyEvent("request.received", "start", correlation, { path: PAYMENT_SETTLE_PROXY_PATH });
|
|
1690
1766
|
try {
|
|
1691
1767
|
let payload;
|
|
1692
1768
|
try {
|
|
@@ -1750,6 +1826,9 @@ async function startProxy(options) {
|
|
|
1750
1826
|
res.end(createWalletRequiredBody());
|
|
1751
1827
|
return;
|
|
1752
1828
|
}
|
|
1829
|
+
correlation.quote_id = quoteId;
|
|
1830
|
+
correlation.wallet_address = walletAddress;
|
|
1831
|
+
emitProxyEvent("payment.settle", "start", correlation);
|
|
1753
1832
|
const paymentFetch = createPaymentFetch(walletPrivateKey).fetch;
|
|
1754
1833
|
const backendResponse = await forwardPaymentSettleToBackend(quoteId, walletAddress, {
|
|
1755
1834
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
@@ -1763,6 +1842,7 @@ async function startProxy(options) {
|
|
|
1763
1842
|
logProxyEvent("info", "proxy_payment_settle_backend_response", {
|
|
1764
1843
|
status: backendResponse.status
|
|
1765
1844
|
});
|
|
1845
|
+
emitProxyEvent("payment.settle", "result", correlation, { status: backendResponse.status });
|
|
1766
1846
|
const authFailure = normalizeBackendAuthFailure(
|
|
1767
1847
|
backendResponse.status,
|
|
1768
1848
|
backendResponse.bodyText
|
|
@@ -1783,7 +1863,13 @@ async function startProxy(options) {
|
|
|
1783
1863
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
1784
1864
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1785
1865
|
res.end(backendResponse.bodyText);
|
|
1866
|
+
emitProxyEvent("terminal.success", "success", correlation, {
|
|
1867
|
+
status: backendResponse.status
|
|
1868
|
+
});
|
|
1786
1869
|
} catch (err) {
|
|
1870
|
+
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
1871
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1872
|
+
});
|
|
1787
1873
|
logProxyEvent("error", "proxy_payment_settle_forward_failed", {
|
|
1788
1874
|
error: err instanceof Error ? err.message : String(err)
|
|
1789
1875
|
});
|
|
@@ -1795,7 +1881,12 @@ async function startProxy(options) {
|
|
|
1795
1881
|
return;
|
|
1796
1882
|
}
|
|
1797
1883
|
if (req.method === "POST" && matchesProxyPath(req.url, UPLOAD_PROXY_PATH)) {
|
|
1884
|
+
const correlation = {
|
|
1885
|
+
trace_id: randomUUID(),
|
|
1886
|
+
operation_id: randomUUID()
|
|
1887
|
+
};
|
|
1798
1888
|
logProxyEvent("info", "proxy_upload_received");
|
|
1889
|
+
emitProxyEvent("request.received", "start", correlation, { path: UPLOAD_PROXY_PATH });
|
|
1799
1890
|
try {
|
|
1800
1891
|
let payload;
|
|
1801
1892
|
try {
|
|
@@ -1817,6 +1908,10 @@ async function startProxy(options) {
|
|
|
1817
1908
|
});
|
|
1818
1909
|
return;
|
|
1819
1910
|
}
|
|
1911
|
+
correlation.quote_id = requestPayload.quote_id;
|
|
1912
|
+
correlation.wallet_address = requestPayload.wallet_address;
|
|
1913
|
+
correlation.object_id = requestPayload.object_id;
|
|
1914
|
+
emitProxyEvent("storage.call", "start", correlation, { target: "storage/upload" });
|
|
1820
1915
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
1821
1916
|
logProxyEvent("warn", "proxy_upload_wallet_mismatch", {
|
|
1822
1917
|
request_wallet: requestPayload.wallet_address,
|
|
@@ -1886,6 +1981,7 @@ async function startProxy(options) {
|
|
|
1886
1981
|
return;
|
|
1887
1982
|
}
|
|
1888
1983
|
const uploadPaymentFetch = createPaymentFetch(walletPrivateKey).fetch;
|
|
1984
|
+
emitProxyEvent("payment.settle", "start", correlation, { via: "upload" });
|
|
1889
1985
|
const settleResponse = await forwardPaymentSettleToBackend(
|
|
1890
1986
|
requestPayload.quote_id,
|
|
1891
1987
|
requestPayload.wallet_address,
|
|
@@ -1895,6 +1991,7 @@ async function startProxy(options) {
|
|
|
1895
1991
|
fetchImpl: uploadPaymentFetch
|
|
1896
1992
|
}
|
|
1897
1993
|
);
|
|
1994
|
+
emitProxyEvent("payment.settle", "result", correlation, { status: settleResponse.status });
|
|
1898
1995
|
const settledAlready = isAlreadySettledConflict(
|
|
1899
1996
|
settleResponse.status,
|
|
1900
1997
|
settleResponse.bodyText
|
|
@@ -1913,6 +2010,10 @@ async function startProxy(options) {
|
|
|
1913
2010
|
return;
|
|
1914
2011
|
}
|
|
1915
2012
|
if (settledAlready) {
|
|
2013
|
+
emitProxyEvent("retry.decision", "decision", correlation, {
|
|
2014
|
+
reason: "payment_already_settled_conflict",
|
|
2015
|
+
status: settleResponse.status
|
|
2016
|
+
});
|
|
1916
2017
|
logProxyEvent("info", "proxy_upload_settle_already_confirmed", {
|
|
1917
2018
|
status: settleResponse.status
|
|
1918
2019
|
});
|
|
@@ -1925,6 +2026,7 @@ async function startProxy(options) {
|
|
|
1925
2026
|
logProxyEvent("info", "proxy_upload_backend_response", {
|
|
1926
2027
|
status: backendResponse.status
|
|
1927
2028
|
});
|
|
2029
|
+
emitProxyEvent("storage.call", "result", correlation, { status: backendResponse.status });
|
|
1928
2030
|
const authFailure = normalizeBackendAuthFailure(
|
|
1929
2031
|
backendResponse.status,
|
|
1930
2032
|
backendResponse.bodyText
|
|
@@ -1945,7 +2047,13 @@ async function startProxy(options) {
|
|
|
1945
2047
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
1946
2048
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
1947
2049
|
res.end(backendResponse.bodyText);
|
|
2050
|
+
emitProxyEvent("terminal.success", "success", correlation, {
|
|
2051
|
+
status: backendResponse.status
|
|
2052
|
+
});
|
|
1948
2053
|
} catch (err) {
|
|
2054
|
+
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2055
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2056
|
+
});
|
|
1949
2057
|
logProxyEvent("error", "proxy_upload_forward_failed", {
|
|
1950
2058
|
error: err instanceof Error ? err.message : String(err)
|
|
1951
2059
|
});
|
|
@@ -2040,7 +2148,12 @@ async function startProxy(options) {
|
|
|
2040
2148
|
return;
|
|
2041
2149
|
}
|
|
2042
2150
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_LS_PROXY_PATH)) {
|
|
2151
|
+
const correlation = {
|
|
2152
|
+
trace_id: randomUUID(),
|
|
2153
|
+
operation_id: randomUUID()
|
|
2154
|
+
};
|
|
2043
2155
|
logProxyEvent("info", "proxy_ls_received");
|
|
2156
|
+
emitProxyEvent("request.received", "start", correlation, { path: STORAGE_LS_PROXY_PATH });
|
|
2044
2157
|
try {
|
|
2045
2158
|
let payload;
|
|
2046
2159
|
try {
|
|
@@ -2062,6 +2175,8 @@ async function startProxy(options) {
|
|
|
2062
2175
|
});
|
|
2063
2176
|
return;
|
|
2064
2177
|
}
|
|
2178
|
+
correlation.wallet_address = requestPayload.wallet_address;
|
|
2179
|
+
correlation.object_key = requestPayload.object_key;
|
|
2065
2180
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
2066
2181
|
logProxyEvent("warn", "proxy_ls_wallet_mismatch");
|
|
2067
2182
|
sendJson(res, 403, {
|
|
@@ -2081,11 +2196,13 @@ async function startProxy(options) {
|
|
|
2081
2196
|
res.end(createWalletRequiredBody());
|
|
2082
2197
|
return;
|
|
2083
2198
|
}
|
|
2199
|
+
emitProxyEvent("storage.call", "start", correlation, { target: "storage/ls" });
|
|
2084
2200
|
const backendResponse = await forwardStorageLsToBackend(requestPayload, {
|
|
2085
2201
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
2086
2202
|
walletSignature
|
|
2087
2203
|
});
|
|
2088
2204
|
logProxyEvent("info", "proxy_ls_backend_response", { status: backendResponse.status });
|
|
2205
|
+
emitProxyEvent("storage.call", "result", correlation, { status: backendResponse.status });
|
|
2089
2206
|
const authFailure = normalizeBackendAuthFailure(
|
|
2090
2207
|
backendResponse.status,
|
|
2091
2208
|
backendResponse.bodyText
|
|
@@ -2104,7 +2221,13 @@ async function startProxy(options) {
|
|
|
2104
2221
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
2105
2222
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
2106
2223
|
res.end(backendResponse.bodyText);
|
|
2224
|
+
emitProxyEvent("terminal.success", "success", correlation, {
|
|
2225
|
+
status: backendResponse.status
|
|
2226
|
+
});
|
|
2107
2227
|
} catch (err) {
|
|
2228
|
+
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2229
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2230
|
+
});
|
|
2108
2231
|
logProxyEvent("error", "proxy_ls_forward_failed", {
|
|
2109
2232
|
error: err instanceof Error ? err.message : String(err)
|
|
2110
2233
|
});
|
|
@@ -2116,7 +2239,14 @@ async function startProxy(options) {
|
|
|
2116
2239
|
return;
|
|
2117
2240
|
}
|
|
2118
2241
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_DOWNLOAD_PROXY_PATH)) {
|
|
2242
|
+
const correlation = {
|
|
2243
|
+
trace_id: randomUUID(),
|
|
2244
|
+
operation_id: randomUUID()
|
|
2245
|
+
};
|
|
2119
2246
|
logProxyEvent("info", "proxy_download_received");
|
|
2247
|
+
emitProxyEvent("request.received", "start", correlation, {
|
|
2248
|
+
path: STORAGE_DOWNLOAD_PROXY_PATH
|
|
2249
|
+
});
|
|
2120
2250
|
try {
|
|
2121
2251
|
let payload;
|
|
2122
2252
|
try {
|
|
@@ -2138,6 +2268,8 @@ async function startProxy(options) {
|
|
|
2138
2268
|
});
|
|
2139
2269
|
return;
|
|
2140
2270
|
}
|
|
2271
|
+
correlation.wallet_address = requestPayload.wallet_address;
|
|
2272
|
+
correlation.object_key = requestPayload.object_key;
|
|
2141
2273
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
2142
2274
|
logProxyEvent("warn", "proxy_download_wallet_mismatch");
|
|
2143
2275
|
sendJson(res, 403, {
|
|
@@ -2157,6 +2289,7 @@ async function startProxy(options) {
|
|
|
2157
2289
|
res.end(createWalletRequiredBody());
|
|
2158
2290
|
return;
|
|
2159
2291
|
}
|
|
2292
|
+
emitProxyEvent("storage.call", "start", correlation, { target: "storage/download" });
|
|
2160
2293
|
const backendResponse = await forwardStorageDownloadToBackend(requestPayload, {
|
|
2161
2294
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
2162
2295
|
walletSignature
|
|
@@ -2164,6 +2297,7 @@ async function startProxy(options) {
|
|
|
2164
2297
|
logProxyEvent("info", "proxy_download_backend_response", {
|
|
2165
2298
|
status: backendResponse.status
|
|
2166
2299
|
});
|
|
2300
|
+
emitProxyEvent("storage.call", "result", correlation, { status: backendResponse.status });
|
|
2167
2301
|
const authFailure = normalizeBackendAuthFailure(
|
|
2168
2302
|
backendResponse.status,
|
|
2169
2303
|
backendResponse.bodyText
|
|
@@ -2204,7 +2338,13 @@ async function startProxy(options) {
|
|
|
2204
2338
|
file_path: downloadResult.filePath,
|
|
2205
2339
|
bytes_written: downloadResult.bytesWritten
|
|
2206
2340
|
});
|
|
2341
|
+
emitProxyEvent("terminal.success", "success", correlation, {
|
|
2342
|
+
status: 200
|
|
2343
|
+
});
|
|
2207
2344
|
} catch (err) {
|
|
2345
|
+
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2346
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2347
|
+
});
|
|
2208
2348
|
logProxyEvent("error", "proxy_download_forward_failed", {
|
|
2209
2349
|
error: err instanceof Error ? err.message : String(err)
|
|
2210
2350
|
});
|
|
@@ -2216,7 +2356,12 @@ async function startProxy(options) {
|
|
|
2216
2356
|
return;
|
|
2217
2357
|
}
|
|
2218
2358
|
if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_DELETE_PROXY_PATH)) {
|
|
2359
|
+
const correlation = {
|
|
2360
|
+
trace_id: randomUUID(),
|
|
2361
|
+
operation_id: randomUUID()
|
|
2362
|
+
};
|
|
2219
2363
|
logProxyEvent("info", "proxy_delete_received");
|
|
2364
|
+
emitProxyEvent("request.received", "start", correlation, { path: STORAGE_DELETE_PROXY_PATH });
|
|
2220
2365
|
try {
|
|
2221
2366
|
let payload;
|
|
2222
2367
|
try {
|
|
@@ -2238,6 +2383,8 @@ async function startProxy(options) {
|
|
|
2238
2383
|
});
|
|
2239
2384
|
return;
|
|
2240
2385
|
}
|
|
2386
|
+
correlation.wallet_address = requestPayload.wallet_address;
|
|
2387
|
+
correlation.object_key = requestPayload.object_key;
|
|
2241
2388
|
if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
|
|
2242
2389
|
logProxyEvent("warn", "proxy_delete_wallet_mismatch");
|
|
2243
2390
|
sendJson(res, 403, {
|
|
@@ -2257,6 +2404,7 @@ async function startProxy(options) {
|
|
|
2257
2404
|
res.end(createWalletRequiredBody());
|
|
2258
2405
|
return;
|
|
2259
2406
|
}
|
|
2407
|
+
emitProxyEvent("storage.call", "start", correlation, { target: "storage/delete" });
|
|
2260
2408
|
const backendResponse = await forwardStorageDeleteToBackend(requestPayload, {
|
|
2261
2409
|
backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
|
|
2262
2410
|
walletSignature
|
|
@@ -2264,6 +2412,7 @@ async function startProxy(options) {
|
|
|
2264
2412
|
logProxyEvent("info", "proxy_delete_backend_response", {
|
|
2265
2413
|
status: backendResponse.status
|
|
2266
2414
|
});
|
|
2415
|
+
emitProxyEvent("storage.call", "result", correlation, { status: backendResponse.status });
|
|
2267
2416
|
const authFailure = normalizeBackendAuthFailure(
|
|
2268
2417
|
backendResponse.status,
|
|
2269
2418
|
backendResponse.bodyText
|
|
@@ -2284,7 +2433,13 @@ async function startProxy(options) {
|
|
|
2284
2433
|
const responseHeaders = createBackendForwardHeaders(backendResponse);
|
|
2285
2434
|
res.writeHead(backendResponse.status, responseHeaders);
|
|
2286
2435
|
res.end(backendResponse.bodyText);
|
|
2436
|
+
emitProxyEvent("terminal.success", "success", correlation, {
|
|
2437
|
+
status: backendResponse.status
|
|
2438
|
+
});
|
|
2287
2439
|
} catch (err) {
|
|
2440
|
+
emitProxyEvent("terminal.failure", "failure", correlation, {
|
|
2441
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2442
|
+
});
|
|
2288
2443
|
logProxyEvent("error", "proxy_delete_forward_failed", {
|
|
2289
2444
|
error: err instanceof Error ? err.message : String(err)
|
|
2290
2445
|
});
|
|
@@ -2438,9 +2593,9 @@ async function startProxy(options) {
|
|
|
2438
2593
|
}
|
|
2439
2594
|
|
|
2440
2595
|
// src/auth.ts
|
|
2441
|
-
import { writeFile as writeFile2, readFile as readFile2, mkdir as
|
|
2442
|
-
import { join as
|
|
2443
|
-
import { homedir as
|
|
2596
|
+
import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir3 } from "fs/promises";
|
|
2597
|
+
import { join as join5 } from "path";
|
|
2598
|
+
import { homedir as homedir4 } from "os";
|
|
2444
2599
|
import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
2445
2600
|
|
|
2446
2601
|
// src/wallet-key.ts
|
|
@@ -2449,10 +2604,10 @@ function isValidWalletPrivateKey(value) {
|
|
|
2449
2604
|
}
|
|
2450
2605
|
|
|
2451
2606
|
// src/auth.ts
|
|
2452
|
-
var LEGACY_WALLET_DIR =
|
|
2453
|
-
var LEGACY_WALLET_FILE =
|
|
2454
|
-
var WALLET_DIR =
|
|
2455
|
-
var WALLET_FILE =
|
|
2607
|
+
var LEGACY_WALLET_DIR = join5(homedir4(), ".openclaw", "blockrun");
|
|
2608
|
+
var LEGACY_WALLET_FILE = join5(LEGACY_WALLET_DIR, "wallet.key");
|
|
2609
|
+
var WALLET_DIR = join5(homedir4(), ".openclaw", "mnemospark", "wallet");
|
|
2610
|
+
var WALLET_FILE = join5(WALLET_DIR, "wallet.key");
|
|
2456
2611
|
async function loadSavedWallet() {
|
|
2457
2612
|
for (const path of [WALLET_FILE, LEGACY_WALLET_FILE]) {
|
|
2458
2613
|
try {
|
|
@@ -2475,7 +2630,7 @@ async function loadSavedWallet() {
|
|
|
2475
2630
|
async function generateAndSaveWallet() {
|
|
2476
2631
|
const key = generatePrivateKey();
|
|
2477
2632
|
const account = privateKeyToAccount4(key);
|
|
2478
|
-
await
|
|
2633
|
+
await mkdir3(WALLET_DIR, { recursive: true });
|
|
2479
2634
|
await writeFile2(WALLET_FILE, key + "\n", { mode: 384 });
|
|
2480
2635
|
try {
|
|
2481
2636
|
const verification = (await readFile2(WALLET_FILE, "utf-8")).trim();
|
|
@@ -2508,11 +2663,11 @@ async function resolveOrGenerateWalletKey() {
|
|
|
2508
2663
|
// src/version.ts
|
|
2509
2664
|
import { createRequire } from "module";
|
|
2510
2665
|
import { fileURLToPath } from "url";
|
|
2511
|
-
import { dirname as
|
|
2666
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
2512
2667
|
var __filename = fileURLToPath(import.meta.url);
|
|
2513
|
-
var __dirname =
|
|
2668
|
+
var __dirname = dirname3(__filename);
|
|
2514
2669
|
var require2 = createRequire(import.meta.url);
|
|
2515
|
-
var pkg = require2(
|
|
2670
|
+
var pkg = require2(join6(__dirname, "..", "package.json"));
|
|
2516
2671
|
var VERSION = pkg.version;
|
|
2517
2672
|
var USER_AGENT = `mnemospark/${VERSION}`;
|
|
2518
2673
|
|
|
@@ -2522,20 +2677,383 @@ import {
|
|
|
2522
2677
|
createCipheriv,
|
|
2523
2678
|
createHash as createHash2,
|
|
2524
2679
|
randomBytes as randomBytesNode,
|
|
2525
|
-
randomUUID
|
|
2680
|
+
randomUUID as randomUUID3
|
|
2526
2681
|
} from "crypto";
|
|
2527
|
-
import { createReadStream, statfsSync } from "fs";
|
|
2528
|
-
import { appendFile, lstat, mkdir as
|
|
2529
|
-
import { homedir as
|
|
2530
|
-
import { basename, dirname as
|
|
2682
|
+
import { createReadStream as createReadStream2, statfsSync } from "fs";
|
|
2683
|
+
import { appendFile as appendFile2, lstat, mkdir as mkdir5, readFile as readFile3, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
2684
|
+
import { homedir as homedir6 } from "os";
|
|
2685
|
+
import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
|
|
2531
2686
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
2687
|
+
|
|
2688
|
+
// src/cloud-datastore.ts
|
|
2689
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2690
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
2691
|
+
import { homedir as homedir5 } from "os";
|
|
2692
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
2693
|
+
var DB_SUBPATH = join7(".openclaw", "mnemospark", "state.db");
|
|
2694
|
+
var SCHEMA_VERSION = 2;
|
|
2695
|
+
function resolveDbPath(homeDir) {
|
|
2696
|
+
return join7(homeDir ?? homedir5(), DB_SUBPATH);
|
|
2697
|
+
}
|
|
2698
|
+
function nowIso() {
|
|
2699
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
2700
|
+
}
|
|
2701
|
+
async function createCloudDatastore(homeDir) {
|
|
2702
|
+
const dbPath = resolveDbPath(homeDir);
|
|
2703
|
+
let db = null;
|
|
2704
|
+
const ensureReady = async () => {
|
|
2705
|
+
if (db) return;
|
|
2706
|
+
if (process.env.MNEMOSPARK_DISABLE_SQLITE === "1") {
|
|
2707
|
+
throw new Error("SQLite disabled by MNEMOSPARK_DISABLE_SQLITE=1");
|
|
2708
|
+
}
|
|
2709
|
+
await mkdir4(dirname4(dbPath), { recursive: true });
|
|
2710
|
+
const sqliteMod = await import("sqlite");
|
|
2711
|
+
const DatabaseSync = sqliteMod.DatabaseSync;
|
|
2712
|
+
if (!DatabaseSync) {
|
|
2713
|
+
throw new Error("node:sqlite DatabaseSync is unavailable");
|
|
2714
|
+
}
|
|
2715
|
+
const nextDb = new DatabaseSync(dbPath);
|
|
2716
|
+
nextDb.exec("PRAGMA journal_mode=WAL;");
|
|
2717
|
+
nextDb.exec("PRAGMA foreign_keys=ON;");
|
|
2718
|
+
nextDb.exec(`
|
|
2719
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
2720
|
+
version INTEGER PRIMARY KEY,
|
|
2721
|
+
applied_at TEXT NOT NULL
|
|
2722
|
+
);
|
|
2723
|
+
|
|
2724
|
+
CREATE TABLE IF NOT EXISTS objects (
|
|
2725
|
+
object_id TEXT PRIMARY KEY,
|
|
2726
|
+
object_key TEXT,
|
|
2727
|
+
wallet_address TEXT NOT NULL,
|
|
2728
|
+
quote_id TEXT,
|
|
2729
|
+
provider TEXT,
|
|
2730
|
+
bucket_name TEXT,
|
|
2731
|
+
region TEXT,
|
|
2732
|
+
sha256 TEXT,
|
|
2733
|
+
status TEXT NOT NULL,
|
|
2734
|
+
created_at TEXT NOT NULL,
|
|
2735
|
+
updated_at TEXT NOT NULL
|
|
2736
|
+
);
|
|
2737
|
+
CREATE INDEX IF NOT EXISTS idx_objects_wallet ON objects(wallet_address);
|
|
2738
|
+
CREATE INDEX IF NOT EXISTS idx_objects_quote ON objects(quote_id);
|
|
2739
|
+
CREATE INDEX IF NOT EXISTS idx_objects_object_key ON objects(object_key);
|
|
2740
|
+
|
|
2741
|
+
CREATE TABLE IF NOT EXISTS payments (
|
|
2742
|
+
quote_id TEXT PRIMARY KEY,
|
|
2743
|
+
wallet_address TEXT NOT NULL,
|
|
2744
|
+
trans_id TEXT,
|
|
2745
|
+
amount REAL NOT NULL,
|
|
2746
|
+
network TEXT,
|
|
2747
|
+
status TEXT NOT NULL,
|
|
2748
|
+
settled_at TEXT,
|
|
2749
|
+
created_at TEXT NOT NULL,
|
|
2750
|
+
updated_at TEXT NOT NULL
|
|
2751
|
+
);
|
|
2752
|
+
CREATE INDEX IF NOT EXISTS idx_payments_wallet ON payments(wallet_address);
|
|
2753
|
+
|
|
2754
|
+
CREATE TABLE IF NOT EXISTS cron_jobs (
|
|
2755
|
+
cron_id TEXT PRIMARY KEY,
|
|
2756
|
+
object_id TEXT NOT NULL,
|
|
2757
|
+
object_key TEXT NOT NULL,
|
|
2758
|
+
quote_id TEXT NOT NULL,
|
|
2759
|
+
schedule TEXT NOT NULL,
|
|
2760
|
+
command TEXT NOT NULL,
|
|
2761
|
+
status TEXT NOT NULL,
|
|
2762
|
+
created_at TEXT NOT NULL,
|
|
2763
|
+
updated_at TEXT NOT NULL
|
|
2764
|
+
);
|
|
2765
|
+
CREATE INDEX IF NOT EXISTS idx_cron_jobs_object_key ON cron_jobs(object_key);
|
|
2766
|
+
|
|
2767
|
+
CREATE TABLE IF NOT EXISTS operations (
|
|
2768
|
+
operation_id TEXT PRIMARY KEY,
|
|
2769
|
+
type TEXT NOT NULL,
|
|
2770
|
+
object_id TEXT,
|
|
2771
|
+
quote_id TEXT,
|
|
2772
|
+
status TEXT NOT NULL,
|
|
2773
|
+
error_code TEXT,
|
|
2774
|
+
error_message TEXT,
|
|
2775
|
+
started_at TEXT,
|
|
2776
|
+
finished_at TEXT,
|
|
2777
|
+
created_at TEXT NOT NULL,
|
|
2778
|
+
updated_at TEXT NOT NULL
|
|
2779
|
+
);
|
|
2780
|
+
CREATE INDEX IF NOT EXISTS idx_operations_type ON operations(type);
|
|
2781
|
+
CREATE INDEX IF NOT EXISTS idx_operations_object_id ON operations(object_id);
|
|
2782
|
+
CREATE INDEX IF NOT EXISTS idx_operations_quote_id ON operations(quote_id);
|
|
2783
|
+
|
|
2784
|
+
CREATE TABLE IF NOT EXISTS friendly_names (
|
|
2785
|
+
friendly_name_id TEXT PRIMARY KEY,
|
|
2786
|
+
friendly_name TEXT NOT NULL,
|
|
2787
|
+
object_id TEXT NOT NULL,
|
|
2788
|
+
object_key TEXT,
|
|
2789
|
+
quote_id TEXT,
|
|
2790
|
+
wallet_address TEXT NOT NULL,
|
|
2791
|
+
created_at TEXT NOT NULL,
|
|
2792
|
+
updated_at TEXT NOT NULL,
|
|
2793
|
+
is_active INTEGER NOT NULL DEFAULT 1
|
|
2794
|
+
);
|
|
2795
|
+
CREATE INDEX IF NOT EXISTS idx_friendly_names_name ON friendly_names(friendly_name);
|
|
2796
|
+
CREATE INDEX IF NOT EXISTS idx_friendly_names_object_id ON friendly_names(object_id);
|
|
2797
|
+
CREATE INDEX IF NOT EXISTS idx_friendly_names_wallet ON friendly_names(wallet_address);
|
|
2798
|
+
CREATE INDEX IF NOT EXISTS idx_friendly_names_created_at ON friendly_names(created_at);
|
|
2799
|
+
`);
|
|
2800
|
+
nextDb.prepare(
|
|
2801
|
+
`INSERT INTO schema_migrations(version, applied_at)
|
|
2802
|
+
VALUES(?, ?)
|
|
2803
|
+
ON CONFLICT(version) DO NOTHING`
|
|
2804
|
+
).run(SCHEMA_VERSION, nowIso());
|
|
2805
|
+
db = nextDb;
|
|
2806
|
+
};
|
|
2807
|
+
const safe = async (fn, fallback) => {
|
|
2808
|
+
try {
|
|
2809
|
+
await ensureReady();
|
|
2810
|
+
return fn();
|
|
2811
|
+
} catch {
|
|
2812
|
+
return fallback;
|
|
2813
|
+
}
|
|
2814
|
+
};
|
|
2815
|
+
return {
|
|
2816
|
+
dbPath,
|
|
2817
|
+
ensureReady,
|
|
2818
|
+
upsertObject: async (row) => {
|
|
2819
|
+
await safe(() => {
|
|
2820
|
+
const ts = nowIso();
|
|
2821
|
+
db.prepare(
|
|
2822
|
+
`INSERT INTO objects(object_id, object_key, wallet_address, quote_id, provider, bucket_name, region, sha256, status, created_at, updated_at)
|
|
2823
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2824
|
+
ON CONFLICT(object_id) DO UPDATE SET
|
|
2825
|
+
object_key=excluded.object_key,
|
|
2826
|
+
wallet_address=excluded.wallet_address,
|
|
2827
|
+
quote_id=excluded.quote_id,
|
|
2828
|
+
provider=excluded.provider,
|
|
2829
|
+
bucket_name=excluded.bucket_name,
|
|
2830
|
+
region=excluded.region,
|
|
2831
|
+
sha256=excluded.sha256,
|
|
2832
|
+
status=excluded.status,
|
|
2833
|
+
updated_at=excluded.updated_at`
|
|
2834
|
+
).run(
|
|
2835
|
+
row.object_id,
|
|
2836
|
+
row.object_key,
|
|
2837
|
+
row.wallet_address,
|
|
2838
|
+
row.quote_id,
|
|
2839
|
+
row.provider,
|
|
2840
|
+
row.bucket_name,
|
|
2841
|
+
row.region,
|
|
2842
|
+
row.sha256,
|
|
2843
|
+
row.status,
|
|
2844
|
+
ts,
|
|
2845
|
+
ts
|
|
2846
|
+
);
|
|
2847
|
+
}, void 0);
|
|
2848
|
+
},
|
|
2849
|
+
findObjectByObjectKey: async (objectKey) => safe(() => {
|
|
2850
|
+
const row = db.prepare(
|
|
2851
|
+
`SELECT object_id, object_key, wallet_address, quote_id, provider, bucket_name, region, sha256, status
|
|
2852
|
+
FROM objects
|
|
2853
|
+
WHERE object_key = ?
|
|
2854
|
+
ORDER BY updated_at DESC
|
|
2855
|
+
LIMIT 1`
|
|
2856
|
+
).get(objectKey);
|
|
2857
|
+
return row ?? null;
|
|
2858
|
+
}, null),
|
|
2859
|
+
findObjectById: async (objectId) => safe(() => {
|
|
2860
|
+
const row = db.prepare(
|
|
2861
|
+
`SELECT object_id, object_key, wallet_address, quote_id, provider, bucket_name, region, sha256, status
|
|
2862
|
+
FROM objects
|
|
2863
|
+
WHERE object_id = ?
|
|
2864
|
+
LIMIT 1`
|
|
2865
|
+
).get(objectId);
|
|
2866
|
+
return row ?? null;
|
|
2867
|
+
}, null),
|
|
2868
|
+
upsertPayment: async (row) => {
|
|
2869
|
+
await safe(() => {
|
|
2870
|
+
const ts = nowIso();
|
|
2871
|
+
db.prepare(
|
|
2872
|
+
`INSERT INTO payments(quote_id, wallet_address, trans_id, amount, network, status, settled_at, created_at, updated_at)
|
|
2873
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2874
|
+
ON CONFLICT(quote_id) DO UPDATE SET
|
|
2875
|
+
wallet_address=excluded.wallet_address,
|
|
2876
|
+
trans_id=excluded.trans_id,
|
|
2877
|
+
amount=excluded.amount,
|
|
2878
|
+
network=excluded.network,
|
|
2879
|
+
status=excluded.status,
|
|
2880
|
+
settled_at=excluded.settled_at,
|
|
2881
|
+
updated_at=excluded.updated_at`
|
|
2882
|
+
).run(
|
|
2883
|
+
row.quote_id,
|
|
2884
|
+
row.wallet_address,
|
|
2885
|
+
row.trans_id,
|
|
2886
|
+
row.amount,
|
|
2887
|
+
row.network,
|
|
2888
|
+
row.status,
|
|
2889
|
+
row.settled_at ?? null,
|
|
2890
|
+
ts,
|
|
2891
|
+
ts
|
|
2892
|
+
);
|
|
2893
|
+
}, void 0);
|
|
2894
|
+
},
|
|
2895
|
+
upsertCronJob: async (row) => {
|
|
2896
|
+
await safe(() => {
|
|
2897
|
+
const ts = nowIso();
|
|
2898
|
+
db.prepare(
|
|
2899
|
+
`INSERT INTO cron_jobs(cron_id, object_id, object_key, quote_id, schedule, command, status, created_at, updated_at)
|
|
2900
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2901
|
+
ON CONFLICT(cron_id) DO UPDATE SET
|
|
2902
|
+
object_id=excluded.object_id,
|
|
2903
|
+
object_key=excluded.object_key,
|
|
2904
|
+
quote_id=excluded.quote_id,
|
|
2905
|
+
schedule=excluded.schedule,
|
|
2906
|
+
command=excluded.command,
|
|
2907
|
+
status=excluded.status,
|
|
2908
|
+
updated_at=excluded.updated_at`
|
|
2909
|
+
).run(
|
|
2910
|
+
row.cron_id,
|
|
2911
|
+
row.object_id,
|
|
2912
|
+
row.object_key,
|
|
2913
|
+
row.quote_id,
|
|
2914
|
+
row.schedule,
|
|
2915
|
+
row.command,
|
|
2916
|
+
row.status,
|
|
2917
|
+
ts,
|
|
2918
|
+
ts
|
|
2919
|
+
);
|
|
2920
|
+
}, void 0);
|
|
2921
|
+
},
|
|
2922
|
+
removeCronJob: async (cronId) => safe(() => {
|
|
2923
|
+
const res = db.prepare(`DELETE FROM cron_jobs WHERE cron_id = ?`).run(cronId);
|
|
2924
|
+
return Number(res.changes ?? 0) > 0;
|
|
2925
|
+
}, false),
|
|
2926
|
+
findCronByObjectKey: async (objectKey) => safe(() => {
|
|
2927
|
+
const row = db.prepare(
|
|
2928
|
+
`SELECT cron_id, object_id FROM cron_jobs WHERE object_key = ? ORDER BY updated_at DESC LIMIT 1`
|
|
2929
|
+
).get(objectKey);
|
|
2930
|
+
if (!row) return null;
|
|
2931
|
+
return { cronId: row.cron_id, objectId: row.object_id };
|
|
2932
|
+
}, null),
|
|
2933
|
+
upsertOperation: async (row) => {
|
|
2934
|
+
await safe(() => {
|
|
2935
|
+
const ts = nowIso();
|
|
2936
|
+
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(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2939
|
+
ON CONFLICT(operation_id) DO UPDATE SET
|
|
2940
|
+
type=excluded.type,
|
|
2941
|
+
object_id=excluded.object_id,
|
|
2942
|
+
quote_id=excluded.quote_id,
|
|
2943
|
+
status=excluded.status,
|
|
2944
|
+
error_code=excluded.error_code,
|
|
2945
|
+
error_message=excluded.error_message,
|
|
2946
|
+
started_at=COALESCE(excluded.started_at, operations.started_at),
|
|
2947
|
+
finished_at=COALESCE(excluded.finished_at, operations.finished_at),
|
|
2948
|
+
updated_at=excluded.updated_at`
|
|
2949
|
+
).run(
|
|
2950
|
+
row.operation_id,
|
|
2951
|
+
row.type,
|
|
2952
|
+
row.object_id,
|
|
2953
|
+
row.quote_id,
|
|
2954
|
+
row.status,
|
|
2955
|
+
row.error_code,
|
|
2956
|
+
row.error_message,
|
|
2957
|
+
row.status === "started" ? ts : null,
|
|
2958
|
+
row.status === "succeeded" || row.status === "failed" ? ts : null,
|
|
2959
|
+
ts,
|
|
2960
|
+
ts
|
|
2961
|
+
);
|
|
2962
|
+
}, void 0);
|
|
2963
|
+
},
|
|
2964
|
+
findOperationById: async (operationId) => safe(() => {
|
|
2965
|
+
const row = db.prepare(
|
|
2966
|
+
`SELECT operation_id, type, status, error_code, error_message, started_at, finished_at, updated_at
|
|
2967
|
+
FROM operations
|
|
2968
|
+
WHERE operation_id = ?
|
|
2969
|
+
LIMIT 1`
|
|
2970
|
+
).get(operationId);
|
|
2971
|
+
return row ?? null;
|
|
2972
|
+
}, null),
|
|
2973
|
+
findQuoteById: async (quoteId) => safe(() => {
|
|
2974
|
+
const row = db.prepare(
|
|
2975
|
+
`SELECT quote_id, amount, wallet_address FROM payments WHERE quote_id = ? ORDER BY updated_at DESC LIMIT 1`
|
|
2976
|
+
).get(quoteId);
|
|
2977
|
+
const object = db.prepare(
|
|
2978
|
+
`SELECT object_id, sha256, provider, region FROM objects WHERE quote_id = ? ORDER BY updated_at DESC LIMIT 1`
|
|
2979
|
+
).get(quoteId);
|
|
2980
|
+
if (!row || !object) return null;
|
|
2981
|
+
if (object.sha256 === null || object.provider === null || object.region === null)
|
|
2982
|
+
return null;
|
|
2983
|
+
return {
|
|
2984
|
+
quoteId,
|
|
2985
|
+
storagePrice: Number(row.amount),
|
|
2986
|
+
walletAddress: row.wallet_address,
|
|
2987
|
+
objectId: object.object_id,
|
|
2988
|
+
objectIdHash: object.sha256,
|
|
2989
|
+
provider: object.provider,
|
|
2990
|
+
location: object.region
|
|
2991
|
+
};
|
|
2992
|
+
}, null),
|
|
2993
|
+
upsertFriendlyName: async (row) => {
|
|
2994
|
+
await safe(() => {
|
|
2995
|
+
const ts = nowIso();
|
|
2996
|
+
db.prepare(
|
|
2997
|
+
`INSERT INTO friendly_names(friendly_name_id, friendly_name, object_id, object_key, quote_id, wallet_address, created_at, updated_at, is_active)
|
|
2998
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
2999
|
+
).run(
|
|
3000
|
+
randomUUID2(),
|
|
3001
|
+
row.friendly_name,
|
|
3002
|
+
row.object_id,
|
|
3003
|
+
row.object_key,
|
|
3004
|
+
row.quote_id,
|
|
3005
|
+
row.wallet_address,
|
|
3006
|
+
ts,
|
|
3007
|
+
ts,
|
|
3008
|
+
row.is_active ?? 1
|
|
3009
|
+
);
|
|
3010
|
+
}, void 0);
|
|
3011
|
+
},
|
|
3012
|
+
resolveFriendlyName: async (params) => safe(() => {
|
|
3013
|
+
const atIso = params.at ? new Date(params.at).toISOString() : null;
|
|
3014
|
+
const row = params.latest || !atIso ? db.prepare(
|
|
3015
|
+
`SELECT friendly_name_id, friendly_name, object_id, object_key, quote_id, wallet_address, created_at
|
|
3016
|
+
FROM friendly_names
|
|
3017
|
+
WHERE wallet_address = ? AND friendly_name = ? AND is_active = 1
|
|
3018
|
+
ORDER BY created_at DESC
|
|
3019
|
+
LIMIT 1`
|
|
3020
|
+
).get(params.walletAddress, params.friendlyName) : db.prepare(
|
|
3021
|
+
`SELECT friendly_name_id, friendly_name, object_id, object_key, quote_id, wallet_address, created_at
|
|
3022
|
+
FROM friendly_names
|
|
3023
|
+
WHERE wallet_address = ? AND friendly_name = ? AND is_active = 1 AND created_at <= ?
|
|
3024
|
+
ORDER BY created_at DESC
|
|
3025
|
+
LIMIT 1`
|
|
3026
|
+
).get(params.walletAddress, params.friendlyName, atIso);
|
|
3027
|
+
if (!row) return null;
|
|
3028
|
+
return {
|
|
3029
|
+
friendlyNameId: row.friendly_name_id,
|
|
3030
|
+
friendlyName: row.friendly_name,
|
|
3031
|
+
objectId: row.object_id,
|
|
3032
|
+
objectKey: row.object_key,
|
|
3033
|
+
quoteId: row.quote_id,
|
|
3034
|
+
walletAddress: row.wallet_address,
|
|
3035
|
+
createdAt: row.created_at
|
|
3036
|
+
};
|
|
3037
|
+
}, null),
|
|
3038
|
+
countFriendlyNameMatches: async (walletAddress, friendlyName) => safe(() => {
|
|
3039
|
+
const row = db.prepare(
|
|
3040
|
+
`SELECT COUNT(1) AS cnt
|
|
3041
|
+
FROM friendly_names
|
|
3042
|
+
WHERE wallet_address = ? AND friendly_name = ? AND is_active = 1`
|
|
3043
|
+
).get(walletAddress, friendlyName);
|
|
3044
|
+
return Number(row?.cnt ?? 0);
|
|
3045
|
+
}, 0)
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
// src/cloud-command.ts
|
|
2532
3050
|
var SUPPORTED_BACKUP_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux"]);
|
|
2533
|
-
var BACKUP_DIR_SUBPATH =
|
|
2534
|
-
var DEFAULT_BACKUP_DIR =
|
|
2535
|
-
var OBJECT_LOG_SUBPATH =
|
|
2536
|
-
var CRON_TABLE_SUBPATH =
|
|
2537
|
-
var BLOCKRUN_WALLET_KEY_SUBPATH =
|
|
2538
|
-
var MNEMOSPARK_WALLET_KEY_SUBPATH =
|
|
3051
|
+
var BACKUP_DIR_SUBPATH = join8(".openclaw", "mnemospark", "backup");
|
|
3052
|
+
var DEFAULT_BACKUP_DIR = join8(homedir6(), BACKUP_DIR_SUBPATH);
|
|
3053
|
+
var OBJECT_LOG_SUBPATH = join8(".openclaw", "mnemospark", "object.log");
|
|
3054
|
+
var CRON_TABLE_SUBPATH = join8(".openclaw", "mnemospark", "crontab.txt");
|
|
3055
|
+
var BLOCKRUN_WALLET_KEY_SUBPATH = join8(".openclaw", "blockrun", "wallet.key");
|
|
3056
|
+
var MNEMOSPARK_WALLET_KEY_SUBPATH = join8(".openclaw", "mnemospark", "wallet", "wallet.key");
|
|
2539
3057
|
var INLINE_UPLOAD_MAX_BYTES = 45e5;
|
|
2540
3058
|
var PAYMENT_REMINDER_INTERVAL_DAYS = 30;
|
|
2541
3059
|
var PAYMENT_DELETE_DEADLINE_DAYS = 32;
|
|
@@ -2544,14 +3062,17 @@ var CRON_LOG_ROW_PREFIX = "cron";
|
|
|
2544
3062
|
var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
|
|
2545
3063
|
var REQUIRED_PRICE_STORAGE = "--wallet-address, --object-id, --object-id-hash, --gb, --provider, --region";
|
|
2546
3064
|
var REQUIRED_UPLOAD = "--quote-id, --wallet-address, --object-id, --object-id-hash";
|
|
2547
|
-
var REQUIRED_STORAGE_OBJECT = "--wallet-address
|
|
3065
|
+
var REQUIRED_STORAGE_OBJECT = "--wallet-address and one of (--object-key | --name [--latest|--at])";
|
|
3066
|
+
var BOOLEAN_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["latest"]);
|
|
3067
|
+
var BOOLEAN_ASYNC_FLAGS = /* @__PURE__ */ new Set(["async"]);
|
|
3068
|
+
var BOOLEAN_SELECTOR_AND_ASYNC_FLAGS = /* @__PURE__ */ new Set(["latest", "async"]);
|
|
2548
3069
|
function expandTilde(path) {
|
|
2549
3070
|
const trimmed = path.trim();
|
|
2550
3071
|
if (trimmed === "~") {
|
|
2551
|
-
return
|
|
3072
|
+
return homedir6();
|
|
2552
3073
|
}
|
|
2553
3074
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
2554
|
-
return
|
|
3075
|
+
return join8(homedir6(), trimmed.slice(2));
|
|
2555
3076
|
}
|
|
2556
3077
|
return path;
|
|
2557
3078
|
}
|
|
@@ -2560,24 +3081,27 @@ var CLOUD_HELP_TEXT = [
|
|
|
2560
3081
|
"",
|
|
2561
3082
|
"\u2022 `/mnemospark-cloud` or `/mnemospark-cloud help` \u2014 show this message",
|
|
2562
3083
|
"",
|
|
2563
|
-
"\u2022 `/mnemospark-cloud backup <file>` or `/mnemospark-cloud backup <directory
|
|
3084
|
+
"\u2022 `/mnemospark-cloud backup <file>` or `/mnemospark-cloud backup <directory> [--name <friendly-name>]`",
|
|
2564
3085
|
" Required: <file> or <directory> (path to back up)",
|
|
2565
3086
|
"",
|
|
2566
3087
|
"\u2022 `/mnemospark-cloud price-storage --wallet-address <addr> --object-id <id> --object-id-hash <hash> --gb <gb> --provider <provider> --region <region>`",
|
|
2567
3088
|
" Required: " + REQUIRED_PRICE_STORAGE,
|
|
2568
3089
|
"",
|
|
2569
|
-
"\u2022 `/mnemospark-cloud upload --quote-id <quote-id> --wallet-address <addr> --object-id <id> --object-id-hash <hash
|
|
3090
|
+
"\u2022 `/mnemospark-cloud upload --quote-id <quote-id> --wallet-address <addr> --object-id <id> --object-id-hash <hash> [--name <friendly-name>] [--async]`",
|
|
2570
3091
|
" Required: " + REQUIRED_UPLOAD,
|
|
2571
3092
|
"",
|
|
2572
|
-
"\u2022 `/mnemospark-cloud ls --wallet-address <addr> --object-key <object-key
|
|
3093
|
+
"\u2022 `/mnemospark-cloud ls --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
2573
3094
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
2574
3095
|
"",
|
|
2575
|
-
"\u2022 `/mnemospark-cloud download --wallet-address <addr> --object-key <object-key
|
|
3096
|
+
"\u2022 `/mnemospark-cloud download --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>] [--async]`",
|
|
2576
3097
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
2577
3098
|
"",
|
|
2578
|
-
"\u2022 `/mnemospark-cloud delete --wallet-address <addr> --object-key <object-key
|
|
3099
|
+
"\u2022 `/mnemospark-cloud delete --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
2579
3100
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
2580
3101
|
"",
|
|
3102
|
+
"\u2022 `/mnemospark-cloud op-status --operation-id <id>`",
|
|
3103
|
+
" Required: --operation-id",
|
|
3104
|
+
"",
|
|
2581
3105
|
"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."
|
|
2582
3106
|
].join("\n");
|
|
2583
3107
|
var UnsupportedBackupPlatformError = class extends Error {
|
|
@@ -2600,15 +3124,17 @@ function stripWrappingQuotes(input) {
|
|
|
2600
3124
|
}
|
|
2601
3125
|
return trimmed;
|
|
2602
3126
|
}
|
|
2603
|
-
function
|
|
3127
|
+
function tokenizeArgsRaw(input) {
|
|
2604
3128
|
const tokens = input.match(/"[^"]*"|'[^']*'|\S+/g);
|
|
2605
3129
|
if (!tokens) {
|
|
2606
3130
|
return [];
|
|
2607
3131
|
}
|
|
2608
|
-
return tokens
|
|
3132
|
+
return tokens;
|
|
2609
3133
|
}
|
|
2610
|
-
function
|
|
2611
|
-
|
|
3134
|
+
function tokenizeArgs(input) {
|
|
3135
|
+
return tokenizeArgsRaw(input).map((token) => stripWrappingQuotes(token));
|
|
3136
|
+
}
|
|
3137
|
+
function parseNamedFlagsTokens(tokens, booleanFlags = /* @__PURE__ */ new Set()) {
|
|
2612
3138
|
if (tokens.length === 0) {
|
|
2613
3139
|
return null;
|
|
2614
3140
|
}
|
|
@@ -2621,6 +3147,10 @@ function parseNamedFlags(input) {
|
|
|
2621
3147
|
const key = keyToken.slice(2).toLowerCase();
|
|
2622
3148
|
const value = tokens[i + 1];
|
|
2623
3149
|
if (!value || value.startsWith("--")) {
|
|
3150
|
+
if (booleanFlags.has(key)) {
|
|
3151
|
+
parsed[key] = "true";
|
|
3152
|
+
continue;
|
|
3153
|
+
}
|
|
2624
3154
|
return null;
|
|
2625
3155
|
}
|
|
2626
3156
|
parsed[key] = value;
|
|
@@ -2628,6 +3158,44 @@ function parseNamedFlags(input) {
|
|
|
2628
3158
|
}
|
|
2629
3159
|
return parsed;
|
|
2630
3160
|
}
|
|
3161
|
+
function parseNamedFlags(input, booleanFlags = /* @__PURE__ */ new Set()) {
|
|
3162
|
+
const tokens = tokenizeArgs(input);
|
|
3163
|
+
return parseNamedFlagsTokens(tokens, booleanFlags);
|
|
3164
|
+
}
|
|
3165
|
+
function parseObjectSelector(flags) {
|
|
3166
|
+
const objectKey = flags["object-key"]?.trim();
|
|
3167
|
+
const name = flags.name?.trim();
|
|
3168
|
+
const latest = flags.latest === "true";
|
|
3169
|
+
const at = flags.at?.trim();
|
|
3170
|
+
if (objectKey && name) return null;
|
|
3171
|
+
if (!objectKey && !name) return null;
|
|
3172
|
+
if (latest && at) return null;
|
|
3173
|
+
if (objectKey) return { objectKey };
|
|
3174
|
+
return { nameSelector: { name, latest, at } };
|
|
3175
|
+
}
|
|
3176
|
+
function parseStorageObjectRequestInput(flags, selector) {
|
|
3177
|
+
const walletAddress = flags["wallet-address"]?.trim();
|
|
3178
|
+
if (!walletAddress) {
|
|
3179
|
+
return null;
|
|
3180
|
+
}
|
|
3181
|
+
const location = flags.location?.trim() || flags.region?.trim() || void 0;
|
|
3182
|
+
if (!selector.objectKey) {
|
|
3183
|
+
return {
|
|
3184
|
+
wallet_address: walletAddress,
|
|
3185
|
+
location
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
3188
|
+
return parseStorageObjectRequest({
|
|
3189
|
+
wallet_address: walletAddress,
|
|
3190
|
+
object_key: selector.objectKey,
|
|
3191
|
+
location
|
|
3192
|
+
});
|
|
3193
|
+
}
|
|
3194
|
+
function stripAsyncFlag(args) {
|
|
3195
|
+
const tokens = tokenizeArgsRaw(args ?? "");
|
|
3196
|
+
const filtered = tokens.filter((token) => token.toLowerCase() !== "--async");
|
|
3197
|
+
return filtered.join(" ");
|
|
3198
|
+
}
|
|
2631
3199
|
function parseCloudArgs(args) {
|
|
2632
3200
|
const trimmed = args?.trim() ?? "";
|
|
2633
3201
|
if (!trimmed) {
|
|
@@ -2640,11 +3208,16 @@ function parseCloudArgs(args) {
|
|
|
2640
3208
|
return { mode: "help" };
|
|
2641
3209
|
}
|
|
2642
3210
|
if (subcommand === "backup") {
|
|
2643
|
-
const
|
|
3211
|
+
const tokens = tokenizeArgs(rest);
|
|
3212
|
+
if (tokens.length === 0) {
|
|
3213
|
+
return { mode: "unknown" };
|
|
3214
|
+
}
|
|
3215
|
+
const backupTarget = tokens[0] ?? "";
|
|
2644
3216
|
if (!backupTarget) {
|
|
2645
3217
|
return { mode: "unknown" };
|
|
2646
3218
|
}
|
|
2647
|
-
|
|
3219
|
+
const flags = parseNamedFlagsTokens(tokens.slice(1));
|
|
3220
|
+
return { mode: "backup", backupTarget, friendlyName: flags?.name?.trim() || void 0 };
|
|
2648
3221
|
}
|
|
2649
3222
|
if (subcommand === "price-storage") {
|
|
2650
3223
|
const flags = parseNamedFlags(rest);
|
|
@@ -2666,7 +3239,7 @@ function parseCloudArgs(args) {
|
|
|
2666
3239
|
return { mode: "price-storage", priceStorageRequest: request };
|
|
2667
3240
|
}
|
|
2668
3241
|
if (subcommand === "upload") {
|
|
2669
|
-
const flags = parseNamedFlags(rest);
|
|
3242
|
+
const flags = parseNamedFlags(rest, BOOLEAN_ASYNC_FLAGS);
|
|
2670
3243
|
if (!flags) {
|
|
2671
3244
|
return { mode: "upload-invalid" };
|
|
2672
3245
|
}
|
|
@@ -2679,6 +3252,8 @@ function parseCloudArgs(args) {
|
|
|
2679
3252
|
}
|
|
2680
3253
|
return {
|
|
2681
3254
|
mode: "upload",
|
|
3255
|
+
friendlyName: flags.name?.trim() || void 0,
|
|
3256
|
+
async: flags.async === "true",
|
|
2682
3257
|
uploadRequest: {
|
|
2683
3258
|
quote_id: quoteId,
|
|
2684
3259
|
wallet_address: walletAddress,
|
|
@@ -2688,62 +3263,75 @@ function parseCloudArgs(args) {
|
|
|
2688
3263
|
};
|
|
2689
3264
|
}
|
|
2690
3265
|
if (subcommand === "ls") {
|
|
2691
|
-
const flags = parseNamedFlags(rest);
|
|
3266
|
+
const flags = parseNamedFlags(rest, BOOLEAN_SELECTOR_FLAGS);
|
|
2692
3267
|
if (!flags) {
|
|
2693
3268
|
return { mode: "ls-invalid" };
|
|
2694
3269
|
}
|
|
2695
|
-
const
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
3270
|
+
const selector = parseObjectSelector(flags);
|
|
3271
|
+
if (!selector) {
|
|
3272
|
+
return { mode: "ls-invalid" };
|
|
3273
|
+
}
|
|
3274
|
+
const request = parseStorageObjectRequestInput(flags, selector);
|
|
2700
3275
|
if (!request) {
|
|
2701
3276
|
return { mode: "ls-invalid" };
|
|
2702
3277
|
}
|
|
2703
|
-
return { mode: "ls", storageObjectRequest: request };
|
|
3278
|
+
return { mode: "ls", storageObjectRequest: request, nameSelector: selector.nameSelector };
|
|
2704
3279
|
}
|
|
2705
3280
|
if (subcommand === "download") {
|
|
2706
|
-
const flags = parseNamedFlags(rest);
|
|
3281
|
+
const flags = parseNamedFlags(rest, BOOLEAN_SELECTOR_AND_ASYNC_FLAGS);
|
|
2707
3282
|
if (!flags) {
|
|
2708
3283
|
return { mode: "download-invalid" };
|
|
2709
3284
|
}
|
|
2710
|
-
const
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
3285
|
+
const selector = parseObjectSelector(flags);
|
|
3286
|
+
if (!selector) {
|
|
3287
|
+
return { mode: "download-invalid" };
|
|
3288
|
+
}
|
|
3289
|
+
const request = parseStorageObjectRequestInput(flags, selector);
|
|
2715
3290
|
if (!request) {
|
|
2716
3291
|
return { mode: "download-invalid" };
|
|
2717
3292
|
}
|
|
2718
|
-
return {
|
|
3293
|
+
return {
|
|
3294
|
+
mode: "download",
|
|
3295
|
+
storageObjectRequest: request,
|
|
3296
|
+
nameSelector: selector.nameSelector,
|
|
3297
|
+
async: flags.async === "true"
|
|
3298
|
+
};
|
|
2719
3299
|
}
|
|
2720
3300
|
if (subcommand === "delete") {
|
|
2721
|
-
const flags = parseNamedFlags(rest);
|
|
3301
|
+
const flags = parseNamedFlags(rest, BOOLEAN_SELECTOR_FLAGS);
|
|
2722
3302
|
if (!flags) {
|
|
2723
3303
|
return { mode: "delete-invalid" };
|
|
2724
3304
|
}
|
|
2725
|
-
const
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
3305
|
+
const selector = parseObjectSelector(flags);
|
|
3306
|
+
if (!selector) {
|
|
3307
|
+
return { mode: "delete-invalid" };
|
|
3308
|
+
}
|
|
3309
|
+
const request = parseStorageObjectRequestInput(flags, selector);
|
|
2730
3310
|
if (!request) {
|
|
2731
3311
|
return { mode: "delete-invalid" };
|
|
2732
3312
|
}
|
|
2733
|
-
return { mode: "delete", storageObjectRequest: request };
|
|
3313
|
+
return { mode: "delete", storageObjectRequest: request, nameSelector: selector.nameSelector };
|
|
3314
|
+
}
|
|
3315
|
+
if (subcommand === "op-status") {
|
|
3316
|
+
const flags = parseNamedFlags(rest);
|
|
3317
|
+
const operationId = flags?.["operation-id"]?.trim();
|
|
3318
|
+
if (!operationId) {
|
|
3319
|
+
return { mode: "op-status-invalid" };
|
|
3320
|
+
}
|
|
3321
|
+
return { mode: "op-status", operationId };
|
|
2734
3322
|
}
|
|
2735
3323
|
return { mode: "unknown" };
|
|
2736
3324
|
}
|
|
2737
3325
|
function resolveObjectLogPath(homeDir) {
|
|
2738
|
-
return
|
|
3326
|
+
return join8(homeDir ?? homedir6(), OBJECT_LOG_SUBPATH);
|
|
2739
3327
|
}
|
|
2740
3328
|
function resolveCronTablePath(homeDir) {
|
|
2741
|
-
return
|
|
3329
|
+
return join8(homeDir ?? homedir6(), CRON_TABLE_SUBPATH);
|
|
2742
3330
|
}
|
|
2743
3331
|
async function appendObjectLogLine(line, homeDir) {
|
|
2744
3332
|
const objectLogPath = resolveObjectLogPath(homeDir);
|
|
2745
|
-
await
|
|
2746
|
-
await
|
|
3333
|
+
await mkdir5(dirname5(objectLogPath), { recursive: true });
|
|
3334
|
+
await appendFile2(objectLogPath, `${line}
|
|
2747
3335
|
`, "utf-8");
|
|
2748
3336
|
return objectLogPath;
|
|
2749
3337
|
}
|
|
@@ -2756,9 +3344,9 @@ async function calculateInputSizeBytes(targetPath) {
|
|
|
2756
3344
|
throw new Error("Backup target must be a file or directory");
|
|
2757
3345
|
}
|
|
2758
3346
|
let total = 0;
|
|
2759
|
-
const entries = await
|
|
3347
|
+
const entries = await readdir2(targetPath, { withFileTypes: true });
|
|
2760
3348
|
for (const entry of entries) {
|
|
2761
|
-
total += await calculateInputSizeBytes(
|
|
3349
|
+
total += await calculateInputSizeBytes(join8(targetPath, entry.name));
|
|
2762
3350
|
}
|
|
2763
3351
|
return total;
|
|
2764
3352
|
}
|
|
@@ -2770,8 +3358,8 @@ function getAvailableDiskBytes(tmpDir, options) {
|
|
|
2770
3358
|
return stats.bavail * stats.bsize;
|
|
2771
3359
|
}
|
|
2772
3360
|
async function runTarGzip(archivePath, sourcePath) {
|
|
2773
|
-
const sourceDir =
|
|
2774
|
-
const sourceName =
|
|
3361
|
+
const sourceDir = dirname5(sourcePath);
|
|
3362
|
+
const sourceName = basename2(sourcePath);
|
|
2775
3363
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
2776
3364
|
let stderr = "";
|
|
2777
3365
|
const child = spawn("tar", ["-czf", archivePath, "-C", sourceDir, sourceName], {
|
|
@@ -2793,7 +3381,7 @@ async function runTarGzip(archivePath, sourcePath) {
|
|
|
2793
3381
|
async function sha256File(filePath) {
|
|
2794
3382
|
const hash = createHash2("sha256");
|
|
2795
3383
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
2796
|
-
const stream =
|
|
3384
|
+
const stream = createReadStream2(filePath);
|
|
2797
3385
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
2798
3386
|
stream.on("error", rejectPromise);
|
|
2799
3387
|
stream.on("end", () => resolvePromise());
|
|
@@ -2818,11 +3406,11 @@ async function buildBackupObject(targetPathArg, options = {}) {
|
|
|
2818
3406
|
const tmpDir = options.tmpDir ?? DEFAULT_BACKUP_DIR;
|
|
2819
3407
|
let tmpStats;
|
|
2820
3408
|
try {
|
|
2821
|
-
tmpStats = await
|
|
3409
|
+
tmpStats = await stat2(tmpDir);
|
|
2822
3410
|
} catch (error) {
|
|
2823
3411
|
if (error.code === "ENOENT") {
|
|
2824
|
-
await
|
|
2825
|
-
tmpStats = await
|
|
3412
|
+
await mkdir5(tmpDir, { recursive: true });
|
|
3413
|
+
tmpStats = await stat2(tmpDir);
|
|
2826
3414
|
} else {
|
|
2827
3415
|
throw error;
|
|
2828
3416
|
}
|
|
@@ -2837,10 +3425,10 @@ async function buildBackupObject(targetPathArg, options = {}) {
|
|
|
2837
3425
|
throw new Error("Insufficient disk space for backup object");
|
|
2838
3426
|
}
|
|
2839
3427
|
const objectId = createObjectId(options);
|
|
2840
|
-
const archivePath =
|
|
3428
|
+
const archivePath = join8(tmpDir, objectId);
|
|
2841
3429
|
try {
|
|
2842
3430
|
await runTarGzip(archivePath, targetPath);
|
|
2843
|
-
const archiveStats = await
|
|
3431
|
+
const archiveStats = await stat2(archivePath);
|
|
2844
3432
|
const objectIdHash = await sha256File(archivePath);
|
|
2845
3433
|
const objectSizeGb = toGbString(archiveStats.size);
|
|
2846
3434
|
const objectLogPath = await appendObjectLogLine(
|
|
@@ -3059,8 +3647,8 @@ async function appendStoragePaymentCronLog(cronJob, homeDir) {
|
|
|
3059
3647
|
}
|
|
3060
3648
|
async function appendStoragePaymentCronJob(cronJob, homeDir) {
|
|
3061
3649
|
const cronTablePath = resolveCronTablePath(homeDir);
|
|
3062
|
-
await
|
|
3063
|
-
await
|
|
3650
|
+
await mkdir5(dirname5(cronTablePath), { recursive: true });
|
|
3651
|
+
await appendFile2(cronTablePath, `${JSON.stringify(cronJob)}
|
|
3064
3652
|
`, "utf-8");
|
|
3065
3653
|
return cronTablePath;
|
|
3066
3654
|
}
|
|
@@ -3093,14 +3681,14 @@ async function removeStoragePaymentCronJob(cronId, homeDir) {
|
|
|
3093
3681
|
if (!removed) {
|
|
3094
3682
|
return false;
|
|
3095
3683
|
}
|
|
3096
|
-
await
|
|
3684
|
+
await mkdir5(dirname5(cronTablePath), { recursive: true });
|
|
3097
3685
|
const nextContent = keptLines.length > 0 ? `${keptLines.join("\n")}
|
|
3098
3686
|
` : "";
|
|
3099
3687
|
await writeFile3(cronTablePath, nextContent, "utf-8");
|
|
3100
3688
|
return true;
|
|
3101
3689
|
}
|
|
3102
3690
|
async function createStoragePaymentCronJob(upload, storagePrice, homeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
|
|
3103
|
-
const cronId =
|
|
3691
|
+
const cronId = randomUUID3();
|
|
3104
3692
|
const createdAt = formatTimestamp(nowDateFn());
|
|
3105
3693
|
const cronJob = {
|
|
3106
3694
|
cronId,
|
|
@@ -3142,9 +3730,9 @@ async function resolveWalletPrivateKey(homeDir) {
|
|
|
3142
3730
|
if (isValidWalletPrivateKey(envKey)) {
|
|
3143
3731
|
return envKey;
|
|
3144
3732
|
}
|
|
3145
|
-
const baseHome = homeDir ??
|
|
3146
|
-
const primaryWalletPath =
|
|
3147
|
-
const fallbackWalletPath =
|
|
3733
|
+
const baseHome = homeDir ?? homedir6();
|
|
3734
|
+
const primaryWalletPath = join8(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
|
|
3735
|
+
const fallbackWalletPath = join8(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
|
|
3148
3736
|
const fromPrimary = await readWalletKeyIfPresent(primaryWalletPath);
|
|
3149
3737
|
if (fromPrimary) {
|
|
3150
3738
|
return fromPrimary;
|
|
@@ -3175,7 +3763,7 @@ function encryptAesGcm(plaintext, key, randomFn = randomBytesNode) {
|
|
|
3175
3763
|
}
|
|
3176
3764
|
async function loadOrCreateKek(walletAddress, homeDir) {
|
|
3177
3765
|
const keyPath = resolveWalletKekPath(walletAddress, homeDir);
|
|
3178
|
-
await
|
|
3766
|
+
await mkdir5(dirname5(keyPath), { recursive: true });
|
|
3179
3767
|
try {
|
|
3180
3768
|
const existing = await readFile3(keyPath);
|
|
3181
3769
|
return { kek: parseStoredAes256Key(existing), keyPath };
|
|
@@ -3326,7 +3914,7 @@ function createCloudCommand(options = {}) {
|
|
|
3326
3914
|
createPaymentFetchFn: options.createPaymentFetchFn ?? createPaymentFetch,
|
|
3327
3915
|
fetchImpl: options.fetchImpl ?? fetch,
|
|
3328
3916
|
nowDateFn: options.nowDateFn ?? (() => /* @__PURE__ */ new Date()),
|
|
3329
|
-
idempotencyKeyFn: options.idempotencyKeyFn ??
|
|
3917
|
+
idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID3,
|
|
3330
3918
|
requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
|
|
3331
3919
|
requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
|
|
3332
3920
|
requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
|
|
@@ -3344,6 +3932,57 @@ function createCloudCommand(options = {}) {
|
|
|
3344
3932
|
}
|
|
3345
3933
|
};
|
|
3346
3934
|
}
|
|
3935
|
+
async function resolveNameSelectorIfNeeded(datastore, request, selector) {
|
|
3936
|
+
if (!selector) {
|
|
3937
|
+
const parsedRequest2 = parseStorageObjectRequest(request);
|
|
3938
|
+
if (!parsedRequest2) {
|
|
3939
|
+
return { error: "Cannot resolve storage object request." };
|
|
3940
|
+
}
|
|
3941
|
+
return { request: parsedRequest2 };
|
|
3942
|
+
}
|
|
3943
|
+
const matches = await datastore.countFriendlyNameMatches(request.wallet_address, selector.name);
|
|
3944
|
+
if (matches > 1 && !selector.latest && !selector.at) {
|
|
3945
|
+
return {
|
|
3946
|
+
error: `Multiple objects match --name ${selector.name}. Add --latest or --at <timestamp>.`
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
const resolved = await datastore.resolveFriendlyName({
|
|
3950
|
+
walletAddress: request.wallet_address,
|
|
3951
|
+
friendlyName: selector.name,
|
|
3952
|
+
latest: selector.latest,
|
|
3953
|
+
at: selector.at
|
|
3954
|
+
});
|
|
3955
|
+
if (!resolved || !resolved.objectKey) {
|
|
3956
|
+
return { error: `No object found for --name ${selector.name}.` };
|
|
3957
|
+
}
|
|
3958
|
+
const parsedRequest = parseStorageObjectRequest({
|
|
3959
|
+
...request,
|
|
3960
|
+
object_key: resolved.objectKey
|
|
3961
|
+
});
|
|
3962
|
+
if (!parsedRequest) {
|
|
3963
|
+
return { error: "Cannot resolve storage object request." };
|
|
3964
|
+
}
|
|
3965
|
+
return {
|
|
3966
|
+
request: parsedRequest
|
|
3967
|
+
};
|
|
3968
|
+
}
|
|
3969
|
+
async function emitCloudEvent(eventType, details, homeDir) {
|
|
3970
|
+
await appendJsonlEvent(
|
|
3971
|
+
"events.jsonl",
|
|
3972
|
+
{
|
|
3973
|
+
...details,
|
|
3974
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3975
|
+
event_type: eventType
|
|
3976
|
+
},
|
|
3977
|
+
homeDir
|
|
3978
|
+
);
|
|
3979
|
+
}
|
|
3980
|
+
async function emitCloudEventBestEffort(eventType, details, homeDir) {
|
|
3981
|
+
try {
|
|
3982
|
+
await emitCloudEvent(eventType, details, homeDir);
|
|
3983
|
+
} catch {
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3347
3986
|
async function runCloudCommandHandler(ctx, options) {
|
|
3348
3987
|
const parsed = parseCloudArgs(ctx.args);
|
|
3349
3988
|
const objectLogHomeDir = options.objectLogHomeDir;
|
|
@@ -3395,9 +4034,109 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3395
4034
|
isError: true
|
|
3396
4035
|
};
|
|
3397
4036
|
}
|
|
4037
|
+
if (parsed.mode === "op-status-invalid") {
|
|
4038
|
+
return {
|
|
4039
|
+
text: "Cannot get operation status: required arguments are --operation-id.",
|
|
4040
|
+
isError: true
|
|
4041
|
+
};
|
|
4042
|
+
}
|
|
4043
|
+
const datastore = await createCloudDatastore(objectLogHomeDir);
|
|
4044
|
+
if (parsed.mode === "op-status") {
|
|
4045
|
+
const operation = await datastore.findOperationById(parsed.operationId);
|
|
4046
|
+
if (!operation) {
|
|
4047
|
+
return {
|
|
4048
|
+
text: `Operation not found: ${parsed.operationId}`,
|
|
4049
|
+
isError: true
|
|
4050
|
+
};
|
|
4051
|
+
}
|
|
4052
|
+
return {
|
|
4053
|
+
text: [
|
|
4054
|
+
`operation-id: ${operation.operation_id}`,
|
|
4055
|
+
`type: ${operation.type}`,
|
|
4056
|
+
`status: ${operation.status}`,
|
|
4057
|
+
`started-at: ${operation.started_at ?? "n/a"}`,
|
|
4058
|
+
`finished-at: ${operation.finished_at ?? "n/a"}`,
|
|
4059
|
+
operation.error_code ? `error-code: ${operation.error_code}` : null,
|
|
4060
|
+
operation.error_message ? `error-message: ${operation.error_message}` : null
|
|
4061
|
+
].filter((v) => Boolean(v)).join("\n"),
|
|
4062
|
+
isError: operation.status === "failed"
|
|
4063
|
+
};
|
|
4064
|
+
}
|
|
4065
|
+
if ((parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4066
|
+
const operationId = randomUUID3();
|
|
4067
|
+
const opType = parsed.mode;
|
|
4068
|
+
const opObject = parsed.mode === "upload" ? parsed.uploadRequest.object_id : parsed.storageObjectRequest.object_key ?? null;
|
|
4069
|
+
const opQuote = parsed.mode === "upload" ? parsed.uploadRequest.quote_id : null;
|
|
4070
|
+
await datastore.upsertOperation({
|
|
4071
|
+
operation_id: operationId,
|
|
4072
|
+
type: opType,
|
|
4073
|
+
object_id: opObject,
|
|
4074
|
+
quote_id: opQuote,
|
|
4075
|
+
status: "started",
|
|
4076
|
+
error_code: null,
|
|
4077
|
+
error_message: null
|
|
4078
|
+
});
|
|
4079
|
+
const syncArgs = stripAsyncFlag(ctx.args);
|
|
4080
|
+
void runCloudCommandHandler({ args: syncArgs }, options).then(async (result) => {
|
|
4081
|
+
await datastore.upsertOperation({
|
|
4082
|
+
operation_id: operationId,
|
|
4083
|
+
type: opType,
|
|
4084
|
+
object_id: opObject,
|
|
4085
|
+
quote_id: opQuote,
|
|
4086
|
+
status: result.isError ? "failed" : "succeeded",
|
|
4087
|
+
error_code: result.isError ? "ASYNC_FAILED" : null,
|
|
4088
|
+
error_message: result.isError ? result.text : null
|
|
4089
|
+
});
|
|
4090
|
+
}).catch(async (err) => {
|
|
4091
|
+
await datastore.upsertOperation({
|
|
4092
|
+
operation_id: operationId,
|
|
4093
|
+
type: opType,
|
|
4094
|
+
object_id: opObject,
|
|
4095
|
+
quote_id: opQuote,
|
|
4096
|
+
status: "failed",
|
|
4097
|
+
error_code: "ASYNC_EXCEPTION",
|
|
4098
|
+
error_message: err instanceof Error ? err.message : String(err)
|
|
4099
|
+
});
|
|
4100
|
+
});
|
|
4101
|
+
return {
|
|
4102
|
+
text: `Operation started in background. operation-id: ${operationId}
|
|
4103
|
+
Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
4104
|
+
};
|
|
4105
|
+
}
|
|
3398
4106
|
if (parsed.mode === "backup") {
|
|
3399
4107
|
try {
|
|
3400
4108
|
const result = await backupBuilder(parsed.backupTarget, options.backupOptions);
|
|
4109
|
+
await emitCloudEventBestEffort(
|
|
4110
|
+
"backup.completed",
|
|
4111
|
+
{
|
|
4112
|
+
operation_id: randomUUID3(),
|
|
4113
|
+
object_id: result.objectId,
|
|
4114
|
+
status: "succeeded",
|
|
4115
|
+
details: { friendly_name: parsed.friendlyName ?? basename2(parsed.backupTarget) }
|
|
4116
|
+
},
|
|
4117
|
+
objectLogHomeDir
|
|
4118
|
+
);
|
|
4119
|
+
await datastore.upsertObject({
|
|
4120
|
+
object_id: result.objectId,
|
|
4121
|
+
object_key: null,
|
|
4122
|
+
wallet_address: "unknown",
|
|
4123
|
+
quote_id: null,
|
|
4124
|
+
provider: null,
|
|
4125
|
+
bucket_name: null,
|
|
4126
|
+
region: null,
|
|
4127
|
+
sha256: result.objectIdHash,
|
|
4128
|
+
status: "backed_up"
|
|
4129
|
+
});
|
|
4130
|
+
const friendlyName = parsed.friendlyName?.trim() || basename2(parsed.backupTarget);
|
|
4131
|
+
if (friendlyName) {
|
|
4132
|
+
await datastore.upsertFriendlyName({
|
|
4133
|
+
friendly_name: friendlyName,
|
|
4134
|
+
object_id: result.objectId,
|
|
4135
|
+
object_key: null,
|
|
4136
|
+
quote_id: null,
|
|
4137
|
+
wallet_address: "unknown"
|
|
4138
|
+
});
|
|
4139
|
+
}
|
|
3401
4140
|
return {
|
|
3402
4141
|
text: [
|
|
3403
4142
|
`object-id: ${result.objectId}`,
|
|
@@ -3425,6 +4164,25 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3425
4164
|
options.proxyQuoteOptions
|
|
3426
4165
|
);
|
|
3427
4166
|
await appendPriceStorageQuoteLog(quote, objectLogHomeDir);
|
|
4167
|
+
await datastore.upsertObject({
|
|
4168
|
+
object_id: quote.object_id,
|
|
4169
|
+
object_key: null,
|
|
4170
|
+
wallet_address: quote.addr,
|
|
4171
|
+
quote_id: quote.quote_id,
|
|
4172
|
+
provider: quote.provider,
|
|
4173
|
+
bucket_name: null,
|
|
4174
|
+
region: quote.location,
|
|
4175
|
+
sha256: quote.object_id_hash,
|
|
4176
|
+
status: "quoted"
|
|
4177
|
+
});
|
|
4178
|
+
await datastore.upsertPayment({
|
|
4179
|
+
quote_id: quote.quote_id,
|
|
4180
|
+
wallet_address: quote.addr,
|
|
4181
|
+
trans_id: null,
|
|
4182
|
+
amount: quote.storage_price,
|
|
4183
|
+
network: null,
|
|
4184
|
+
status: "quoted"
|
|
4185
|
+
});
|
|
3428
4186
|
return {
|
|
3429
4187
|
text: formatPriceStorageUserMessage(quote)
|
|
3430
4188
|
};
|
|
@@ -3438,10 +4196,7 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3438
4196
|
}
|
|
3439
4197
|
if (parsed.mode === "upload") {
|
|
3440
4198
|
try {
|
|
3441
|
-
const loggedQuote = await findLoggedPriceStorageQuote(
|
|
3442
|
-
parsed.uploadRequest.quote_id,
|
|
3443
|
-
objectLogHomeDir
|
|
3444
|
-
);
|
|
4199
|
+
const loggedQuote = await datastore.findQuoteById(parsed.uploadRequest.quote_id) ?? await findLoggedPriceStorageQuote(parsed.uploadRequest.quote_id, objectLogHomeDir);
|
|
3445
4200
|
if (!loggedQuote) {
|
|
3446
4201
|
return {
|
|
3447
4202
|
text: "Cannot upload storage object: quote-id not found in object.log. Run /mnemospark-cloud price-storage first.",
|
|
@@ -3454,13 +4209,13 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3454
4209
|
isError: true
|
|
3455
4210
|
};
|
|
3456
4211
|
}
|
|
3457
|
-
const archivePath =
|
|
4212
|
+
const archivePath = join8(
|
|
3458
4213
|
options.backupOptions?.tmpDir ?? DEFAULT_BACKUP_DIR,
|
|
3459
4214
|
parsed.uploadRequest.object_id
|
|
3460
4215
|
);
|
|
3461
4216
|
let archiveStats;
|
|
3462
4217
|
try {
|
|
3463
|
-
archiveStats = await
|
|
4218
|
+
archiveStats = await stat2(archivePath);
|
|
3464
4219
|
} catch {
|
|
3465
4220
|
return {
|
|
3466
4221
|
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /mnemospark-cloud backup first.`,
|
|
@@ -3561,6 +4316,71 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3561
4316
|
objectLogHomeDir,
|
|
3562
4317
|
nowDateFn
|
|
3563
4318
|
);
|
|
4319
|
+
await datastore.upsertObject({
|
|
4320
|
+
object_id: finalizedUploadResponse.object_id,
|
|
4321
|
+
object_key: finalizedUploadResponse.object_key,
|
|
4322
|
+
wallet_address: finalizedUploadResponse.addr,
|
|
4323
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
4324
|
+
provider: finalizedUploadResponse.provider,
|
|
4325
|
+
bucket_name: finalizedUploadResponse.bucket_name,
|
|
4326
|
+
region: finalizedUploadResponse.location,
|
|
4327
|
+
sha256: parsed.uploadRequest.object_id_hash,
|
|
4328
|
+
status: "uploaded"
|
|
4329
|
+
});
|
|
4330
|
+
await datastore.upsertPayment({
|
|
4331
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
4332
|
+
wallet_address: finalizedUploadResponse.addr,
|
|
4333
|
+
trans_id: finalizedUploadResponse.trans_id ?? null,
|
|
4334
|
+
amount: cronStoragePrice,
|
|
4335
|
+
network: null,
|
|
4336
|
+
status: "settled",
|
|
4337
|
+
settled_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4338
|
+
});
|
|
4339
|
+
await datastore.upsertCronJob({
|
|
4340
|
+
cron_id: cronJob.cronId,
|
|
4341
|
+
object_id: cronJob.objectId,
|
|
4342
|
+
object_key: cronJob.objectKey,
|
|
4343
|
+
quote_id: cronJob.quoteId,
|
|
4344
|
+
schedule: cronJob.schedule,
|
|
4345
|
+
command: cronJob.command,
|
|
4346
|
+
status: "active"
|
|
4347
|
+
});
|
|
4348
|
+
if (parsed.friendlyName?.trim()) {
|
|
4349
|
+
await datastore.upsertFriendlyName({
|
|
4350
|
+
friendly_name: parsed.friendlyName.trim(),
|
|
4351
|
+
object_id: finalizedUploadResponse.object_id,
|
|
4352
|
+
object_key: finalizedUploadResponse.object_key,
|
|
4353
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
4354
|
+
wallet_address: finalizedUploadResponse.addr
|
|
4355
|
+
});
|
|
4356
|
+
try {
|
|
4357
|
+
await appendJsonlEvent(
|
|
4358
|
+
"manifest.jsonl",
|
|
4359
|
+
{
|
|
4360
|
+
friendly_name: parsed.friendlyName.trim(),
|
|
4361
|
+
object_id: finalizedUploadResponse.object_id,
|
|
4362
|
+
object_key: finalizedUploadResponse.object_key,
|
|
4363
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
4364
|
+
wallet_address: finalizedUploadResponse.addr,
|
|
4365
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4366
|
+
},
|
|
4367
|
+
objectLogHomeDir
|
|
4368
|
+
);
|
|
4369
|
+
} catch {
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
await emitCloudEventBestEffort(
|
|
4373
|
+
"upload.completed",
|
|
4374
|
+
{
|
|
4375
|
+
operation_id: idempotencyKey,
|
|
4376
|
+
wallet_address: finalizedUploadResponse.addr,
|
|
4377
|
+
object_id: finalizedUploadResponse.object_id,
|
|
4378
|
+
object_key: finalizedUploadResponse.object_key,
|
|
4379
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
4380
|
+
status: "succeeded"
|
|
4381
|
+
},
|
|
4382
|
+
objectLogHomeDir
|
|
4383
|
+
);
|
|
3564
4384
|
await maybeCleanupLocalBackupArchive(archivePath);
|
|
3565
4385
|
return {
|
|
3566
4386
|
text: formatStorageUploadUserMessage(finalizedUploadResponse, cronJob.cronId)
|
|
@@ -3574,18 +4394,72 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3574
4394
|
}
|
|
3575
4395
|
}
|
|
3576
4396
|
if (parsed.mode === "ls") {
|
|
4397
|
+
const resolved = await resolveNameSelectorIfNeeded(
|
|
4398
|
+
datastore,
|
|
4399
|
+
parsed.storageObjectRequest,
|
|
4400
|
+
parsed.nameSelector
|
|
4401
|
+
);
|
|
4402
|
+
if (resolved.error || !resolved.request) {
|
|
4403
|
+
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4404
|
+
}
|
|
4405
|
+
const resolvedRequest = resolved.request;
|
|
4406
|
+
const operationId = randomUUID3();
|
|
4407
|
+
await datastore.upsertOperation({
|
|
4408
|
+
operation_id: operationId,
|
|
4409
|
+
type: "ls",
|
|
4410
|
+
object_id: resolvedRequest.object_key,
|
|
4411
|
+
quote_id: null,
|
|
4412
|
+
status: "started",
|
|
4413
|
+
error_code: null,
|
|
4414
|
+
error_message: null
|
|
4415
|
+
});
|
|
3577
4416
|
try {
|
|
3578
|
-
const lsResult = await requestStorageLs(
|
|
3579
|
-
parsed.storageObjectRequest,
|
|
3580
|
-
options.proxyStorageOptions
|
|
3581
|
-
);
|
|
4417
|
+
const lsResult = await requestStorageLs(resolvedRequest, options.proxyStorageOptions);
|
|
3582
4418
|
if (!lsResult.success) {
|
|
3583
4419
|
throw new Error("ls failed");
|
|
3584
4420
|
}
|
|
4421
|
+
await datastore.upsertOperation({
|
|
4422
|
+
operation_id: operationId,
|
|
4423
|
+
type: "ls",
|
|
4424
|
+
object_id: resolvedRequest.object_key,
|
|
4425
|
+
quote_id: null,
|
|
4426
|
+
status: "succeeded",
|
|
4427
|
+
error_code: null,
|
|
4428
|
+
error_message: null
|
|
4429
|
+
});
|
|
4430
|
+
await emitCloudEventBestEffort(
|
|
4431
|
+
"ls.completed",
|
|
4432
|
+
{
|
|
4433
|
+
operation_id: operationId,
|
|
4434
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
4435
|
+
object_key: resolvedRequest.object_key,
|
|
4436
|
+
status: "succeeded"
|
|
4437
|
+
},
|
|
4438
|
+
objectLogHomeDir
|
|
4439
|
+
);
|
|
3585
4440
|
return {
|
|
3586
|
-
text: formatStorageLsUserMessage(lsResult,
|
|
4441
|
+
text: formatStorageLsUserMessage(lsResult, resolvedRequest.object_key)
|
|
3587
4442
|
};
|
|
3588
4443
|
} catch {
|
|
4444
|
+
await datastore.upsertOperation({
|
|
4445
|
+
operation_id: operationId,
|
|
4446
|
+
type: "ls",
|
|
4447
|
+
object_id: resolvedRequest.object_key,
|
|
4448
|
+
quote_id: null,
|
|
4449
|
+
status: "failed",
|
|
4450
|
+
error_code: "LS_FAILED",
|
|
4451
|
+
error_message: "Cannot list storage object"
|
|
4452
|
+
});
|
|
4453
|
+
await emitCloudEventBestEffort(
|
|
4454
|
+
"ls.completed",
|
|
4455
|
+
{
|
|
4456
|
+
operation_id: operationId,
|
|
4457
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
4458
|
+
object_key: resolvedRequest.object_key,
|
|
4459
|
+
status: "failed"
|
|
4460
|
+
},
|
|
4461
|
+
objectLogHomeDir
|
|
4462
|
+
);
|
|
3589
4463
|
return {
|
|
3590
4464
|
text: "Cannot list storage object",
|
|
3591
4465
|
isError: true
|
|
@@ -3593,18 +4467,75 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3593
4467
|
}
|
|
3594
4468
|
}
|
|
3595
4469
|
if (parsed.mode === "download") {
|
|
4470
|
+
const resolved = await resolveNameSelectorIfNeeded(
|
|
4471
|
+
datastore,
|
|
4472
|
+
parsed.storageObjectRequest,
|
|
4473
|
+
parsed.nameSelector
|
|
4474
|
+
);
|
|
4475
|
+
if (resolved.error || !resolved.request) {
|
|
4476
|
+
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4477
|
+
}
|
|
4478
|
+
const resolvedRequest = resolved.request;
|
|
4479
|
+
const operationId = randomUUID3();
|
|
4480
|
+
await datastore.upsertOperation({
|
|
4481
|
+
operation_id: operationId,
|
|
4482
|
+
type: "download",
|
|
4483
|
+
object_id: resolvedRequest.object_key,
|
|
4484
|
+
quote_id: null,
|
|
4485
|
+
status: "started",
|
|
4486
|
+
error_code: null,
|
|
4487
|
+
error_message: null
|
|
4488
|
+
});
|
|
3596
4489
|
try {
|
|
3597
4490
|
const downloadResult = await requestStorageDownload(
|
|
3598
|
-
|
|
4491
|
+
resolvedRequest,
|
|
3599
4492
|
options.proxyStorageOptions
|
|
3600
4493
|
);
|
|
3601
4494
|
if (!downloadResult.success) {
|
|
3602
4495
|
throw new Error("download failed");
|
|
3603
4496
|
}
|
|
4497
|
+
await datastore.upsertOperation({
|
|
4498
|
+
operation_id: operationId,
|
|
4499
|
+
type: "download",
|
|
4500
|
+
object_id: resolvedRequest.object_key,
|
|
4501
|
+
quote_id: null,
|
|
4502
|
+
status: "succeeded",
|
|
4503
|
+
error_code: null,
|
|
4504
|
+
error_message: null
|
|
4505
|
+
});
|
|
4506
|
+
await emitCloudEventBestEffort(
|
|
4507
|
+
"download.completed",
|
|
4508
|
+
{
|
|
4509
|
+
operation_id: operationId,
|
|
4510
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
4511
|
+
object_key: resolvedRequest.object_key,
|
|
4512
|
+
status: "succeeded"
|
|
4513
|
+
},
|
|
4514
|
+
objectLogHomeDir
|
|
4515
|
+
);
|
|
3604
4516
|
return {
|
|
3605
|
-
text: `File ${
|
|
4517
|
+
text: `File ${resolvedRequest.object_key} downloaded to ${downloadResult.file_path}`
|
|
3606
4518
|
};
|
|
3607
4519
|
} catch {
|
|
4520
|
+
await datastore.upsertOperation({
|
|
4521
|
+
operation_id: operationId,
|
|
4522
|
+
type: "download",
|
|
4523
|
+
object_id: resolvedRequest.object_key,
|
|
4524
|
+
quote_id: null,
|
|
4525
|
+
status: "failed",
|
|
4526
|
+
error_code: "DOWNLOAD_FAILED",
|
|
4527
|
+
error_message: "Cannot download file"
|
|
4528
|
+
});
|
|
4529
|
+
await emitCloudEventBestEffort(
|
|
4530
|
+
"download.completed",
|
|
4531
|
+
{
|
|
4532
|
+
operation_id: operationId,
|
|
4533
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
4534
|
+
object_key: resolvedRequest.object_key,
|
|
4535
|
+
status: "failed"
|
|
4536
|
+
},
|
|
4537
|
+
objectLogHomeDir
|
|
4538
|
+
);
|
|
3608
4539
|
return {
|
|
3609
4540
|
text: "Cannot download file",
|
|
3610
4541
|
isError: true
|
|
@@ -3612,15 +4543,33 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3612
4543
|
}
|
|
3613
4544
|
}
|
|
3614
4545
|
if (parsed.mode === "delete") {
|
|
4546
|
+
const resolved = await resolveNameSelectorIfNeeded(
|
|
4547
|
+
datastore,
|
|
4548
|
+
parsed.storageObjectRequest,
|
|
4549
|
+
parsed.nameSelector
|
|
4550
|
+
);
|
|
4551
|
+
if (resolved.error || !resolved.request) {
|
|
4552
|
+
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4553
|
+
}
|
|
4554
|
+
const resolvedRequest = resolved.request;
|
|
4555
|
+
const operationId = randomUUID3();
|
|
4556
|
+
const existingObjectByKey = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
3615
4557
|
try {
|
|
3616
|
-
const deleteResult = await requestStorageDelete(
|
|
3617
|
-
parsed.storageObjectRequest,
|
|
3618
|
-
options.proxyStorageOptions
|
|
3619
|
-
);
|
|
4558
|
+
const deleteResult = await requestStorageDelete(resolvedRequest, options.proxyStorageOptions);
|
|
3620
4559
|
if (!deleteResult.success) {
|
|
3621
4560
|
throw new Error("delete failed");
|
|
3622
4561
|
}
|
|
3623
4562
|
} catch {
|
|
4563
|
+
await emitCloudEventBestEffort(
|
|
4564
|
+
"delete.completed",
|
|
4565
|
+
{
|
|
4566
|
+
operation_id: operationId,
|
|
4567
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
4568
|
+
object_key: resolvedRequest.object_key,
|
|
4569
|
+
status: "failed"
|
|
4570
|
+
},
|
|
4571
|
+
objectLogHomeDir
|
|
4572
|
+
);
|
|
3624
4573
|
return {
|
|
3625
4574
|
text: "Cannot delete file",
|
|
3626
4575
|
isError: true
|
|
@@ -3629,16 +4578,58 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3629
4578
|
let cronEntry = null;
|
|
3630
4579
|
let cronDeleted = false;
|
|
3631
4580
|
try {
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
4581
|
+
const dbCron = await datastore.findCronByObjectKey(resolvedRequest.object_key);
|
|
4582
|
+
if (dbCron) {
|
|
4583
|
+
cronEntry = {
|
|
4584
|
+
cronId: dbCron.cronId,
|
|
4585
|
+
objectId: dbCron.objectId,
|
|
4586
|
+
objectKey: resolvedRequest.object_key
|
|
4587
|
+
};
|
|
4588
|
+
}
|
|
4589
|
+
if (!cronEntry) {
|
|
4590
|
+
cronEntry = await findLoggedStoragePaymentCronByObjectKey(
|
|
4591
|
+
resolvedRequest.object_key,
|
|
4592
|
+
objectLogHomeDir
|
|
4593
|
+
);
|
|
4594
|
+
}
|
|
4595
|
+
if (cronEntry) {
|
|
4596
|
+
const fileCronDeleted = await removeStoragePaymentCronJob(
|
|
4597
|
+
cronEntry.cronId,
|
|
4598
|
+
objectLogHomeDir
|
|
4599
|
+
);
|
|
4600
|
+
const dbCronDeleted = await datastore.removeCronJob(cronEntry.cronId);
|
|
4601
|
+
cronDeleted = fileCronDeleted || dbCronDeleted;
|
|
4602
|
+
}
|
|
3637
4603
|
} catch {
|
|
3638
4604
|
}
|
|
4605
|
+
const objectId = cronEntry?.objectId ?? existingObjectByKey?.object_id ?? null;
|
|
4606
|
+
if (objectId) {
|
|
4607
|
+
const existingObject = existingObjectByKey?.object_id === objectId ? existingObjectByKey : await datastore.findObjectById(objectId);
|
|
4608
|
+
await datastore.upsertObject({
|
|
4609
|
+
object_id: objectId,
|
|
4610
|
+
object_key: resolvedRequest.object_key,
|
|
4611
|
+
wallet_address: existingObject?.wallet_address ?? resolvedRequest.wallet_address,
|
|
4612
|
+
quote_id: existingObject?.quote_id ?? null,
|
|
4613
|
+
provider: existingObject?.provider ?? null,
|
|
4614
|
+
bucket_name: existingObject?.bucket_name ?? null,
|
|
4615
|
+
region: resolvedRequest.location ?? existingObject?.region ?? null,
|
|
4616
|
+
sha256: existingObject?.sha256 ?? null,
|
|
4617
|
+
status: "deleted"
|
|
4618
|
+
});
|
|
4619
|
+
}
|
|
4620
|
+
await emitCloudEventBestEffort(
|
|
4621
|
+
"delete.completed",
|
|
4622
|
+
{
|
|
4623
|
+
operation_id: operationId,
|
|
4624
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
4625
|
+
object_key: resolvedRequest.object_key,
|
|
4626
|
+
status: "succeeded"
|
|
4627
|
+
},
|
|
4628
|
+
objectLogHomeDir
|
|
4629
|
+
);
|
|
3639
4630
|
return {
|
|
3640
4631
|
text: formatStorageDeleteUserMessage(
|
|
3641
|
-
|
|
4632
|
+
resolvedRequest.object_key,
|
|
3642
4633
|
cronEntry?.cronId ?? null,
|
|
3643
4634
|
cronDeleted
|
|
3644
4635
|
)
|
|
@@ -3652,11 +4643,11 @@ async function runCloudCommandHandler(ctx, options) {
|
|
|
3652
4643
|
|
|
3653
4644
|
// src/cli.ts
|
|
3654
4645
|
import { spawn as spawn2 } from "child_process";
|
|
3655
|
-
import { dirname as
|
|
4646
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
3656
4647
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3657
|
-
import { mkdir as
|
|
4648
|
+
import { mkdir as mkdir6, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
3658
4649
|
import { existsSync } from "fs";
|
|
3659
|
-
import { homedir as
|
|
4650
|
+
import { homedir as homedir7 } from "os";
|
|
3660
4651
|
function isHexPrivateKey(value) {
|
|
3661
4652
|
return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value.trim());
|
|
3662
4653
|
}
|
|
@@ -3761,20 +4752,20 @@ function parseArgs(args) {
|
|
|
3761
4752
|
return result;
|
|
3762
4753
|
}
|
|
3763
4754
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
3764
|
-
var __dirname2 =
|
|
3765
|
-
var PACKAGE_ROOT =
|
|
4755
|
+
var __dirname2 = dirname6(__filename2);
|
|
4756
|
+
var PACKAGE_ROOT = dirname6(__dirname2);
|
|
3766
4757
|
async function ensureDir(path) {
|
|
3767
|
-
await
|
|
4758
|
+
await mkdir6(path, { recursive: true });
|
|
3768
4759
|
}
|
|
3769
4760
|
async function deployExtensionFiles() {
|
|
3770
|
-
const scriptsSource =
|
|
4761
|
+
const scriptsSource = join9(PACKAGE_ROOT, "scripts");
|
|
3771
4762
|
if (!existsSync(scriptsSource)) return;
|
|
3772
|
-
const mnemoScriptsDir =
|
|
4763
|
+
const mnemoScriptsDir = join9(homedir7(), ".openclaw", "mnemospark", "scripts");
|
|
3773
4764
|
await ensureDir(mnemoScriptsDir);
|
|
3774
|
-
const uninstallSrc =
|
|
4765
|
+
const uninstallSrc = join9(scriptsSource, "uninstall.sh");
|
|
3775
4766
|
if (existsSync(uninstallSrc)) {
|
|
3776
4767
|
const content = await readFile4(uninstallSrc);
|
|
3777
|
-
await writeFile4(
|
|
4768
|
+
await writeFile4(join9(mnemoScriptsDir, "uninstall.sh"), content, { mode: 493 });
|
|
3778
4769
|
}
|
|
3779
4770
|
}
|
|
3780
4771
|
function isOpenClawAvailable() {
|
|
@@ -3788,8 +4779,8 @@ function isOpenClawAvailable() {
|
|
|
3788
4779
|
});
|
|
3789
4780
|
}
|
|
3790
4781
|
function getOpenClawConfigPath() {
|
|
3791
|
-
const stateDir = process.env.OPENCLAW_STATE_DIR ??
|
|
3792
|
-
return
|
|
4782
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR ?? join9(homedir7(), ".openclaw");
|
|
4783
|
+
return join9(stateDir, "openclaw.json");
|
|
3793
4784
|
}
|
|
3794
4785
|
async function ensureMnemosparkInPluginsAllow() {
|
|
3795
4786
|
const configPath = getOpenClawConfigPath();
|
|
@@ -3851,7 +4842,7 @@ async function readLegacyWalletIfPresent() {
|
|
|
3851
4842
|
}
|
|
3852
4843
|
}
|
|
3853
4844
|
async function writeMnemosparkWallet(key) {
|
|
3854
|
-
const dir =
|
|
4845
|
+
const dir = dirname6(WALLET_FILE);
|
|
3855
4846
|
await ensureDir(dir);
|
|
3856
4847
|
await writeFile4(WALLET_FILE, `${key}
|
|
3857
4848
|
`, { mode: 384 });
|