mnemospark 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3004,17 +3004,17 @@ var CLOUD_ONBOARDING_BLOCK_LINES = [
3004
3004
  ];
3005
3005
 
3006
3006
  // src/cloud-command.ts
3007
- import { spawn } from "child_process";
3007
+ import { spawn as spawn2 } from "child_process";
3008
3008
  import {
3009
3009
  createCipheriv,
3010
3010
  createHash as createHash2,
3011
3011
  randomBytes as randomBytesNode,
3012
- randomUUID as randomUUID3
3012
+ randomUUID as randomUUID4
3013
3013
  } from "crypto";
3014
3014
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, statfsSync } from "fs";
3015
- import { lstat, mkdir as mkdir5, readFile as readFile3, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
3016
- import { homedir as homedir6, tmpdir } from "os";
3017
- import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
3015
+ import { lstat, mkdir as mkdir6, readFile as readFile4, readdir as readdir2, rm, stat as stat2, writeFile as writeFile4 } from "fs/promises";
3016
+ import { homedir as homedir7, tmpdir } from "os";
3017
+ import { basename as basename2, dirname as dirname6, join as join10, resolve as resolve2 } from "path";
3018
3018
  import { Readable } from "stream";
3019
3019
  import { finished } from "stream/promises";
3020
3020
  import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
@@ -4139,7 +4139,8 @@ var priceStorageSchema = {
4139
4139
  args: [
4140
4140
  { name: "wallet-address", aliases: ["wallet"], required: true },
4141
4141
  { name: "object-id", aliases: ["object"], required: true },
4142
- { 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"] },
4143
4144
  { name: "gb", required: true },
4144
4145
  { name: "provider", required: true },
4145
4146
  { name: "region", required: true }
@@ -4218,12 +4219,226 @@ var opStatusSchema = {
4218
4219
  ]
4219
4220
  };
4220
4221
 
4222
+ // src/openclaw-cli.ts
4223
+ import { spawn } from "child_process";
4224
+ import { join as join8 } from "path";
4225
+ async function runOpenClawCli(args, homeDir) {
4226
+ return await new Promise((resolvePromise, rejectPromise) => {
4227
+ let stdout = "";
4228
+ let stderr = "";
4229
+ const child = spawn("openclaw", args, {
4230
+ stdio: ["ignore", "pipe", "pipe"],
4231
+ env: {
4232
+ ...process.env,
4233
+ HOME: homeDir ?? process.env.HOME
4234
+ }
4235
+ });
4236
+ child.stdout.on("data", (chunk) => {
4237
+ stdout += chunk.toString();
4238
+ });
4239
+ child.stderr.on("data", (chunk) => {
4240
+ stderr += chunk.toString();
4241
+ });
4242
+ child.on("error", rejectPromise);
4243
+ child.on("close", (code) => {
4244
+ if (code === 0) {
4245
+ resolvePromise({ stdout, stderr });
4246
+ return;
4247
+ }
4248
+ rejectPromise(
4249
+ new Error(
4250
+ stderr.trim() || stdout.trim() || `openclaw ${args.join(" ")} exited with code ${code ?? "unknown"}`
4251
+ )
4252
+ );
4253
+ });
4254
+ });
4255
+ }
4256
+ function parseOpenClawCliJson(stdout, commandLabel) {
4257
+ const trimmed = stdout.trim();
4258
+ if (!trimmed) {
4259
+ throw new Error(`openclaw ${commandLabel} returned empty JSON output`);
4260
+ }
4261
+ try {
4262
+ return JSON.parse(trimmed);
4263
+ } catch {
4264
+ throw new Error(`openclaw ${commandLabel} returned invalid JSON output`);
4265
+ }
4266
+ }
4267
+ async function resolveOpenClawConfigFilePath(homeDir) {
4268
+ const { stdout } = await runOpenClawCli(["config", "file"], homeDir);
4269
+ const trimmed = stdout.trim();
4270
+ if (trimmed.startsWith("~/")) {
4271
+ return join8(homeDir, trimmed.slice(2));
4272
+ }
4273
+ if (trimmed.startsWith("~\\")) {
4274
+ return join8(homeDir, trimmed.slice(2));
4275
+ }
4276
+ return trimmed;
4277
+ }
4278
+
4279
+ // src/openclaw-renewal-runbook.ts
4280
+ import { mkdir as mkdir5, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
4281
+ import { homedir as homedir6 } from "os";
4282
+ import { dirname as dirname5, join as join9 } from "path";
4283
+ import { randomUUID as randomUUID3 } from "crypto";
4284
+ var DEFAULT_RENEWAL_AGENT_ID = "mnemospark-renewal";
4285
+ var RENEWAL_NODE_ALLOWLIST_ID = "node-usr-bin-node";
4286
+ function getRenewalAgentId() {
4287
+ const fromEnv = process.env.MNEMOSPARK_CRON_AGENT_ID?.trim();
4288
+ return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_RENEWAL_AGENT_ID;
4289
+ }
4290
+ function getRenewalNodeBinary() {
4291
+ const fromEnv = process.env.MNEMOSPARK_CRON_NODE_BIN?.trim();
4292
+ return fromEnv && fromEnv.length > 0 ? fromEnv : "/usr/bin/node";
4293
+ }
4294
+ function runbookRenewalAgentEntry(agentId = getRenewalAgentId()) {
4295
+ return {
4296
+ id: agentId,
4297
+ tools: {
4298
+ deny: ["subagents"],
4299
+ exec: { ask: "off" }
4300
+ }
4301
+ };
4302
+ }
4303
+ function isRecord(value) {
4304
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4305
+ }
4306
+ function renewalAgentEntrySatisfied(existing, desired) {
4307
+ if (!isRecord(existing)) {
4308
+ return false;
4309
+ }
4310
+ if (existing.id !== desired.id) {
4311
+ return false;
4312
+ }
4313
+ const tools = existing.tools;
4314
+ if (!isRecord(tools)) {
4315
+ return false;
4316
+ }
4317
+ const deny = tools.deny;
4318
+ if (!Array.isArray(deny) || !deny.includes("subagents")) {
4319
+ return false;
4320
+ }
4321
+ const exec = tools.exec;
4322
+ if (!isRecord(exec) || exec.ask !== "off") {
4323
+ return false;
4324
+ }
4325
+ return true;
4326
+ }
4327
+ function mergeRenewalAgentIntoAgentsList(list, desired) {
4328
+ const arr = Array.isArray(list) ? [...list] : [];
4329
+ const idx = arr.findIndex((e) => isRecord(e) && typeof e.id === "string" && e.id === desired.id);
4330
+ if (idx === -1) {
4331
+ return { list: [...arr, desired], changed: true };
4332
+ }
4333
+ if (renewalAgentEntrySatisfied(arr[idx], desired)) {
4334
+ return { list: arr, changed: false };
4335
+ }
4336
+ const next = [...arr];
4337
+ next[idx] = desired;
4338
+ return { list: next, changed: true };
4339
+ }
4340
+ function mergeExecApprovalsAllowlist(doc, agentId, nodeBinary) {
4341
+ const prevAgents = doc.agents && isRecord(doc.agents) ? doc.agents : {};
4342
+ const block = prevAgents[agentId];
4343
+ const allowlist = Array.isArray(block?.allowlist) ? [...block.allowlist] : [];
4344
+ const hasPattern = allowlist.some((e) => e?.pattern === nodeBinary);
4345
+ if (hasPattern) {
4346
+ return { doc, changed: false };
4347
+ }
4348
+ allowlist.push({
4349
+ id: RENEWAL_NODE_ALLOWLIST_ID,
4350
+ pattern: nodeBinary,
4351
+ source: "manual",
4352
+ lastUsedAt: Date.now()
4353
+ });
4354
+ const nextAgents = {
4355
+ ...prevAgents,
4356
+ [agentId]: {
4357
+ ...block && isRecord(block) ? block : {},
4358
+ allowlist
4359
+ }
4360
+ };
4361
+ return { doc: { ...doc, agents: nextAgents }, changed: true };
4362
+ }
4363
+ async function readJsonFile(path) {
4364
+ const raw = await readFile3(path, "utf-8");
4365
+ return JSON.parse(raw);
4366
+ }
4367
+ async function writeFileAtomic(path, contents) {
4368
+ await mkdir5(dirname5(path), { recursive: true });
4369
+ const tmp = join9(dirname5(path), `.tmp-${randomUUID3()}`);
4370
+ await writeFile3(tmp, contents, "utf-8");
4371
+ await rename2(tmp, path);
4372
+ }
4373
+ async function ensureOpenClawRenewalPrerequisites(options = {}) {
4374
+ if (options.disabled ?? process.env.MNEMOSPARK_DISABLE_OPENCLAW_PREREQ === "1") {
4375
+ return;
4376
+ }
4377
+ const homeDir = options.homeDir ?? homedir6();
4378
+ const agentId = getRenewalAgentId();
4379
+ const desired = runbookRenewalAgentEntry(agentId);
4380
+ const nodeBinary = getRenewalNodeBinary();
4381
+ const configPath = await resolveOpenClawConfigFilePath(homeDir);
4382
+ let configRaw = "{}";
4383
+ try {
4384
+ configRaw = await readFile3(configPath, "utf-8");
4385
+ } catch (err) {
4386
+ if (err.code !== "ENOENT") {
4387
+ throw err;
4388
+ }
4389
+ }
4390
+ let parsed;
4391
+ try {
4392
+ parsed = JSON.parse(configRaw);
4393
+ } catch {
4394
+ throw new Error(
4395
+ `openclaw.json at ${configPath} is not valid JSON; fix or remove it before upload.`
4396
+ );
4397
+ }
4398
+ const agents = isRecord(parsed.agents) ? parsed.agents : {};
4399
+ const { list: mergedList, changed: agentChanged } = mergeRenewalAgentIntoAgentsList(
4400
+ agents.list,
4401
+ desired
4402
+ );
4403
+ if (agentChanged) {
4404
+ const listJson = JSON.stringify(mergedList);
4405
+ await runOpenClawCli(["config", "set", "agents.list", listJson, "--strict-json"], homeDir);
4406
+ await runOpenClawCli(["config", "validate"], homeDir);
4407
+ }
4408
+ const execPath = join9(homeDir, ".openclaw", "exec-approvals.json");
4409
+ let execDoc = {};
4410
+ try {
4411
+ const raw = await readJsonFile(execPath);
4412
+ execDoc = isRecord(raw) ? raw : {};
4413
+ } catch (err) {
4414
+ if (err.code !== "ENOENT") {
4415
+ throw err;
4416
+ }
4417
+ }
4418
+ const { doc: mergedExec, changed: execChanged } = mergeExecApprovalsAllowlist(
4419
+ execDoc,
4420
+ agentId,
4421
+ nodeBinary
4422
+ );
4423
+ if (execChanged) {
4424
+ await writeFileAtomic(execPath, `${JSON.stringify(mergedExec, null, 2)}
4425
+ `);
4426
+ }
4427
+ const skipRestart = options.skipGatewayRestart ?? process.env.MNEMOSPARK_SKIP_GATEWAY_RESTART === "1";
4428
+ if (!skipRestart && (agentChanged || execChanged)) {
4429
+ try {
4430
+ await runOpenClawCli(["gateway", "restart", "--json"], homeDir);
4431
+ } catch {
4432
+ }
4433
+ }
4434
+ }
4435
+
4221
4436
  // src/cloud-command.ts
4222
4437
  var SUPPORTED_BACKUP_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux"]);
4223
- var BACKUP_DIR_SUBPATH = join8(".openclaw", "mnemospark", "backup");
4224
- var DEFAULT_BACKUP_DIR = join8(homedir6(), BACKUP_DIR_SUBPATH);
4225
- var BLOCKRUN_WALLET_KEY_SUBPATH = join8(".openclaw", "blockrun", "wallet.key");
4226
- var MNEMOSPARK_WALLET_KEY_SUBPATH = join8(".openclaw", "mnemospark", "wallet", "wallet.key");
4438
+ var BACKUP_DIR_SUBPATH = join10(".openclaw", "mnemospark", "backup");
4439
+ var DEFAULT_BACKUP_DIR = join10(homedir7(), BACKUP_DIR_SUBPATH);
4440
+ var BLOCKRUN_WALLET_KEY_SUBPATH = join10(".openclaw", "blockrun", "wallet.key");
4441
+ var MNEMOSPARK_WALLET_KEY_SUBPATH = join10(".openclaw", "mnemospark", "wallet", "wallet.key");
4227
4442
  var INLINE_UPLOAD_MAX_BYTES = 45e5;
4228
4443
  var NODE_FS_MAX_READFILE_BYTES = 2147483648;
4229
4444
  var PAYMENT_CRON_SCHEDULE = "0 0 1 * *";
@@ -4231,7 +4446,7 @@ var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
4231
4446
  var QUOTE_VALIDITY_USER_NOTE = "Quotes are valid for one hour. Please run price-storage again if you need a new quote.";
4232
4447
  var MNEMOSPARK_SUPPORT_EMAIL = "pluggedin@mnemospark.ai";
4233
4448
  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.";
4234
- var REQUIRED_PRICE_STORAGE = "wallet-address:, object-id:, object-id-hash:, gb:, provider:, region:";
4449
+ var REQUIRED_PRICE_STORAGE = "wallet-address:, object-id:, gb:, provider:, region: (object-id-hash: optional if the object exists in local SQLite after backup)";
4235
4450
  var REQUIRED_UPLOAD = "quote-id:, wallet-address:, object-id:, object-id-hash:";
4236
4451
  var REQUIRED_BACKUP = "<file|directory> and name:<friendly-name>";
4237
4452
  var REQUIRED_PAYMENT_SETTLE = "wallet-address: and (quote-id: | renewal:true with object-key:)";
@@ -4241,10 +4456,10 @@ var ORCHESTRATOR_MODES = /* @__PURE__ */ new Set(["inline", "subagent"]);
4241
4456
  function expandTilde(path) {
4242
4457
  const trimmed = path.trim();
4243
4458
  if (trimmed === "~") {
4244
- return homedir6();
4459
+ return homedir7();
4245
4460
  }
4246
4461
  if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
4247
- return join8(homedir6(), trimmed.slice(2));
4462
+ return join10(homedir7(), trimmed.slice(2));
4248
4463
  }
4249
4464
  return path;
4250
4465
  }
@@ -4261,10 +4476,10 @@ var CLOUD_HELP_TEXT = [
4261
4476
  " 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.",
4262
4477
  " Required: " + REQUIRED_BACKUP,
4263
4478
  "",
4264
- "\u2022 `/mnemospark cloud price-storage wallet-address:<addr> object-id:<id> object-id-hash:<hash> gb:<gb> provider:aws region:us-east-1`",
4265
- " Purpose: request a storage quote before upload (defaults shown; override `provider:` / `region:` for other regions).",
4479
+ "\u2022 `/mnemospark cloud price-storage wallet-address:<addr> object-id:<id> [object-id-hash:<hash>] gb:<gb> provider:aws region:us-east-1`",
4480
+ " 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.",
4266
4481
  " Required: " + REQUIRED_PRICE_STORAGE,
4267
- " Shorter: `wallet:\u2026 object:\u2026 hash:\u2026 gb:\u2026 provider:\u2026 region:\u2026`",
4482
+ " Shorter: `wallet:\u2026 object:\u2026 [hash:\u2026] gb:\u2026 provider:\u2026 region:\u2026`",
4268
4483
  "",
4269
4484
  "\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>]`",
4270
4485
  " Purpose: upload an encrypted object using a valid quote-id.",
@@ -4475,6 +4690,41 @@ function stripAsyncControlFlags(args) {
4475
4690
  function mergeArgParseWarnings(a, b) {
4476
4691
  return [...a, ...b];
4477
4692
  }
4693
+ async function resolvePriceStorageHashFromDatastore(datastore, partial) {
4694
+ await datastore.ensureReady();
4695
+ const row = await datastore.findObjectById(partial.object_id.trim());
4696
+ const wallet = partial.wallet_address.trim().toLowerCase();
4697
+ if (!row) {
4698
+ return {
4699
+ ok: false,
4700
+ 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."
4701
+ };
4702
+ }
4703
+ if (row.wallet_address.trim().toLowerCase() !== wallet) {
4704
+ return {
4705
+ ok: false,
4706
+ message: "Cannot resolve object-id-hash: wallet-address does not match the object record in ~/.openclaw/mnemospark/state.db."
4707
+ };
4708
+ }
4709
+ const sha = row.sha256?.trim();
4710
+ if (!sha) {
4711
+ return {
4712
+ ok: false,
4713
+ message: "Cannot resolve object-id-hash: local object record has no sha256 yet. Run backup first, or pass --object-id-hash explicitly."
4714
+ };
4715
+ }
4716
+ return {
4717
+ ok: true,
4718
+ request: {
4719
+ wallet_address: partial.wallet_address.trim(),
4720
+ object_id: partial.object_id.trim(),
4721
+ object_id_hash: sha.replace(/\s/g, ""),
4722
+ gb: partial.gb,
4723
+ provider: partial.provider.trim(),
4724
+ region: partial.region.trim()
4725
+ }
4726
+ };
4727
+ }
4478
4728
  function parseCloudArgs(args) {
4479
4729
  const trimmed = args?.trim() ?? "";
4480
4730
  if (!trimmed) {
@@ -4544,18 +4794,38 @@ function parseCloudArgs(args) {
4544
4794
  }
4545
4795
  const flags = valuesToStringRecord(parsed.values);
4546
4796
  const gb = Number.parseFloat(flags.gb ?? "");
4547
- const request = parsePriceStorageQuoteRequest({
4548
- wallet_address: flags["wallet-address"],
4549
- object_id: flags["object-id"],
4550
- object_id_hash: flags["object-id-hash"],
4551
- gb,
4552
- provider: flags.provider,
4553
- region: flags.region
4554
- });
4555
- if (!request) {
4797
+ const hashRaw = flags["object-id-hash"]?.trim();
4798
+ const walletAddress = flags["wallet-address"]?.trim();
4799
+ const objectId = flags["object-id"]?.trim();
4800
+ const provider = flags.provider?.trim();
4801
+ const region = flags.region?.trim();
4802
+ if (!walletAddress || !objectId || !provider || !region || !Number.isFinite(gb)) {
4556
4803
  return { mode: "price-storage-invalid" };
4557
4804
  }
4558
- return { mode: "price-storage", priceStorageRequest: request };
4805
+ if (hashRaw) {
4806
+ const request = parsePriceStorageQuoteRequest({
4807
+ wallet_address: walletAddress,
4808
+ object_id: objectId,
4809
+ object_id_hash: hashRaw,
4810
+ gb,
4811
+ provider,
4812
+ region
4813
+ });
4814
+ if (!request) {
4815
+ return { mode: "price-storage-invalid" };
4816
+ }
4817
+ return { mode: "price-storage", priceStorageRequest: request };
4818
+ }
4819
+ return {
4820
+ mode: "price-storage-resolve-hash",
4821
+ priceStoragePartial: {
4822
+ wallet_address: walletAddress,
4823
+ object_id: objectId,
4824
+ gb,
4825
+ provider,
4826
+ region
4827
+ }
4828
+ };
4559
4829
  }
4560
4830
  if (subcommand === "upload") {
4561
4831
  const parsed = parseCommandArgs(rest, uploadSchema);
@@ -4757,7 +5027,7 @@ async function calculateInputSizeBytes(targetPath) {
4757
5027
  let total = 0;
4758
5028
  const entries = await readdir2(targetPath, { withFileTypes: true });
4759
5029
  for (const entry of entries) {
4760
- total += await calculateInputSizeBytes(join8(targetPath, entry.name));
5030
+ total += await calculateInputSizeBytes(join10(targetPath, entry.name));
4761
5031
  }
4762
5032
  return total;
4763
5033
  }
@@ -4769,11 +5039,11 @@ function getAvailableDiskBytes(tmpDir, options) {
4769
5039
  return stats.bavail * stats.bsize;
4770
5040
  }
4771
5041
  async function runTarGzip(archivePath, sourcePath) {
4772
- const sourceDir = dirname5(sourcePath);
5042
+ const sourceDir = dirname6(sourcePath);
4773
5043
  const sourceName = basename2(sourcePath);
4774
5044
  await new Promise((resolvePromise, rejectPromise) => {
4775
5045
  let stderr = "";
4776
- const child = spawn("tar", ["-czf", archivePath, "-C", sourceDir, sourceName], {
5046
+ const child = spawn2("tar", ["-czf", archivePath, "-C", sourceDir, sourceName], {
4777
5047
  stdio: ["ignore", "ignore", "pipe"]
4778
5048
  });
4779
5049
  child.stderr.on("data", (chunk) => {
@@ -4803,7 +5073,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
4803
5073
  if (friendlyName?.trim()) {
4804
5074
  try {
4805
5075
  const sanitized = sanitizeFriendlyNameForLocalBasename(friendlyName);
4806
- const candidate = join8(backupDir, sanitized);
5076
+ const candidate = join10(backupDir, sanitized);
4807
5077
  try {
4808
5078
  const st = await stat2(candidate);
4809
5079
  if (st.isFile()) {
@@ -4814,7 +5084,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
4814
5084
  } catch {
4815
5085
  }
4816
5086
  }
4817
- const legacyPath = join8(backupDir, objectId);
5087
+ const legacyPath = join10(backupDir, objectId);
4818
5088
  try {
4819
5089
  const legacyStats = await stat2(legacyPath);
4820
5090
  if (!legacyStats.isFile()) {
@@ -4852,7 +5122,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
4852
5122
  tmpStats = await stat2(tmpDir);
4853
5123
  } catch (error) {
4854
5124
  if (error.code === "ENOENT") {
4855
- await mkdir5(tmpDir, { recursive: true });
5125
+ await mkdir6(tmpDir, { recursive: true });
4856
5126
  tmpStats = await stat2(tmpDir);
4857
5127
  } else {
4858
5128
  throw error;
@@ -4869,7 +5139,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
4869
5139
  }
4870
5140
  const objectId = createObjectId(options);
4871
5141
  const archiveBaseSegment = options.archiveBasename?.trim() || objectId;
4872
- const archivePath = join8(tmpDir, archiveBaseSegment);
5142
+ const archivePath = join10(tmpDir, archiveBaseSegment);
4873
5143
  if (options.archiveBasename?.trim()) {
4874
5144
  try {
4875
5145
  const existing = await stat2(archivePath);
@@ -4925,48 +5195,6 @@ function normalizeOpenClawCronJobForLookup(value) {
4925
5195
  message: payloadMessage
4926
5196
  };
4927
5197
  }
4928
- async function runOpenClawCli(args, homeDir) {
4929
- return await new Promise((resolvePromise, rejectPromise) => {
4930
- let stdout = "";
4931
- let stderr = "";
4932
- const child = spawn("openclaw", args, {
4933
- stdio: ["ignore", "pipe", "pipe"],
4934
- env: {
4935
- ...process.env,
4936
- HOME: homeDir ?? process.env.HOME
4937
- }
4938
- });
4939
- child.stdout.on("data", (chunk) => {
4940
- stdout += chunk.toString();
4941
- });
4942
- child.stderr.on("data", (chunk) => {
4943
- stderr += chunk.toString();
4944
- });
4945
- child.on("error", rejectPromise);
4946
- child.on("close", (code) => {
4947
- if (code === 0) {
4948
- resolvePromise({ stdout, stderr });
4949
- return;
4950
- }
4951
- rejectPromise(
4952
- new Error(
4953
- stderr.trim() || stdout.trim() || `openclaw ${args.join(" ")} exited with code ${code ?? "unknown"}`
4954
- )
4955
- );
4956
- });
4957
- });
4958
- }
4959
- function parseOpenClawCliJson(stdout, commandLabel) {
4960
- const trimmed = stdout.trim();
4961
- if (!trimmed) {
4962
- throw new Error(`openclaw ${commandLabel} returned empty JSON output`);
4963
- }
4964
- try {
4965
- return JSON.parse(trimmed);
4966
- } catch {
4967
- throw new Error(`openclaw ${commandLabel} returned invalid JSON output`);
4968
- }
4969
- }
4970
5198
  function createOpenClawCliCronAdapter(homeDir) {
4971
5199
  return {
4972
5200
  add: async (job) => {
@@ -4982,11 +5210,11 @@ function createOpenClawCliCronAdapter(homeDir) {
4982
5210
  job.schedule.tz,
4983
5211
  "--session",
4984
5212
  job.sessionTarget,
5213
+ "--agent",
5214
+ job.agentId,
4985
5215
  "--message",
4986
5216
  job.payload.message,
4987
- "--announce",
4988
- "--description",
4989
- job.delivery.text,
5217
+ "--no-deliver",
4990
5218
  "--json"
4991
5219
  ],
4992
5220
  homeDir
@@ -5051,8 +5279,9 @@ function buildStoragePaymentCronCommand(job) {
5051
5279
  return `/mnemospark cloud ${buildStoragePaymentRenewalArgs(job)}`;
5052
5280
  }
5053
5281
  function buildOpenClawRenewalAgentMessage(openClawHome, renewalArgs) {
5054
- const cliPath = join8(openClawHome, ".openclaw/extensions/mnemospark/dist/cli.js");
5055
- return `Execute: node ${cliPath} cloud ${renewalArgs}`;
5282
+ const cliPath = join10(openClawHome, ".openclaw/extensions/mnemospark/dist/cli.js");
5283
+ const nodeBin = getRenewalNodeBinary();
5284
+ return `Command: ${nodeBin} ${cliPath} cloud ${renewalArgs}`;
5056
5285
  }
5057
5286
  function parseStoragePaymentCronCommand(command) {
5058
5287
  const objectIdMatch = command.match(/--object-id\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
@@ -5098,10 +5327,7 @@ async function appendStoragePaymentCronJob(cronJob, adapter, payloadMessage) {
5098
5327
  message: payloadMessage
5099
5328
  },
5100
5329
  sessionTarget: "isolated",
5101
- delivery: {
5102
- mode: "announce",
5103
- text: "Thank you for using mnemospark cloud storage. Your renewal has been processed."
5104
- }
5330
+ agentId: getRenewalAgentId()
5105
5331
  };
5106
5332
  return adapter.add(openClawJob);
5107
5333
  }
@@ -5109,6 +5335,7 @@ async function removeStoragePaymentCronJob(cronId, adapter) {
5109
5335
  return adapter.remove(cronId);
5110
5336
  }
5111
5337
  async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAdapter, openClawHomeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
5338
+ await ensureOpenClawRenewalPrerequisites({ homeDir: openClawHomeDir });
5112
5339
  const renewalFields = {
5113
5340
  walletAddress: upload.addr,
5114
5341
  objectId: upload.object_id,
@@ -5116,7 +5343,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
5116
5343
  storagePrice
5117
5344
  };
5118
5345
  const renewalArgs = buildStoragePaymentRenewalArgs(renewalFields);
5119
- const provisionalCronId = randomUUID3();
5346
+ const provisionalCronId = randomUUID4();
5120
5347
  const cronJob = {
5121
5348
  cronId: provisionalCronId,
5122
5349
  createdAt: nowDateFn().toISOString(),
@@ -5140,7 +5367,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
5140
5367
  }
5141
5368
  async function readWalletKeyIfPresent(walletPath) {
5142
5369
  try {
5143
- const key = (await readFile3(walletPath, "utf-8")).trim();
5370
+ const key = (await readFile4(walletPath, "utf-8")).trim();
5144
5371
  return isValidWalletPrivateKey(key) ? key : null;
5145
5372
  } catch (error) {
5146
5373
  if (error.code === "ENOENT") {
@@ -5154,9 +5381,9 @@ async function resolveWalletPrivateKey(homeDir) {
5154
5381
  if (isValidWalletPrivateKey(envKey)) {
5155
5382
  return envKey;
5156
5383
  }
5157
- const baseHome = homeDir ?? homedir6();
5158
- const primaryWalletPath = join8(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
5159
- const fallbackWalletPath = join8(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
5384
+ const baseHome = homeDir ?? homedir7();
5385
+ const primaryWalletPath = join10(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
5386
+ const fallbackWalletPath = join10(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
5160
5387
  const fromPrimary = await readWalletKeyIfPresent(primaryWalletPath);
5161
5388
  if (fromPrimary) {
5162
5389
  return fromPrimary;
@@ -5220,9 +5447,9 @@ async function encryptPlaintextFileToAesGcmPath(plaintextPath, dek, outPath, ran
5220
5447
  }
5221
5448
  async function loadOrCreateKek(walletAddress, homeDir) {
5222
5449
  const keyPath = resolveWalletKekPath(walletAddress, homeDir);
5223
- await mkdir5(dirname5(keyPath), { recursive: true });
5450
+ await mkdir6(dirname6(keyPath), { recursive: true });
5224
5451
  try {
5225
- const existing = await readFile3(keyPath);
5452
+ const existing = await readFile4(keyPath);
5226
5453
  return { kek: parseStoredAes256Key(existing), keyPath };
5227
5454
  } catch (error) {
5228
5455
  if (error.code !== "ENOENT") {
@@ -5230,7 +5457,7 @@ async function loadOrCreateKek(walletAddress, homeDir) {
5230
5457
  }
5231
5458
  }
5232
5459
  const generated = randomBytesNode(32);
5233
- await writeFile3(keyPath, generated, { mode: 384 });
5460
+ await writeFile4(keyPath, generated, { mode: 384 });
5234
5461
  return { kek: generated, keyPath };
5235
5462
  }
5236
5463
  async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
@@ -5242,7 +5469,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
5242
5469
  const dek = randomBytesNode(32);
5243
5470
  const wrappedDek = encryptAesGcm(dek, kek);
5244
5471
  if (archiveStat.size >= NODE_FS_MAX_READFILE_BYTES) {
5245
- const encryptedTempPath = join8(tmpdir(), `mnemospark-upload-${randomUUID3()}.enc`);
5472
+ const encryptedTempPath = join10(tmpdir(), `mnemospark-upload-${randomUUID4()}.enc`);
5246
5473
  try {
5247
5474
  await encryptPlaintextFileToAesGcmPath(archivePath, dek, encryptedTempPath);
5248
5475
  const encStat = await stat2(encryptedTempPath);
@@ -5268,7 +5495,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
5268
5495
  throw err;
5269
5496
  }
5270
5497
  }
5271
- const plaintext = await readFile3(archivePath);
5498
+ const plaintext = await readFile4(archivePath);
5272
5499
  const encryptedContent = encryptAesGcm(plaintext, dek);
5273
5500
  const payloadHash = sha256Buffer(encryptedContent);
5274
5501
  const payload = {
@@ -5513,7 +5740,7 @@ function createInProcessSubagentOrchestrator() {
5513
5740
  };
5514
5741
  return {
5515
5742
  dispatch: async (input) => {
5516
- const sessionId = `agent:mnemospark:subagent:${randomUUID3()}`;
5743
+ const sessionId = `agent:mnemospark:subagent:${randomUUID4()}`;
5517
5744
  const state = {
5518
5745
  terminal: false,
5519
5746
  cancelRequested: false,
@@ -5615,7 +5842,7 @@ function createCloudCommand(options = {}) {
5615
5842
  createPaymentFetchFn: options.createPaymentFetchFn ?? createPaymentFetch,
5616
5843
  fetchImpl: options.fetchImpl ?? fetch,
5617
5844
  nowDateFn: options.nowDateFn ?? (() => /* @__PURE__ */ new Date()),
5618
- idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID3,
5845
+ idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID4,
5619
5846
  requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
5620
5847
  requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
5621
5848
  requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
@@ -5759,8 +5986,8 @@ async function emitOperationEventBestEffort(eventType, context, homeDir) {
5759
5986
  }
5760
5987
  }
5761
5988
  function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
5762
- const operationId = forcedOperationId?.trim() || randomUUID3();
5763
- const traceId = forcedTraceId?.trim() || randomUUID3();
5989
+ const operationId = forcedOperationId?.trim() || randomUUID4();
5990
+ const traceId = forcedTraceId?.trim() || randomUUID4();
5764
5991
  return { operationId, traceId };
5765
5992
  }
5766
5993
  function parseTransIdFromPaymentSettleBody(bodyText) {
@@ -6048,7 +6275,7 @@ ${operation.result_text}` : meta;
6048
6275
  };
6049
6276
  }
6050
6277
  if (!isTerminalOperationStatus(operation.status)) {
6051
- const traceId = operation.trace_id ?? randomUUID3();
6278
+ const traceId = operation.trace_id ?? randomUUID4();
6052
6279
  const cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
6053
6280
  await datastore.upsertOperation({
6054
6281
  operation_id: operation.operation_id,
@@ -6646,7 +6873,7 @@ operation-id: ${operationId}`,
6646
6873
  await emitCloudEventBestEffort(
6647
6874
  "backup.completed",
6648
6875
  {
6649
- operation_id: executionContext.forcedOperationId?.trim() || randomUUID3(),
6876
+ operation_id: executionContext.forcedOperationId?.trim() || randomUUID4(),
6650
6877
  object_id: result.objectId,
6651
6878
  status: "succeeded",
6652
6879
  details: {
@@ -6692,10 +6919,23 @@ operation-id: ${operationId}`,
6692
6919
  };
6693
6920
  }
6694
6921
  }
6695
- if (parsed.mode === "price-storage") {
6922
+ if (parsed.mode === "price-storage" || parsed.mode === "price-storage-resolve-hash") {
6923
+ let priceStorageRequest;
6924
+ if (parsed.mode === "price-storage-resolve-hash") {
6925
+ const resolved = await resolvePriceStorageHashFromDatastore(
6926
+ datastore,
6927
+ parsed.priceStoragePartial
6928
+ );
6929
+ if (!resolved.ok) {
6930
+ return { text: resolved.message, isError: true };
6931
+ }
6932
+ priceStorageRequest = resolved.request;
6933
+ } else {
6934
+ priceStorageRequest = parsed.priceStorageRequest;
6935
+ }
6696
6936
  const correlation = buildRequestCorrelation();
6697
6937
  try {
6698
- const quote = await requestPriceStorageQuote(parsed.priceStorageRequest, {
6938
+ const quote = await requestPriceStorageQuote(priceStorageRequest, {
6699
6939
  ...options.proxyQuoteOptions,
6700
6940
  correlation
6701
6941
  });
@@ -6753,8 +6993,8 @@ operation-id: ${operationId}`,
6753
6993
  {
6754
6994
  operation_id: correlation.operationId,
6755
6995
  trace_id: correlation.traceId,
6756
- wallet_address: parsed.priceStorageRequest.wallet_address,
6757
- object_id: parsed.priceStorageRequest.object_id,
6996
+ wallet_address: priceStorageRequest.wallet_address,
6997
+ object_id: priceStorageRequest.object_id,
6758
6998
  status: "failed"
6759
6999
  },
6760
7000
  mnemosparkHomeDir
@@ -6920,7 +7160,7 @@ operation-id: ${operationId}`,
6920
7160
  finalizedUploadResponse,
6921
7161
  cronStoragePrice,
6922
7162
  openClawCronAdapter,
6923
- mnemosparkHomeDir ?? homedir6(),
7163
+ mnemosparkHomeDir ?? homedir7(),
6924
7164
  nowDateFn
6925
7165
  );
6926
7166
  await datastore.upsertObject({
@@ -7530,12 +7770,12 @@ async function buildWalletExportResponse() {
7530
7770
  }
7531
7771
 
7532
7772
  // src/cli.ts
7533
- import { spawn as spawn2 } from "child_process";
7534
- import { dirname as dirname6, join as join9 } from "path";
7773
+ import { spawn as spawn3 } from "child_process";
7774
+ import { dirname as dirname7, join as join11 } from "path";
7535
7775
  import { fileURLToPath as fileURLToPath2 } from "url";
7536
- import { mkdir as mkdir6, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
7776
+ import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
7537
7777
  import { existsSync as existsSync2 } from "fs";
7538
- import { homedir as homedir7 } from "os";
7778
+ import { homedir as homedir8 } from "os";
7539
7779
  function isHexPrivateKey(value) {
7540
7780
  return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value.trim());
7541
7781
  }
@@ -7643,25 +7883,25 @@ function parseArgs(args) {
7643
7883
  return result;
7644
7884
  }
7645
7885
  var __filename2 = fileURLToPath2(import.meta.url);
7646
- var __dirname2 = dirname6(__filename2);
7647
- var PACKAGE_ROOT = dirname6(__dirname2);
7886
+ var __dirname2 = dirname7(__filename2);
7887
+ var PACKAGE_ROOT = dirname7(__dirname2);
7648
7888
  async function ensureDir(path) {
7649
- await mkdir6(path, { recursive: true });
7889
+ await mkdir7(path, { recursive: true });
7650
7890
  }
7651
7891
  async function deployExtensionFiles() {
7652
- const scriptsSource = join9(PACKAGE_ROOT, "scripts");
7892
+ const scriptsSource = join11(PACKAGE_ROOT, "scripts");
7653
7893
  if (!existsSync2(scriptsSource)) return;
7654
- const mnemoScriptsDir = join9(homedir7(), ".openclaw", "mnemospark", "scripts");
7894
+ const mnemoScriptsDir = join11(homedir8(), ".openclaw", "mnemospark", "scripts");
7655
7895
  await ensureDir(mnemoScriptsDir);
7656
- const uninstallSrc = join9(scriptsSource, "uninstall.sh");
7896
+ const uninstallSrc = join11(scriptsSource, "uninstall.sh");
7657
7897
  if (existsSync2(uninstallSrc)) {
7658
- const content = await readFile4(uninstallSrc);
7659
- await writeFile4(join9(mnemoScriptsDir, "uninstall.sh"), content, { mode: 493 });
7898
+ const content = await readFile5(uninstallSrc);
7899
+ await writeFile5(join11(mnemoScriptsDir, "uninstall.sh"), content, { mode: 493 });
7660
7900
  }
7661
7901
  }
7662
7902
  function isOpenClawAvailable() {
7663
7903
  return new Promise((resolve3) => {
7664
- const child = spawn2("openclaw", ["--version"], {
7904
+ const child = spawn3("openclaw", ["--version"], {
7665
7905
  stdio: "ignore",
7666
7906
  shell: true
7667
7907
  });
@@ -7670,13 +7910,13 @@ function isOpenClawAvailable() {
7670
7910
  });
7671
7911
  }
7672
7912
  function getOpenClawConfigPath() {
7673
- const stateDir = process.env.OPENCLAW_STATE_DIR ?? join9(homedir7(), ".openclaw");
7674
- return join9(stateDir, "openclaw.json");
7913
+ const stateDir = process.env.OPENCLAW_STATE_DIR ?? join11(homedir8(), ".openclaw");
7914
+ return join11(stateDir, "openclaw.json");
7675
7915
  }
7676
7916
  async function ensureMnemosparkInPluginsAllow() {
7677
7917
  const configPath = getOpenClawConfigPath();
7678
7918
  try {
7679
- const raw = await readFile4(configPath, "utf-8");
7919
+ const raw = await readFile5(configPath, "utf-8");
7680
7920
  const config = JSON.parse(raw);
7681
7921
  if (!config.plugins || typeof config.plugins !== "object") {
7682
7922
  config.plugins = {};
@@ -7688,7 +7928,7 @@ async function ensureMnemosparkInPluginsAllow() {
7688
7928
  const allow = plugins.allow;
7689
7929
  if (!allow.includes("mnemospark")) {
7690
7930
  allow.push("mnemospark");
7691
- await writeFile4(configPath, JSON.stringify(config, null, 2), "utf-8");
7931
+ await writeFile5(configPath, JSON.stringify(config, null, 2), "utf-8");
7692
7932
  console.log("[mnemospark] Added mnemospark to plugins.allow in openclaw.json.");
7693
7933
  }
7694
7934
  } catch (err) {
@@ -7701,7 +7941,7 @@ async function promptOrRunOpenClawPluginInstall() {
7701
7941
  const available = await isOpenClawAvailable();
7702
7942
  if (available) {
7703
7943
  console.log("\n[mnemospark] Registering plugin with OpenClaw...");
7704
- const child = spawn2("openclaw", ["plugins", "install", "mnemospark"], {
7944
+ const child = spawn3("openclaw", ["plugins", "install", "mnemospark"], {
7705
7945
  stdio: "inherit",
7706
7946
  shell: true
7707
7947
  });
@@ -7723,7 +7963,7 @@ async function promptOrRunOpenClawPluginInstall() {
7723
7963
  }
7724
7964
  async function readLegacyWalletIfPresent() {
7725
7965
  try {
7726
- const key = (await readFile4(LEGACY_WALLET_FILE, "utf-8")).trim();
7966
+ const key = (await readFile5(LEGACY_WALLET_FILE, "utf-8")).trim();
7727
7967
  return isHexPrivateKey(key) ? key : null;
7728
7968
  } catch (error) {
7729
7969
  if (error.code === "ENOENT") {
@@ -7733,9 +7973,9 @@ async function readLegacyWalletIfPresent() {
7733
7973
  }
7734
7974
  }
7735
7975
  async function writeMnemosparkWallet(key) {
7736
- const dir = dirname6(WALLET_FILE);
7976
+ const dir = dirname7(WALLET_FILE);
7737
7977
  await ensureDir(dir);
7738
- await writeFile4(WALLET_FILE, `${key}
7978
+ await writeFile5(WALLET_FILE, `${key}
7739
7979
  `, { mode: 384 });
7740
7980
  }
7741
7981
  async function promptReuseLegacyWallet() {