mnemospark 1.2.2 → 1.4.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 +32 -20
- package/dist/cli.js +286 -107
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +20 -6
- package/dist/index.js +259 -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,23 @@ 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
|
+
};
|
|
748
|
+
/**
|
|
749
|
+
* Apply the Mnemospark Renewal Agent Runbook (install/update / plugin load):
|
|
750
|
+
* - `agents.list` entry via `openclaw config set` + `openclaw config validate`
|
|
751
|
+
* - `~/.openclaw/exec-approvals.json` merge for the node binary
|
|
752
|
+
*
|
|
753
|
+
* Gateway restart is not performed here; OpenClaw restarts the gateway when a plugin is installed or updated.
|
|
754
|
+
*/
|
|
755
|
+
declare function ensureOpenClawRenewalPrerequisites(options?: EnsureOpenClawRenewalPrerequisitesOptions): Promise<void>;
|
|
756
|
+
|
|
743
757
|
type RunMnemosparkSlashHandlerOptions = {
|
|
744
758
|
cloudCommandHandler?: PluginCommandHandler;
|
|
745
759
|
};
|
|
@@ -757,4 +771,4 @@ declare function runMnemosparkSlashHandler(ctx: PluginCommandContext, options?:
|
|
|
757
771
|
|
|
758
772
|
declare const plugin: OpenClawPluginDefinition;
|
|
759
773
|
|
|
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 };
|
|
774
|
+
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,219 @@ 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 applying renewal prerequisites.`
|
|
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
|
+
}
|
|
4473
|
+
|
|
4267
4474
|
// src/cloud-command.ts
|
|
4268
4475
|
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 =
|
|
4476
|
+
var BACKUP_DIR_SUBPATH = join10(".openclaw", "mnemospark", "backup");
|
|
4477
|
+
var DEFAULT_BACKUP_DIR = join10(homedir7(), BACKUP_DIR_SUBPATH);
|
|
4478
|
+
var BLOCKRUN_WALLET_KEY_SUBPATH = join10(".openclaw", "blockrun", "wallet.key");
|
|
4479
|
+
var MNEMOSPARK_WALLET_KEY_SUBPATH = join10(".openclaw", "mnemospark", "wallet", "wallet.key");
|
|
4273
4480
|
var INLINE_UPLOAD_MAX_BYTES = 45e5;
|
|
4274
4481
|
var NODE_FS_MAX_READFILE_BYTES = 2147483648;
|
|
4275
4482
|
var PAYMENT_CRON_SCHEDULE = "0 0 1 * *";
|
|
@@ -4287,10 +4494,10 @@ var ORCHESTRATOR_MODES = /* @__PURE__ */ new Set(["inline", "subagent"]);
|
|
|
4287
4494
|
function expandTilde(path) {
|
|
4288
4495
|
const trimmed = path.trim();
|
|
4289
4496
|
if (trimmed === "~") {
|
|
4290
|
-
return
|
|
4497
|
+
return homedir7();
|
|
4291
4498
|
}
|
|
4292
4499
|
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
4293
|
-
return
|
|
4500
|
+
return join10(homedir7(), trimmed.slice(2));
|
|
4294
4501
|
}
|
|
4295
4502
|
return path;
|
|
4296
4503
|
}
|
|
@@ -4858,7 +5065,7 @@ async function calculateInputSizeBytes(targetPath) {
|
|
|
4858
5065
|
let total = 0;
|
|
4859
5066
|
const entries = await readdir2(targetPath, { withFileTypes: true });
|
|
4860
5067
|
for (const entry of entries) {
|
|
4861
|
-
total += await calculateInputSizeBytes(
|
|
5068
|
+
total += await calculateInputSizeBytes(join10(targetPath, entry.name));
|
|
4862
5069
|
}
|
|
4863
5070
|
return total;
|
|
4864
5071
|
}
|
|
@@ -4870,11 +5077,11 @@ function getAvailableDiskBytes(tmpDir, options) {
|
|
|
4870
5077
|
return stats.bavail * stats.bsize;
|
|
4871
5078
|
}
|
|
4872
5079
|
async function runTarGzip(archivePath, sourcePath) {
|
|
4873
|
-
const sourceDir =
|
|
5080
|
+
const sourceDir = dirname6(sourcePath);
|
|
4874
5081
|
const sourceName = basename2(sourcePath);
|
|
4875
5082
|
await new Promise((resolvePromise, rejectPromise) => {
|
|
4876
5083
|
let stderr = "";
|
|
4877
|
-
const child =
|
|
5084
|
+
const child = spawn2("tar", ["-czf", archivePath, "-C", sourceDir, sourceName], {
|
|
4878
5085
|
stdio: ["ignore", "ignore", "pipe"]
|
|
4879
5086
|
});
|
|
4880
5087
|
child.stderr.on("data", (chunk) => {
|
|
@@ -4904,7 +5111,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
|
|
|
4904
5111
|
if (friendlyName?.trim()) {
|
|
4905
5112
|
try {
|
|
4906
5113
|
const sanitized = sanitizeFriendlyNameForLocalBasename(friendlyName);
|
|
4907
|
-
const candidate =
|
|
5114
|
+
const candidate = join10(backupDir, sanitized);
|
|
4908
5115
|
try {
|
|
4909
5116
|
const st = await stat2(candidate);
|
|
4910
5117
|
if (st.isFile()) {
|
|
@@ -4915,7 +5122,7 @@ async function resolveLocalUploadArchivePath(backupDir, objectId, friendlyName)
|
|
|
4915
5122
|
} catch {
|
|
4916
5123
|
}
|
|
4917
5124
|
}
|
|
4918
|
-
const legacyPath =
|
|
5125
|
+
const legacyPath = join10(backupDir, objectId);
|
|
4919
5126
|
try {
|
|
4920
5127
|
const legacyStats = await stat2(legacyPath);
|
|
4921
5128
|
if (!legacyStats.isFile()) {
|
|
@@ -4953,7 +5160,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
|
|
|
4953
5160
|
tmpStats = await stat2(tmpDir);
|
|
4954
5161
|
} catch (error) {
|
|
4955
5162
|
if (error.code === "ENOENT") {
|
|
4956
|
-
await
|
|
5163
|
+
await mkdir6(tmpDir, { recursive: true });
|
|
4957
5164
|
tmpStats = await stat2(tmpDir);
|
|
4958
5165
|
} else {
|
|
4959
5166
|
throw error;
|
|
@@ -4970,7 +5177,7 @@ async function buildBackupObject(targetPathArg, options = {}) {
|
|
|
4970
5177
|
}
|
|
4971
5178
|
const objectId = createObjectId(options);
|
|
4972
5179
|
const archiveBaseSegment = options.archiveBasename?.trim() || objectId;
|
|
4973
|
-
const archivePath =
|
|
5180
|
+
const archivePath = join10(tmpDir, archiveBaseSegment);
|
|
4974
5181
|
if (options.archiveBasename?.trim()) {
|
|
4975
5182
|
try {
|
|
4976
5183
|
const existing = await stat2(archivePath);
|
|
@@ -5026,48 +5233,6 @@ function normalizeOpenClawCronJobForLookup(value) {
|
|
|
5026
5233
|
message: payloadMessage
|
|
5027
5234
|
};
|
|
5028
5235
|
}
|
|
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
5236
|
function createOpenClawCliCronAdapter(homeDir) {
|
|
5072
5237
|
return {
|
|
5073
5238
|
add: async (job) => {
|
|
@@ -5083,11 +5248,11 @@ function createOpenClawCliCronAdapter(homeDir) {
|
|
|
5083
5248
|
job.schedule.tz,
|
|
5084
5249
|
"--session",
|
|
5085
5250
|
job.sessionTarget,
|
|
5251
|
+
"--agent",
|
|
5252
|
+
job.agentId,
|
|
5086
5253
|
"--message",
|
|
5087
5254
|
job.payload.message,
|
|
5088
|
-
"--
|
|
5089
|
-
"--description",
|
|
5090
|
-
job.delivery.text,
|
|
5255
|
+
"--no-deliver",
|
|
5091
5256
|
"--json"
|
|
5092
5257
|
],
|
|
5093
5258
|
homeDir
|
|
@@ -5152,8 +5317,9 @@ function buildStoragePaymentCronCommand(job) {
|
|
|
5152
5317
|
return `/mnemospark cloud ${buildStoragePaymentRenewalArgs(job)}`;
|
|
5153
5318
|
}
|
|
5154
5319
|
function buildOpenClawRenewalAgentMessage(openClawHome, renewalArgs) {
|
|
5155
|
-
const cliPath =
|
|
5156
|
-
|
|
5320
|
+
const cliPath = join10(openClawHome, ".openclaw/extensions/mnemospark/dist/cli.js");
|
|
5321
|
+
const nodeBin = getRenewalNodeBinary();
|
|
5322
|
+
return `Command: ${nodeBin} ${cliPath} cloud ${renewalArgs}`;
|
|
5157
5323
|
}
|
|
5158
5324
|
function parseStoragePaymentCronCommand(command) {
|
|
5159
5325
|
const objectIdMatch = command.match(/--object-id\s+("([^"\\]|\\.)*"|'([^'\\]|\\.)*'|\S+)/);
|
|
@@ -5199,10 +5365,7 @@ async function appendStoragePaymentCronJob(cronJob, adapter, payloadMessage) {
|
|
|
5199
5365
|
message: payloadMessage
|
|
5200
5366
|
},
|
|
5201
5367
|
sessionTarget: "isolated",
|
|
5202
|
-
|
|
5203
|
-
mode: "announce",
|
|
5204
|
-
text: "Thank you for using mnemospark cloud storage. Your renewal has been processed."
|
|
5205
|
-
}
|
|
5368
|
+
agentId: getRenewalAgentId()
|
|
5206
5369
|
};
|
|
5207
5370
|
return adapter.add(openClawJob);
|
|
5208
5371
|
}
|
|
@@ -5217,7 +5380,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
|
|
|
5217
5380
|
storagePrice
|
|
5218
5381
|
};
|
|
5219
5382
|
const renewalArgs = buildStoragePaymentRenewalArgs(renewalFields);
|
|
5220
|
-
const provisionalCronId =
|
|
5383
|
+
const provisionalCronId = randomUUID4();
|
|
5221
5384
|
const cronJob = {
|
|
5222
5385
|
cronId: provisionalCronId,
|
|
5223
5386
|
createdAt: nowDateFn().toISOString(),
|
|
@@ -5241,7 +5404,7 @@ async function createStoragePaymentCronJob(upload, storagePrice, openClawCronAda
|
|
|
5241
5404
|
}
|
|
5242
5405
|
async function readWalletKeyIfPresent(walletPath) {
|
|
5243
5406
|
try {
|
|
5244
|
-
const key = (await
|
|
5407
|
+
const key = (await readFile4(walletPath, "utf-8")).trim();
|
|
5245
5408
|
return isValidWalletPrivateKey(key) ? key : null;
|
|
5246
5409
|
} catch (error) {
|
|
5247
5410
|
if (error.code === "ENOENT") {
|
|
@@ -5255,9 +5418,9 @@ async function resolveWalletPrivateKey(homeDir) {
|
|
|
5255
5418
|
if (isValidWalletPrivateKey(envKey)) {
|
|
5256
5419
|
return envKey;
|
|
5257
5420
|
}
|
|
5258
|
-
const baseHome = homeDir ??
|
|
5259
|
-
const primaryWalletPath =
|
|
5260
|
-
const fallbackWalletPath =
|
|
5421
|
+
const baseHome = homeDir ?? homedir7();
|
|
5422
|
+
const primaryWalletPath = join10(baseHome, MNEMOSPARK_WALLET_KEY_SUBPATH);
|
|
5423
|
+
const fallbackWalletPath = join10(baseHome, BLOCKRUN_WALLET_KEY_SUBPATH);
|
|
5261
5424
|
const fromPrimary = await readWalletKeyIfPresent(primaryWalletPath);
|
|
5262
5425
|
if (fromPrimary) {
|
|
5263
5426
|
return fromPrimary;
|
|
@@ -5321,9 +5484,9 @@ async function encryptPlaintextFileToAesGcmPath(plaintextPath, dek, outPath, ran
|
|
|
5321
5484
|
}
|
|
5322
5485
|
async function loadOrCreateKek(walletAddress, homeDir) {
|
|
5323
5486
|
const keyPath = resolveWalletKekPath(walletAddress, homeDir);
|
|
5324
|
-
await
|
|
5487
|
+
await mkdir6(dirname6(keyPath), { recursive: true });
|
|
5325
5488
|
try {
|
|
5326
|
-
const existing = await
|
|
5489
|
+
const existing = await readFile4(keyPath);
|
|
5327
5490
|
return { kek: parseStoredAes256Key(existing), keyPath };
|
|
5328
5491
|
} catch (error) {
|
|
5329
5492
|
if (error.code !== "ENOENT") {
|
|
@@ -5331,7 +5494,7 @@ async function loadOrCreateKek(walletAddress, homeDir) {
|
|
|
5331
5494
|
}
|
|
5332
5495
|
}
|
|
5333
5496
|
const generated = randomBytesNode(32);
|
|
5334
|
-
await
|
|
5497
|
+
await writeFile4(keyPath, generated, { mode: 384 });
|
|
5335
5498
|
return { kek: generated, keyPath };
|
|
5336
5499
|
}
|
|
5337
5500
|
async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
@@ -5343,7 +5506,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
|
5343
5506
|
const dek = randomBytesNode(32);
|
|
5344
5507
|
const wrappedDek = encryptAesGcm(dek, kek);
|
|
5345
5508
|
if (archiveStat.size >= NODE_FS_MAX_READFILE_BYTES) {
|
|
5346
|
-
const encryptedTempPath =
|
|
5509
|
+
const encryptedTempPath = join10(tmpdir(), `mnemospark-upload-${randomUUID4()}.enc`);
|
|
5347
5510
|
try {
|
|
5348
5511
|
await encryptPlaintextFileToAesGcmPath(archivePath, dek, encryptedTempPath);
|
|
5349
5512
|
const encStat = await stat2(encryptedTempPath);
|
|
@@ -5369,7 +5532,7 @@ async function prepareUploadPayload(archivePath, walletAddress, homeDir) {
|
|
|
5369
5532
|
throw err;
|
|
5370
5533
|
}
|
|
5371
5534
|
}
|
|
5372
|
-
const plaintext = await
|
|
5535
|
+
const plaintext = await readFile4(archivePath);
|
|
5373
5536
|
const encryptedContent = encryptAesGcm(plaintext, dek);
|
|
5374
5537
|
const payloadHash = sha256Buffer(encryptedContent);
|
|
5375
5538
|
const payload = {
|
|
@@ -5614,7 +5777,7 @@ function createInProcessSubagentOrchestrator() {
|
|
|
5614
5777
|
};
|
|
5615
5778
|
return {
|
|
5616
5779
|
dispatch: async (input) => {
|
|
5617
|
-
const sessionId = `agent:mnemospark:subagent:${
|
|
5780
|
+
const sessionId = `agent:mnemospark:subagent:${randomUUID4()}`;
|
|
5618
5781
|
const state = {
|
|
5619
5782
|
terminal: false,
|
|
5620
5783
|
cancelRequested: false,
|
|
@@ -5716,7 +5879,7 @@ function createCloudCommand(options = {}) {
|
|
|
5716
5879
|
createPaymentFetchFn: options.createPaymentFetchFn ?? createPaymentFetch,
|
|
5717
5880
|
fetchImpl: options.fetchImpl ?? fetch,
|
|
5718
5881
|
nowDateFn: options.nowDateFn ?? (() => /* @__PURE__ */ new Date()),
|
|
5719
|
-
idempotencyKeyFn: options.idempotencyKeyFn ??
|
|
5882
|
+
idempotencyKeyFn: options.idempotencyKeyFn ?? randomUUID4,
|
|
5720
5883
|
requestStorageLsFn: options.requestStorageLsFn ?? requestStorageLsViaProxy,
|
|
5721
5884
|
requestStorageDownloadFn: options.requestStorageDownloadFn ?? requestStorageDownloadViaProxy,
|
|
5722
5885
|
requestStorageDeleteFn: options.requestStorageDeleteFn ?? requestStorageDeleteViaProxy,
|
|
@@ -5860,8 +6023,8 @@ async function emitOperationEventBestEffort(eventType, context, homeDir) {
|
|
|
5860
6023
|
}
|
|
5861
6024
|
}
|
|
5862
6025
|
function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
|
|
5863
|
-
const operationId = forcedOperationId?.trim() ||
|
|
5864
|
-
const traceId = forcedTraceId?.trim() ||
|
|
6026
|
+
const operationId = forcedOperationId?.trim() || randomUUID4();
|
|
6027
|
+
const traceId = forcedTraceId?.trim() || randomUUID4();
|
|
5865
6028
|
return { operationId, traceId };
|
|
5866
6029
|
}
|
|
5867
6030
|
function parseTransIdFromPaymentSettleBody(bodyText) {
|
|
@@ -6149,7 +6312,7 @@ ${operation.result_text}` : meta;
|
|
|
6149
6312
|
};
|
|
6150
6313
|
}
|
|
6151
6314
|
if (!isTerminalOperationStatus(operation.status)) {
|
|
6152
|
-
const traceId = operation.trace_id ??
|
|
6315
|
+
const traceId = operation.trace_id ?? randomUUID4();
|
|
6153
6316
|
const cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6154
6317
|
await datastore.upsertOperation({
|
|
6155
6318
|
operation_id: operation.operation_id,
|
|
@@ -6747,7 +6910,7 @@ operation-id: ${operationId}`,
|
|
|
6747
6910
|
await emitCloudEventBestEffort(
|
|
6748
6911
|
"backup.completed",
|
|
6749
6912
|
{
|
|
6750
|
-
operation_id: executionContext.forcedOperationId?.trim() ||
|
|
6913
|
+
operation_id: executionContext.forcedOperationId?.trim() || randomUUID4(),
|
|
6751
6914
|
object_id: result.objectId,
|
|
6752
6915
|
status: "succeeded",
|
|
6753
6916
|
details: {
|
|
@@ -7034,7 +7197,7 @@ operation-id: ${operationId}`,
|
|
|
7034
7197
|
finalizedUploadResponse,
|
|
7035
7198
|
cronStoragePrice,
|
|
7036
7199
|
openClawCronAdapter,
|
|
7037
|
-
mnemosparkHomeDir ??
|
|
7200
|
+
mnemosparkHomeDir ?? homedir7(),
|
|
7038
7201
|
nowDateFn
|
|
7039
7202
|
);
|
|
7040
7203
|
await datastore.upsertObject({
|
|
@@ -7764,6 +7927,13 @@ var plugin = {
|
|
|
7764
7927
|
if (isCompletionMode()) {
|
|
7765
7928
|
return;
|
|
7766
7929
|
}
|
|
7930
|
+
try {
|
|
7931
|
+
await ensureOpenClawRenewalPrerequisites();
|
|
7932
|
+
} catch (err) {
|
|
7933
|
+
api.logger.warn(
|
|
7934
|
+
`mnemospark renewal prerequisites: ${err instanceof Error ? err.message : String(err)}`
|
|
7935
|
+
);
|
|
7936
|
+
}
|
|
7767
7937
|
try {
|
|
7768
7938
|
api.registerCommand({
|
|
7769
7939
|
name: "mnemospark",
|
|
@@ -7828,8 +7998,11 @@ export {
|
|
|
7828
7998
|
createCloudCommand,
|
|
7829
7999
|
createPaymentFetch,
|
|
7830
8000
|
index_default as default,
|
|
8001
|
+
ensureOpenClawRenewalPrerequisites,
|
|
7831
8002
|
fetchWithRetry,
|
|
7832
8003
|
getProxyPort,
|
|
8004
|
+
getRenewalAgentId,
|
|
8005
|
+
getRenewalNodeBinary,
|
|
7833
8006
|
isBalanceError,
|
|
7834
8007
|
isEmptyWalletError,
|
|
7835
8008
|
isInsufficientFundsError,
|