mnemospark 1.2.0 β†’ 1.2.2

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
@@ -3,7 +3,7 @@
3
3
  **Wallet and go. πŸ’™**
4
4
  _No forms. No email. Just Base. πŸ’™_
5
5
 
6
- mnemospark is an agentic service layer for OpenClaw and a standalone x402 payment and wallet-proof verification backend. It provides encrypted, authenticated, and fully autonomous access to cloud infrastructure and proprietary data, paid via x402 with USDC on Base. No human onboarding. No API keys. The blockchain transaction is the record.
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
8
  ---
9
9
 
@@ -11,7 +11,7 @@ mnemospark is an agentic service layer for OpenClaw and a standalone x402 paymen
11
11
 
12
12
  mnemospark connects OpenClaw agents to cloud workflows with wallet-native auth + payment rails.
13
13
 
14
- - **Wallet-proof authentication** for storage/API actions
14
+ - **Wallet-native authentication** for storage/API actions
15
15
  - **x402-native payments** with USDC on Base
16
16
  - **Agent-first flow** (quote β†’ pay β†’ provision)
17
17
  - **Encrypted payload support** and structured request signing
@@ -24,7 +24,7 @@ mnemospark connects OpenClaw agents to cloud workflows with wallet-native auth +
24
24
  ### 1) Install the plugin in OpenClaw
25
25
 
26
26
  ```bash
27
- openclaw plugins install mnemospark
27
+ openclaw plugins install mnemospark --dangerously-force-unsafe-install
28
28
  openclaw gateway start
29
29
  ```
30
30
 
@@ -52,40 +52,63 @@ openclaw gateway restart
52
52
 
53
53
  ---
54
54
 
55
+ ## Slash command reference
56
+
57
+ **Syntax**
58
+
59
+ - **Chat / OpenClaw (primary):** use `key:value` for arguments (for example `wallet-address:0x…`, `region:us-east-1`). `key=value` is also accepted.
60
+ - **Agent-driven workflows and CLI-style usage:** prefer `--parameter value` (for example `npx mnemospark` or shell scripts).
61
+
62
+ **Top-level routes** (after `/mnemospark`)
63
+
64
+ - `/mnemospark` or `/mnemospark help` β€” overview
65
+ - `/mnemospark cloud …` β€” cloud storage (see below)
66
+ - `/mnemospark wallet …` β€” wallet helpers
67
+
68
+ **`/mnemospark cloud`** β€” includes: `cloud` / `cloud help`, `backup`, `price-storage`, `upload`, `payment-settle`, `ls`, `download`, `delete`, `op-status`. Optional flags include `async:true`, `orchestrator:inline|subagent`, `timeout-seconds:<n>`. For the full cloud guide, run `/mnemospark cloud help` in chat.
69
+
70
+ **`/mnemospark wallet`**
71
+
72
+ - `/mnemospark wallet` β€” address, balance, and key file path
73
+ - `/mnemospark wallet help` β€” command list and funding link
74
+ - `/mnemospark wallet export` β€” export private key for backup (sensitive)
75
+
76
+ ---
77
+
55
78
  ## Core Commands
56
79
 
57
- Use via `/mnemospark cloud …` (or `/mnemospark wallet …`) in OpenClaw chat.
80
+ Use via `/mnemospark cloud …` (or `/mnemospark wallet …`) in OpenClaw chat. Prefer `key:value` in chat; use `--wallet-address` style for scripted or agent-driven flows.
58
81
 
59
82
  ### Get a storage quote
60
83
 
61
84
  ```text
62
- /mnemospark cloud price-storage --wallet-address <addr> --object-id <id> --object-id-hash <sha256> --gb <gb> --provider aws --region us-east-1
85
+ /mnemospark cloud price-storage wallet-address:<addr> object-id:<id> object-id-hash:<sha256> gb:<gb> provider:aws region:us-east-1
63
86
  ```
64
87
 
65
- Use other regions by changing `--provider` and `--region` (defaults: `aws` / `us-east-1`).
88
+ Use other regions by changing `provider:` and `region:` (defaults: `aws` / `us-east-1`).
66
89
 
67
90
  ### Upload using quote
68
91
 
69
92
  ```text
70
- /mnemospark cloud upload --quote-id <quote-id> --wallet-address <addr> --object-id <id> --object-id-hash <sha256>
93
+ /mnemospark cloud upload quote-id:<quote-id> wallet-address:<addr> object-id:<id> object-id-hash:<sha256>
71
94
  ```
72
95
 
73
96
  ### List objects
74
97
 
75
98
  ```text
76
- /mnemospark cloud ls --wallet-address <addr> --object-key <object-key>
99
+ /mnemospark cloud ls wallet-address:<addr> object-key:<object-key>
77
100
  ```
78
101
 
79
102
  ### Download object
80
103
 
81
104
  ```text
82
- /mnemospark cloud download --wallet-address <addr> --object-key <object-key>
105
+ /mnemospark cloud download wallet-address:<addr> object-key:<object-key>
83
106
  ```
84
107
 
85
108
  ### Delete object
86
109
 
87
110
  ```text
88
- /mnemospark cloud delete --wallet-address <addr> --object-key <object-key>
111
+ /mnemospark cloud delete wallet-address:<addr> object-key:<object-key>
89
112
  ```
90
113
 
91
114
  ---
@@ -95,7 +118,7 @@ Use other regions by changing `--provider` and `--region` (defaults: `aws` / `us
95
118
  mnemospark follows a quote-and-pay execution model:
96
119
 
97
120
  1. Agent requests a quote.
98
- 2. Agent provides wallet-proof + payment authorization.
121
+ 2. Agent provides wallet-native + payment authorization.
99
122
  3. Backend verifies payment/auth context.
100
123
  4. Storage action executes.
101
124
 
@@ -128,6 +151,7 @@ Optional unless noted. All names use the `MNEMOSPARK_` prefix.
128
151
  | `MNEMOSPARK_DISABLED` | Set to `true` or `1` to disable plugin registration. |
129
152
  | `MNEMOSPARK_DISABLE_SQLITE` | Set to `1` to disable local SQLite (`state.db`); cloud commands that need local state will fail. |
130
153
  | `MNEMOSPARK_SQLITE_STRICT` | Set to `1` so certain SQLite consistency checks (e.g. friendly-name verification after upload) throw instead of warning. |
154
+ | `MNEMOSPARK_PROXY_VERBOSE_404` | When `1`, `true`, or `yes`, the local HTTP proxy includes a `message` field on **404** responses describing supported paths. Default (unset) is a generic JSON body `{ "error": "Not found" }` only (reduces reconnaissance). |
131
155
 
132
156
  ---
133
157
 
@@ -141,11 +165,23 @@ Optional unless noted. All names use the `MNEMOSPARK_` prefix.
141
165
 
142
166
  ---
143
167
 
168
+ ## OpenClaw Install Warning
169
+
170
+ If OpenClaw shows a warning about **dangerous code patterns** when installing or updating mnemosparkβ€”often mentioning shell execution (`child_process`), environment variables, and network accessβ€”here is what is going on.
171
+
172
+ mnemospark is an **OpenClaw plugin** that talks to **your configured mnemospark backend**, runs a **local HTTP proxy** for storage workflows, and can **invoke the `openclaw` CLI** and system tools when needed (for example creating archives with `tar` or running `npm` when you use the update command). Those features use the same low-level Node.js APIsβ€”`child_process` and `fetch`β€”that security tools also associate with risky software, so the installer may warn you even when the behavior is intentional and benign.
173
+
174
+ We also read **environment variables** you set on purpose (such as `MNEMOSPARK_BACKEND_API_BASE_URL`, `MNEMOSPARK_PROXY_PORT`, or wallet-related settings) so you can configure the plugin without editing code. Automated scans sometimes flag β€œenvironment access + network” as a possible credential-stealing pattern. In mnemospark, that combination exists because the plugin is **configurable and networked by design**, not because we are harvesting your unrelated secrets.
175
+
176
+ mnemospark is **open source**. If you want extra assurance, review the repository, search for `child_process`, `process.env`, and `fetch`, and run your own tests in a safe environment. The warning helps keep the ecosystem safe; for mnemospark it reflects **capabilities**, not a finding of malicious intent.
177
+
178
+ ---
179
+
144
180
  ## Troubleshooting
145
181
 
146
182
  - **Missing wallet/auth errors**: verify wallet key is present and request signature headers are generated.
147
183
  - **402 payment required**: expected in challenge flow; ensure client retries with payment authorization.
148
- - **Upload/storage backend errors**: verify cloud permissions (e.g., bucket access + IAM role rights).
184
+ - **Upload/storage backend errors**: verify cloud permissions (e.g. bucket access + IAM role rights).
149
185
  - **Command not recognized**: confirm plugin installed and gateway restarted.
150
186
  - **One-step operation correlation**: run `./skills/mnemospark/scripts/debug-operation.sh <operation-id>` (or omit ID to use latest).
151
187
 
package/dist/cli.js CHANGED
@@ -157,6 +157,10 @@ var PROXY_PORT = (() => {
157
157
  return DEFAULT_PORT;
158
158
  })();
159
159
  var MNEMOSPARK_BACKEND_API_BASE_URL = (process.env.MNEMOSPARK_BACKEND_API_BASE_URL ?? "").trim();
160
+ var MNEMOSPARK_PROXY_VERBOSE_404 = (() => {
161
+ const v = process.env.MNEMOSPARK_PROXY_VERBOSE_404?.trim().toLowerCase();
162
+ return v === "1" || v === "true" || v === "yes";
163
+ })();
160
164
 
161
165
  // src/mnemospark-request-sign.ts
162
166
  import { getAddress } from "viem";
@@ -1733,8 +1737,15 @@ async function readProxyJsonBody(req) {
1733
1737
  }
1734
1738
  return JSON.parse(bodyText);
1735
1739
  }
1740
+ function createJsonResponseHeaders() {
1741
+ return {
1742
+ "Content-Type": "application/json",
1743
+ "X-Content-Type-Options": "nosniff",
1744
+ "Cache-Control": "no-store"
1745
+ };
1746
+ }
1736
1747
  function sendJson(res, status, body) {
1737
- res.writeHead(status, { "Content-Type": "application/json" });
1748
+ res.writeHead(status, createJsonResponseHeaders());
1738
1749
  res.end(JSON.stringify(body));
1739
1750
  }
1740
1751
  function logProxyEvent(level, event, fields = {}) {
@@ -1794,7 +1805,9 @@ function isAlreadySettledConflict(status, bodyText) {
1794
1805
  }
1795
1806
  function createBackendForwardHeaders(response) {
1796
1807
  const responseHeaders = {
1797
- "Content-Type": response.contentType
1808
+ "Content-Type": response.contentType,
1809
+ "X-Content-Type-Options": "nosniff",
1810
+ "Cache-Control": "no-store"
1798
1811
  };
1799
1812
  if (response.paymentRequired) {
1800
1813
  responseHeaders["PAYMENT-REQUIRED"] = response.paymentRequired;
@@ -1832,6 +1845,10 @@ function createWalletRequiredBody() {
1832
1845
  message: "wallet required for storage endpoints"
1833
1846
  });
1834
1847
  }
1848
+ function sendWalletRequired(res) {
1849
+ res.writeHead(400, createJsonResponseHeaders());
1850
+ res.end(createWalletRequiredBody());
1851
+ }
1835
1852
  function getProxyPort() {
1836
1853
  return PROXY_PORT;
1837
1854
  }
@@ -1931,6 +1948,18 @@ async function startProxy(options) {
1931
1948
  }
1932
1949
  correlation.wallet_address = requestPayload.wallet_address;
1933
1950
  correlation.object_id = requestPayload.object_id;
1951
+ if (requestPayload.wallet_address.toLowerCase() !== proxyWalletAddressLower) {
1952
+ logProxyEvent("warn", "proxy_price_storage_wallet_mismatch", {
1953
+ request_wallet: requestPayload.wallet_address,
1954
+ proxy_wallet: account.address
1955
+ });
1956
+ emitProxyTerminalFromStatus(correlation, 403, { reason: "wallet_mismatch" });
1957
+ sendJson(res, 403, {
1958
+ error: "wallet_proof_invalid",
1959
+ message: "wallet proof invalid"
1960
+ });
1961
+ return;
1962
+ }
1934
1963
  emitProxyEvent("storage.call", "start", correlation, { target: "price-storage" });
1935
1964
  const walletSignature = await createBackendWalletSignature(
1936
1965
  "POST",
@@ -1939,8 +1968,7 @@ async function startProxy(options) {
1939
1968
  );
1940
1969
  if (!walletSignature) {
1941
1970
  logProxyEvent("warn", "proxy_price_storage_wallet_signature_missing");
1942
- res.writeHead(400, { "Content-Type": "application/json" });
1943
- res.end(createWalletRequiredBody());
1971
+ sendWalletRequired(res);
1944
1972
  emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
1945
1973
  return;
1946
1974
  }
@@ -2089,8 +2117,7 @@ async function startProxy(options) {
2089
2117
  );
2090
2118
  if (!walletSignature) {
2091
2119
  logProxyEvent("warn", "proxy_payment_settle_wallet_signature_missing");
2092
- res.writeHead(400, { "Content-Type": "application/json" });
2093
- res.end(createWalletRequiredBody());
2120
+ sendWalletRequired(res);
2094
2121
  emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
2095
2122
  return;
2096
2123
  }
@@ -2198,8 +2225,7 @@ async function startProxy(options) {
2198
2225
  );
2199
2226
  if (!walletSignature) {
2200
2227
  logProxyEvent("warn", "proxy_upload_wallet_signature_missing");
2201
- res.writeHead(400, { "Content-Type": "application/json" });
2202
- res.end(createWalletRequiredBody());
2228
+ sendWalletRequired(res);
2203
2229
  emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
2204
2230
  return;
2205
2231
  }
@@ -2246,8 +2272,7 @@ async function startProxy(options) {
2246
2272
  );
2247
2273
  if (!settleWalletSignature) {
2248
2274
  logProxyEvent("warn", "proxy_upload_settle_signature_missing");
2249
- res.writeHead(400, { "Content-Type": "application/json" });
2250
- res.end(createWalletRequiredBody());
2275
+ sendWalletRequired(res);
2251
2276
  emitProxyTerminalFromStatus(correlation, 400, { reason: "settle_signature_missing" });
2252
2277
  return;
2253
2278
  }
@@ -2387,8 +2412,7 @@ async function startProxy(options) {
2387
2412
  );
2388
2413
  if (!walletSignature) {
2389
2414
  logProxyEvent("warn", "proxy_upload_confirm_wallet_signature_missing");
2390
- res.writeHead(400, { "Content-Type": "application/json" });
2391
- res.end(createWalletRequiredBody());
2415
+ sendWalletRequired(res);
2392
2416
  emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
2393
2417
  return;
2394
2418
  }
@@ -2482,8 +2506,7 @@ async function startProxy(options) {
2482
2506
  );
2483
2507
  if (!walletSignature) {
2484
2508
  logProxyEvent("warn", "proxy_ls_wallet_signature_missing");
2485
- res.writeHead(400, { "Content-Type": "application/json" });
2486
- res.end(createWalletRequiredBody());
2509
+ sendWalletRequired(res);
2487
2510
  emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
2488
2511
  return;
2489
2512
  }
@@ -2577,8 +2600,7 @@ async function startProxy(options) {
2577
2600
  );
2578
2601
  if (!walletSignature) {
2579
2602
  logProxyEvent("warn", "proxy_download_wallet_signature_missing");
2580
- res.writeHead(400, { "Content-Type": "application/json" });
2581
- res.end(createWalletRequiredBody());
2603
+ sendWalletRequired(res);
2582
2604
  emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
2583
2605
  return;
2584
2606
  }
@@ -2694,8 +2716,7 @@ async function startProxy(options) {
2694
2716
  );
2695
2717
  if (!walletSignature) {
2696
2718
  logProxyEvent("warn", "proxy_delete_wallet_signature_missing");
2697
- res.writeHead(400, { "Content-Type": "application/json" });
2698
- res.end(createWalletRequiredBody());
2719
+ sendWalletRequired(res);
2699
2720
  emitProxyTerminalFromStatus(correlation, 400, { reason: "wallet_signature_missing" });
2700
2721
  return;
2701
2722
  }
@@ -2765,10 +2786,14 @@ async function startProxy(options) {
2765
2786
  sendJson(res, 200, response);
2766
2787
  return;
2767
2788
  }
2768
- sendJson(res, 404, {
2769
- error: "Not found",
2770
- message: "Supported paths: /health and /mnemospark/* storage endpoints"
2771
- });
2789
+ if (MNEMOSPARK_PROXY_VERBOSE_404) {
2790
+ sendJson(res, 404, {
2791
+ error: "Not found",
2792
+ message: "Supported paths: /health and /mnemospark/* storage endpoints"
2793
+ });
2794
+ } else {
2795
+ sendJson(res, 404, { error: "Not found" });
2796
+ }
2772
2797
  });
2773
2798
  const tryListen = (attempt) => {
2774
2799
  return new Promise((resolveAttempt, rejectAttempt) => {
@@ -4114,7 +4139,8 @@ var priceStorageSchema = {
4114
4139
  args: [
4115
4140
  { name: "wallet-address", aliases: ["wallet"], required: true },
4116
4141
  { name: "object-id", aliases: ["object"], required: true },
4117
- { name: "object-id-hash", aliases: ["hash"], required: true },
4142
+ // Optional: omit when the object exists in local SQLite after backup; CLI resolves sha256 from state.db.
4143
+ { name: "object-id-hash", aliases: ["hash"] },
4118
4144
  { name: "gb", required: true },
4119
4145
  { name: "provider", required: true },
4120
4146
  { name: "region", required: true }
@@ -4206,7 +4232,7 @@ var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
4206
4232
  var QUOTE_VALIDITY_USER_NOTE = "Quotes are valid for one hour. Please run price-storage again if you need a new quote.";
4207
4233
  var MNEMOSPARK_SUPPORT_EMAIL = "pluggedin@mnemospark.ai";
4208
4234
  var CLOUD_HELP_FOOTER_STATE = "Local state: mnemospark records quotes, objects, payments, cron jobs, friendly names, and operation metadata in ~/.openclaw/mnemospark/state.db (SQLite). For troubleshooting and correlation, commands and the HTTP proxy append structured JSON lines to ~/.openclaw/mnemospark/events.jsonl. Monthly storage billing jobs are listed in ~/.openclaw/cron/jobs.json for OpenClaw scheduling.";
4209
- var REQUIRED_PRICE_STORAGE = "wallet-address:, object-id:, object-id-hash:, gb:, provider:, region:";
4235
+ var REQUIRED_PRICE_STORAGE = "wallet-address:, object-id:, gb:, provider:, region: (object-id-hash: optional if the object exists in local SQLite after backup)";
4210
4236
  var REQUIRED_UPLOAD = "quote-id:, wallet-address:, object-id:, object-id-hash:";
4211
4237
  var REQUIRED_BACKUP = "<file|directory> and name:<friendly-name>";
4212
4238
  var REQUIRED_PAYMENT_SETTLE = "wallet-address: and (quote-id: | renewal:true with object-key:)";
@@ -4236,10 +4262,10 @@ var CLOUD_HELP_TEXT = [
4236
4262
  " Purpose: create a local tar+gzip archive under ~/.openclaw/mnemospark/backup (filename from sanitized friendly name) and record metadata in SQLite for later price-storage and upload.",
4237
4263
  " Required: " + REQUIRED_BACKUP,
4238
4264
  "",
4239
- "\u2022 `/mnemospark cloud price-storage wallet-address:<addr> object-id:<id> object-id-hash:<hash> gb:<gb> provider:aws region:us-east-1`",
4240
- " Purpose: request a storage quote before upload (defaults shown; override `provider:` / `region:` for other regions).",
4265
+ "\u2022 `/mnemospark cloud price-storage wallet-address:<addr> object-id:<id> [object-id-hash:<hash>] gb:<gb> provider:aws region:us-east-1`",
4266
+ " Purpose: request a storage quote before upload (defaults shown; override `provider:` / `region:` for other regions). Omit `object-id-hash:` when the object is already in local SQLite (e.g. after backup); mnemospark reads sha256 from ~/.openclaw/mnemospark/state.db.",
4241
4267
  " Required: " + REQUIRED_PRICE_STORAGE,
4242
- " Shorter: `wallet:\u2026 object:\u2026 hash:\u2026 gb:\u2026 provider:\u2026 region:\u2026`",
4268
+ " Shorter: `wallet:\u2026 object:\u2026 [hash:\u2026] gb:\u2026 provider:\u2026 region:\u2026`",
4243
4269
  "",
4244
4270
  "\u2022 `/mnemospark cloud upload quote-id:<quote-id> wallet-address:<addr> object-id:<id> object-id-hash:<hash> [name:<friendly-name>] [async:true] [orchestrator:<inline|subagent>] [timeout-seconds:<n>]`",
4245
4271
  " Purpose: upload an encrypted object using a valid quote-id.",
@@ -4450,6 +4476,41 @@ function stripAsyncControlFlags(args) {
4450
4476
  function mergeArgParseWarnings(a, b) {
4451
4477
  return [...a, ...b];
4452
4478
  }
4479
+ async function resolvePriceStorageHashFromDatastore(datastore, partial) {
4480
+ await datastore.ensureReady();
4481
+ const row = await datastore.findObjectById(partial.object_id.trim());
4482
+ const wallet = partial.wallet_address.trim().toLowerCase();
4483
+ if (!row) {
4484
+ return {
4485
+ ok: false,
4486
+ message: "Cannot resolve object-id-hash: no object found in local SQLite for this object-id. Run backup first, or pass --object-id-hash explicitly."
4487
+ };
4488
+ }
4489
+ if (row.wallet_address.trim().toLowerCase() !== wallet) {
4490
+ return {
4491
+ ok: false,
4492
+ message: "Cannot resolve object-id-hash: wallet-address does not match the object record in ~/.openclaw/mnemospark/state.db."
4493
+ };
4494
+ }
4495
+ const sha = row.sha256?.trim();
4496
+ if (!sha) {
4497
+ return {
4498
+ ok: false,
4499
+ message: "Cannot resolve object-id-hash: local object record has no sha256 yet. Run backup first, or pass --object-id-hash explicitly."
4500
+ };
4501
+ }
4502
+ return {
4503
+ ok: true,
4504
+ request: {
4505
+ wallet_address: partial.wallet_address.trim(),
4506
+ object_id: partial.object_id.trim(),
4507
+ object_id_hash: sha.replace(/\s/g, ""),
4508
+ gb: partial.gb,
4509
+ provider: partial.provider.trim(),
4510
+ region: partial.region.trim()
4511
+ }
4512
+ };
4513
+ }
4453
4514
  function parseCloudArgs(args) {
4454
4515
  const trimmed = args?.trim() ?? "";
4455
4516
  if (!trimmed) {
@@ -4519,18 +4580,38 @@ function parseCloudArgs(args) {
4519
4580
  }
4520
4581
  const flags = valuesToStringRecord(parsed.values);
4521
4582
  const gb = Number.parseFloat(flags.gb ?? "");
4522
- const request = parsePriceStorageQuoteRequest({
4523
- wallet_address: flags["wallet-address"],
4524
- object_id: flags["object-id"],
4525
- object_id_hash: flags["object-id-hash"],
4526
- gb,
4527
- provider: flags.provider,
4528
- region: flags.region
4529
- });
4530
- if (!request) {
4583
+ const hashRaw = flags["object-id-hash"]?.trim();
4584
+ const walletAddress = flags["wallet-address"]?.trim();
4585
+ const objectId = flags["object-id"]?.trim();
4586
+ const provider = flags.provider?.trim();
4587
+ const region = flags.region?.trim();
4588
+ if (!walletAddress || !objectId || !provider || !region || !Number.isFinite(gb)) {
4531
4589
  return { mode: "price-storage-invalid" };
4532
4590
  }
4533
- return { mode: "price-storage", priceStorageRequest: request };
4591
+ if (hashRaw) {
4592
+ const request = parsePriceStorageQuoteRequest({
4593
+ wallet_address: walletAddress,
4594
+ object_id: objectId,
4595
+ object_id_hash: hashRaw,
4596
+ gb,
4597
+ provider,
4598
+ region
4599
+ });
4600
+ if (!request) {
4601
+ return { mode: "price-storage-invalid" };
4602
+ }
4603
+ return { mode: "price-storage", priceStorageRequest: request };
4604
+ }
4605
+ return {
4606
+ mode: "price-storage-resolve-hash",
4607
+ priceStoragePartial: {
4608
+ wallet_address: walletAddress,
4609
+ object_id: objectId,
4610
+ gb,
4611
+ provider,
4612
+ region
4613
+ }
4614
+ };
4534
4615
  }
4535
4616
  if (subcommand === "upload") {
4536
4617
  const parsed = parseCommandArgs(rest, uploadSchema);
@@ -6667,10 +6748,23 @@ operation-id: ${operationId}`,
6667
6748
  };
6668
6749
  }
6669
6750
  }
6670
- if (parsed.mode === "price-storage") {
6751
+ if (parsed.mode === "price-storage" || parsed.mode === "price-storage-resolve-hash") {
6752
+ let priceStorageRequest;
6753
+ if (parsed.mode === "price-storage-resolve-hash") {
6754
+ const resolved = await resolvePriceStorageHashFromDatastore(
6755
+ datastore,
6756
+ parsed.priceStoragePartial
6757
+ );
6758
+ if (!resolved.ok) {
6759
+ return { text: resolved.message, isError: true };
6760
+ }
6761
+ priceStorageRequest = resolved.request;
6762
+ } else {
6763
+ priceStorageRequest = parsed.priceStorageRequest;
6764
+ }
6671
6765
  const correlation = buildRequestCorrelation();
6672
6766
  try {
6673
- const quote = await requestPriceStorageQuote(parsed.priceStorageRequest, {
6767
+ const quote = await requestPriceStorageQuote(priceStorageRequest, {
6674
6768
  ...options.proxyQuoteOptions,
6675
6769
  correlation
6676
6770
  });
@@ -6728,8 +6822,8 @@ operation-id: ${operationId}`,
6728
6822
  {
6729
6823
  operation_id: correlation.operationId,
6730
6824
  trace_id: correlation.traceId,
6731
- wallet_address: parsed.priceStorageRequest.wallet_address,
6732
- object_id: parsed.priceStorageRequest.object_id,
6825
+ wallet_address: priceStorageRequest.wallet_address,
6826
+ object_id: priceStorageRequest.object_id,
6733
6827
  status: "failed"
6734
6828
  },
6735
6829
  mnemosparkHomeDir