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/index.js CHANGED
@@ -3049,17 +3049,17 @@ var CLOUD_ONBOARDING_BLOCK_LINES = [
3049
3049
  ];
3050
3050
 
3051
3051
  // src/cloud-command.ts
3052
- import { spawn } from "child_process";
3052
+ import { spawn as spawn2 } from "child_process";
3053
3053
  import {
3054
3054
  createCipheriv,
3055
3055
  createHash as createHash2,
3056
3056
  randomBytes as randomBytesNode,
3057
- randomUUID as randomUUID3
3057
+ randomUUID as randomUUID4
3058
3058
  } from "crypto";
3059
3059
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, statfsSync } from "fs";
3060
- import { lstat, mkdir as mkdir5, readFile as readFile3, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
3061
- import { homedir as homedir6, tmpdir } from "os";
3062
- import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
3060
+ import { lstat, mkdir as mkdir6, readFile as readFile4, readdir as readdir2, rm, stat as stat2, writeFile as writeFile4 } from "fs/promises";
3061
+ import { homedir as homedir7, tmpdir } from "os";
3062
+ import { basename as basename2, dirname as dirname6, join as join10, resolve as resolve2 } from "path";
3063
3063
  import { Readable } from "stream";
3064
3064
  import { finished } from "stream/promises";
3065
3065
  import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
@@ -4184,7 +4184,8 @@ var priceStorageSchema = {
4184
4184
  args: [
4185
4185
  { name: "wallet-address", aliases: ["wallet"], required: true },
4186
4186
  { name: "object-id", aliases: ["object"], required: true },
4187
- { name: "object-id-hash", aliases: ["hash"], required: true },
4187
+ // Optional: omit when the object exists in local SQLite after backup; CLI resolves sha256 from state.db.
4188
+ { name: "object-id-hash", aliases: ["hash"] },
4188
4189
  { name: "gb", required: true },
4189
4190
  { name: "provider", required: true },
4190
4191
  { name: "region", required: true }
@@ -4263,12 +4264,226 @@ var opStatusSchema = {
4263
4264
  ]
4264
4265
  };
4265
4266
 
4267
+ // src/openclaw-cli.ts
4268
+ import { spawn } from "child_process";
4269
+ import { join as join8 } from "path";
4270
+ async function runOpenClawCli(args, homeDir) {
4271
+ return await new Promise((resolvePromise, rejectPromise) => {
4272
+ let stdout = "";
4273
+ let stderr = "";
4274
+ const child = spawn("openclaw", args, {
4275
+ stdio: ["ignore", "pipe", "pipe"],
4276
+ env: {
4277
+ ...process.env,
4278
+ HOME: homeDir ?? process.env.HOME
4279
+ }
4280
+ });
4281
+ child.stdout.on("data", (chunk) => {
4282
+ stdout += chunk.toString();
4283
+ });
4284
+ child.stderr.on("data", (chunk) => {
4285
+ stderr += chunk.toString();
4286
+ });
4287
+ child.on("error", rejectPromise);
4288
+ child.on("close", (code) => {
4289
+ if (code === 0) {
4290
+ resolvePromise({ stdout, stderr });
4291
+ return;
4292
+ }
4293
+ rejectPromise(
4294
+ new Error(
4295
+ stderr.trim() || stdout.trim() || `openclaw ${args.join(" ")} exited with code ${code ?? "unknown"}`
4296
+ )
4297
+ );
4298
+ });
4299
+ });
4300
+ }
4301
+ function parseOpenClawCliJson(stdout, commandLabel) {
4302
+ const trimmed = stdout.trim();
4303
+ if (!trimmed) {
4304
+ throw new Error(`openclaw ${commandLabel} returned empty JSON output`);
4305
+ }
4306
+ try {
4307
+ return JSON.parse(trimmed);
4308
+ } catch {
4309
+ throw new Error(`openclaw ${commandLabel} returned invalid JSON output`);
4310
+ }
4311
+ }
4312
+ async function resolveOpenClawConfigFilePath(homeDir) {
4313
+ const { stdout } = await runOpenClawCli(["config", "file"], homeDir);
4314
+ const trimmed = stdout.trim();
4315
+ if (trimmed.startsWith("~/")) {
4316
+ return join8(homeDir, trimmed.slice(2));
4317
+ }
4318
+ if (trimmed.startsWith("~\\")) {
4319
+ return join8(homeDir, trimmed.slice(2));
4320
+ }
4321
+ return trimmed;
4322
+ }
4323
+
4324
+ // src/openclaw-renewal-runbook.ts
4325
+ import { mkdir as mkdir5, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
4326
+ import { homedir as homedir6 } from "os";
4327
+ import { dirname as dirname5, join as join9 } from "path";
4328
+ import { randomUUID as randomUUID3 } from "crypto";
4329
+ var DEFAULT_RENEWAL_AGENT_ID = "mnemospark-renewal";
4330
+ var RENEWAL_NODE_ALLOWLIST_ID = "node-usr-bin-node";
4331
+ function getRenewalAgentId() {
4332
+ const fromEnv = process.env.MNEMOSPARK_CRON_AGENT_ID?.trim();
4333
+ return fromEnv && fromEnv.length > 0 ? fromEnv : DEFAULT_RENEWAL_AGENT_ID;
4334
+ }
4335
+ function getRenewalNodeBinary() {
4336
+ const fromEnv = process.env.MNEMOSPARK_CRON_NODE_BIN?.trim();
4337
+ return fromEnv && fromEnv.length > 0 ? fromEnv : "/usr/bin/node";
4338
+ }
4339
+ function runbookRenewalAgentEntry(agentId = getRenewalAgentId()) {
4340
+ return {
4341
+ id: agentId,
4342
+ tools: {
4343
+ deny: ["subagents"],
4344
+ exec: { ask: "off" }
4345
+ }
4346
+ };
4347
+ }
4348
+ function isRecord(value) {
4349
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4350
+ }
4351
+ function renewalAgentEntrySatisfied(existing, desired) {
4352
+ if (!isRecord(existing)) {
4353
+ return false;
4354
+ }
4355
+ if (existing.id !== desired.id) {
4356
+ return false;
4357
+ }
4358
+ const tools = existing.tools;
4359
+ if (!isRecord(tools)) {
4360
+ return false;
4361
+ }
4362
+ const deny = tools.deny;
4363
+ if (!Array.isArray(deny) || !deny.includes("subagents")) {
4364
+ return false;
4365
+ }
4366
+ const exec = tools.exec;
4367
+ if (!isRecord(exec) || exec.ask !== "off") {
4368
+ return false;
4369
+ }
4370
+ return true;
4371
+ }
4372
+ function mergeRenewalAgentIntoAgentsList(list, desired) {
4373
+ const arr = Array.isArray(list) ? [...list] : [];
4374
+ const idx = arr.findIndex((e) => isRecord(e) && typeof e.id === "string" && e.id === desired.id);
4375
+ if (idx === -1) {
4376
+ return { list: [...arr, desired], changed: true };
4377
+ }
4378
+ if (renewalAgentEntrySatisfied(arr[idx], desired)) {
4379
+ return { list: arr, changed: false };
4380
+ }
4381
+ const next = [...arr];
4382
+ next[idx] = desired;
4383
+ return { list: next, changed: true };
4384
+ }
4385
+ function mergeExecApprovalsAllowlist(doc, agentId, nodeBinary) {
4386
+ const prevAgents = doc.agents && isRecord(doc.agents) ? doc.agents : {};
4387
+ const block = prevAgents[agentId];
4388
+ const allowlist = Array.isArray(block?.allowlist) ? [...block.allowlist] : [];
4389
+ const hasPattern = allowlist.some((e) => e?.pattern === nodeBinary);
4390
+ if (hasPattern) {
4391
+ return { doc, changed: false };
4392
+ }
4393
+ allowlist.push({
4394
+ id: RENEWAL_NODE_ALLOWLIST_ID,
4395
+ pattern: nodeBinary,
4396
+ source: "manual",
4397
+ lastUsedAt: Date.now()
4398
+ });
4399
+ const nextAgents = {
4400
+ ...prevAgents,
4401
+ [agentId]: {
4402
+ ...block && isRecord(block) ? block : {},
4403
+ allowlist
4404
+ }
4405
+ };
4406
+ return { doc: { ...doc, agents: nextAgents }, changed: true };
4407
+ }
4408
+ async function readJsonFile(path) {
4409
+ const raw = await readFile3(path, "utf-8");
4410
+ return JSON.parse(raw);
4411
+ }
4412
+ async function writeFileAtomic(path, contents) {
4413
+ await mkdir5(dirname5(path), { recursive: true });
4414
+ const tmp = join9(dirname5(path), `.tmp-${randomUUID3()}`);
4415
+ await writeFile3(tmp, contents, "utf-8");
4416
+ await rename2(tmp, path);
4417
+ }
4418
+ async function ensureOpenClawRenewalPrerequisites(options = {}) {
4419
+ if (options.disabled ?? process.env.MNEMOSPARK_DISABLE_OPENCLAW_PREREQ === "1") {
4420
+ return;
4421
+ }
4422
+ const homeDir = options.homeDir ?? homedir6();
4423
+ const agentId = getRenewalAgentId();
4424
+ const desired = runbookRenewalAgentEntry(agentId);
4425
+ const nodeBinary = getRenewalNodeBinary();
4426
+ const configPath = await resolveOpenClawConfigFilePath(homeDir);
4427
+ let configRaw = "{}";
4428
+ try {
4429
+ configRaw = await readFile3(configPath, "utf-8");
4430
+ } catch (err) {
4431
+ if (err.code !== "ENOENT") {
4432
+ throw err;
4433
+ }
4434
+ }
4435
+ let parsed;
4436
+ try {
4437
+ parsed = JSON.parse(configRaw);
4438
+ } catch {
4439
+ throw new Error(
4440
+ `openclaw.json at ${configPath} is not valid JSON; fix or remove it before upload.`
4441
+ );
4442
+ }
4443
+ const agents = isRecord(parsed.agents) ? parsed.agents : {};
4444
+ const { list: mergedList, changed: agentChanged } = mergeRenewalAgentIntoAgentsList(
4445
+ agents.list,
4446
+ desired
4447
+ );
4448
+ if (agentChanged) {
4449
+ const listJson = JSON.stringify(mergedList);
4450
+ await runOpenClawCli(["config", "set", "agents.list", listJson, "--strict-json"], homeDir);
4451
+ await runOpenClawCli(["config", "validate"], homeDir);
4452
+ }
4453
+ const execPath = join9(homeDir, ".openclaw", "exec-approvals.json");
4454
+ let execDoc = {};
4455
+ try {
4456
+ const raw = await readJsonFile(execPath);
4457
+ execDoc = isRecord(raw) ? raw : {};
4458
+ } catch (err) {
4459
+ if (err.code !== "ENOENT") {
4460
+ throw err;
4461
+ }
4462
+ }
4463
+ const { doc: mergedExec, changed: execChanged } = mergeExecApprovalsAllowlist(
4464
+ execDoc,
4465
+ agentId,
4466
+ nodeBinary
4467
+ );
4468
+ if (execChanged) {
4469
+ await writeFileAtomic(execPath, `${JSON.stringify(mergedExec, null, 2)}
4470
+ `);
4471
+ }
4472
+ const skipRestart = options.skipGatewayRestart ?? process.env.MNEMOSPARK_SKIP_GATEWAY_RESTART === "1";
4473
+ if (!skipRestart && (agentChanged || execChanged)) {
4474
+ try {
4475
+ await runOpenClawCli(["gateway", "restart", "--json"], homeDir);
4476
+ } catch {
4477
+ }
4478
+ }
4479
+ }
4480
+
4266
4481
  // src/cloud-command.ts
4267
4482
  var SUPPORTED_BACKUP_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux"]);
4268
- var BACKUP_DIR_SUBPATH = join8(".openclaw", "mnemospark", "backup");
4269
- var DEFAULT_BACKUP_DIR = join8(homedir6(), BACKUP_DIR_SUBPATH);
4270
- var BLOCKRUN_WALLET_KEY_SUBPATH = join8(".openclaw", "blockrun", "wallet.key");
4271
- var MNEMOSPARK_WALLET_KEY_SUBPATH = join8(".openclaw", "mnemospark", "wallet", "wallet.key");
4483
+ var BACKUP_DIR_SUBPATH = join10(".openclaw", "mnemospark", "backup");
4484
+ var DEFAULT_BACKUP_DIR = join10(homedir7(), BACKUP_DIR_SUBPATH);
4485
+ var BLOCKRUN_WALLET_KEY_SUBPATH = join10(".openclaw", "blockrun", "wallet.key");
4486
+ var MNEMOSPARK_WALLET_KEY_SUBPATH = join10(".openclaw", "mnemospark", "wallet", "wallet.key");
4272
4487
  var INLINE_UPLOAD_MAX_BYTES = 45e5;
4273
4488
  var NODE_FS_MAX_READFILE_BYTES = 2147483648;
4274
4489
  var PAYMENT_CRON_SCHEDULE = "0 0 1 * *";
@@ -4276,7 +4491,7 @@ var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
4276
4491
  var QUOTE_VALIDITY_USER_NOTE = "Quotes are valid for one hour. Please run price-storage again if you need a new quote.";
4277
4492
  var MNEMOSPARK_SUPPORT_EMAIL = "pluggedin@mnemospark.ai";
4278
4493
  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.";
4279
- var REQUIRED_PRICE_STORAGE = "wallet-address:, object-id:, object-id-hash:, gb:, provider:, region:";
4494
+ var REQUIRED_PRICE_STORAGE = "wallet-address:, object-id:, gb:, provider:, region: (object-id-hash: optional if the object exists in local SQLite after backup)";
4280
4495
  var REQUIRED_UPLOAD = "quote-id:, wallet-address:, object-id:, object-id-hash:";
4281
4496
  var REQUIRED_BACKUP = "<file|directory> and name:<friendly-name>";
4282
4497
  var REQUIRED_PAYMENT_SETTLE = "wallet-address: and (quote-id: | renewal:true with object-key:)";
@@ -4286,10 +4501,10 @@ var ORCHESTRATOR_MODES = /* @__PURE__ */ new Set(["inline", "subagent"]);
4286
4501
  function expandTilde(path) {
4287
4502
  const trimmed = path.trim();
4288
4503
  if (trimmed === "~") {
4289
- return homedir6();
4504
+ return homedir7();
4290
4505
  }
4291
4506
  if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
4292
- return join8(homedir6(), trimmed.slice(2));
4507
+ return join10(homedir7(), trimmed.slice(2));
4293
4508
  }
4294
4509
  return path;
4295
4510
  }
@@ -4306,10 +4521,10 @@ var CLOUD_HELP_TEXT = [
4306
4521
  " 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.",
4307
4522
  " Required: " + REQUIRED_BACKUP,
4308
4523
  "",
4309
- "\u2022 `/mnemospark cloud price-storage wallet-address:<addr> object-id:<id> object-id-hash:<hash> gb:<gb> provider:aws region:us-east-1`",
4310
- " Purpose: request a storage quote before upload (defaults shown; override `provider:` / `region:` for other regions).",
4524
+ "\u2022 `/mnemospark cloud price-storage wallet-address:<addr> object-id:<id> [object-id-hash:<hash>] gb:<gb> provider:aws region:us-east-1`",
4525
+ " 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.",
4311
4526
  " Required: " + REQUIRED_PRICE_STORAGE,
4312
- " Shorter: `wallet:\u2026 object:\u2026 hash:\u2026 gb:\u2026 provider:\u2026 region:\u2026`",
4527
+ " Shorter: `wallet:\u2026 object:\u2026 [hash:\u2026] gb:\u2026 provider:\u2026 region:\u2026`",
4313
4528
  "",
4314
4529
  "\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>]`",
4315
4530
  " Purpose: upload an encrypted object using a valid quote-id.",
@@ -4520,6 +4735,41 @@ function stripAsyncControlFlags(args) {
4520
4735
  function mergeArgParseWarnings(a, b) {
4521
4736
  return [...a, ...b];
4522
4737
  }
4738
+ async function resolvePriceStorageHashFromDatastore(datastore, partial) {
4739
+ await datastore.ensureReady();
4740
+ const row = await datastore.findObjectById(partial.object_id.trim());
4741
+ const wallet = partial.wallet_address.trim().toLowerCase();
4742
+ if (!row) {
4743
+ return {
4744
+ ok: false,
4745
+ 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."
4746
+ };
4747
+ }
4748
+ if (row.wallet_address.trim().toLowerCase() !== wallet) {
4749
+ return {
4750
+ ok: false,
4751
+ message: "Cannot resolve object-id-hash: wallet-address does not match the object record in ~/.openclaw/mnemospark/state.db."
4752
+ };
4753
+ }
4754
+ const sha = row.sha256?.trim();
4755
+ if (!sha) {
4756
+ return {
4757
+ ok: false,
4758
+ message: "Cannot resolve object-id-hash: local object record has no sha256 yet. Run backup first, or pass --object-id-hash explicitly."
4759
+ };
4760
+ }
4761
+ return {
4762
+ ok: true,
4763
+ request: {
4764
+ wallet_address: partial.wallet_address.trim(),
4765
+ object_id: partial.object_id.trim(),
4766
+ object_id_hash: sha.replace(/\s/g, ""),
4767
+ gb: partial.gb,
4768
+ provider: partial.provider.trim(),
4769
+ region: partial.region.trim()
4770
+ }
4771
+ };
4772
+ }
4523
4773
  function parseCloudArgs(args) {
4524
4774
  const trimmed = args?.trim() ?? "";
4525
4775
  if (!trimmed) {
@@ -4589,18 +4839,38 @@ function parseCloudArgs(args) {
4589
4839
  }
4590
4840
  const flags = valuesToStringRecord(parsed.values);
4591
4841
  const gb = Number.parseFloat(flags.gb ?? "");
4592
- const request = parsePriceStorageQuoteRequest({
4593
- wallet_address: flags["wallet-address"],
4594
- object_id: flags["object-id"],
4595
- object_id_hash: flags["object-id-hash"],
4596
- gb,
4597
- provider: flags.provider,
4598
- region: flags.region
4599
- });
4600
- if (!request) {
4842
+ const hashRaw = flags["object-id-hash"]?.trim();
4843
+ const walletAddress = flags["wallet-address"]?.trim();
4844
+ const objectId = flags["object-id"]?.trim();
4845
+ const provider = flags.provider?.trim();
4846
+ const region = flags.region?.trim();
4847
+ if (!walletAddress || !objectId || !provider || !region || !Number.isFinite(gb)) {
4601
4848
  return { mode: "price-storage-invalid" };
4602
4849
  }
4603
- return { mode: "price-storage", priceStorageRequest: request };
4850
+ if (hashRaw) {
4851
+ const request = parsePriceStorageQuoteRequest({
4852
+ wallet_address: walletAddress,
4853
+ object_id: objectId,
4854
+ object_id_hash: hashRaw,
4855
+ gb,
4856
+ provider,
4857
+ region
4858
+ });
4859
+ if (!request) {
4860
+ return { mode: "price-storage-invalid" };
4861
+ }
4862
+ return { mode: "price-storage", priceStorageRequest: request };
4863
+ }
4864
+ return {
4865
+ mode: "price-storage-resolve-hash",
4866
+ priceStoragePartial: {
4867
+ wallet_address: walletAddress,
4868
+ object_id: objectId,
4869
+ gb,
4870
+ provider,
4871
+ region
4872
+ }
4873
+ };
4604
4874
  }
4605
4875
  if (subcommand === "upload") {
4606
4876
  const parsed = parseCommandArgs(rest, uploadSchema);
@@ -4802,7 +5072,7 @@ async function calculateInputSizeBytes(targetPath) {
4802
5072
  let total = 0;
4803
5073
  const entries = await readdir2(targetPath, { withFileTypes: true });
4804
5074
  for (const entry of entries) {
4805
- total += await calculateInputSizeBytes(join8(targetPath, entry.name));
5075
+ total += await calculateInputSizeBytes(join10(targetPath, entry.name));
4806
5076
  }
4807
5077
  return total;
4808
5078
  }
@@ -4814,11 +5084,11 @@ function getAvailableDiskBytes(tmpDir, options) {
4814
5084
  return stats.bavail * stats.bsize;
4815
5085
  }
4816
5086
  async function runTarGzip(archivePath, sourcePath) {
4817
- const sourceDir = dirname5(sourcePath);
5087
+ const sourceDir = dirname6(sourcePath);
4818
5088
  const sourceName = basename2(sourcePath);
4819
5089
  await new Promise((resolvePromise, rejectPromise) => {
4820
5090
  let stderr = "";
4821
- const child = spawn("tar", ["-czf", archivePath, "-C", sourceDir, sourceName], {
5091
+ const child = spawn2("tar", ["-czf", archivePath, "-C", sourceDir, sourceName], {
4822
5092
  stdio: ["ignore", "ignore", "pipe"]
4823
5093
  });
4824
5094
  child.stderr.on("data", (chunk) => {
@@ -4848,7 +5118,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
4848
5118
  if (friendlyName?.trim()) {
4849
5119
  try {
4850
5120
  const sanitized = sanitizeFriendlyNameForLocalBasename(friendlyName);
4851
- const candidate = join8(backupDir, sanitized);
5121
+ const candidate = join10(backupDir, sanitized);
4852
5122
  try {
4853
5123
  const st = await stat2(candidate);
4854
5124
  if (st.isFile()) {
@@ -4859,7 +5129,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
4859
5129
  } catch {
4860
5130
  }
4861
5131
  }
4862
- const legacyPath = join8(backupDir, objectId);
5132
+ const legacyPath = join10(backupDir, objectId);
4863
5133
  try {
4864
5134
  const legacyStats = await stat2(legacyPath);
4865
5135
  if (!legacyStats.isFile()) {
@@ -4897,7 +5167,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
4897
5167
  tmpStats = await stat2(tmpDir);
4898
5168
  } catch (error) {
4899
5169
  if (error.code === "ENOENT") {
4900
- await mkdir5(tmpDir, { recursive: true });
5170
+ await mkdir6(tmpDir, { recursive: true });
4901
5171
  tmpStats = await stat2(tmpDir);
4902
5172
  } else {
4903
5173
  throw error;
@@ -4914,7 +5184,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
4914
5184
  }
4915
5185
  const objectId = createObjectId(options);
4916
5186
  const archiveBaseSegment = options.archiveBasename?.trim() || objectId;
4917
- const archivePath = join8(tmpDir, archiveBaseSegment);
5187
+ const archivePath = join10(tmpDir, archiveBaseSegment);
4918
5188
  if (options.archiveBasename?.trim()) {
4919
5189
  try {
4920
5190
  const existing = await stat2(archivePath);
@@ -4970,48 +5240,6 @@ function normalizeOpenClawCronJobForLookup(value) {
4970
5240
  message: payloadMessage
4971
5241
  };
4972
5242
  }
4973
- async function runOpenClawCli(args, homeDir) {
4974
- return await new Promise((resolvePromise, rejectPromise) => {
4975
- let stdout = "";
4976
- let stderr = "";
4977
- const child = spawn("openclaw", args, {
4978
- stdio: ["ignore", "pipe", "pipe"],
4979
- env: {
4980
- ...process.env,
4981
- HOME: homeDir ?? process.env.HOME
4982
- }
4983
- });
4984
- child.stdout.on("data", (chunk) => {
4985
- stdout += chunk.toString();
4986
- });
4987
- child.stderr.on("data", (chunk) => {
4988
- stderr += chunk.toString();
4989
- });
4990
- child.on("error", rejectPromise);
4991
- child.on("close", (code) => {
4992
- if (code === 0) {
4993
- resolvePromise({ stdout, stderr });
4994
- return;
4995
- }
4996
- rejectPromise(
4997
- new Error(
4998
- stderr.trim() || stdout.trim() || `openclaw ${args.join(" ")} exited with code ${code ?? "unknown"}`
4999
- )
5000
- );
5001
- });
5002
- });
5003
- }
5004
- function parseOpenClawCliJson(stdout, commandLabel) {
5005
- const trimmed = stdout.trim();
5006
- if (!trimmed) {
5007
- throw new Error(`openclaw ${commandLabel} returned empty JSON output`);
5008
- }
5009
- try {
5010
- return JSON.parse(trimmed);
5011
- } catch {
5012
- throw new Error(`openclaw ${commandLabel} returned invalid JSON output`);
5013
- }
5014
- }
5015
5243
  function createOpenClawCliCronAdapter(homeDir) {
5016
5244
  return {
5017
5245
  add: async (job) => {
@@ -5027,11 +5255,11 @@ function createOpenClawCliCronAdapter(homeDir) {
5027
5255
  job.schedule.tz,
5028
5256
  "--session",
5029
5257
  job.sessionTarget,
5258
+ "--agent",
5259
+ job.agentId,
5030
5260
  "--message",
5031
5261
  job.payload.message,
5032
- "--announce",
5033
- "--description",
5034
- job.delivery.text,
5262
+ "--no-deliver",
5035
5263
  "--json"
5036
5264
  ],
5037
5265
  homeDir
@@ -5096,8 +5324,9 @@ function buildStoragePaymentCronCommand(job) {
5096
5324
  return `/mnemospark cloud ${buildStoragePaymentRenewalArgs(job)}`;
5097
5325
  }
5098
5326
  function buildOpenClawRenewalAgentMessage(openClawHome, renewalArgs) {
5099
- const cliPath = join8(openClawHome, ".openclaw/extensions/mnemospark/dist/cli.js");
5100
- return `Execute: node ${cliPath} cloud ${renewalArgs}`;
5327
+ const cliPath = join10(openClawHome, ".openclaw/extensions/mnemospark/dist/cli.js");
5328
+ const nodeBin = getRenewalNodeBinary();
5329
+ return `Command: ${nodeBin} ${cliPath} cloud ${renewalArgs}`;
5101
5330
  }
5102
5331
  function parseStoragePaymentCronCommand(command) {
5103
5332
  const objectIdMatch = command.match(/--object-id\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
@@ -5143,10 +5372,7 @@ async function appendStoragePaymentCronJob(cronJob, adapter, payloadMessage) {
5143
5372
  message: payloadMessage
5144
5373
  },
5145
5374
  sessionTarget: "isolated",
5146
- delivery: {
5147
- mode: "announce",
5148
- text: "Thank you for using mnemospark cloud storage. Your renewal has been processed."
5149
- }
5375
+ agentId: getRenewalAgentId()
5150
5376
  };
5151
5377
  return adapter.add(openClawJob);
5152
5378
  }
@@ -5154,6 +5380,7 @@ async function removeStoragePaymentCronJob(cronId, adapter) {
5154
5380
  return adapter.remove(cronId);
5155
5381
  }
5156
5382
  async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAdapter, openClawHomeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
5383
+ await ensureOpenClawRenewalPrerequisites({ homeDir: openClawHomeDir });
5157
5384
  const renewalFields = {
5158
5385
  walletAddress: upload.addr,
5159
5386
  objectId: upload.object_id,
@@ -5161,7 +5388,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
5161
5388
  storagePrice
5162
5389
  };
5163
5390
  const renewalArgs = buildStoragePaymentRenewalArgs(renewalFields);
5164
- const provisionalCronId = randomUUID3();
5391
+ const provisionalCronId = randomUUID4();
5165
5392
  const cronJob = {
5166
5393
  cronId: provisionalCronId,
5167
5394
  createdAt: nowDateFn().toISOString(),
@@ -5185,7 +5412,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
5185
5412
  }
5186
5413
  async function readWalletKeyIfPresent(walletPath) {
5187
5414
  try {
5188
- const key = (await readFile3(walletPath, "utf-8")).trim();
5415
+ const key = (await readFile4(walletPath, "utf-8")).trim();
5189
5416
  return isValidWalletPrivateKey(key) ? key : null;
5190
5417
  } catch (error) {
5191
5418
  if (error.code === "ENOENT") {
@@ -5199,9 +5426,9 @@ async function resolveWalletPrivateKey(homeDir) {
5199
5426
  if (isValidWalletPrivateKey(envKey)) {
5200
5427
  return envKey;
5201
5428
  }
5202
- const baseHome = homeDir ?? homedir6();
5203
- const primaryWalletPath = join8(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
5204
- const fallbackWalletPath = join8(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
5429
+ const baseHome = homeDir ?? homedir7();
5430
+ const primaryWalletPath = join10(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
5431
+ const fallbackWalletPath = join10(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
5205
5432
  const fromPrimary = await readWalletKeyIfPresent(primaryWalletPath);
5206
5433
  if (fromPrimary) {
5207
5434
  return fromPrimary;
@@ -5265,9 +5492,9 @@ async function encryptPlaintextFileToAesGcmPath(plaintextPath, dek, outPath, ran
5265
5492
  }
5266
5493
  async function loadOrCreateKek(walletAddress, homeDir) {
5267
5494
  const keyPath = resolveWalletKekPath(walletAddress, homeDir);
5268
- await mkdir5(dirname5(keyPath), { recursive: true });
5495
+ await mkdir6(dirname6(keyPath), { recursive: true });
5269
5496
  try {
5270
- const existing = await readFile3(keyPath);
5497
+ const existing = await readFile4(keyPath);
5271
5498
  return { kek: parseStoredAes256Key(existing), keyPath };
5272
5499
  } catch (error) {
5273
5500
  if (error.code !== "ENOENT") {
@@ -5275,7 +5502,7 @@ async function loadOrCreateKek(walletAddress, homeDir) {
5275
5502
  }
5276
5503
  }
5277
5504
  const generated = randomBytesNode(32);
5278
- await writeFile3(keyPath, generated, { mode: 384 });
5505
+ await writeFile4(keyPath, generated, { mode: 384 });
5279
5506
  return { kek: generated, keyPath };
5280
5507
  }
5281
5508
  async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
@@ -5287,7 +5514,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
5287
5514
  const dek = randomBytesNode(32);
5288
5515
  const wrappedDek = encryptAesGcm(dek, kek);
5289
5516
  if (archiveStat.size >= NODE_FS_MAX_READFILE_BYTES) {
5290
- const encryptedTempPath = join8(tmpdir(), `mnemospark-upload-${randomUUID3()}.enc`);
5517
+ const encryptedTempPath = join10(tmpdir(), `mnemospark-upload-${randomUUID4()}.enc`);
5291
5518
  try {
5292
5519
  await encryptPlaintextFileToAesGcmPath(archivePath, dek, encryptedTempPath);
5293
5520
  const encStat = await stat2(encryptedTempPath);
@@ -5313,7 +5540,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
5313
5540
  throw err;
5314
5541
  }
5315
5542
  }
5316
- const plaintext = await readFile3(archivePath);
5543
+ const plaintext = await readFile4(archivePath);
5317
5544
  const encryptedContent = encryptAesGcm(plaintext, dek);
5318
5545
  const payloadHash = sha256Buffer(encryptedContent);
5319
5546
  const payload = {
@@ -5558,7 +5785,7 @@ function createInProcessSubagentOrchestrator() {
5558
5785
  };
5559
5786
  return {
5560
5787
  dispatch: async (input) => {
5561
- const sessionId = `agent:mnemospark:subagent:${randomUUID3()}`;
5788
+ const sessionId = `agent:mnemospark:subagent:${randomUUID4()}`;
5562
5789
  const state = {
5563
5790
  terminal: false,
5564
5791
  cancelRequested: false,
@@ -5660,7 +5887,7 @@ function createCloudCommand(options = {}) {
5660
5887
  createPaymentFetchFn: options.createPaymentFetchFn ?? createPaymentFetch,
5661
5888
  fetchImpl: options.fetchImpl ?? fetch,
5662
5889
  nowDateFn: options.nowDateFn ?? (() => /* @__PURE__ */ new Date()),
5663
- idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID3,
5890
+ idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID4,
5664
5891
  requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
5665
5892
  requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
5666
5893
  requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
@@ -5804,8 +6031,8 @@ async function emitOperationEventBestEffort(eventType, context, homeDir) {
5804
6031
  }
5805
6032
  }
5806
6033
  function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
5807
- const operationId = forcedOperationId?.trim() || randomUUID3();
5808
- const traceId = forcedTraceId?.trim() || randomUUID3();
6034
+ const operationId = forcedOperationId?.trim() || randomUUID4();
6035
+ const traceId = forcedTraceId?.trim() || randomUUID4();
5809
6036
  return { operationId, traceId };
5810
6037
  }
5811
6038
  function parseTransIdFromPaymentSettleBody(bodyText) {
@@ -6093,7 +6320,7 @@ ${operation.result_text}` : meta;
6093
6320
  };
6094
6321
  }
6095
6322
  if (!isTerminalOperationStatus(operation.status)) {
6096
- const traceId = operation.trace_id ?? randomUUID3();
6323
+ const traceId = operation.trace_id ?? randomUUID4();
6097
6324
  const cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
6098
6325
  await datastore.upsertOperation({
6099
6326
  operation_id: operation.operation_id,
@@ -6691,7 +6918,7 @@ operation-id: ${operationId}`,
6691
6918
  await emitCloudEventBestEffort(
6692
6919
  "backup.completed",
6693
6920
  {
6694
- operation_id: executionContext.forcedOperationId?.trim() || randomUUID3(),
6921
+ operation_id: executionContext.forcedOperationId?.trim() || randomUUID4(),
6695
6922
  object_id: result.objectId,
6696
6923
  status: "succeeded",
6697
6924
  details: {
@@ -6737,10 +6964,23 @@ operation-id: ${operationId}`,
6737
6964
  };
6738
6965
  }
6739
6966
  }
6740
- if (parsed.mode === "price-storage") {
6967
+ if (parsed.mode === "price-storage" || parsed.mode === "price-storage-resolve-hash") {
6968
+ let priceStorageRequest;
6969
+ if (parsed.mode === "price-storage-resolve-hash") {
6970
+ const resolved = await resolvePriceStorageHashFromDatastore(
6971
+ datastore,
6972
+ parsed.priceStoragePartial
6973
+ );
6974
+ if (!resolved.ok) {
6975
+ return { text: resolved.message, isError: true };
6976
+ }
6977
+ priceStorageRequest = resolved.request;
6978
+ } else {
6979
+ priceStorageRequest = parsed.priceStorageRequest;
6980
+ }
6741
6981
  const correlation = buildRequestCorrelation();
6742
6982
  try {
6743
- const quote = await requestPriceStorageQuote(parsed.priceStorageRequest, {
6983
+ const quote = await requestPriceStorageQuote(priceStorageRequest, {
6744
6984
  ...options.proxyQuoteOptions,
6745
6985
  correlation
6746
6986
  });
@@ -6798,8 +7038,8 @@ operation-id: ${operationId}`,
6798
7038
  {
6799
7039
  operation_id: correlation.operationId,
6800
7040
  trace_id: correlation.traceId,
6801
- wallet_address: parsed.priceStorageRequest.wallet_address,
6802
- object_id: parsed.priceStorageRequest.object_id,
7041
+ wallet_address: priceStorageRequest.wallet_address,
7042
+ object_id: priceStorageRequest.object_id,
6803
7043
  status: "failed"
6804
7044
  },
6805
7045
  mnemosparkHomeDir
@@ -6965,7 +7205,7 @@ operation-id: ${operationId}`,
6965
7205
  finalizedUploadResponse,
6966
7206
  cronStoragePrice,
6967
7207
  openClawCronAdapter,
6968
- mnemosparkHomeDir ?? homedir6(),
7208
+ mnemosparkHomeDir ?? homedir7(),
6969
7209
  nowDateFn
6970
7210
  );
6971
7211
  await datastore.upsertObject({
@@ -7759,8 +7999,11 @@ export {
7759
7999
  createCloudCommand,
7760
8000
  createPaymentFetch,
7761
8001
  index_default as default,
8002
+ ensureOpenClawRenewalPrerequisites,
7762
8003
  fetchWithRetry,
7763
8004
  getProxyPort,
8005
+ getRenewalAgentId,
8006
+ getRenewalNodeBinary,
7764
8007
  isBalanceError,
7765
8008
  isEmptyWalletError,
7766
8009
  isInsufficientFundsError,