mnemospark 1.2.2 → 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/README.md +18 -12
- package/dist/cli.js +278 -107
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +21 -6
- package/dist/index.js +260 -86
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/skills/mnemospark/references/commands.md +1 -0
package/dist/index.d.ts
CHANGED
|
@@ -715,17 +715,14 @@ type OpenClawCronPayload = {
|
|
|
715
715
|
kind: "agentTurn";
|
|
716
716
|
message: string;
|
|
717
717
|
};
|
|
718
|
-
type OpenClawCronDelivery = {
|
|
719
|
-
mode: "announce";
|
|
720
|
-
text: string;
|
|
721
|
-
};
|
|
722
718
|
type OpenClawCronJobEntry = {
|
|
723
719
|
jobId: string;
|
|
724
720
|
name: string;
|
|
725
721
|
schedule: OpenClawCronSchedule;
|
|
726
722
|
payload: OpenClawCronPayload;
|
|
727
723
|
sessionTarget: "isolated";
|
|
728
|
-
|
|
724
|
+
/** OpenClaw 2026.4.x: bind isolated renewal work to the dedicated agent. */
|
|
725
|
+
agentId: string;
|
|
729
726
|
};
|
|
730
727
|
type OpenClawCronJobForLookup = {
|
|
731
728
|
jobId: string;
|
|
@@ -740,6 +737,24 @@ type OpenClawCronAdapter = {
|
|
|
740
737
|
};
|
|
741
738
|
declare function createCloudCommand(options?: CreateCloudCommandOptions): OpenClawPluginCommandDefinition;
|
|
742
739
|
|
|
740
|
+
declare function getRenewalAgentId(): string;
|
|
741
|
+
/** Absolute path to node for renewal exec (override with MNEMOSPARK_CRON_NODE_BIN). */
|
|
742
|
+
declare function getRenewalNodeBinary(): string;
|
|
743
|
+
type EnsureOpenClawRenewalPrerequisitesOptions = {
|
|
744
|
+
homeDir?: string;
|
|
745
|
+
/** Skip all OpenClaw mutations (tests). */
|
|
746
|
+
disabled?: boolean;
|
|
747
|
+
/** Skip gateway restart (tests). */
|
|
748
|
+
skipGatewayRestart?: boolean;
|
|
749
|
+
};
|
|
750
|
+
/**
|
|
751
|
+
* Apply the Mnemospark Renewal Agent Runbook before registering the first renewal cron:
|
|
752
|
+
* - `agents.list` entry via `openclaw config set` + `openclaw config validate`
|
|
753
|
+
* - `~/.openclaw/exec-approvals.json` merge for the node binary
|
|
754
|
+
* - best-effort `openclaw gateway restart`
|
|
755
|
+
*/
|
|
756
|
+
declare function ensureOpenClawRenewalPrerequisites(options?: EnsureOpenClawRenewalPrerequisitesOptions): Promise<void>;
|
|
757
|
+
|
|
743
758
|
type RunMnemosparkSlashHandlerOptions = {
|
|
744
759
|
cloudCommandHandler?: PluginCommandHandler;
|
|
745
760
|
};
|
|
@@ -757,4 +772,4 @@ declare function runMnemosparkSlashHandler(ctx: PluginCommandContext, options?:
|
|
|
757
772
|
|
|
758
773
|
declare const plugin: OpenClawPluginDefinition;
|
|
759
774
|
|
|
760
|
-
export { BALANCE_THRESHOLDS, type BalanceInfo, BalanceMonitor, type CachedPaymentParams, DEFAULT_RETRY_CONFIG, EmptyWalletError, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, PaymentCache, type PaymentFetchResult, type PreAuthParams, type ProxyHandle, type ProxyOptions, type RetryConfig, RpcError, type SufficiencyResult, createCloudCommand, createPaymentFetch, plugin as default, fetchWithRetry, getProxyPort, isBalanceError, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, runMnemosparkSlashHandler, startProxy };
|
|
775
|
+
export { BALANCE_THRESHOLDS, type BalanceInfo, BalanceMonitor, type CachedPaymentParams, DEFAULT_RETRY_CONFIG, EmptyWalletError, type EnsureOpenClawRenewalPrerequisitesOptions, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, PaymentCache, type PaymentFetchResult, type PreAuthParams, type ProxyHandle, type ProxyOptions, type RetryConfig, RpcError, type SufficiencyResult, createCloudCommand, createPaymentFetch, plugin as default, ensureOpenClawRenewalPrerequisites, fetchWithRetry, getProxyPort, getRenewalAgentId, getRenewalNodeBinary, isBalanceError, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, runMnemosparkSlashHandler, startProxy };
|
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
|
|
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
|
|
3061
|
-
import { homedir as
|
|
3062
|
-
import { basename as basename2, dirname as
|
|
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";
|
|
@@ -4264,12 +4264,226 @@ var opStatusSchema = {
|
|
|
4264
4264
|
]
|
|
4265
4265
|
};
|
|
4266
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
|
+
|
|
4267
4481
|
// src/cloud-command.ts
|
|
4268
4482
|
var SUPPORTED_BACKUP_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux"]);
|
|
4269
|
-
var BACKUP_DIR_SUBPATH =
|
|
4270
|
-
var DEFAULT_BACKUP_DIR =
|
|
4271
|
-
var BLOCKRUN_WALLET_KEY_SUBPATH =
|
|
4272
|
-
var MNEMOSPARK_WALLET_KEY_SUBPATH =
|
|
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");
|
|
4273
4487
|
var INLINE_UPLOAD_MAX_BYTES = 45e5;
|
|
4274
4488
|
var NODE_FS_MAX_READFILE_BYTES = 2147483648;
|
|
4275
4489
|
var PAYMENT_CRON_SCHEDULE = "0 0 1 * *";
|
|
@@ -4287,10 +4501,10 @@ var ORCHESTRATOR_MODES = /* @__PURE__ */ new Set(["inline", "subagent"]);
|
|
|
4287
4501
|
function expandTilde(path) {
|
|
4288
4502
|
const trimmed = path.trim();
|
|
4289
4503
|
if (trimmed === "~") {
|
|
4290
|
-
return
|
|
4504
|
+
return homedir7();
|
|
4291
4505
|
}
|
|
4292
4506
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
4293
|
-
return
|
|
4507
|
+
return join10(homedir7(), trimmed.slice(2));
|
|
4294
4508
|
}
|
|
4295
4509
|
return path;
|
|
4296
4510
|
}
|
|
@@ -4858,7 +5072,7 @@ async function calculateInputSizeBytes(targetPath) {
|
|
|
4858
5072
|
let total = 0;
|
|
4859
5073
|
const entries = await readdir2(targetPath, { withFileTypes: true });
|
|
4860
5074
|
for (const entry of entries) {
|
|
4861
|
-
total += await calculateInputSizeBytes(
|
|
5075
|
+
total += await calculateInputSizeBytes(join10(targetPath, entry.name));
|
|
4862
5076
|
}
|
|
4863
5077
|
return total;
|
|
4864
5078
|
}
|
|
@@ -4870,11 +5084,11 @@ function getAvailableDiskBytes(tmpDir, options) {
|
|
|
4870
5084
|
return stats.bavail * stats.bsize;
|
|
4871
5085
|
}
|
|
4872
5086
|
async function runTarGzip(archivePath, sourcePath) {
|
|
4873
|
-
const sourceDir =
|
|
5087
|
+
const sourceDir = dirname6(sourcePath);
|
|
4874
5088
|
const sourceName = basename2(sourcePath);
|
|
4875
5089
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
4876
5090
|
let stderr = "";
|
|
4877
|
-
const child =
|
|
5091
|
+
const child = spawn2("tar", ["-czf", archivePath, "-C", sourceDir, sourceName], {
|
|
4878
5092
|
stdio: ["ignore", "ignore", "pipe"]
|
|
4879
5093
|
});
|
|
4880
5094
|
child.stderr.on("data", (chunk) => {
|
|
@@ -4904,7 +5118,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
|
|
|
4904
5118
|
if (friendlyName?.trim()) {
|
|
4905
5119
|
try {
|
|
4906
5120
|
const sanitized = sanitizeFriendlyNameForLocalBasename(friendlyName);
|
|
4907
|
-
const candidate =
|
|
5121
|
+
const candidate = join10(backupDir, sanitized);
|
|
4908
5122
|
try {
|
|
4909
5123
|
const st = await stat2(candidate);
|
|
4910
5124
|
if (st.isFile()) {
|
|
@@ -4915,7 +5129,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
|
|
|
4915
5129
|
} catch {
|
|
4916
5130
|
}
|
|
4917
5131
|
}
|
|
4918
|
-
const legacyPath =
|
|
5132
|
+
const legacyPath = join10(backupDir, objectId);
|
|
4919
5133
|
try {
|
|
4920
5134
|
const legacyStats = await stat2(legacyPath);
|
|
4921
5135
|
if (!legacyStats.isFile()) {
|
|
@@ -4953,7 +5167,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
|
|
|
4953
5167
|
tmpStats = await stat2(tmpDir);
|
|
4954
5168
|
} catch (error) {
|
|
4955
5169
|
if (error.code === "ENOENT") {
|
|
4956
|
-
await
|
|
5170
|
+
await mkdir6(tmpDir, { recursive: true });
|
|
4957
5171
|
tmpStats = await stat2(tmpDir);
|
|
4958
5172
|
} else {
|
|
4959
5173
|
throw error;
|
|
@@ -4970,7 +5184,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
|
|
|
4970
5184
|
}
|
|
4971
5185
|
const objectId = createObjectId(options);
|
|
4972
5186
|
const archiveBaseSegment = options.archiveBasename?.trim() || objectId;
|
|
4973
|
-
const archivePath =
|
|
5187
|
+
const archivePath = join10(tmpDir, archiveBaseSegment);
|
|
4974
5188
|
if (options.archiveBasename?.trim()) {
|
|
4975
5189
|
try {
|
|
4976
5190
|
const existing = await stat2(archivePath);
|
|
@@ -5026,48 +5240,6 @@ function normalizeOpenClawCronJobForLookup(value) {
|
|
|
5026
5240
|
message: payloadMessage
|
|
5027
5241
|
};
|
|
5028
5242
|
}
|
|
5029
|
-
async function runOpenClawCli(args, homeDir) {
|
|
5030
|
-
return await new Promise((resolvePromise, rejectPromise) => {
|
|
5031
|
-
let stdout = "";
|
|
5032
|
-
let stderr = "";
|
|
5033
|
-
const child = spawn("openclaw", args, {
|
|
5034
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
5035
|
-
env: {
|
|
5036
|
-
...process.env,
|
|
5037
|
-
HOME: homeDir ?? process.env.HOME
|
|
5038
|
-
}
|
|
5039
|
-
});
|
|
5040
|
-
child.stdout.on("data", (chunk) => {
|
|
5041
|
-
stdout += chunk.toString();
|
|
5042
|
-
});
|
|
5043
|
-
child.stderr.on("data", (chunk) => {
|
|
5044
|
-
stderr += chunk.toString();
|
|
5045
|
-
});
|
|
5046
|
-
child.on("error", rejectPromise);
|
|
5047
|
-
child.on("close", (code) => {
|
|
5048
|
-
if (code === 0) {
|
|
5049
|
-
resolvePromise({ stdout, stderr });
|
|
5050
|
-
return;
|
|
5051
|
-
}
|
|
5052
|
-
rejectPromise(
|
|
5053
|
-
new Error(
|
|
5054
|
-
stderr.trim() || stdout.trim() || `openclaw ${args.join(" ")} exited with code ${code ?? "unknown"}`
|
|
5055
|
-
)
|
|
5056
|
-
);
|
|
5057
|
-
});
|
|
5058
|
-
});
|
|
5059
|
-
}
|
|
5060
|
-
function parseOpenClawCliJson(stdout, commandLabel) {
|
|
5061
|
-
const trimmed = stdout.trim();
|
|
5062
|
-
if (!trimmed) {
|
|
5063
|
-
throw new Error(`openclaw ${commandLabel} returned empty JSON output`);
|
|
5064
|
-
}
|
|
5065
|
-
try {
|
|
5066
|
-
return JSON.parse(trimmed);
|
|
5067
|
-
} catch {
|
|
5068
|
-
throw new Error(`openclaw ${commandLabel} returned invalid JSON output`);
|
|
5069
|
-
}
|
|
5070
|
-
}
|
|
5071
5243
|
function createOpenClawCliCronAdapter(homeDir) {
|
|
5072
5244
|
return {
|
|
5073
5245
|
add: async (job) => {
|
|
@@ -5083,11 +5255,11 @@ function createOpenClawCliCronAdapter(homeDir) {
|
|
|
5083
5255
|
job.schedule.tz,
|
|
5084
5256
|
"--session",
|
|
5085
5257
|
job.sessionTarget,
|
|
5258
|
+
"--agent",
|
|
5259
|
+
job.agentId,
|
|
5086
5260
|
"--message",
|
|
5087
5261
|
job.payload.message,
|
|
5088
|
-
"--
|
|
5089
|
-
"--description",
|
|
5090
|
-
job.delivery.text,
|
|
5262
|
+
"--no-deliver",
|
|
5091
5263
|
"--json"
|
|
5092
5264
|
],
|
|
5093
5265
|
homeDir
|
|
@@ -5152,8 +5324,9 @@ function buildStoragePaymentCronCommand(job) {
|
|
|
5152
5324
|
return `/mnemospark cloud ${buildStoragePaymentRenewalArgs(job)}`;
|
|
5153
5325
|
}
|
|
5154
5326
|
function buildOpenClawRenewalAgentMessage(openClawHome, renewalArgs) {
|
|
5155
|
-
const cliPath =
|
|
5156
|
-
|
|
5327
|
+
const cliPath = join10(openClawHome, ".openclaw/extensions/mnemospark/dist/cli.js");
|
|
5328
|
+
const nodeBin = getRenewalNodeBinary();
|
|
5329
|
+
return `Command: ${nodeBin} ${cliPath} cloud ${renewalArgs}`;
|
|
5157
5330
|
}
|
|
5158
5331
|
function parseStoragePaymentCronCommand(command) {
|
|
5159
5332
|
const objectIdMatch = command.match(/--object-id\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
|
|
@@ -5199,10 +5372,7 @@ async function appendStoragePaymentCronJob(cronJob, adapter, payloadMessage) {
|
|
|
5199
5372
|
message: payloadMessage
|
|
5200
5373
|
},
|
|
5201
5374
|
sessionTarget: "isolated",
|
|
5202
|
-
|
|
5203
|
-
mode: "announce",
|
|
5204
|
-
text: "Thank you for using mnemospark cloud storage. Your renewal has been processed."
|
|
5205
|
-
}
|
|
5375
|
+
agentId: getRenewalAgentId()
|
|
5206
5376
|
};
|
|
5207
5377
|
return adapter.add(openClawJob);
|
|
5208
5378
|
}
|
|
@@ -5210,6 +5380,7 @@ async function removeStoragePaymentCronJob(cronId, adapter) {
|
|
|
5210
5380
|
return adapter.remove(cronId);
|
|
5211
5381
|
}
|
|
5212
5382
|
async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAdapter, openClawHomeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
|
|
5383
|
+
await ensureOpenClawRenewalPrerequisites({ homeDir: openClawHomeDir });
|
|
5213
5384
|
const renewalFields = {
|
|
5214
5385
|
walletAddress: upload.addr,
|
|
5215
5386
|
objectId: upload.object_id,
|
|
@@ -5217,7 +5388,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
|
|
|
5217
5388
|
storagePrice
|
|
5218
5389
|
};
|
|
5219
5390
|
const renewalArgs = buildStoragePaymentRenewalArgs(renewalFields);
|
|
5220
|
-
const provisionalCronId =
|
|
5391
|
+
const provisionalCronId = randomUUID4();
|
|
5221
5392
|
const cronJob = {
|
|
5222
5393
|
cronId: provisionalCronId,
|
|
5223
5394
|
createdAt: nowDateFn().toISOString(),
|
|
@@ -5241,7 +5412,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
|
|
|
5241
5412
|
}
|
|
5242
5413
|
async function readWalletKeyIfPresent(walletPath) {
|
|
5243
5414
|
try {
|
|
5244
|
-
const key = (await
|
|
5415
|
+
const key = (await readFile4(walletPath, "utf-8")).trim();
|
|
5245
5416
|
return isValidWalletPrivateKey(key) ? key : null;
|
|
5246
5417
|
} catch (error) {
|
|
5247
5418
|
if (error.code === "ENOENT") {
|
|
@@ -5255,9 +5426,9 @@ async function resolveWalletPrivateKey(homeDir) {
|
|
|
5255
5426
|
if (isValidWalletPrivateKey(envKey)) {
|
|
5256
5427
|
return envKey;
|
|
5257
5428
|
}
|
|
5258
|
-
const baseHome = homeDir ??
|
|
5259
|
-
const primaryWalletPath =
|
|
5260
|
-
const fallbackWalletPath =
|
|
5429
|
+
const baseHome = homeDir ?? homedir7();
|
|
5430
|
+
const primaryWalletPath = join10(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
|
|
5431
|
+
const fallbackWalletPath = join10(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
|
|
5261
5432
|
const fromPrimary = await readWalletKeyIfPresent(primaryWalletPath);
|
|
5262
5433
|
if (fromPrimary) {
|
|
5263
5434
|
return fromPrimary;
|
|
@@ -5321,9 +5492,9 @@ async function encryptPlaintextFileToAesGcmPath(plaintextPath, dek, outPath, ran
|
|
|
5321
5492
|
}
|
|
5322
5493
|
async function loadOrCreateKek(walletAddress, homeDir) {
|
|
5323
5494
|
const keyPath = resolveWalletKekPath(walletAddress, homeDir);
|
|
5324
|
-
await
|
|
5495
|
+
await mkdir6(dirname6(keyPath), { recursive: true });
|
|
5325
5496
|
try {
|
|
5326
|
-
const existing = await
|
|
5497
|
+
const existing = await readFile4(keyPath);
|
|
5327
5498
|
return { kek: parseStoredAes256Key(existing), keyPath };
|
|
5328
5499
|
} catch (error) {
|
|
5329
5500
|
if (error.code !== "ENOENT") {
|
|
@@ -5331,7 +5502,7 @@ async function loadOrCreateKek(walletAddress, homeDir) {
|
|
|
5331
5502
|
}
|
|
5332
5503
|
}
|
|
5333
5504
|
const generated = randomBytesNode(32);
|
|
5334
|
-
await
|
|
5505
|
+
await writeFile4(keyPath, generated, { mode: 384 });
|
|
5335
5506
|
return { kek: generated, keyPath };
|
|
5336
5507
|
}
|
|
5337
5508
|
async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
@@ -5343,7 +5514,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
|
5343
5514
|
const dek = randomBytesNode(32);
|
|
5344
5515
|
const wrappedDek = encryptAesGcm(dek, kek);
|
|
5345
5516
|
if (archiveStat.size >= NODE_FS_MAX_READFILE_BYTES) {
|
|
5346
|
-
const encryptedTempPath =
|
|
5517
|
+
const encryptedTempPath = join10(tmpdir(), `mnemospark-upload-${randomUUID4()}.enc`);
|
|
5347
5518
|
try {
|
|
5348
5519
|
await encryptPlaintextFileToAesGcmPath(archivePath, dek, encryptedTempPath);
|
|
5349
5520
|
const encStat = await stat2(encryptedTempPath);
|
|
@@ -5369,7 +5540,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
|
5369
5540
|
throw err;
|
|
5370
5541
|
}
|
|
5371
5542
|
}
|
|
5372
|
-
const plaintext = await
|
|
5543
|
+
const plaintext = await readFile4(archivePath);
|
|
5373
5544
|
const encryptedContent = encryptAesGcm(plaintext, dek);
|
|
5374
5545
|
const payloadHash = sha256Buffer(encryptedContent);
|
|
5375
5546
|
const payload = {
|
|
@@ -5614,7 +5785,7 @@ function createInProcessSubagentOrchestrator() {
|
|
|
5614
5785
|
};
|
|
5615
5786
|
return {
|
|
5616
5787
|
dispatch: async (input) => {
|
|
5617
|
-
const sessionId = `agent:mnemospark:subagent:${
|
|
5788
|
+
const sessionId = `agent:mnemospark:subagent:${randomUUID4()}`;
|
|
5618
5789
|
const state = {
|
|
5619
5790
|
terminal: false,
|
|
5620
5791
|
cancelRequested: false,
|
|
@@ -5716,7 +5887,7 @@ function createCloudCommand(options = {}) {
|
|
|
5716
5887
|
createPaymentFetchFn: options.createPaymentFetchFn ?? createPaymentFetch,
|
|
5717
5888
|
fetchImpl: options.fetchImpl ?? fetch,
|
|
5718
5889
|
nowDateFn: options.nowDateFn ?? (() => /* @__PURE__ */ new Date()),
|
|
5719
|
-
idempotencyKeyFn: options.idempotencyKeyFn ??
|
|
5890
|
+
idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID4,
|
|
5720
5891
|
requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
|
|
5721
5892
|
requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
|
|
5722
5893
|
requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
|
|
@@ -5860,8 +6031,8 @@ async function emitOperationEventBestEffort(eventType, context, homeDir) {
|
|
|
5860
6031
|
}
|
|
5861
6032
|
}
|
|
5862
6033
|
function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
|
|
5863
|
-
const operationId = forcedOperationId?.trim() ||
|
|
5864
|
-
const traceId = forcedTraceId?.trim() ||
|
|
6034
|
+
const operationId = forcedOperationId?.trim() || randomUUID4();
|
|
6035
|
+
const traceId = forcedTraceId?.trim() || randomUUID4();
|
|
5865
6036
|
return { operationId, traceId };
|
|
5866
6037
|
}
|
|
5867
6038
|
function parseTransIdFromPaymentSettleBody(bodyText) {
|
|
@@ -6149,7 +6320,7 @@ ${operation.result_text}` : meta;
|
|
|
6149
6320
|
};
|
|
6150
6321
|
}
|
|
6151
6322
|
if (!isTerminalOperationStatus(operation.status)) {
|
|
6152
|
-
const traceId = operation.trace_id ??
|
|
6323
|
+
const traceId = operation.trace_id ?? randomUUID4();
|
|
6153
6324
|
const cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6154
6325
|
await datastore.upsertOperation({
|
|
6155
6326
|
operation_id: operation.operation_id,
|
|
@@ -6747,7 +6918,7 @@ operation-id: ${operationId}`,
|
|
|
6747
6918
|
await emitCloudEventBestEffort(
|
|
6748
6919
|
"backup.completed",
|
|
6749
6920
|
{
|
|
6750
|
-
operation_id: executionContext.forcedOperationId?.trim() ||
|
|
6921
|
+
operation_id: executionContext.forcedOperationId?.trim() || randomUUID4(),
|
|
6751
6922
|
object_id: result.objectId,
|
|
6752
6923
|
status: "succeeded",
|
|
6753
6924
|
details: {
|
|
@@ -7034,7 +7205,7 @@ operation-id: ${operationId}`,
|
|
|
7034
7205
|
finalizedUploadResponse,
|
|
7035
7206
|
cronStoragePrice,
|
|
7036
7207
|
openClawCronAdapter,
|
|
7037
|
-
mnemosparkHomeDir ??
|
|
7208
|
+
mnemosparkHomeDir ?? homedir7(),
|
|
7038
7209
|
nowDateFn
|
|
7039
7210
|
);
|
|
7040
7211
|
await datastore.upsertObject({
|
|
@@ -7828,8 +7999,11 @@ export {
|
|
|
7828
7999
|
createCloudCommand,
|
|
7829
8000
|
createPaymentFetch,
|
|
7830
8001
|
index_default as default,
|
|
8002
|
+
ensureOpenClawRenewalPrerequisites,
|
|
7831
8003
|
fetchWithRetry,
|
|
7832
8004
|
getProxyPort,
|
|
8005
|
+
getRenewalAgentId,
|
|
8006
|
+
getRenewalNodeBinary,
|
|
7833
8007
|
isBalanceError,
|
|
7834
8008
|
isEmptyWalletError,
|
|
7835
8009
|
isInsufficientFundsError,
|