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