mnemospark 0.8.0 → 0.8.1

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) {
4430
- return null;
4431
- }
4432
- let payload;
4433
- try {
4434
- payload = JSON.parse(trimmed);
4435
- } catch {
4407
+ function normalizeOpenClawCronJobForLookup(value) {
4408
+ if (!value || typeof value !== "object") {
4436
4409
  return null;
4437
4410
  }
4438
- if (!payload || typeof payload !== "object") {
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") {
4439
4416
  return null;
4440
4417
  }
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
  };
@@ -4511,54 +4549,65 @@ function buildStoragePaymentCronCommand(job) {
4511
4549
  quoteCronArgument(job.storagePrice)
4512
4550
  ].join(" ");
4513
4551
  }
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;
4520
- }
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;
4552
+ function parseStoragePaymentCronCommand(command) {
4553
+ const objectIdMatch = command.match(/--object-id\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
4554
+ const objectKeyMatch = command.match(/--object-key\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
4555
+ if (!objectIdMatch || !objectKeyMatch) {
4556
+ return null;
4531
4557
  }
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();
4558
+ const parseToken = (token) => {
4559
+ const trimmed = token.trim();
4537
4560
  if (!trimmed) {
4538
- continue;
4561
+ return null;
4539
4562
  }
4540
- const parsed = parseStoragePaymentCronJobLine(trimmed);
4541
- if (parsed && parsed.cronId === cronId) {
4542
- removed = true;
4543
- continue;
4563
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
4564
+ try {
4565
+ return JSON.parse(trimmed);
4566
+ } catch {
4567
+ return trimmed.slice(1, -1);
4568
+ }
4544
4569
  }
4545
- keptLines.push(trimmed);
4546
- }
4547
- if (!removed) {
4548
- return false;
4570
+ if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
4571
+ return trimmed.slice(1, -1);
4572
+ }
4573
+ return trimmed;
4574
+ };
4575
+ const objectId = parseToken(objectIdMatch[1] ?? "");
4576
+ const objectKey = parseToken(objectKeyMatch[1] ?? "");
4577
+ if (!objectId || !objectKey) {
4578
+ return null;
4549
4579
  }
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;
4580
+ return { objectId, objectKey };
4555
4581
  }
4556
- async function createStoragePaymentCronJob(upload, storagePrice, homeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
4557
- const cronId = randomUUID3();
4558
- const createdAt = formatTimestamp(nowDateFn());
4582
+ async function appendStoragePaymentCronJob(cronJob, adapter) {
4583
+ const openClawJob = {
4584
+ jobId: cronJob.cronId,
4585
+ name: "Mnemospark Monthly Renewal",
4586
+ schedule: {
4587
+ kind: "cron",
4588
+ expr: PAYMENT_CRON_SCHEDULE,
4589
+ tz: "UTC"
4590
+ },
4591
+ payload: {
4592
+ kind: "agentTurn",
4593
+ message: cronJob.command
4594
+ },
4595
+ sessionTarget: "isolated",
4596
+ delivery: {
4597
+ mode: "announce",
4598
+ text: "Thank you for using mnemospark cloud storage. Your renewal has been processed."
4599
+ }
4600
+ };
4601
+ return adapter.add(openClawJob);
4602
+ }
4603
+ async function removeStoragePaymentCronJob(cronId, adapter) {
4604
+ return adapter.remove(cronId);
4605
+ }
4606
+ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAdapter, nowDateFn = () => /* @__PURE__ */ new Date()) {
4607
+ const provisionalCronId = randomUUID3();
4559
4608
  const cronJob = {
4560
- cronId,
4561
- createdAt,
4609
+ cronId: provisionalCronId,
4610
+ createdAt: nowDateFn().toISOString(),
4562
4611
  schedule: PAYMENT_CRON_SCHEDULE,
4563
4612
  command: buildStoragePaymentCronCommand({
4564
4613
  walletAddress: upload.addr,
@@ -4575,7 +4624,10 @@ async function createStoragePaymentCronJob(upload, storagePrice, homeDir, nowDat
4575
4624
  bucketName: upload.bucket_name,
4576
4625
  location: upload.location
4577
4626
  };
4578
- await appendStoragePaymentCronJob(cronJob, homeDir);
4627
+ const created = await appendStoragePaymentCronJob(cronJob, openClawCronAdapter);
4628
+ if (created.jobId?.trim()) {
4629
+ cronJob.cronId = created.jobId.trim();
4630
+ }
4579
4631
  return cronJob;
4580
4632
  }
4581
4633
  async function readWalletKeyIfPresent(walletPath) {
@@ -4965,6 +5017,9 @@ function createCloudCommand(options = {}) {
4965
5017
  requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
4966
5018
  requestPaymentSettleViaProxyFn: options.requestPaymentSettleViaProxyFn ?? requestPaymentSettleViaProxy,
4967
5019
  mnemosparkHomeDir: options.mnemosparkHomeDir ?? options.backupOptions?.homeDir,
5020
+ openClawCronAdapter: options.openClawCronAdapter ?? createOpenClawCliCronAdapter(
5021
+ options.mnemosparkHomeDir ?? options.backupOptions?.homeDir
5022
+ ),
4968
5023
  backupOptions: options.backupOptions,
4969
5024
  proxyQuoteOptions: options.proxyQuoteOptions,
4970
5025
  proxyUploadOptions: options.proxyUploadOptions,
@@ -5260,6 +5315,7 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
5260
5315
  const requestStorageDownload = options.requestStorageDownloadFn;
5261
5316
  const requestStorageDelete = options.requestStorageDeleteFn;
5262
5317
  const requestPaymentSettleViaProxy2 = options.requestPaymentSettleViaProxyFn;
5318
+ const openClawCronAdapter = options.openClawCronAdapter;
5263
5319
  const subagentOrchestrator = options.subagentOrchestrator;
5264
5320
  if (parsed.mode === "help" || parsed.mode === "unknown") {
5265
5321
  return {
@@ -6232,7 +6288,7 @@ operation-id: ${operationId}`,
6232
6288
  const cronJob = await createStoragePaymentCronJob(
6233
6289
  finalizedUploadResponse,
6234
6290
  cronStoragePrice,
6235
- mnemosparkHomeDir,
6291
+ openClawCronAdapter,
6236
6292
  nowDateFn
6237
6293
  );
6238
6294
  await datastore.upsertObject({
@@ -6596,15 +6652,15 @@ operation-id: ${operationId}`,
6596
6652
  };
6597
6653
  }
6598
6654
  if (!cronEntry) {
6599
- cronEntry = await findCronJobInCrontabByObjectKey(
6655
+ cronEntry = await findCronJobInOpenClawCronJobsByObjectKey(
6600
6656
  resolvedRequest.object_key,
6601
- mnemosparkHomeDir
6657
+ openClawCronAdapter
6602
6658
  );
6603
6659
  }
6604
6660
  if (cronEntry) {
6605
6661
  const fileCronDeleted = await removeStoragePaymentCronJob(
6606
6662
  cronEntry.cronId,
6607
- mnemosparkHomeDir
6663
+ openClawCronAdapter
6608
6664
  );
6609
6665
  const dbCronDeleted = await datastore.removeCronJob(cronEntry.cronId);
6610
6666
  cronDeleted = fileCronDeleted || dbCronDeleted;