mnemospark 0.8.0 → 0.8.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/dist/cli.js CHANGED
@@ -2974,7 +2974,7 @@ import {
2974
2974
  randomUUID as randomUUID3
2975
2975
  } from "crypto";
2976
2976
  import { createReadStream as createReadStream2, statfsSync } from "fs";
2977
- import { appendFile as appendFile2, lstat, mkdir as mkdir5, readFile as readFile3, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
2977
+ import { lstat, mkdir as mkdir5, readFile as readFile3, readdir as readdir2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
2978
2978
  import { homedir as homedir6 } from "os";
2979
2979
  import { basename as basename2, dirname as dirname5, join as join8, resolve as resolve2 } from "path";
2980
2980
  import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
@@ -3760,7 +3760,6 @@ async function createCloudDatastore(homeDir) {
3760
3760
  var SUPPORTED_BACKUP_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux"]);
3761
3761
  var BACKUP_DIR_SUBPATH = join8(".openclaw", "mnemospark", "backup");
3762
3762
  var DEFAULT_BACKUP_DIR = join8(homedir6(), BACKUP_DIR_SUBPATH);
3763
- var CRON_TABLE_SUBPATH = join8(".openclaw", "mnemospark", "crontab.txt");
3764
3763
  var BLOCKRUN_WALLET_KEY_SUBPATH = join8(".openclaw", "blockrun", "wallet.key");
3765
3764
  var MNEMOSPARK_WALLET_KEY_SUBPATH = join8(".openclaw", "mnemospark", "wallet", "wallet.key");
3766
3765
  var INLINE_UPLOAD_MAX_BYTES = 45e5;
@@ -3768,7 +3767,7 @@ var PAYMENT_CRON_SCHEDULE = "0 0 1 * *";
3768
3767
  var TAR_OVERHEAD_BYTES = 10 * 1024 * 1024;
3769
3768
  var QUOTE_VALIDITY_USER_NOTE = "Quotes are valid for one hour. Please run price-storage again if you need a new quote.";
3770
3769
  var MNEMOSPARK_SUPPORT_EMAIL = "pluggedin@mnemospark.ai";
3771
- 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/mnemospark/crontab.txt for your system scheduler.";
3770
+ 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.";
3772
3771
  var REQUIRED_PRICE_STORAGE = "--wallet-address, --object-id, --object-id-hash, --gb, --provider, --region";
3773
3772
  var REQUIRED_UPLOAD = "--quote-id, --wallet-address, --object-id, --object-id-hash";
3774
3773
  var REQUIRED_BACKUP = "<file|directory> and --name <friendly-name>";
@@ -4248,9 +4247,6 @@ function parseCloudArgs(args) {
4248
4247
  }
4249
4248
  return { mode: "unknown" };
4250
4249
  }
4251
- function resolveCronTablePath(homeDir) {
4252
- return join8(homeDir ?? homedir6(), CRON_TABLE_SUBPATH);
4253
- }
4254
4250
  async function calculateInputSizeBytes(targetPath) {
4255
4251
  const targetStats = await lstat(targetPath);
4256
4252
  if (targetStats.isFile() || targetStats.isSymbolicLink()) {
@@ -4408,84 +4404,126 @@ async function buildBackupObject(targetPathArg, options = {}) {
4408
4404
  throw error;
4409
4405
  }
4410
4406
  }
4411
- function formatTimestamp(date) {
4412
- const pad = (value) => value.toString().padStart(2, "0");
4413
- return [
4414
- date.getFullYear().toString(),
4415
- "-",
4416
- pad(date.getMonth() + 1),
4417
- "-",
4418
- pad(date.getDate()),
4419
- " ",
4420
- pad(date.getHours()),
4421
- ":",
4422
- pad(date.getMinutes()),
4423
- ":",
4424
- pad(date.getSeconds())
4425
- ].join("");
4426
- }
4427
- function parseStoragePaymentCronJobLine(line) {
4428
- const trimmed = line.trim();
4429
- if (!trimmed) {
4407
+ function normalizeOpenClawCronJobForLookup(value) {
4408
+ if (!value || typeof value !== "object") {
4430
4409
  return null;
4431
4410
  }
4432
- let payload;
4433
- try {
4434
- payload = JSON.parse(trimmed);
4435
- } catch {
4411
+ const record = value;
4412
+ const jobIdRaw = typeof record.jobId === "string" ? record.jobId : record.id;
4413
+ const jobId = typeof jobIdRaw === "string" ? jobIdRaw.trim() : "";
4414
+ const payloadRaw = record.payload;
4415
+ if (!jobId || !payloadRaw || typeof payloadRaw !== "object") {
4436
4416
  return null;
4437
4417
  }
4438
- if (!payload || typeof payload !== "object") {
4439
- return null;
4440
- }
4441
- const record = payload;
4442
- const cronId = typeof record.cronId === "string" ? record.cronId.trim() : "";
4443
- const createdAt = typeof record.createdAt === "string" ? record.createdAt.trim() : "";
4444
- const schedule = typeof record.schedule === "string" ? record.schedule.trim() : "";
4445
- const command = typeof record.command === "string" ? record.command.trim() : "";
4446
- const quoteId = typeof record.quoteId === "string" ? record.quoteId.trim() : "";
4447
- const storagePrice = typeof record.storagePrice === "number" ? record.storagePrice : Number.NaN;
4448
- const walletAddress = typeof record.walletAddress === "string" ? record.walletAddress.trim() : "";
4449
- const objectId = typeof record.objectId === "string" ? record.objectId.trim() : "";
4450
- const objectKey = typeof record.objectKey === "string" ? record.objectKey.trim() : "";
4451
- const provider = typeof record.provider === "string" ? record.provider.trim() : "";
4452
- const bucketName = typeof record.bucketName === "string" ? record.bucketName.trim() : "";
4453
- const location = typeof record.location === "string" ? record.location.trim() : "";
4454
- if (!cronId || !createdAt || !schedule || !command || !quoteId || !Number.isFinite(storagePrice) || storagePrice <= 0 || !walletAddress || !objectId || !objectKey || !provider || !bucketName || !location) {
4418
+ const payloadRecord = payloadRaw;
4419
+ const payloadKind = payloadRecord.kind;
4420
+ const payloadMessage = typeof payloadRecord.message === "string" ? payloadRecord.message.trim() : "";
4421
+ if (payloadKind !== "agentTurn" || !payloadMessage) {
4455
4422
  return null;
4456
4423
  }
4457
4424
  return {
4458
- cronId,
4459
- createdAt,
4460
- schedule,
4461
- command,
4462
- quoteId,
4463
- storagePrice,
4464
- walletAddress,
4465
- objectId,
4466
- objectKey,
4467
- provider,
4468
- bucketName,
4469
- location
4425
+ jobId,
4426
+ message: payloadMessage
4470
4427
  };
4471
4428
  }
4472
- async function findCronJobInCrontabByObjectKey(objectKey, homeDir) {
4473
- const cronTablePath = resolveCronTablePath(homeDir);
4474
- let content;
4429
+ async function runOpenClawCli(args, homeDir) {
4430
+ return await new Promise((resolvePromise, rejectPromise) => {
4431
+ let stdout = "";
4432
+ let stderr = "";
4433
+ const child = spawn("openclaw", args, {
4434
+ stdio: ["ignore", "pipe", "pipe"],
4435
+ env: {
4436
+ ...process.env,
4437
+ HOME: homeDir ?? process.env.HOME
4438
+ }
4439
+ });
4440
+ child.stdout.on("data", (chunk) => {
4441
+ stdout += chunk.toString();
4442
+ });
4443
+ child.stderr.on("data", (chunk) => {
4444
+ stderr += chunk.toString();
4445
+ });
4446
+ child.on("error", rejectPromise);
4447
+ child.on("close", (code) => {
4448
+ if (code === 0) {
4449
+ resolvePromise({ stdout, stderr });
4450
+ return;
4451
+ }
4452
+ rejectPromise(
4453
+ new Error(
4454
+ stderr.trim() || stdout.trim() || `openclaw ${args.join(" ")} exited with code ${code ?? "unknown"}`
4455
+ )
4456
+ );
4457
+ });
4458
+ });
4459
+ }
4460
+ function parseOpenClawCliJson(stdout, commandLabel) {
4461
+ const trimmed = stdout.trim();
4462
+ if (!trimmed) {
4463
+ throw new Error(`openclaw ${commandLabel} returned empty JSON output`);
4464
+ }
4475
4465
  try {
4476
- content = await readFile3(cronTablePath, "utf-8");
4477
- } catch (error) {
4478
- if (error.code === "ENOENT") {
4479
- return null;
4480
- }
4481
- throw error;
4466
+ return JSON.parse(trimmed);
4467
+ } catch {
4468
+ throw new Error(`openclaw ${commandLabel} returned invalid JSON output`);
4482
4469
  }
4483
- const lines = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
4484
- for (let idx = lines.length - 1; idx >= 0; idx -= 1) {
4485
- const parsed = parseStoragePaymentCronJobLine(lines[idx]);
4470
+ }
4471
+ function createOpenClawCliCronAdapter(homeDir) {
4472
+ return {
4473
+ add: async (job) => {
4474
+ const { stdout } = await runOpenClawCli(
4475
+ [
4476
+ "cron",
4477
+ "add",
4478
+ "--name",
4479
+ job.name,
4480
+ "--cron",
4481
+ job.schedule.expr,
4482
+ "--tz",
4483
+ job.schedule.tz,
4484
+ "--session",
4485
+ job.sessionTarget,
4486
+ "--message",
4487
+ job.payload.message,
4488
+ "--announce",
4489
+ "--description",
4490
+ job.delivery.text,
4491
+ "--json"
4492
+ ],
4493
+ homeDir
4494
+ );
4495
+ const payload = parseOpenClawCliJson(stdout, "cron add");
4496
+ const createdIdRaw = typeof payload.id === "string" ? payload.id : payload.jobId;
4497
+ const createdId = typeof createdIdRaw === "string" ? createdIdRaw.trim() : "";
4498
+ if (!createdId) {
4499
+ throw new Error("openclaw cron add did not return a job id");
4500
+ }
4501
+ return { jobId: createdId };
4502
+ },
4503
+ remove: async (jobId) => {
4504
+ const { stdout } = await runOpenClawCli(["cron", "rm", jobId, "--json"], homeDir);
4505
+ const payload = parseOpenClawCliJson(stdout, "cron rm");
4506
+ if (typeof payload.removed === "boolean") {
4507
+ return payload.removed;
4508
+ }
4509
+ return true;
4510
+ },
4511
+ list: async () => {
4512
+ const { stdout } = await runOpenClawCli(["cron", "list", "--all", "--json"], homeDir);
4513
+ const payload = parseOpenClawCliJson(stdout, "cron list");
4514
+ const jobsRaw = Array.isArray(payload.jobs) ? payload.jobs : [];
4515
+ return jobsRaw.map((entry) => normalizeOpenClawCronJobForLookup(entry)).filter((entry) => entry !== null);
4516
+ }
4517
+ };
4518
+ }
4519
+ async function findCronJobInOpenClawCronJobsByObjectKey(objectKey, adapter) {
4520
+ const cronJobs = await adapter.list();
4521
+ for (let idx = cronJobs.length - 1; idx >= 0; idx -= 1) {
4522
+ const cronJob = cronJobs[idx];
4523
+ const parsed = parseStoragePaymentCronCommand(cronJob.message);
4486
4524
  if (parsed && parsed.objectKey === objectKey) {
4487
4525
  return {
4488
- cronId: parsed.cronId,
4526
+ cronId: cronJob.jobId,
4489
4527
  objectId: parsed.objectId,
4490
4528
  objectKey: parsed.objectKey
4491
4529
  };
@@ -4496,9 +4534,8 @@ async function findCronJobInCrontabByObjectKey(objectKey, homeDir) {
4496
4534
  function quoteCronArgument(value) {
4497
4535
  return JSON.stringify(String(value));
4498
4536
  }
4499
- function buildStoragePaymentCronCommand(job) {
4537
+ function buildStoragePaymentRenewalArgs(job) {
4500
4538
  return [
4501
- "/mnemospark_cloud",
4502
4539
  "payment-settle",
4503
4540
  "--renewal",
4504
4541
  "--wallet-address",
@@ -4511,61 +4548,81 @@ function buildStoragePaymentCronCommand(job) {
4511
4548
  quoteCronArgument(job.storagePrice)
4512
4549
  ].join(" ");
4513
4550
  }
4514
- async function appendStoragePaymentCronJob(cronJob, homeDir) {
4515
- const cronTablePath = resolveCronTablePath(homeDir);
4516
- await mkdir5(dirname5(cronTablePath), { recursive: true });
4517
- await appendFile2(cronTablePath, `${JSON.stringify(cronJob)}
4518
- `, "utf-8");
4519
- return cronTablePath;
4551
+ function buildStoragePaymentCronCommand(job) {
4552
+ return `/mnemospark_cloud ${buildStoragePaymentRenewalArgs(job)}`;
4520
4553
  }
4521
- async function removeStoragePaymentCronJob(cronId, homeDir) {
4522
- const cronTablePath = resolveCronTablePath(homeDir);
4523
- let content;
4524
- try {
4525
- content = await readFile3(cronTablePath, "utf-8");
4526
- } catch (error) {
4527
- if (error.code === "ENOENT") {
4528
- return false;
4529
- }
4530
- throw error;
4554
+ function buildOpenClawRenewalAgentMessage(openClawHome, renewalArgs) {
4555
+ const cliPath = join8(openClawHome, ".openclaw/extensions/mnemospark/dist/cli.js");
4556
+ return `Run Mnemospark monthly renewal: node ${cliPath} cloud ${renewalArgs}`;
4557
+ }
4558
+ function parseStoragePaymentCronCommand(command) {
4559
+ const objectIdMatch = command.match(/--object-id\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
4560
+ const objectKeyMatch = command.match(/--object-key\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
4561
+ if (!objectIdMatch || !objectKeyMatch) {
4562
+ return null;
4531
4563
  }
4532
- const lines = content.split(/\r?\n/);
4533
- let removed = false;
4534
- const keptLines = [];
4535
- for (const line of lines) {
4536
- const trimmed = line.trim();
4564
+ const parseToken = (token) => {
4565
+ const trimmed = token.trim();
4537
4566
  if (!trimmed) {
4538
- continue;
4567
+ return null;
4539
4568
  }
4540
- const parsed = parseStoragePaymentCronJobLine(trimmed);
4541
- if (parsed && parsed.cronId === cronId) {
4542
- removed = true;
4543
- continue;
4569
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
4570
+ try {
4571
+ return JSON.parse(trimmed);
4572
+ } catch {
4573
+ return trimmed.slice(1, -1);
4574
+ }
4544
4575
  }
4545
- keptLines.push(trimmed);
4546
- }
4547
- if (!removed) {
4548
- return false;
4576
+ if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
4577
+ return trimmed.slice(1, -1);
4578
+ }
4579
+ return trimmed;
4580
+ };
4581
+ const objectId = parseToken(objectIdMatch[1] ?? "");
4582
+ const objectKey = parseToken(objectKeyMatch[1] ?? "");
4583
+ if (!objectId || !objectKey) {
4584
+ return null;
4549
4585
  }
4550
- await mkdir5(dirname5(cronTablePath), { recursive: true });
4551
- const nextContent = keptLines.length > 0 ? `${keptLines.join("\n")}
4552
- ` : "";
4553
- await writeFile3(cronTablePath, nextContent, "utf-8");
4554
- return true;
4586
+ return { objectId, objectKey };
4555
4587
  }
4556
- async function createStoragePaymentCronJob(upload, storagePrice, homeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
4557
- const cronId = randomUUID3();
4558
- const createdAt = formatTimestamp(nowDateFn());
4588
+ async function appendStoragePaymentCronJob(cronJob, adapter, payloadMessage) {
4589
+ const openClawJob = {
4590
+ jobId: cronJob.cronId,
4591
+ name: "Mnemospark Monthly Renewal",
4592
+ schedule: {
4593
+ kind: "cron",
4594
+ expr: PAYMENT_CRON_SCHEDULE,
4595
+ tz: "UTC"
4596
+ },
4597
+ payload: {
4598
+ kind: "agentTurn",
4599
+ message: payloadMessage
4600
+ },
4601
+ sessionTarget: "isolated",
4602
+ delivery: {
4603
+ mode: "announce",
4604
+ text: "Thank you for using mnemospark cloud storage. Your renewal has been processed."
4605
+ }
4606
+ };
4607
+ return adapter.add(openClawJob);
4608
+ }
4609
+ async function removeStoragePaymentCronJob(cronId, adapter) {
4610
+ return adapter.remove(cronId);
4611
+ }
4612
+ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAdapter, openClawHomeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
4613
+ const renewalFields = {
4614
+ walletAddress: upload.addr,
4615
+ objectId: upload.object_id,
4616
+ objectKey: upload.object_key,
4617
+ storagePrice
4618
+ };
4619
+ const renewalArgs = buildStoragePaymentRenewalArgs(renewalFields);
4620
+ const provisionalCronId = randomUUID3();
4559
4621
  const cronJob = {
4560
- cronId,
4561
- createdAt,
4622
+ cronId: provisionalCronId,
4623
+ createdAt: nowDateFn().toISOString(),
4562
4624
  schedule: PAYMENT_CRON_SCHEDULE,
4563
- command: buildStoragePaymentCronCommand({
4564
- walletAddress: upload.addr,
4565
- objectId: upload.object_id,
4566
- objectKey: upload.object_key,
4567
- storagePrice
4568
- }),
4625
+ command: buildStoragePaymentCronCommand(renewalFields),
4569
4626
  quoteId: upload.quote_id,
4570
4627
  storagePrice,
4571
4628
  walletAddress: upload.addr,
@@ -4575,7 +4632,11 @@ async function createStoragePaymentCronJob(upload, storagePrice, homeDir, nowDat
4575
4632
  bucketName: upload.bucket_name,
4576
4633
  location: upload.location
4577
4634
  };
4578
- await appendStoragePaymentCronJob(cronJob, homeDir);
4635
+ const payloadMessage = buildOpenClawRenewalAgentMessage(openClawHomeDir, renewalArgs);
4636
+ const created = await appendStoragePaymentCronJob(cronJob, openClawCronAdapter, payloadMessage);
4637
+ if (created.jobId?.trim()) {
4638
+ cronJob.cronId = created.jobId.trim();
4639
+ }
4579
4640
  return cronJob;
4580
4641
  }
4581
4642
  async function readWalletKeyIfPresent(walletPath) {
@@ -4965,6 +5026,9 @@ function createCloudCommand(options = {}) {
4965
5026
  requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
4966
5027
  requestPaymentSettleViaProxyFn: options.requestPaymentSettleViaProxyFn ?? requestPaymentSettleViaProxy,
4967
5028
  mnemosparkHomeDir: options.mnemosparkHomeDir ?? options.backupOptions?.homeDir,
5029
+ openClawCronAdapter: options.openClawCronAdapter ?? createOpenClawCliCronAdapter(
5030
+ options.mnemosparkHomeDir ?? options.backupOptions?.homeDir
5031
+ ),
4968
5032
  backupOptions: options.backupOptions,
4969
5033
  proxyQuoteOptions: options.proxyQuoteOptions,
4970
5034
  proxyUploadOptions: options.proxyUploadOptions,
@@ -5260,6 +5324,7 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
5260
5324
  const requestStorageDownload = options.requestStorageDownloadFn;
5261
5325
  const requestStorageDelete = options.requestStorageDeleteFn;
5262
5326
  const requestPaymentSettleViaProxy2 = options.requestPaymentSettleViaProxyFn;
5327
+ const openClawCronAdapter = options.openClawCronAdapter;
5263
5328
  const subagentOrchestrator = options.subagentOrchestrator;
5264
5329
  if (parsed.mode === "help" || parsed.mode === "unknown") {
5265
5330
  return {
@@ -6232,7 +6297,8 @@ operation-id: ${operationId}`,
6232
6297
  const cronJob = await createStoragePaymentCronJob(
6233
6298
  finalizedUploadResponse,
6234
6299
  cronStoragePrice,
6235
- mnemosparkHomeDir,
6300
+ openClawCronAdapter,
6301
+ mnemosparkHomeDir ?? homedir6(),
6236
6302
  nowDateFn
6237
6303
  );
6238
6304
  await datastore.upsertObject({
@@ -6596,15 +6662,15 @@ operation-id: ${operationId}`,
6596
6662
  };
6597
6663
  }
6598
6664
  if (!cronEntry) {
6599
- cronEntry = await findCronJobInCrontabByObjectKey(
6665
+ cronEntry = await findCronJobInOpenClawCronJobsByObjectKey(
6600
6666
  resolvedRequest.object_key,
6601
- mnemosparkHomeDir
6667
+ openClawCronAdapter
6602
6668
  );
6603
6669
  }
6604
6670
  if (cronEntry) {
6605
6671
  const fileCronDeleted = await removeStoragePaymentCronJob(
6606
6672
  cronEntry.cronId,
6607
- mnemosparkHomeDir
6673
+ openClawCronAdapter
6608
6674
  );
6609
6675
  const dbCronDeleted = await datastore.removeCronJob(cronEntry.cronId);
6610
6676
  cronDeleted = fileCronDeleted || dbCronDeleted;