@agentlayer.tech/wallet 0.1.73 → 0.1.74
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/.openclaw/extensions/agent-wallet/openclaw.plugin.json +1 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/CHANGELOG.md +46 -0
- package/RELEASING.md +23 -4
- package/VERSION +1 -1
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/boot_key_migration.py +10 -2
- package/agent-wallet/agent_wallet/boot_key_recovery.py +6 -4
- package/agent-wallet/agent_wallet/config.py +125 -23
- package/agent-wallet/agent_wallet/encrypted_storage.py +85 -0
- package/agent-wallet/agent_wallet/evm_user_wallets.py +116 -23
- package/agent-wallet/agent_wallet/keystore.py +82 -2
- package/agent-wallet/agent_wallet/sealed_keys.py +19 -18
- package/agent-wallet/agent_wallet/user_wallets.py +6 -5
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/bootstrap_openclaw_evm.py +8 -3
- package/agent-wallet/scripts/install_agent_wallet.py +4 -1
- package/bin/openclaw-agent-wallet.mjs +291 -96
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- package/claude-code/plugins/agent-wallet/.mcp.json +1 -2
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
- package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
- package/package.json +2 -1
- package/wdk-btc-wallet/package.json +1 -1
- package/wdk-evm-wallet/package.json +3 -2
- package/wdk-evm-wallet/src/config.js +24 -0
- package/wdk-evm-wallet/src/server.js +2 -0
|
@@ -380,6 +380,69 @@ function releaseRootFor(version, env = process.env) {
|
|
|
380
380
|
return path.join(resolveRuntimeBase(env), "releases", version);
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
function stagingRootFor(version, env = process.env) {
|
|
384
|
+
return path.join(
|
|
385
|
+
resolveRuntimeBase(env),
|
|
386
|
+
"releases",
|
|
387
|
+
`.staging-${version}-${process.pid}-${Date.now()}`,
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function updateJournalPath(env = process.env) {
|
|
392
|
+
return path.join(resolveRuntimeBase(env), "update-journal.json");
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function writeUpdateJournal(state, details = {}, env = process.env) {
|
|
396
|
+
writeJsonFile(updateJournalPath(env), {
|
|
397
|
+
schema_version: 1,
|
|
398
|
+
state,
|
|
399
|
+
version: packageVersion,
|
|
400
|
+
updated_at: new Date().toISOString(),
|
|
401
|
+
...details,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function writeReleaseState(runtimeRoot, state, details = {}) {
|
|
406
|
+
writeJsonFile(path.join(runtimeRoot, ".agent-wallet-release.json"), {
|
|
407
|
+
schema_version: 1,
|
|
408
|
+
version: packageVersion,
|
|
409
|
+
state,
|
|
410
|
+
updated_at: new Date().toISOString(),
|
|
411
|
+
...details,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function failStagingRuntime(stagingRoot, error) {
|
|
416
|
+
if (!stagingRoot || !fs.existsSync(stagingRoot)) return null;
|
|
417
|
+
writeReleaseState(stagingRoot, "failed", { error: String(error || "install failed") });
|
|
418
|
+
const failedRoot = path.join(
|
|
419
|
+
path.dirname(stagingRoot),
|
|
420
|
+
`.failed-${packageVersion}-${Date.now()}`,
|
|
421
|
+
);
|
|
422
|
+
fs.renameSync(stagingRoot, failedRoot);
|
|
423
|
+
return failedRoot;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function commitStagedRuntime(stagingRoot, releaseRoot) {
|
|
427
|
+
if (!stagingRoot) return null;
|
|
428
|
+
let replacedRoot = null;
|
|
429
|
+
if (fs.existsSync(releaseRoot)) {
|
|
430
|
+
replacedRoot = uniquePathWithSuffix(
|
|
431
|
+
path.join(path.dirname(releaseRoot), `${path.basename(releaseRoot)}-replaced`),
|
|
432
|
+
);
|
|
433
|
+
fs.renameSync(releaseRoot, replacedRoot);
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
fs.renameSync(stagingRoot, releaseRoot);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
if (replacedRoot && !fs.existsSync(releaseRoot)) {
|
|
439
|
+
fs.renameSync(replacedRoot, releaseRoot);
|
|
440
|
+
}
|
|
441
|
+
throw error;
|
|
442
|
+
}
|
|
443
|
+
return replacedRoot;
|
|
444
|
+
}
|
|
445
|
+
|
|
383
446
|
function currentRuntimePath(env = process.env) {
|
|
384
447
|
return path.join(resolveRuntimeBase(env), "current");
|
|
385
448
|
}
|
|
@@ -507,7 +570,21 @@ function listReleases(env = process.env) {
|
|
|
507
570
|
try {
|
|
508
571
|
return fs
|
|
509
572
|
.readdirSync(releasesDir, { withFileTypes: true })
|
|
510
|
-
.filter((entry) => entry.isDirectory())
|
|
573
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
|
574
|
+
.map((entry) => entry.name)
|
|
575
|
+
.sort();
|
|
576
|
+
} catch (error) {
|
|
577
|
+
if (error?.code === "ENOENT") return [];
|
|
578
|
+
throw error;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function listFailedReleases(env = process.env) {
|
|
583
|
+
const releasesDir = path.join(resolveRuntimeBase(env), "releases");
|
|
584
|
+
try {
|
|
585
|
+
return fs
|
|
586
|
+
.readdirSync(releasesDir, { withFileTypes: true })
|
|
587
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith(".failed-"))
|
|
511
588
|
.map((entry) => entry.name)
|
|
512
589
|
.sort();
|
|
513
590
|
} catch (error) {
|
|
@@ -944,6 +1021,44 @@ function readBootKeyFromKeystore(env = process.env) {
|
|
|
944
1021
|
}
|
|
945
1022
|
}
|
|
946
1023
|
|
|
1024
|
+
// New runtimes own boot-key precedence and verify candidates against
|
|
1025
|
+
// sealed_keys.json. An import failure means the active runtime predates this
|
|
1026
|
+
// bridge, so callers may use the legacy JS fallback below.
|
|
1027
|
+
function readBootKeyFromRuntimeResolver(env = process.env) {
|
|
1028
|
+
const runtimeRoot = resolvedCurrentRuntimeRoot(env);
|
|
1029
|
+
if (!runtimeRoot) return { supported: false, key: "" };
|
|
1030
|
+
const py = resolveVenvPython(runtimeRoot);
|
|
1031
|
+
if (!py) return { supported: false, key: "" };
|
|
1032
|
+
try {
|
|
1033
|
+
const res = spawnSync(
|
|
1034
|
+
py,
|
|
1035
|
+
["-c", "from agent_wallet.config import resolve_boot_key_for_installer as r; print(r())"],
|
|
1036
|
+
{
|
|
1037
|
+
cwd: path.join(runtimeRoot, "agent-wallet"),
|
|
1038
|
+
encoding: "utf8",
|
|
1039
|
+
timeout: positiveIntEnv("AGENT_WALLET_KEYSTORE_BRIDGE_TIMEOUT_MS", KEYSTORE_BRIDGE_TIMEOUT_MS, env),
|
|
1040
|
+
env: { ...env, OPENCLAW_HOME: resolveOpenclawHome(env) },
|
|
1041
|
+
},
|
|
1042
|
+
);
|
|
1043
|
+
if ((res.status ?? 1) !== 0) return { supported: false, key: "" };
|
|
1044
|
+
return { supported: true, key: String(res.stdout || "").trim() };
|
|
1045
|
+
} catch {
|
|
1046
|
+
return { supported: false, key: "" };
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function resolveLegacyInstallerBootKey(env = process.env) {
|
|
1051
|
+
for (const [source, key] of [
|
|
1052
|
+
["legacy_keystore", readBootKeyFromKeystore(env)],
|
|
1053
|
+
["current_runtime_env", currentBootKey(env)],
|
|
1054
|
+
["configured_file", resolveBootKeyFromFile(env)],
|
|
1055
|
+
["default_file", readTextIfExists(defaultBootKeyFile(env)).trim()],
|
|
1056
|
+
]) {
|
|
1057
|
+
if (key) return { key, source };
|
|
1058
|
+
}
|
|
1059
|
+
return { key: "", source: "none" };
|
|
1060
|
+
}
|
|
1061
|
+
|
|
947
1062
|
// Provision the boot key into the OS keystore via the freshly installed runtime's
|
|
948
1063
|
// Python. Returns true only when the import stored AND verified the key, so the
|
|
949
1064
|
// caller may safely drop the plaintext .env copy. Best-effort: false on any failure
|
|
@@ -1218,6 +1333,7 @@ function runStatus(args = []) {
|
|
|
1218
1333
|
previous_runtime: readLinkOrNull(previousRuntimePath()),
|
|
1219
1334
|
active_version: activeVersion(),
|
|
1220
1335
|
available_releases: listReleases(),
|
|
1336
|
+
failed_releases: listFailedReleases(),
|
|
1221
1337
|
update_available: computeUpdateAvailability(),
|
|
1222
1338
|
runtime_in_sync: computeRuntimeInSync(),
|
|
1223
1339
|
};
|
|
@@ -1236,15 +1352,17 @@ function buildInstallerEnv(args) {
|
|
|
1236
1352
|
const sealedKeysPath = path.join(resolveOpenclawHome(env), "sealed_keys.json");
|
|
1237
1353
|
const sealedKeysExist = fs.existsSync(sealedKeysPath);
|
|
1238
1354
|
const dryRun = hasFlag(args, "--dry-run");
|
|
1355
|
+
let bootKeySource = env.AGENT_WALLET_BOOT_KEY ? "environment" : "none";
|
|
1239
1356
|
if (!env.AGENT_WALLET_BOOT_KEY) {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1357
|
+
const runtimeResolution = readBootKeyFromRuntimeResolver(env);
|
|
1358
|
+
const fallback = runtimeResolution.supported
|
|
1359
|
+
? {
|
|
1360
|
+
key: runtimeResolution.key,
|
|
1361
|
+
source: runtimeResolution.key ? "runtime_verified" : "runtime_rejected",
|
|
1362
|
+
}
|
|
1363
|
+
: resolveLegacyInstallerBootKey(env);
|
|
1364
|
+
const existingBootKey = fallback.key;
|
|
1365
|
+
bootKeySource = fallback.source;
|
|
1248
1366
|
if (existingBootKey) {
|
|
1249
1367
|
env.AGENT_WALLET_BOOT_KEY = existingBootKey;
|
|
1250
1368
|
}
|
|
@@ -1264,6 +1382,7 @@ function buildInstallerEnv(args) {
|
|
|
1264
1382
|
if (!sealedKeysExist && shouldGenerateSecrets && !env.AGENT_WALLET_BOOT_KEY) {
|
|
1265
1383
|
generated.AGENT_WALLET_BOOT_KEY = token();
|
|
1266
1384
|
env.AGENT_WALLET_BOOT_KEY = generated.AGENT_WALLET_BOOT_KEY;
|
|
1385
|
+
bootKeySource = "generated";
|
|
1267
1386
|
}
|
|
1268
1387
|
if (!sealedKeysExist && shouldGenerateSecrets && !env.AGENT_WALLET_MASTER_KEY) {
|
|
1269
1388
|
generated.AGENT_WALLET_MASTER_KEY = token();
|
|
@@ -1273,7 +1392,7 @@ function buildInstallerEnv(args) {
|
|
|
1273
1392
|
generated.AGENT_WALLET_APPROVAL_SECRET = token();
|
|
1274
1393
|
env.AGENT_WALLET_APPROVAL_SECRET = generated.AGENT_WALLET_APPROVAL_SECRET;
|
|
1275
1394
|
}
|
|
1276
|
-
return { env, generated };
|
|
1395
|
+
return { env, generated, bootKeySource };
|
|
1277
1396
|
}
|
|
1278
1397
|
|
|
1279
1398
|
function runInstall(args, { commandName = "install" } = {}) {
|
|
@@ -1290,9 +1409,11 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1290
1409
|
const previousPath = previousRuntimePath();
|
|
1291
1410
|
const installerArgs = withoutCliOnlyArgs(args);
|
|
1292
1411
|
const dryRun = hasFlag(args, "--dry-run");
|
|
1412
|
+
const stagingRoot = !explicitRuntimeRoot && !dryRun ? stagingRootFor(packageVersion) : null;
|
|
1413
|
+
const installRoot = stagingRoot || releaseRoot;
|
|
1293
1414
|
|
|
1294
1415
|
if (!hasFlag(installerArgs, "--runtime-root")) {
|
|
1295
|
-
installerArgs.push("--runtime-root",
|
|
1416
|
+
installerArgs.push("--runtime-root", installRoot);
|
|
1296
1417
|
}
|
|
1297
1418
|
if (!hasFlag(installerArgs, "--install-from-runtime")) {
|
|
1298
1419
|
installerArgs.push("--install-from-runtime");
|
|
@@ -1305,7 +1426,11 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1305
1426
|
console.error(error.message);
|
|
1306
1427
|
return 1;
|
|
1307
1428
|
}
|
|
1308
|
-
const { env, generated } = installerEnv;
|
|
1429
|
+
const { env, generated, bootKeySource } = installerEnv;
|
|
1430
|
+
if (stagingRoot) {
|
|
1431
|
+
env.OPENCLAW_INSTALL_FINAL_ROOT = releaseRoot;
|
|
1432
|
+
writeUpdateJournal("preparing", { staging_root: stagingRoot, release_root: releaseRoot }, env);
|
|
1433
|
+
}
|
|
1309
1434
|
const result = spawnSync("sh", [setupPath, ...installerArgs], {
|
|
1310
1435
|
cwd: packageRoot,
|
|
1311
1436
|
stdio: "inherit",
|
|
@@ -1313,10 +1438,22 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1313
1438
|
});
|
|
1314
1439
|
|
|
1315
1440
|
if (result.error) {
|
|
1441
|
+
const failedRoot = failStagingRuntime(stagingRoot, result.error.message);
|
|
1442
|
+
if (!dryRun) {
|
|
1443
|
+
writeUpdateJournal("failed", { failed_runtime: failedRoot, error: result.error.message }, env);
|
|
1444
|
+
}
|
|
1316
1445
|
console.error(result.error.message);
|
|
1317
1446
|
return 1;
|
|
1318
1447
|
}
|
|
1319
1448
|
if ((result.status ?? 1) !== 0) {
|
|
1449
|
+
const failedRoot = failStagingRuntime(stagingRoot, `installer exited with ${result.status ?? 1}`);
|
|
1450
|
+
if (!dryRun) {
|
|
1451
|
+
writeUpdateJournal(
|
|
1452
|
+
"failed",
|
|
1453
|
+
{ failed_runtime: failedRoot, error: `installer exited with ${result.status ?? 1}` },
|
|
1454
|
+
env,
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1320
1457
|
return result.status ?? 1;
|
|
1321
1458
|
}
|
|
1322
1459
|
|
|
@@ -1324,34 +1461,21 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1324
1461
|
return 0;
|
|
1325
1462
|
}
|
|
1326
1463
|
|
|
1327
|
-
const currentTarget = existingRuntimePointerTarget(currentPath);
|
|
1328
|
-
if (currentTarget) {
|
|
1329
|
-
switchSymlink(previousPath, currentTarget);
|
|
1330
|
-
}
|
|
1331
|
-
switchSymlink(currentPath, releaseRoot);
|
|
1332
|
-
|
|
1333
1464
|
// Installs that pass --skip-python-setup may have no venv, so this handshake
|
|
1334
1465
|
// would fail and trigger a spurious rollback; such flows must set
|
|
1335
1466
|
// AGENT_WALLET_VERIFY_DISABLE=1 (verifyRuntime then skips).
|
|
1336
|
-
const
|
|
1467
|
+
const currentTarget = existingRuntimePointerTarget(currentPath);
|
|
1468
|
+
const verification = verifyRuntime(installRoot, env);
|
|
1337
1469
|
if (!verification.ok && !verification.skipped) {
|
|
1338
|
-
const
|
|
1339
|
-
const rolledBack = Boolean(
|
|
1340
|
-
if (rolledBack) {
|
|
1341
|
-
switchSymlink(currentPath, rollbackTarget);
|
|
1342
|
-
}
|
|
1470
|
+
const failedRoot = failStagingRuntime(stagingRoot, verification.error);
|
|
1471
|
+
const rolledBack = Boolean(currentTarget);
|
|
1343
1472
|
const previousVersion = rolledBack
|
|
1344
|
-
? path.basename(
|
|
1473
|
+
? path.basename(currentTarget)
|
|
1345
1474
|
: null;
|
|
1346
1475
|
|
|
1347
1476
|
let human;
|
|
1348
1477
|
let fix;
|
|
1349
1478
|
if (!rolledBack) {
|
|
1350
|
-
// First install / no good fallback — leave no active runtime rather than a
|
|
1351
|
-
// broken one. Deliberately do NOT point `previous` at the broken release,
|
|
1352
|
-
// so a later `rollback` cannot reactivate it; the release stays under
|
|
1353
|
-
// releases/<version> for inspection via `install --version`.
|
|
1354
|
-
removeRuntimePointer(currentPath);
|
|
1355
1479
|
human =
|
|
1356
1480
|
verification.category === "broken_release"
|
|
1357
1481
|
? `Release ${packageVersion} is broken and there is no previous working version to fall back to. Nothing is active. This is a bad release — please report it; a patched version will follow.`
|
|
@@ -1380,7 +1504,9 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1380
1504
|
category: verification.category || "unknown",
|
|
1381
1505
|
error: `runtime verification failed: ${verification.error}`,
|
|
1382
1506
|
rolled_back: rolledBack,
|
|
1507
|
+
switched_current: false,
|
|
1383
1508
|
kept_version: previousVersion,
|
|
1509
|
+
failed_runtime: failedRoot,
|
|
1384
1510
|
current_runtime_target: readLinkOrNull(currentPath),
|
|
1385
1511
|
message: human,
|
|
1386
1512
|
fix,
|
|
@@ -1389,15 +1515,20 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1389
1515
|
2,
|
|
1390
1516
|
),
|
|
1391
1517
|
);
|
|
1518
|
+
writeUpdateJournal(
|
|
1519
|
+
"failed",
|
|
1520
|
+
{ failed_runtime: failedRoot, error: verification.error, kept_version: previousVersion },
|
|
1521
|
+
env,
|
|
1522
|
+
);
|
|
1392
1523
|
return 1;
|
|
1393
1524
|
}
|
|
1394
1525
|
|
|
1395
1526
|
if (env.AGENT_WALLET_BOOT_KEY) {
|
|
1396
|
-
const envPath = path.join(
|
|
1527
|
+
const envPath = path.join(installRoot, "agent-wallet", ".env");
|
|
1397
1528
|
// Prefer the OS keystore: provision the boot key there and keep the plaintext
|
|
1398
1529
|
// out of the release .env entirely. Only fall back to the legacy .env write when
|
|
1399
1530
|
// no keystore round-trip is possible, so the runtime can still resolve the key.
|
|
1400
|
-
if (provisionBootKeyToKeystore(
|
|
1531
|
+
if (provisionBootKeyToKeystore(installRoot, env, env.AGENT_WALLET_BOOT_KEY)) {
|
|
1401
1532
|
envFileUnset(envPath, ["AGENT_WALLET_BOOT_KEY"]);
|
|
1402
1533
|
} else {
|
|
1403
1534
|
envFileSet(envPath, {
|
|
@@ -1406,7 +1537,32 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1406
1537
|
}
|
|
1407
1538
|
}
|
|
1408
1539
|
|
|
1409
|
-
|
|
1540
|
+
writeReleaseState(installRoot, "verified", {
|
|
1541
|
+
verification_skipped: Boolean(verification.skipped),
|
|
1542
|
+
});
|
|
1543
|
+
writeUpdateJournal("verified", { staging_root: stagingRoot, release_root: releaseRoot }, env);
|
|
1544
|
+
|
|
1545
|
+
let replacedRoot = null;
|
|
1546
|
+
try {
|
|
1547
|
+
replacedRoot = commitStagedRuntime(stagingRoot, releaseRoot);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
const failedRoot = failStagingRuntime(stagingRoot, error.message);
|
|
1550
|
+
writeUpdateJournal("failed", { failed_runtime: failedRoot, error: error.message }, env);
|
|
1551
|
+
console.error(`Could not commit staged runtime: ${error.message}`);
|
|
1552
|
+
return 1;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
const previousTarget =
|
|
1556
|
+
currentTarget && path.resolve(currentTarget) === path.resolve(releaseRoot) && replacedRoot
|
|
1557
|
+
? replacedRoot
|
|
1558
|
+
: currentTarget;
|
|
1559
|
+
if (previousTarget) {
|
|
1560
|
+
switchSymlink(previousPath, previousTarget);
|
|
1561
|
+
}
|
|
1562
|
+
switchSymlink(currentPath, releaseRoot);
|
|
1563
|
+
writeUpdateJournal("committed", { release_root: releaseRoot, previous_runtime: previousTarget }, env);
|
|
1564
|
+
|
|
1565
|
+
const integrationRefresh = repairInstalledEditorIntegrations(env);
|
|
1410
1566
|
|
|
1411
1567
|
const pythonInfo = activePythonRuntimeInfo(env);
|
|
1412
1568
|
const nodeInfo = activeNodeRuntimeInfo(env)
|
|
@@ -1426,6 +1582,9 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1426
1582
|
current_runtime: currentPath,
|
|
1427
1583
|
previous_runtime: readLinkOrNull(previousPath),
|
|
1428
1584
|
generated_runtime_secrets: Object.keys(generated),
|
|
1585
|
+
boot_key_source: bootKeySource,
|
|
1586
|
+
staged: Boolean(stagingRoot),
|
|
1587
|
+
release_state: "verified",
|
|
1429
1588
|
integration_refresh: integrationRefresh,
|
|
1430
1589
|
},
|
|
1431
1590
|
null,
|
|
@@ -1867,7 +2026,7 @@ function runHermesInstall(args) {
|
|
|
1867
2026
|
function runCodexInstall(args) {
|
|
1868
2027
|
const codexHome = resolveCodexHome();
|
|
1869
2028
|
const pluginSource = resolveCodexPluginSource();
|
|
1870
|
-
const pinnedEnv =
|
|
2029
|
+
const pinnedEnv = { pinned: false, reason: "plugin source is immutable" };
|
|
1871
2030
|
const pluginRoot = resolveCodexPluginInstallRoot();
|
|
1872
2031
|
const pluginTarget = path.join(pluginRoot, "agent-wallet");
|
|
1873
2032
|
const marketplacePath = resolveCodexMarketplacePath();
|
|
@@ -1989,10 +2148,6 @@ function pinHomeIntoMcpFile(mcpPath, env = process.env) {
|
|
|
1989
2148
|
return { pinned: true, openclaw_home: home, path: mcpPath };
|
|
1990
2149
|
}
|
|
1991
2150
|
|
|
1992
|
-
function pinEditorMcpEnv(pluginSource, env = process.env) {
|
|
1993
|
-
return pinHomeIntoMcpFile(path.join(pluginSource, ".mcp.json"), env);
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
2151
|
// Claude Code copies the plugin into a version-keyed cache and reads THAT copy,
|
|
1997
2152
|
// so the bundle pin alone is ineffective once a cache exists. Pin every cached
|
|
1998
2153
|
// copy too. Cache root is overridable for tests.
|
|
@@ -2065,8 +2220,7 @@ function ensureClaudeCodeMarketplace(marketplaceDir, pluginSource, force) {
|
|
|
2065
2220
|
|
|
2066
2221
|
function runClaudeCodeInstall(args) {
|
|
2067
2222
|
const pluginSource = resolveClaudeCodePluginSource();
|
|
2068
|
-
const pinnedEnv =
|
|
2069
|
-
const pinnedCache = pinClaudeCacheCopies();
|
|
2223
|
+
const pinnedEnv = { pinned: false, reason: "plugin source is immutable" };
|
|
2070
2224
|
const force = hasFlag(args, "--force");
|
|
2071
2225
|
const skipEnable = hasFlag(args, "--skip-enable");
|
|
2072
2226
|
const claudeBin = commandPath("claude");
|
|
@@ -2128,6 +2282,7 @@ function runClaudeCodeInstall(args) {
|
|
|
2128
2282
|
}
|
|
2129
2283
|
|
|
2130
2284
|
const ok = enable.skipped || enable.ok;
|
|
2285
|
+
const pinnedCache = pinClaudeCacheCopies();
|
|
2131
2286
|
const pluginDirFlagFull = `claude --plugin-dir ${pluginSource}`;
|
|
2132
2287
|
console.log(
|
|
2133
2288
|
JSON.stringify(
|
|
@@ -2152,75 +2307,115 @@ function runClaudeCodeInstall(args) {
|
|
|
2152
2307
|
return enable.skipped || enable.ok ? 0 : 1;
|
|
2153
2308
|
}
|
|
2154
2309
|
|
|
2155
|
-
function
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
return (
|
|
2165
|
-
fs.existsSync(path.join(resolveCodexPluginInstallRoot(env), "agent-wallet")) ||
|
|
2166
|
-
fs.existsSync(resolveCodexMarketplacePath(env))
|
|
2167
|
-
);
|
|
2310
|
+
function runtimeReleasePath(value, env = process.env) {
|
|
2311
|
+
if (!value) return false;
|
|
2312
|
+
try {
|
|
2313
|
+
const releasesRoot = path.join(resolveRuntimeBase(env), "releases");
|
|
2314
|
+
const relative = path.relative(releasesRoot, path.resolve(expandHome(String(value))));
|
|
2315
|
+
return Boolean(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`);
|
|
2316
|
+
} catch {
|
|
2317
|
+
return false;
|
|
2318
|
+
}
|
|
2168
2319
|
}
|
|
2169
2320
|
|
|
2170
|
-
function
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
)
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
);
|
|
2321
|
+
function pathEntryExists(pathname) {
|
|
2322
|
+
try {
|
|
2323
|
+
fs.lstatSync(pathname);
|
|
2324
|
+
return true;
|
|
2325
|
+
} catch (error) {
|
|
2326
|
+
if (error?.code === "ENOENT") return false;
|
|
2327
|
+
throw error;
|
|
2328
|
+
}
|
|
2179
2329
|
}
|
|
2180
2330
|
|
|
2181
|
-
function
|
|
2182
|
-
|
|
2183
|
-
const lines = [];
|
|
2184
|
-
console.log = (...items) => {
|
|
2185
|
-
lines.push(items.join(" "));
|
|
2186
|
-
};
|
|
2331
|
+
function repairRuntimeSymlink(name, linkPath, desiredTarget, env = process.env) {
|
|
2332
|
+
let rawTarget;
|
|
2187
2333
|
try {
|
|
2188
|
-
const
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
ok: code === 0,
|
|
2194
|
-
code,
|
|
2195
|
-
payload: output ? extractTrailingJson(output) : null,
|
|
2196
|
-
error: code === 0 ? "" : output.trim(),
|
|
2197
|
-
};
|
|
2334
|
+
const stat = fs.lstatSync(linkPath);
|
|
2335
|
+
if (!stat.isSymbolicLink()) {
|
|
2336
|
+
return { name, attempted: false, ok: true, repaired: false, reason: "not a symlink" };
|
|
2337
|
+
}
|
|
2338
|
+
rawTarget = fs.readlinkSync(linkPath);
|
|
2198
2339
|
} catch (error) {
|
|
2199
|
-
|
|
2200
|
-
name,
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2340
|
+
if (error?.code === "ENOENT") {
|
|
2341
|
+
return { name, attempted: false, ok: true, repaired: false, reason: "not installed" };
|
|
2342
|
+
}
|
|
2343
|
+
return { name, attempted: true, ok: false, repaired: false, error: error.message };
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
const absoluteTarget = path.resolve(path.dirname(linkPath), rawTarget);
|
|
2347
|
+
const logicalCurrent = currentRuntimePath(env);
|
|
2348
|
+
if (absoluteTarget === path.resolve(desiredTarget) || absoluteTarget.startsWith(`${logicalCurrent}${path.sep}`)) {
|
|
2349
|
+
return { name, attempted: true, ok: true, repaired: false, reason: "already current" };
|
|
2350
|
+
}
|
|
2351
|
+
if (!runtimeReleasePath(absoluteTarget, env)) {
|
|
2352
|
+
return { name, attempted: false, ok: true, repaired: false, reason: "external target preserved" };
|
|
2209
2353
|
}
|
|
2354
|
+
if (!fs.existsSync(desiredTarget)) {
|
|
2355
|
+
return { name, attempted: true, ok: false, repaired: false, error: `missing ${desiredTarget}` };
|
|
2356
|
+
}
|
|
2357
|
+
switchSymlink(linkPath, desiredTarget);
|
|
2358
|
+
return { name, attempted: true, ok: true, repaired: true, target: desiredTarget };
|
|
2210
2359
|
}
|
|
2211
2360
|
|
|
2212
|
-
function
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2215
|
-
|
|
2361
|
+
function repairHermesEnv(env = process.env) {
|
|
2362
|
+
const envPath = path.join(resolveHermesHome(env), ".env");
|
|
2363
|
+
const existing = readEnvFile(envPath);
|
|
2364
|
+
const repaired = [];
|
|
2365
|
+
const currentPackage = path.join(currentRuntimePath(env), "agent-wallet");
|
|
2366
|
+
if (runtimeReleasePath(existing.AGENT_WALLET_PACKAGE_ROOT, env)) {
|
|
2367
|
+
envFileSet(envPath, { AGENT_WALLET_PACKAGE_ROOT: currentPackage });
|
|
2368
|
+
repaired.push("AGENT_WALLET_PACKAGE_ROOT");
|
|
2369
|
+
}
|
|
2370
|
+
if (runtimeReleasePath(existing.AGENT_WALLET_PYTHON, env)) {
|
|
2371
|
+
const currentPython = resolveVenvPython(currentRuntimePath(env));
|
|
2372
|
+
if (currentPython) {
|
|
2373
|
+
envFileSet(envPath, { AGENT_WALLET_PYTHON: currentPython });
|
|
2374
|
+
} else {
|
|
2375
|
+
envFileUnset(envPath, ["AGENT_WALLET_PYTHON"]);
|
|
2376
|
+
}
|
|
2377
|
+
repaired.push("AGENT_WALLET_PYTHON");
|
|
2216
2378
|
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2379
|
+
return repaired;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
function repairInstalledEditorIntegrations(env = process.env) {
|
|
2383
|
+
const results = [];
|
|
2384
|
+
const currentRoot = currentRuntimePath(env);
|
|
2385
|
+
const hermesTarget = path.join(resolveHermesHome(env), "plugins", "agent_wallet");
|
|
2386
|
+
if (pathEntryExists(hermesTarget) || fs.existsSync(path.join(resolveHermesHome(env), ".env"))) {
|
|
2387
|
+
const result = repairRuntimeSymlink(
|
|
2388
|
+
"hermes",
|
|
2389
|
+
hermesTarget,
|
|
2390
|
+
path.join(currentRoot, "hermes", "plugins", "agent_wallet"),
|
|
2391
|
+
env,
|
|
2392
|
+
);
|
|
2393
|
+
result.env_repaired = repairHermesEnv(env);
|
|
2394
|
+
results.push(result);
|
|
2219
2395
|
}
|
|
2220
|
-
|
|
2396
|
+
|
|
2397
|
+
const codexTarget = path.join(resolveCodexPluginInstallRoot(env), "agent-wallet");
|
|
2398
|
+
if (pathEntryExists(codexTarget)) {
|
|
2221
2399
|
results.push(
|
|
2222
|
-
|
|
2400
|
+
repairRuntimeSymlink(
|
|
2401
|
+
"codex",
|
|
2402
|
+
codexTarget,
|
|
2403
|
+
path.join(currentRoot, "codex", "plugins", "agent-wallet"),
|
|
2404
|
+
env,
|
|
2405
|
+
),
|
|
2406
|
+
);
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
const claudeTarget = path.join(resolveClaudeCodeMarketplaceDir(env), "plugins", "agent-wallet");
|
|
2410
|
+
if (pathEntryExists(claudeTarget)) {
|
|
2411
|
+
const result = repairRuntimeSymlink(
|
|
2412
|
+
"claude-code",
|
|
2413
|
+
claudeTarget,
|
|
2414
|
+
path.join(currentRoot, "claude-code", "plugins", "agent-wallet"),
|
|
2415
|
+
env,
|
|
2223
2416
|
);
|
|
2417
|
+
result.cache_pins = pinClaudeCacheCopies(env);
|
|
2418
|
+
results.push(result);
|
|
2224
2419
|
}
|
|
2225
2420
|
return results;
|
|
2226
2421
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-wallet",
|
|
3
3
|
"displayName": "Agent Wallet",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.74",
|
|
5
5
|
"description": "Claude Code bridge for the existing AgentLayer wallet runtime. Connects to Solana, Bitcoin, and EVM wallets without creating a new one.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "AgentLayer"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentlayer.tech/wallet",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.74",
|
|
4
4
|
"description": "NPM installer for the OpenClaw Agent Wallet local runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"build:openclaw-plugins": "node scripts/manage_openclaw_plugin_packages.mjs build",
|
|
20
20
|
"check:openclaw-plugins": "node scripts/manage_openclaw_plugin_packages.mjs check",
|
|
21
21
|
"check:release-version": "node scripts/check_release_version.mjs",
|
|
22
|
+
"test:release-promotion": "node --test scripts/validate_npm_promotion.test.mjs",
|
|
22
23
|
"version:sync": "node scripts/sync_version.mjs",
|
|
23
24
|
"release:local": "node scripts/release_local.mjs",
|
|
24
25
|
"test:npm-installer": "python3 agent-wallet/tests/smoke_npm_installer.py",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wdk-evm-wallet",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.74",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Separate EVM wallet service built on Tether WDK.",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"test:morpho-runtime": "node --test --test-concurrency=1 tests/smoke_morpho_runtime.mjs",
|
|
15
15
|
"test:lido-runtime": "node --test --test-concurrency=1 tests/smoke_lido_runtime.mjs",
|
|
16
16
|
"test:uniswap-runtime": "node --test --test-concurrency=1 tests/smoke_uniswap_runtime.mjs",
|
|
17
|
-
"test:unit": "node --test tests/unit_uniswap_helpers.mjs"
|
|
17
|
+
"test:unit": "node --test tests/unit_uniswap_helpers.mjs",
|
|
18
|
+
"test:identity": "node --test tests/unit_instance_identity.mjs"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"@morpho-org/morpho-sdk": "^3.2.0",
|