mnemospark 2026.4.8 → 2026.4.9

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 CHANGED
@@ -5,6 +5,8 @@ _No forms. No email. Just Base. 💙_
5
5
 
6
6
  mnemospark is an agentic service layer for OpenClaw plus a standalone x402 payment + verification backend. It enables encrypted, authenticated, fully autonomous cloud and data workflows—paid via x402 with USDC on Base. No human onboarding. No API keys. The blockchain transaction is the record.
7
7
 
8
+ For the story behind the product—agent-native encrypted cloud storage on x402 with USDC on Base—see the [launch post on Paragraph](https://paragraph.com/@mnemospark/launched-mnemospark-agent-native-encrypted-cloud-storage-on-x402-usdc).
9
+
8
10
  ---
9
11
 
10
12
  ## What is mnemospark?
@@ -28,10 +30,10 @@ openclaw plugins install mnemospark --dangerously-force-unsafe-install
28
30
  openclaw gateway start
29
31
  ```
30
32
 
31
- > Plugin registration is done by `openclaw plugins install mnemospark`.
32
- > The install also bundles the `skills/mnemospark` skill package so the main agent can delegate mnemospark workflows.
33
+ Plugin registration is done by `openclaw plugins install mnemospark`.
34
+ The install also bundles the `skills/mnemospark` skill package so the main agent can delegate mnemospark workflows. Tell your agent to read it!
33
35
 
34
- By default the plugin uses the production mnemospark API at **https://api.mnemospark.ai**. Set **`MNEMOSPARK_BACKEND_API_BASE_URL`** only when you need a different endpoint (for example staging or a private API URL).
36
+ By default the plugin uses the mnemospark API at **https://api.mnemospark.ai**.
35
37
 
36
38
  ### 2) Restart gateway after updates
37
39
 
package/dist/cli.js CHANGED
@@ -1189,6 +1189,7 @@ function parseStoredAes256Key(raw, errorMessage = "Invalid key file format") {
1189
1189
 
1190
1190
  // src/cloud-storage.ts
1191
1191
  var STORAGE_LS_PROXY_PATH = "/mnemospark/storage/ls";
1192
+ var STORAGE_LS_WEB_SESSION_PROXY_PATH = "/mnemospark/storage/ls-web/session";
1192
1193
  var STORAGE_DOWNLOAD_PROXY_PATH = "/mnemospark/storage/download";
1193
1194
  var STORAGE_DELETE_PROXY_PATH = "/mnemospark/storage/delete";
1194
1195
  function isStorageLsListResponse(r) {
@@ -1432,6 +1433,13 @@ function jsonBodyForLsRequest(request) {
1432
1433
  }
1433
1434
  return o;
1434
1435
  }
1436
+ function jsonBodyForLsWebSessionRequest(request) {
1437
+ const o = { wallet_address: request.wallet_address };
1438
+ if (request.location) {
1439
+ o.location = request.location;
1440
+ }
1441
+ return o;
1442
+ }
1435
1443
  function parseStorageLsRequestPayload(payload) {
1436
1444
  const record = asRecord(payload);
1437
1445
  if (!record) {
@@ -1456,6 +1464,21 @@ function parseStorageLsRequestPayload(payload) {
1456
1464
  ...prefix ? { prefix } : {}
1457
1465
  };
1458
1466
  }
1467
+ function parseStorageLsWebSessionRequestPayload(payload) {
1468
+ const record = asRecord(payload);
1469
+ if (!record) {
1470
+ return null;
1471
+ }
1472
+ const walletAddress = asNonEmptyString(record.wallet_address);
1473
+ if (!walletAddress) {
1474
+ return null;
1475
+ }
1476
+ const location = asNonEmptyString(record.location) ?? void 0;
1477
+ return {
1478
+ wallet_address: walletAddress,
1479
+ ...location ? { location } : {}
1480
+ };
1481
+ }
1459
1482
  function parseStorageLsResponse(payload) {
1460
1483
  const record = asRecord(payload);
1461
1484
  if (!record) {
@@ -1513,6 +1536,23 @@ function parseStorageLsResponse(payload) {
1513
1536
  object_id: objectId
1514
1537
  };
1515
1538
  }
1539
+ function parseStorageLsWebSessionResponse(payload) {
1540
+ const record = asRecord(payload);
1541
+ if (!record) {
1542
+ throw new Error("Invalid ls-web session response payload");
1543
+ }
1544
+ const code = asNonEmptyString(record.code) ?? asNonEmptyString(record.exchange_code);
1545
+ const appUrl = asNonEmptyString(record.app_url) ?? asNonEmptyString(record.appUrl) ?? void 0;
1546
+ const expiresAt = asNonEmptyString(record.expires_at) ?? asNonEmptyString(record.expiresAt) ?? void 0;
1547
+ if (!code || !expiresAt) {
1548
+ throw new Error("ls-web session response is missing required fields");
1549
+ }
1550
+ return {
1551
+ code,
1552
+ ...appUrl ? { app_url: appUrl } : {},
1553
+ expires_at: expiresAt
1554
+ };
1555
+ }
1516
1556
  function parseStorageDeleteResponse(payload) {
1517
1557
  const record = asRecord(payload);
1518
1558
  if (!record) {
@@ -1555,6 +1595,14 @@ async function requestStorageLsViaProxy(request, options = {}) {
1555
1595
  options
1556
1596
  );
1557
1597
  }
1598
+ async function requestStorageLsWebSessionViaProxy(request, options = {}) {
1599
+ return requestJsonViaProxy(
1600
+ STORAGE_LS_WEB_SESSION_PROXY_PATH,
1601
+ jsonBodyForLsWebSessionRequest(request),
1602
+ parseStorageLsWebSessionResponse,
1603
+ options
1604
+ );
1605
+ }
1558
1606
  async function requestStorageDownloadViaProxy(request, options = {}) {
1559
1607
  return requestJsonViaProxy(
1560
1608
  STORAGE_DOWNLOAD_PROXY_PATH,
@@ -1574,6 +1622,14 @@ async function requestStorageDeleteViaProxy(request, options = {}) {
1574
1622
  async function forwardStorageLsToBackend(request, options = {}) {
1575
1623
  return forwardStorageToBackend("/storage/ls", "POST", jsonBodyForLsRequest(request), options);
1576
1624
  }
1625
+ async function forwardStorageLsWebSessionToBackend(request, options = {}) {
1626
+ return forwardStorageToBackend(
1627
+ "/storage/ls-web/session",
1628
+ "POST",
1629
+ jsonBodyForLsWebSessionRequest(request),
1630
+ options
1631
+ );
1632
+ }
1577
1633
  async function forwardStorageDownloadToBackend(request, options = {}) {
1578
1634
  return forwardStorageToBackend(
1579
1635
  "/storage/download",
@@ -2552,6 +2608,101 @@ async function startProxy(options) {
2552
2608
  }
2553
2609
  return;
2554
2610
  }
2611
+ if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_LS_WEB_SESSION_PROXY_PATH)) {
2612
+ const correlation = createProxyCorrelation(req.headers);
2613
+ logProxyEvent("info", "proxy_ls_web_session_received");
2614
+ emitProxyEvent("request.received", "start", correlation, {
2615
+ path: STORAGE_LS_WEB_SESSION_PROXY_PATH
2616
+ });
2617
+ try {
2618
+ let payload;
2619
+ try {
2620
+ payload = await readProxyJsonBody(req);
2621
+ } catch {
2622
+ logProxyEvent("warn", "proxy_ls_web_session_invalid_json");
2623
+ emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
2624
+ sendJson(res, 400, {
2625
+ error: "Bad request",
2626
+ message: "Invalid JSON body for /mnemospark cloud ls-web"
2627
+ });
2628
+ return;
2629
+ }
2630
+ const requestPayload = parseStorageLsWebSessionRequestPayload(payload);
2631
+ if (!requestPayload) {
2632
+ logProxyEvent("warn", "proxy_ls_web_session_missing_fields");
2633
+ emitProxyTerminalFromStatus(correlation, 400, { reason: "missing_fields" });
2634
+ sendJson(res, 400, {
2635
+ error: "Bad request",
2636
+ message: "Missing required field: wallet_address"
2637
+ });
2638
+ return;
2639
+ }
2640
+ correlation.wallet_address = requestPayload.wallet_address;
2641
+ if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
2642
+ logProxyEvent("warn", "proxy_ls_web_session_wallet_mismatch");
2643
+ emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
2644
+ sendJson(res, 403, {
2645
+ error: "wallet_proof_invalid",
2646
+ message: "wallet proof invalid"
2647
+ });
2648
+ return;
2649
+ }
2650
+ const walletSignature = await createBackendWalletSignature(
2651
+ "POST",
2652
+ "/storage/ls-web/session",
2653
+ requestPayload.wallet_address
2654
+ );
2655
+ if (!walletSignature) {
2656
+ logProxyEvent("warn", "proxy_ls_web_session_wallet_signature_missing");
2657
+ sendWalletRequired(res);
2658
+ emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
2659
+ return;
2660
+ }
2661
+ emitProxyEvent("storage.call", "start", correlation, { target: "storage/ls-web/session" });
2662
+ const backendResponse = await forwardStorageLsWebSessionToBackend(requestPayload, {
2663
+ backendBaseUrl: MNEMOSPARK_BACKEND_API_BASE_URL,
2664
+ walletSignature
2665
+ });
2666
+ logProxyEvent("info", "proxy_ls_web_session_backend_response", {
2667
+ status: backendResponse.status
2668
+ });
2669
+ emitProxyEvent("storage.call", "result", correlation, { status: backendResponse.status });
2670
+ const authFailure = normalizeBackendAuthFailure(
2671
+ backendResponse.status,
2672
+ backendResponse.bodyText
2673
+ );
2674
+ if (authFailure) {
2675
+ logProxyEvent("warn", "proxy_ls_web_session_auth_failure", {
2676
+ status: authFailure.status
2677
+ });
2678
+ const responseHeaders2 = createBackendForwardHeaders({
2679
+ contentType: authFailure.contentType,
2680
+ paymentRequired: backendResponse.paymentRequired,
2681
+ paymentResponse: backendResponse.paymentResponse
2682
+ });
2683
+ res.writeHead(authFailure.status, responseHeaders2);
2684
+ res.end(authFailure.bodyText);
2685
+ emitProxyTerminalFromStatus(correlation, authFailure.status, { reason: "auth_failure" });
2686
+ return;
2687
+ }
2688
+ const responseHeaders = createBackendForwardHeaders(backendResponse);
2689
+ res.writeHead(backendResponse.status, responseHeaders);
2690
+ res.end(backendResponse.bodyText);
2691
+ emitProxyTerminalFromStatus(correlation, backendResponse.status);
2692
+ } catch (err) {
2693
+ emitProxyEvent("terminal.failure", "failure", correlation, {
2694
+ error: err instanceof Error ? err.message : String(err)
2695
+ });
2696
+ logProxyEvent("error", "proxy_ls_web_session_forward_failed", {
2697
+ error: err instanceof Error ? err.message : String(err)
2698
+ });
2699
+ sendJson(res, 502, {
2700
+ error: "proxy_error",
2701
+ message: `Failed to forward /mnemospark cloud ls-web session: ${err instanceof Error ? err.message : String(err)}`
2702
+ });
2703
+ }
2704
+ return;
2705
+ }
2555
2706
  if (req.method === "POST" && matchesProxyPath(req.url, STORAGE_DOWNLOAD_PROXY_PATH)) {
2556
2707
  const correlation = createProxyCorrelation(req.headers);
2557
2708
  logProxyEvent("info", "proxy_download_received");
@@ -4257,6 +4408,13 @@ var lsSchema = {
4257
4408
  { name: "region" }
4258
4409
  ]
4259
4410
  };
4411
+ var lsWebSchema = {
4412
+ args: [
4413
+ { name: "wallet-address", aliases: ["wallet"], required: true },
4414
+ { name: "location" },
4415
+ { name: "region" }
4416
+ ]
4417
+ };
4260
4418
  var downloadSchema = {
4261
4419
  args: [
4262
4420
  { name: "wallet-address", aliases: ["wallet"], required: true },
@@ -4572,6 +4730,10 @@ var CLOUD_HELP_TEXT = [
4572
4730
  " Purpose: stat one object or list all keys in the wallet bucket (S3).",
4573
4731
  " Required: " + REQUIRED_LS,
4574
4732
  "",
4733
+ "\u2022 `/mnemospark cloud ls-web wallet-address:<addr>`",
4734
+ " Purpose: run `ls` in list mode (identical output), then mint a short-lived web session and print a browse URL + expiry.",
4735
+ " Required: wallet-address:<addr>",
4736
+ "",
4575
4737
  "\u2022 `/mnemospark cloud download wallet-address:<addr> [object-key:<object-key> | name:<friendly-name>] [latest:true|at:<timestamp>] [async:true] [orchestrator:<inline|subagent>] [timeout-seconds:<n>]`",
4576
4738
  " Purpose: fetch an object to local disk.",
4577
4739
  " Required: " + REQUIRED_STORAGE_OBJECT,
@@ -4987,6 +5149,26 @@ function parseCloudArgs(args) {
4987
5149
  }
4988
5150
  };
4989
5151
  }
5152
+ if (subcommand === "ls-web") {
5153
+ const parsed = parseCommandArgs(rest, lsWebSchema);
5154
+ if (!parsed.ok) {
5155
+ return {
5156
+ mode: "arg-parse-failure",
5157
+ errors: parsed.errors,
5158
+ warnings: mergeArgParseWarnings(normWarnings, parsed.warnings)
5159
+ };
5160
+ }
5161
+ const flags = valuesToStringRecord(parsed.values);
5162
+ const walletAddress = flags["wallet-address"]?.trim() ?? "";
5163
+ if (!walletAddress) {
5164
+ return { mode: "ls-web-invalid" };
5165
+ }
5166
+ const location = flags.location?.trim() || flags.region?.trim() || void 0;
5167
+ return {
5168
+ mode: "ls-web",
5169
+ storageObjectRequest: { wallet_address: walletAddress, location }
5170
+ };
5171
+ }
4990
5172
  if (subcommand === "ls") {
4991
5173
  const parsed = parseCommandArgs(rest, lsSchema);
4992
5174
  if (!parsed.ok) {
@@ -5922,6 +6104,7 @@ function createCloudCommand(options = {}) {
5922
6104
  nowDateFn: options.nowDateFn ?? (() => /* @__PURE__ */ new Date()),
5923
6105
  idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID4,
5924
6106
  requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
6107
+ requestStorageLsWebSessionFn: options.requestStorageLsWebSessionFn ?? requestStorageLsWebSessionViaProxy,
5925
6108
  requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
5926
6109
  requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
5927
6110
  requestPaymentSettleViaProxyFn: options.requestPaymentSettleViaProxyFn ?? requestPaymentSettleViaProxy,
@@ -6221,6 +6404,7 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
6221
6404
  const nowDateFn = options.nowDateFn;
6222
6405
  const idempotencyKeyFn = options.idempotencyKeyFn;
6223
6406
  const requestStorageLs = options.requestStorageLsFn;
6407
+ const requestStorageLsWebSession = options.requestStorageLsWebSessionFn;
6224
6408
  const requestStorageDownload = options.requestStorageDownloadFn;
6225
6409
  const requestStorageDelete = options.requestStorageDeleteFn;
6226
6410
  const requestPaymentSettleViaProxy2 = options.requestPaymentSettleViaProxyFn;
@@ -6289,6 +6473,12 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
6289
6473
  isError: true
6290
6474
  };
6291
6475
  }
6476
+ if (parsed.mode === "ls-web-invalid") {
6477
+ return {
6478
+ text: "Cannot list storage objects: wallet-address is required.",
6479
+ isError: true
6480
+ };
6481
+ }
6292
6482
  if (parsed.mode === "download-invalid") {
6293
6483
  return {
6294
6484
  text: `Cannot download file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`,
@@ -7441,6 +7631,124 @@ operation-id: ${operationId}`,
7441
7631
  };
7442
7632
  }
7443
7633
  }
7634
+ if (parsed.mode === "ls-web") {
7635
+ const walletAddress = parsed.storageObjectRequest.wallet_address?.trim() ?? "";
7636
+ if (!walletAddress) {
7637
+ return { text: "Cannot list storage objects: wallet-address is required.", isError: true };
7638
+ }
7639
+ const listRequest = { wallet_address: walletAddress };
7640
+ const location = parsed.storageObjectRequest.location?.trim();
7641
+ if (location) {
7642
+ listRequest.location = location;
7643
+ }
7644
+ const correlation = buildRequestCorrelation();
7645
+ const operationId = correlation.operationId;
7646
+ await datastore.upsertOperation({
7647
+ operation_id: operationId,
7648
+ type: "ls",
7649
+ object_id: null,
7650
+ quote_id: null,
7651
+ status: "started",
7652
+ error_code: null,
7653
+ error_message: null
7654
+ });
7655
+ try {
7656
+ const lsResult = await requestStorageLs(listRequest, {
7657
+ ...options.proxyStorageOptions,
7658
+ correlation
7659
+ });
7660
+ if (!lsResult.success) {
7661
+ throw new Error("ls failed");
7662
+ }
7663
+ const lsText = await buildMnemosparkLsMessage(lsResult, {
7664
+ walletAddress,
7665
+ datastore
7666
+ });
7667
+ const session = await requestStorageLsWebSession(
7668
+ { wallet_address: walletAddress, ...location ? { location } : {} },
7669
+ {
7670
+ ...options.proxyStorageOptions,
7671
+ correlation
7672
+ }
7673
+ );
7674
+ const browseUrl = session.app_url?.trim() || `https://app.mnemospark.ai/?code=${encodeURIComponent(session.code)}`;
7675
+ const now = nowDateFn();
7676
+ const expiresDate = new Date(session.expires_at);
7677
+ const expiresUtc = Number.isNaN(expiresDate.valueOf()) ? session.expires_at : expiresDate.toUTCString();
7678
+ let rel = "";
7679
+ if (!Number.isNaN(expiresDate.valueOf())) {
7680
+ const ms = expiresDate.getTime() - now.getTime();
7681
+ if (ms > 0) {
7682
+ const totalMinutes = Math.floor(ms / 6e4);
7683
+ const hours = Math.floor(totalMinutes / 60);
7684
+ const minutes = totalMinutes % 60;
7685
+ rel = ` (in ~${hours}h ${minutes}m)`;
7686
+ }
7687
+ }
7688
+ await datastore.upsertOperation({
7689
+ operation_id: operationId,
7690
+ type: "ls",
7691
+ object_id: null,
7692
+ quote_id: null,
7693
+ status: "succeeded",
7694
+ error_code: null,
7695
+ error_message: null
7696
+ });
7697
+ await emitCloudEventBestEffort(
7698
+ "ls.completed",
7699
+ {
7700
+ operation_id: operationId,
7701
+ trace_id: correlation.traceId,
7702
+ wallet_address: walletAddress,
7703
+ object_key: null,
7704
+ status: "succeeded",
7705
+ list_mode: true
7706
+ },
7707
+ mnemosparkHomeDir
7708
+ );
7709
+ const urlBlock = ["```", browseUrl, "```"].join("\n");
7710
+ const webBlock = [
7711
+ "Browse your files in the web app:",
7712
+ "",
7713
+ urlBlock,
7714
+ "",
7715
+ `Expires: ${expiresUtc}${rel}`
7716
+ ].join("\n");
7717
+ return {
7718
+ text: `${lsText}
7719
+
7720
+ ${webBlock}`
7721
+ };
7722
+ } catch (error) {
7723
+ const lsFriendly = extractLsErrorMessage(error);
7724
+ const message = lsFriendly ?? extractUploadErrorMessage(error) ?? "Cannot run ls-web";
7725
+ await datastore.upsertOperation({
7726
+ operation_id: operationId,
7727
+ type: "ls",
7728
+ object_id: null,
7729
+ quote_id: null,
7730
+ status: "failed",
7731
+ error_code: "LS_WEB_FAILED",
7732
+ error_message: message
7733
+ });
7734
+ await emitCloudEventBestEffort(
7735
+ "ls.completed",
7736
+ {
7737
+ operation_id: operationId,
7738
+ trace_id: correlation.traceId,
7739
+ wallet_address: walletAddress,
7740
+ object_key: null,
7741
+ status: "failed",
7742
+ list_mode: true
7743
+ },
7744
+ mnemosparkHomeDir
7745
+ );
7746
+ return {
7747
+ text: message,
7748
+ isError: true
7749
+ };
7750
+ }
7751
+ }
7444
7752
  if (parsed.mode === "download") {
7445
7753
  const resolved = await resolveNameSelectorIfNeeded(
7446
7754
  datastore,