@agentlayer.tech/wallet 0.1.75 → 0.1.76
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/VERSION +1 -1
- package/agent-wallet/AGENTS.md +1 -0
- package/agent-wallet/UPGRADE_COMPATIBILITY.md +36 -0
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/boot_key_recovery.py +10 -1
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/bin/lib/boot-key.mjs +185 -0
- package/bin/lib/integrations.mjs +424 -0
- package/bin/lib/update-transaction.mjs +235 -0
- package/bin/openclaw-agent-wallet.mjs +225 -520
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- 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 +1 -1
|
@@ -6,6 +6,9 @@ import fs from "node:fs";
|
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { createBootKeyManager } from "./lib/boot-key.mjs";
|
|
10
|
+
import { createHostIntegrationManager, createIntegrationManager } from "./lib/integrations.mjs";
|
|
11
|
+
import { createUpdateTransactionManager } from "./lib/update-transaction.mjs";
|
|
9
12
|
|
|
10
13
|
const cliPath = fileURLToPath(import.meta.url);
|
|
11
14
|
const packageRoot = path.resolve(path.dirname(cliPath), "..");
|
|
@@ -388,74 +391,85 @@ function stagingRootFor(version, env = process.env) {
|
|
|
388
391
|
);
|
|
389
392
|
}
|
|
390
393
|
|
|
391
|
-
function
|
|
392
|
-
return
|
|
394
|
+
function updateTransactions(env = process.env) {
|
|
395
|
+
return createUpdateTransactionManager({
|
|
396
|
+
runtimeBase: resolveRuntimeBase(env),
|
|
397
|
+
packageVersion,
|
|
398
|
+
env,
|
|
399
|
+
});
|
|
393
400
|
}
|
|
394
401
|
|
|
395
|
-
function
|
|
396
|
-
return
|
|
402
|
+
function acquireUpdateLock(env = process.env) {
|
|
403
|
+
return updateTransactions(env).acquireLock();
|
|
397
404
|
}
|
|
398
405
|
|
|
399
|
-
function
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
return payload;
|
|
406
|
+
function releaseUpdateLock(lock) {
|
|
407
|
+
return createUpdateTransactionManager({
|
|
408
|
+
runtimeBase: path.dirname(lock.path),
|
|
409
|
+
packageVersion,
|
|
410
|
+
}).releaseLock(lock);
|
|
405
411
|
}
|
|
406
412
|
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
registry.integrations[name] = {
|
|
410
|
-
...details,
|
|
411
|
-
managed: true,
|
|
412
|
-
installed_version: packageVersion,
|
|
413
|
-
updated_at: new Date().toISOString(),
|
|
414
|
-
};
|
|
415
|
-
registry.updated_at = new Date().toISOString();
|
|
416
|
-
writeJsonFileAtomic(integrationRegistryPath(env), registry);
|
|
417
|
-
return registry.integrations[name];
|
|
413
|
+
function holdUpdateLockForTest(env = process.env) {
|
|
414
|
+
updateTransactions(env).holdLockForTest();
|
|
418
415
|
}
|
|
419
416
|
|
|
420
|
-
function
|
|
421
|
-
|
|
422
|
-
|
|
417
|
+
function readUpdateJournal(env = process.env) {
|
|
418
|
+
return updateTransactions(env).readJournal();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function integrations(env = process.env) {
|
|
422
|
+
return createIntegrationManager({
|
|
423
|
+
runtimeBase: resolveRuntimeBase(env),
|
|
424
|
+
packageVersion,
|
|
425
|
+
activeVersion: () => activeVersion(env),
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function recordManagedIntegration(name, details = {}, env = process.env) {
|
|
430
|
+
return integrations(env).record(name, details);
|
|
423
431
|
}
|
|
424
432
|
|
|
425
433
|
function integrationSyncStatus(env = process.env) {
|
|
426
|
-
|
|
427
|
-
const registry = readIntegrationRegistry(env);
|
|
428
|
-
const integrations = Object.entries(registry.integrations)
|
|
429
|
-
.filter(([, entry]) => entry?.managed === true)
|
|
430
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
431
|
-
.map(([name, entry]) => {
|
|
432
|
-
const versionInSync = active === null || entry.installed_version === active;
|
|
433
|
-
const registrationOk = entry.registration_ok !== false;
|
|
434
|
-
return {
|
|
435
|
-
name,
|
|
436
|
-
installed_version: entry.installed_version || null,
|
|
437
|
-
active_version: active,
|
|
438
|
-
in_sync: versionInSync && registrationOk,
|
|
439
|
-
registration_ok: registrationOk,
|
|
440
|
-
restart_required: Boolean(entry.restart_required),
|
|
441
|
-
};
|
|
442
|
-
});
|
|
443
|
-
return {
|
|
444
|
-
in_sync: integrations.every((entry) => entry.in_sync),
|
|
445
|
-
integrations,
|
|
446
|
-
};
|
|
434
|
+
return integrations(env).status();
|
|
447
435
|
}
|
|
448
436
|
|
|
449
|
-
function
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
437
|
+
function hostIntegrations(env = process.env) {
|
|
438
|
+
return createHostIntegrationManager({
|
|
439
|
+
env,
|
|
440
|
+
packageRoot,
|
|
441
|
+
registry: integrations(env),
|
|
442
|
+
currentRuntimePath: currentRuntimePath(env),
|
|
443
|
+
openclawHome: resolveOpenclawHome(env),
|
|
444
|
+
hermesHome: resolveHermesHome(env),
|
|
445
|
+
codexHome: resolveCodexHome(env),
|
|
446
|
+
codexPluginRoot: resolveCodexPluginInstallRoot(env),
|
|
447
|
+
codexMarketplacePath: resolveCodexMarketplacePath(env),
|
|
448
|
+
claudeMarketplaceDir: resolveClaudeCodeMarketplaceDir(env),
|
|
449
|
+
claudeMarketplaceName: CLAUDE_CODE_MARKETPLACE_NAME,
|
|
450
|
+
expandHome,
|
|
451
|
+
resolveVenvPython,
|
|
452
|
+
commandPath,
|
|
453
|
+
repairRuntimeSymlink,
|
|
454
|
+
repairHermesEnv,
|
|
455
|
+
ensureCodexMarketplaceEntry,
|
|
456
|
+
ensureClaudeCodeMarketplace,
|
|
457
|
+
pinClaudeCacheCopies,
|
|
456
458
|
});
|
|
457
459
|
}
|
|
458
460
|
|
|
461
|
+
function writeUpdateJournal(state, details = {}, env = process.env) {
|
|
462
|
+
updateTransactions(env).writeJournal(state, details);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function recoverInterruptedUpdate(env = process.env) {
|
|
466
|
+
return updateTransactions(env).recover();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function updateRecoveryStatus(env = process.env) {
|
|
470
|
+
return updateTransactions(env).status();
|
|
471
|
+
}
|
|
472
|
+
|
|
459
473
|
function writeReleaseState(runtimeRoot, state, details = {}) {
|
|
460
474
|
writeJsonFile(path.join(runtimeRoot, ".agent-wallet-release.json"), {
|
|
461
475
|
schema_version: 1,
|
|
@@ -477,14 +491,14 @@ function failStagingRuntime(stagingRoot, error) {
|
|
|
477
491
|
return failedRoot;
|
|
478
492
|
}
|
|
479
493
|
|
|
480
|
-
function commitStagedRuntime(stagingRoot, releaseRoot) {
|
|
494
|
+
function commitStagedRuntime(stagingRoot, releaseRoot, replacedRoot = null, env = process.env) {
|
|
481
495
|
if (!stagingRoot) return null;
|
|
482
|
-
let replacedRoot = null;
|
|
483
496
|
if (fs.existsSync(releaseRoot)) {
|
|
484
|
-
replacedRoot
|
|
497
|
+
replacedRoot ||= uniquePathWithSuffix(
|
|
485
498
|
path.join(path.dirname(releaseRoot), `${path.basename(releaseRoot)}-replaced`),
|
|
486
499
|
);
|
|
487
500
|
fs.renameSync(releaseRoot, replacedRoot);
|
|
501
|
+
if (env.AGENT_WALLET_TEST_EXIT_AFTER_RELEASE_RENAME === "1") process.exit(86);
|
|
488
502
|
}
|
|
489
503
|
try {
|
|
490
504
|
fs.renameSync(stagingRoot, releaseRoot);
|
|
@@ -1014,146 +1028,36 @@ function writeJsonFileAtomic(pathname, value, mode = 0o600) {
|
|
|
1014
1028
|
}
|
|
1015
1029
|
}
|
|
1016
1030
|
|
|
1017
|
-
function
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
function writeSecretFile(pathname, value) {
|
|
1033
|
-
fs.mkdirSync(path.dirname(pathname), { recursive: true });
|
|
1034
|
-
fs.writeFileSync(pathname, `${String(value || "").trim()}\n`, { mode: 0o600 });
|
|
1035
|
-
try {
|
|
1036
|
-
fs.chmodSync(pathname, 0o600);
|
|
1037
|
-
} catch {
|
|
1038
|
-
// ignored
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
function resolveBootKeyFromFile(env = process.env) {
|
|
1043
|
-
const keyFile = String(env.AGENT_WALLET_BOOT_KEY_FILE || "").trim();
|
|
1044
|
-
if (!keyFile) return "";
|
|
1045
|
-
return readTextIfExists(path.resolve(expandHome(keyFile))).trim();
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
function defaultBootKeyFile(env = process.env) {
|
|
1049
|
-
return path.join(resolveRuntimeBase(env), "boot-key");
|
|
1031
|
+
function bootKeys(env = process.env) {
|
|
1032
|
+
return createBootKeyManager({
|
|
1033
|
+
runtimeBase: resolveRuntimeBase(env),
|
|
1034
|
+
openclawHome: resolveOpenclawHome(env),
|
|
1035
|
+
currentRuntimeRoot: () => resolvedCurrentRuntimeRoot(env),
|
|
1036
|
+
resolveVenvPython,
|
|
1037
|
+
expandHome,
|
|
1038
|
+
bridgeTimeoutMs: positiveIntEnv(
|
|
1039
|
+
"AGENT_WALLET_KEYSTORE_BRIDGE_TIMEOUT_MS",
|
|
1040
|
+
KEYSTORE_BRIDGE_TIMEOUT_MS,
|
|
1041
|
+
env,
|
|
1042
|
+
),
|
|
1043
|
+
env,
|
|
1044
|
+
});
|
|
1050
1045
|
}
|
|
1051
1046
|
|
|
1052
1047
|
function ensureBootKeyFile(env = process.env) {
|
|
1053
|
-
|
|
1054
|
-
const keyFile = configuredFile ? path.resolve(expandHome(configuredFile)) : defaultBootKeyFile(env);
|
|
1055
|
-
const existing = readTextIfExists(keyFile).trim();
|
|
1056
|
-
if (existing) {
|
|
1057
|
-
return { path: keyFile, status: "existing" };
|
|
1058
|
-
}
|
|
1059
|
-
const bootKey = String(env.AGENT_WALLET_BOOT_KEY || "").trim() || resolveBootKeyFromFile(env) || currentBootKey(env);
|
|
1060
|
-
if (!bootKey) {
|
|
1061
|
-
return { path: keyFile, status: "missing" };
|
|
1062
|
-
}
|
|
1063
|
-
writeSecretFile(keyFile, bootKey);
|
|
1064
|
-
return { path: keyFile, status: "created" };
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
// Read the boot key from the OS keystore via the current runtime's Python
|
|
1068
|
-
// (best-effort, "" on any failure). Lets a re-install after the runtime migration
|
|
1069
|
-
// — which moves the key into the keystore and deletes every plaintext copy — still
|
|
1070
|
-
// resolve the existing boot key instead of refusing to touch sealed secrets.
|
|
1071
|
-
function readBootKeyFromKeystore(env = process.env) {
|
|
1072
|
-
const runtimeRoot = resolvedCurrentRuntimeRoot(env);
|
|
1073
|
-
if (!runtimeRoot) return "";
|
|
1074
|
-
const py = resolveVenvPython(runtimeRoot);
|
|
1075
|
-
if (!py) return "";
|
|
1076
|
-
try {
|
|
1077
|
-
const res = spawnSync(
|
|
1078
|
-
py,
|
|
1079
|
-
["-c", "from agent_wallet.config import read_boot_key_from_keystore as r; print(r())"],
|
|
1080
|
-
{
|
|
1081
|
-
cwd: path.join(runtimeRoot, "agent-wallet"),
|
|
1082
|
-
encoding: "utf8",
|
|
1083
|
-
timeout: positiveIntEnv("AGENT_WALLET_KEYSTORE_BRIDGE_TIMEOUT_MS", KEYSTORE_BRIDGE_TIMEOUT_MS, env),
|
|
1084
|
-
env: { ...env, OPENCLAW_HOME: resolveOpenclawHome(env) },
|
|
1085
|
-
},
|
|
1086
|
-
);
|
|
1087
|
-
if ((res.status ?? 1) !== 0) return "";
|
|
1088
|
-
return String(res.stdout || "").trim();
|
|
1089
|
-
} catch {
|
|
1090
|
-
return "";
|
|
1091
|
-
}
|
|
1048
|
+
return bootKeys(env).ensureFile();
|
|
1092
1049
|
}
|
|
1093
1050
|
|
|
1094
|
-
// New runtimes own boot-key precedence and verify candidates against
|
|
1095
|
-
// sealed_keys.json. An import failure means the active runtime predates this
|
|
1096
|
-
// bridge, so callers may use the legacy JS fallback below.
|
|
1097
1051
|
function readBootKeyFromRuntimeResolver(env = process.env) {
|
|
1098
|
-
|
|
1099
|
-
if (!runtimeRoot) return { supported: false, key: "" };
|
|
1100
|
-
const py = resolveVenvPython(runtimeRoot);
|
|
1101
|
-
if (!py) return { supported: false, key: "" };
|
|
1102
|
-
try {
|
|
1103
|
-
const res = spawnSync(
|
|
1104
|
-
py,
|
|
1105
|
-
["-c", "from agent_wallet.config import resolve_boot_key_for_installer as r; print(r())"],
|
|
1106
|
-
{
|
|
1107
|
-
cwd: path.join(runtimeRoot, "agent-wallet"),
|
|
1108
|
-
encoding: "utf8",
|
|
1109
|
-
timeout: positiveIntEnv("AGENT_WALLET_KEYSTORE_BRIDGE_TIMEOUT_MS", KEYSTORE_BRIDGE_TIMEOUT_MS, env),
|
|
1110
|
-
env: { ...env, OPENCLAW_HOME: resolveOpenclawHome(env) },
|
|
1111
|
-
},
|
|
1112
|
-
);
|
|
1113
|
-
if ((res.status ?? 1) !== 0) return { supported: false, key: "" };
|
|
1114
|
-
return { supported: true, key: String(res.stdout || "").trim() };
|
|
1115
|
-
} catch {
|
|
1116
|
-
return { supported: false, key: "" };
|
|
1117
|
-
}
|
|
1052
|
+
return bootKeys(env).resolveFromRuntime();
|
|
1118
1053
|
}
|
|
1119
1054
|
|
|
1120
1055
|
function resolveLegacyInstallerBootKey(env = process.env) {
|
|
1121
|
-
|
|
1122
|
-
["legacy_keystore", readBootKeyFromKeystore(env)],
|
|
1123
|
-
["current_runtime_env", currentBootKey(env)],
|
|
1124
|
-
["configured_file", resolveBootKeyFromFile(env)],
|
|
1125
|
-
["default_file", readTextIfExists(defaultBootKeyFile(env)).trim()],
|
|
1126
|
-
]) {
|
|
1127
|
-
if (key) return { key, source };
|
|
1128
|
-
}
|
|
1129
|
-
return { key: "", source: "none" };
|
|
1056
|
+
return bootKeys(env).resolveLegacy();
|
|
1130
1057
|
}
|
|
1131
1058
|
|
|
1132
|
-
// Provision the boot key into the OS keystore via the freshly installed runtime's
|
|
1133
|
-
// Python. Returns true only when the import stored AND verified the key, so the
|
|
1134
|
-
// caller may safely drop the plaintext .env copy. Best-effort: false on any failure
|
|
1135
|
-
// (e.g. no usable keystore), in which case the caller keeps the legacy .env write.
|
|
1136
1059
|
function provisionBootKeyToKeystore(releaseRoot, env, bootKey) {
|
|
1137
|
-
|
|
1138
|
-
if (!key) return false;
|
|
1139
|
-
const py = resolveVenvPython(releaseRoot);
|
|
1140
|
-
if (!py) return false;
|
|
1141
|
-
try {
|
|
1142
|
-
const res = spawnSync(
|
|
1143
|
-
py,
|
|
1144
|
-
["-m", "agent_wallet.openclaw_cli", "boot-key-import", "--key-stdin"],
|
|
1145
|
-
{
|
|
1146
|
-
cwd: path.join(releaseRoot, "agent-wallet"),
|
|
1147
|
-
input: key,
|
|
1148
|
-
encoding: "utf8",
|
|
1149
|
-
timeout: positiveIntEnv("AGENT_WALLET_KEYSTORE_BRIDGE_TIMEOUT_MS", KEYSTORE_BRIDGE_TIMEOUT_MS, env),
|
|
1150
|
-
env: { ...env, OPENCLAW_HOME: resolveOpenclawHome(env) },
|
|
1151
|
-
},
|
|
1152
|
-
);
|
|
1153
|
-
return (res.status ?? 1) === 0;
|
|
1154
|
-
} catch {
|
|
1155
|
-
return false;
|
|
1156
|
-
}
|
|
1060
|
+
return bootKeys(env).provision(releaseRoot, bootKey);
|
|
1157
1061
|
}
|
|
1158
1062
|
|
|
1159
1063
|
function resolveVenvPython(releaseRoot) {
|
|
@@ -1231,6 +1135,10 @@ function verifyRuntime(releaseRoot, env = process.env) {
|
|
|
1231
1135
|
};
|
|
1232
1136
|
}
|
|
1233
1137
|
|
|
1138
|
+
function verifyBootKeyWithRuntime(releaseRoot, env = process.env) {
|
|
1139
|
+
return bootKeys(env).verifyWithRuntime(releaseRoot);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1234
1142
|
function resolveEditorServerChecks(env = process.env) {
|
|
1235
1143
|
const checks = [];
|
|
1236
1144
|
// Claude Code's launcher (run_mcp.sh) falls back to the runtime codex
|
|
@@ -1381,10 +1289,20 @@ function runDoctor(args = []) {
|
|
|
1381
1289
|
fix: integrationSync.in_sync ? "" : "wallet update --yes",
|
|
1382
1290
|
});
|
|
1383
1291
|
|
|
1292
|
+
const recoveryStatus = updateRecoveryStatus(env);
|
|
1293
|
+
checks.push({
|
|
1294
|
+
name: "update_recovery_state",
|
|
1295
|
+
ok: true,
|
|
1296
|
+
...recoveryStatus,
|
|
1297
|
+
error: "",
|
|
1298
|
+
fix: recoveryStatus.needs_recovery ? "wallet install --yes" : "",
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1384
1301
|
const ok = checks.every((c) => c.ok);
|
|
1385
1302
|
console.log(
|
|
1386
1303
|
JSON.stringify(
|
|
1387
1304
|
{
|
|
1305
|
+
schema_version: 1,
|
|
1388
1306
|
ok,
|
|
1389
1307
|
package_name: packageJson.name,
|
|
1390
1308
|
package_version: packageVersion,
|
|
@@ -1404,6 +1322,7 @@ function runDoctor(args = []) {
|
|
|
1404
1322
|
|
|
1405
1323
|
function runStatus(args = []) {
|
|
1406
1324
|
const payload = {
|
|
1325
|
+
schema_version: 1,
|
|
1407
1326
|
ok: true,
|
|
1408
1327
|
package_name: packageJson.name,
|
|
1409
1328
|
package_version: packageVersion,
|
|
@@ -1417,6 +1336,7 @@ function runStatus(args = []) {
|
|
|
1417
1336
|
update_available: computeUpdateAvailability(),
|
|
1418
1337
|
runtime_in_sync: computeRuntimeInSync(),
|
|
1419
1338
|
framework_integrations: integrationSyncStatus(),
|
|
1339
|
+
update_recovery: updateRecoveryStatus(),
|
|
1420
1340
|
};
|
|
1421
1341
|
if (hasFlag(args, "--verbose")) {
|
|
1422
1342
|
payload.verbose = true;
|
|
@@ -1434,19 +1354,18 @@ function buildInstallerEnv(args) {
|
|
|
1434
1354
|
const sealedKeysExist = fs.existsSync(sealedKeysPath);
|
|
1435
1355
|
const dryRun = hasFlag(args, "--dry-run");
|
|
1436
1356
|
let bootKeySource = env.AGENT_WALLET_BOOT_KEY ? "environment" : "none";
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
: resolveLegacyInstallerBootKey(env);
|
|
1445
|
-
const existingBootKey = fallback.key;
|
|
1446
|
-
bootKeySource = fallback.source;
|
|
1447
|
-
if (existingBootKey) {
|
|
1448
|
-
env.AGENT_WALLET_BOOT_KEY = existingBootKey;
|
|
1357
|
+
const runtimeResolution = readBootKeyFromRuntimeResolver(env);
|
|
1358
|
+
if (runtimeResolution.supported) {
|
|
1359
|
+
bootKeySource = runtimeResolution.key ? "runtime_verified" : "runtime_rejected";
|
|
1360
|
+
if (runtimeResolution.key) {
|
|
1361
|
+
env.AGENT_WALLET_BOOT_KEY = runtimeResolution.key;
|
|
1362
|
+
} else {
|
|
1363
|
+
delete env.AGENT_WALLET_BOOT_KEY;
|
|
1449
1364
|
}
|
|
1365
|
+
} else if (!env.AGENT_WALLET_BOOT_KEY) {
|
|
1366
|
+
const fallback = resolveLegacyInstallerBootKey(env);
|
|
1367
|
+
bootKeySource = fallback.source;
|
|
1368
|
+
if (fallback.key) env.AGENT_WALLET_BOOT_KEY = fallback.key;
|
|
1450
1369
|
}
|
|
1451
1370
|
|
|
1452
1371
|
const shouldGenerateSecrets =
|
|
@@ -1476,7 +1395,7 @@ function buildInstallerEnv(args) {
|
|
|
1476
1395
|
return { env, generated, bootKeySource };
|
|
1477
1396
|
}
|
|
1478
1397
|
|
|
1479
|
-
function
|
|
1398
|
+
function runInstallUnlocked(args, { commandName = "install" } = {}) {
|
|
1480
1399
|
if (!fs.existsSync(setupPath)) {
|
|
1481
1400
|
console.error(`Missing bundled setup.sh at ${setupPath}`);
|
|
1482
1401
|
return 1;
|
|
@@ -1490,6 +1409,13 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1490
1409
|
const previousPath = previousRuntimePath();
|
|
1491
1410
|
const installerArgs = withoutCliOnlyArgs(args);
|
|
1492
1411
|
const dryRun = hasFlag(args, "--dry-run");
|
|
1412
|
+
const recovery = dryRun
|
|
1413
|
+
? { attempted: false, ok: true, reason: "dry run" }
|
|
1414
|
+
: recoverInterruptedUpdate(process.env);
|
|
1415
|
+
if (!recovery.ok) {
|
|
1416
|
+
console.error(`Could not recover interrupted update: ${recovery.reason || recovery.error}`);
|
|
1417
|
+
return 1;
|
|
1418
|
+
}
|
|
1493
1419
|
const stagingRoot = !explicitRuntimeRoot && !dryRun ? stagingRootFor(packageVersion) : null;
|
|
1494
1420
|
const installRoot = stagingRoot || releaseRoot;
|
|
1495
1421
|
|
|
@@ -1510,7 +1436,17 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1510
1436
|
const { env, generated, bootKeySource } = installerEnv;
|
|
1511
1437
|
if (stagingRoot) {
|
|
1512
1438
|
env.OPENCLAW_INSTALL_FINAL_ROOT = releaseRoot;
|
|
1513
|
-
writeUpdateJournal(
|
|
1439
|
+
writeUpdateJournal(
|
|
1440
|
+
"preparing",
|
|
1441
|
+
{
|
|
1442
|
+
transaction_id: crypto.randomUUID(),
|
|
1443
|
+
staging_root: stagingRoot,
|
|
1444
|
+
release_root: releaseRoot,
|
|
1445
|
+
current_target_before: readLinkOrNull(currentPath),
|
|
1446
|
+
previous_target_before: readLinkOrNull(previousPath),
|
|
1447
|
+
},
|
|
1448
|
+
env,
|
|
1449
|
+
);
|
|
1514
1450
|
}
|
|
1515
1451
|
const result = spawnSync("sh", [setupPath, ...installerArgs], {
|
|
1516
1452
|
cwd: packageRoot,
|
|
@@ -1604,6 +1540,34 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1604
1540
|
return 1;
|
|
1605
1541
|
}
|
|
1606
1542
|
|
|
1543
|
+
const bootKeyVerification = verifyBootKeyWithRuntime(installRoot, env);
|
|
1544
|
+
if (!bootKeyVerification.ok) {
|
|
1545
|
+
const failedRoot = failStagingRuntime(stagingRoot, bootKeyVerification.error);
|
|
1546
|
+
writeUpdateJournal(
|
|
1547
|
+
"failed",
|
|
1548
|
+
{ failed_runtime: failedRoot, error: bootKeyVerification.error },
|
|
1549
|
+
env,
|
|
1550
|
+
);
|
|
1551
|
+
console.error(
|
|
1552
|
+
JSON.stringify(
|
|
1553
|
+
{
|
|
1554
|
+
ok: false,
|
|
1555
|
+
command: commandName,
|
|
1556
|
+
version: packageVersion,
|
|
1557
|
+
category: "boot_key_rejected",
|
|
1558
|
+
error: bootKeyVerification.error,
|
|
1559
|
+
switched_current: false,
|
|
1560
|
+
failed_runtime: failedRoot,
|
|
1561
|
+
current_runtime_target: readLinkOrNull(currentPath),
|
|
1562
|
+
fix: "Remove stale AGENT_WALLET_BOOT_KEY overrides and retry the update.",
|
|
1563
|
+
},
|
|
1564
|
+
null,
|
|
1565
|
+
2,
|
|
1566
|
+
),
|
|
1567
|
+
);
|
|
1568
|
+
return 1;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1607
1571
|
if (env.AGENT_WALLET_BOOT_KEY) {
|
|
1608
1572
|
const envPath = path.join(installRoot, "agent-wallet", ".env");
|
|
1609
1573
|
// Prefer the OS keystore: provision the boot key there and keep the plaintext
|
|
@@ -1621,11 +1585,32 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1621
1585
|
writeReleaseState(installRoot, "verified", {
|
|
1622
1586
|
verification_skipped: Boolean(verification.skipped),
|
|
1623
1587
|
});
|
|
1624
|
-
writeUpdateJournal(
|
|
1588
|
+
writeUpdateJournal(
|
|
1589
|
+
"verified",
|
|
1590
|
+
{ ...readUpdateJournal(env), staging_root: stagingRoot, release_root: releaseRoot },
|
|
1591
|
+
env,
|
|
1592
|
+
);
|
|
1625
1593
|
|
|
1626
1594
|
let replacedRoot = null;
|
|
1627
1595
|
try {
|
|
1628
|
-
replacedRoot =
|
|
1596
|
+
replacedRoot = fs.existsSync(releaseRoot)
|
|
1597
|
+
? uniquePathWithSuffix(
|
|
1598
|
+
path.join(path.dirname(releaseRoot), `${path.basename(releaseRoot)}-replaced`),
|
|
1599
|
+
)
|
|
1600
|
+
: null;
|
|
1601
|
+
writeUpdateJournal(
|
|
1602
|
+
"committing",
|
|
1603
|
+
{
|
|
1604
|
+
...readUpdateJournal(env),
|
|
1605
|
+
staging_root: stagingRoot,
|
|
1606
|
+
release_root: releaseRoot,
|
|
1607
|
+
replaced_root: replacedRoot,
|
|
1608
|
+
current_target_before: readLinkOrNull(currentPath),
|
|
1609
|
+
previous_target_before: readLinkOrNull(previousPath),
|
|
1610
|
+
},
|
|
1611
|
+
env,
|
|
1612
|
+
);
|
|
1613
|
+
replacedRoot = commitStagedRuntime(stagingRoot, releaseRoot, replacedRoot, env);
|
|
1629
1614
|
} catch (error) {
|
|
1630
1615
|
const failedRoot = failStagingRuntime(stagingRoot, error.message);
|
|
1631
1616
|
writeUpdateJournal("failed", { failed_runtime: failedRoot, error: error.message }, env);
|
|
@@ -1641,7 +1626,11 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1641
1626
|
switchSymlink(previousPath, previousTarget);
|
|
1642
1627
|
}
|
|
1643
1628
|
switchSymlink(currentPath, releaseRoot);
|
|
1644
|
-
writeUpdateJournal(
|
|
1629
|
+
writeUpdateJournal(
|
|
1630
|
+
"committed",
|
|
1631
|
+
{ ...readUpdateJournal(env), release_root: releaseRoot, previous_runtime: previousTarget },
|
|
1632
|
+
env,
|
|
1633
|
+
);
|
|
1645
1634
|
|
|
1646
1635
|
recordManagedIntegration(
|
|
1647
1636
|
"openclaw",
|
|
@@ -1656,7 +1645,10 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1656
1645
|
);
|
|
1657
1646
|
|
|
1658
1647
|
const integrationRefresh = repairInstalledEditorIntegrations(env);
|
|
1659
|
-
const globalCliRefresh =
|
|
1648
|
+
const globalCliRefresh = safelyRefreshIntegration(
|
|
1649
|
+
"global-cli",
|
|
1650
|
+
() => refreshGlobalCliIfNeeded(env),
|
|
1651
|
+
);
|
|
1660
1652
|
|
|
1661
1653
|
const pythonInfo = activePythonRuntimeInfo(env);
|
|
1662
1654
|
const nodeInfo = activeNodeRuntimeInfo(env)
|
|
@@ -1679,6 +1671,7 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1679
1671
|
boot_key_source: bootKeySource,
|
|
1680
1672
|
staged: Boolean(stagingRoot),
|
|
1681
1673
|
release_state: "verified",
|
|
1674
|
+
recovery,
|
|
1682
1675
|
integration_refresh: integrationRefresh,
|
|
1683
1676
|
global_cli_refresh: globalCliRefresh,
|
|
1684
1677
|
},
|
|
@@ -1689,6 +1682,34 @@ function runInstall(args, { commandName = "install" } = {}) {
|
|
|
1689
1682
|
return 0;
|
|
1690
1683
|
}
|
|
1691
1684
|
|
|
1685
|
+
function runInstall(args, options = {}) {
|
|
1686
|
+
if (hasFlag(args, "--dry-run")) return runInstallUnlocked(args, options);
|
|
1687
|
+
const lock = acquireUpdateLock(process.env);
|
|
1688
|
+
if (!lock.ok) {
|
|
1689
|
+
console.error(
|
|
1690
|
+
JSON.stringify(
|
|
1691
|
+
{
|
|
1692
|
+
ok: false,
|
|
1693
|
+
category: "update_locked",
|
|
1694
|
+
error: "Another wallet install or update is already running.",
|
|
1695
|
+
lock_path: lock.path,
|
|
1696
|
+
lock_owner: lock.owner,
|
|
1697
|
+
fix: "Wait for the active update to finish, then retry.",
|
|
1698
|
+
},
|
|
1699
|
+
null,
|
|
1700
|
+
2,
|
|
1701
|
+
),
|
|
1702
|
+
);
|
|
1703
|
+
return 1;
|
|
1704
|
+
}
|
|
1705
|
+
try {
|
|
1706
|
+
holdUpdateLockForTest(process.env);
|
|
1707
|
+
return runInstallUnlocked(args, options);
|
|
1708
|
+
} finally {
|
|
1709
|
+
releaseUpdateLock(lock);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1692
1713
|
function resolveUpdatePackageSpec(env = process.env) {
|
|
1693
1714
|
const explicit = String(env[UPDATE_PACKAGE_SPEC_ENV] || "").trim();
|
|
1694
1715
|
if (explicit) return explicit;
|
|
@@ -2500,189 +2521,6 @@ function repairHermesEnv(envPath, env = process.env) {
|
|
|
2500
2521
|
return repaired;
|
|
2501
2522
|
}
|
|
2502
2523
|
|
|
2503
|
-
function legacyHermesIntegration(env = process.env) {
|
|
2504
|
-
const hermesHome = resolveHermesHome(env);
|
|
2505
|
-
const pluginTarget = path.join(hermesHome, "plugins", "agent_wallet");
|
|
2506
|
-
let target;
|
|
2507
|
-
try {
|
|
2508
|
-
if (!fs.lstatSync(pluginTarget).isSymbolicLink()) return null;
|
|
2509
|
-
target = path.resolve(path.dirname(pluginTarget), fs.readlinkSync(pluginTarget));
|
|
2510
|
-
} catch {
|
|
2511
|
-
return null;
|
|
2512
|
-
}
|
|
2513
|
-
const manifest = readTextIfExists(path.join(target, "plugin.yaml"));
|
|
2514
|
-
if (!/^name:\s*agent[-_]wallet\s*$/m.test(manifest)) return null;
|
|
2515
|
-
return recordManagedIntegration(
|
|
2516
|
-
"hermes",
|
|
2517
|
-
{
|
|
2518
|
-
hermes_home: hermesHome,
|
|
2519
|
-
plugin_target: pluginTarget,
|
|
2520
|
-
env_path: path.join(hermesHome, ".env"),
|
|
2521
|
-
adopted_legacy_install: true,
|
|
2522
|
-
},
|
|
2523
|
-
env,
|
|
2524
|
-
);
|
|
2525
|
-
}
|
|
2526
|
-
|
|
2527
|
-
function repairOpenclawIntegration(env = process.env) {
|
|
2528
|
-
const entry = managedIntegration("openclaw", env);
|
|
2529
|
-
if (!entry) {
|
|
2530
|
-
return { name: "openclaw", attempted: false, ok: true, repaired: false, reason: "not managed" };
|
|
2531
|
-
}
|
|
2532
|
-
const configPath = path.resolve(
|
|
2533
|
-
expandHome(entry.config_path || path.join(resolveOpenclawHome(env), "openclaw.json")),
|
|
2534
|
-
);
|
|
2535
|
-
let config;
|
|
2536
|
-
try {
|
|
2537
|
-
config = readJsonFile(configPath);
|
|
2538
|
-
} catch (error) {
|
|
2539
|
-
return { name: "openclaw", attempted: true, ok: false, repaired: false, error: error.message };
|
|
2540
|
-
}
|
|
2541
|
-
if (!config || typeof config !== "object") {
|
|
2542
|
-
return { name: "openclaw", attempted: true, ok: false, repaired: false, error: `missing ${configPath}` };
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
const currentRoot = currentRuntimePath(env);
|
|
2546
|
-
const extensionPath = path.join(currentRoot, ".openclaw", "extensions", "agent-wallet");
|
|
2547
|
-
const packageRootPath = path.join(currentRoot, "agent-wallet");
|
|
2548
|
-
const pythonBin = resolveVenvPython(currentRoot);
|
|
2549
|
-
const plugins = config.plugins && typeof config.plugins === "object" ? config.plugins : (config.plugins = {});
|
|
2550
|
-
const load = plugins.load && typeof plugins.load === "object" ? plugins.load : (plugins.load = {});
|
|
2551
|
-
const paths = Array.isArray(load.paths) ? load.paths : [];
|
|
2552
|
-
load.paths = [
|
|
2553
|
-
...paths.filter((item) => {
|
|
2554
|
-
const value = String(item || "");
|
|
2555
|
-
return !value.replaceAll("\\", "/").endsWith("/.openclaw/extensions/agent-wallet");
|
|
2556
|
-
}),
|
|
2557
|
-
extensionPath,
|
|
2558
|
-
];
|
|
2559
|
-
const entries = plugins.entries && typeof plugins.entries === "object" ? plugins.entries : (plugins.entries = {});
|
|
2560
|
-
const walletEntry = entries["agent-wallet"];
|
|
2561
|
-
if (!walletEntry || typeof walletEntry !== "object") {
|
|
2562
|
-
return {
|
|
2563
|
-
name: "openclaw",
|
|
2564
|
-
attempted: false,
|
|
2565
|
-
ok: true,
|
|
2566
|
-
repaired: false,
|
|
2567
|
-
reason: "plugin entry not configured",
|
|
2568
|
-
};
|
|
2569
|
-
}
|
|
2570
|
-
walletEntry.enabled = true;
|
|
2571
|
-
walletEntry.config = walletEntry.config && typeof walletEntry.config === "object" ? walletEntry.config : {};
|
|
2572
|
-
walletEntry.config.packageRoot = packageRootPath;
|
|
2573
|
-
if (pythonBin) walletEntry.config.pythonBin = pythonBin;
|
|
2574
|
-
writeJsonFileAtomic(configPath, config);
|
|
2575
|
-
recordManagedIntegration(
|
|
2576
|
-
"openclaw",
|
|
2577
|
-
{
|
|
2578
|
-
config_path: configPath,
|
|
2579
|
-
extension_path: extensionPath,
|
|
2580
|
-
package_root: packageRootPath,
|
|
2581
|
-
restart_required: true,
|
|
2582
|
-
},
|
|
2583
|
-
env,
|
|
2584
|
-
);
|
|
2585
|
-
return {
|
|
2586
|
-
name: "openclaw",
|
|
2587
|
-
attempted: true,
|
|
2588
|
-
ok: true,
|
|
2589
|
-
repaired: true,
|
|
2590
|
-
config_path: configPath,
|
|
2591
|
-
restart_required: true,
|
|
2592
|
-
};
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
function symlinkManifestMatches(linkPath, manifestRelativePath, expectedName) {
|
|
2596
|
-
let target;
|
|
2597
|
-
try {
|
|
2598
|
-
if (!fs.lstatSync(linkPath).isSymbolicLink()) return false;
|
|
2599
|
-
target = path.resolve(path.dirname(linkPath), fs.readlinkSync(linkPath));
|
|
2600
|
-
} catch {
|
|
2601
|
-
return false;
|
|
2602
|
-
}
|
|
2603
|
-
try {
|
|
2604
|
-
const manifest = readJsonFile(path.join(target, manifestRelativePath));
|
|
2605
|
-
return manifest && manifest.name === expectedName;
|
|
2606
|
-
} catch {
|
|
2607
|
-
return false;
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
|
|
2611
|
-
function legacyCodexIntegration(env = process.env) {
|
|
2612
|
-
const marketplacePath = resolveCodexMarketplacePath(env);
|
|
2613
|
-
let marketplace;
|
|
2614
|
-
try {
|
|
2615
|
-
marketplace = readJsonFile(marketplacePath);
|
|
2616
|
-
} catch {
|
|
2617
|
-
return null;
|
|
2618
|
-
}
|
|
2619
|
-
const pluginTarget = path.join(resolveCodexPluginInstallRoot(env), "agent-wallet");
|
|
2620
|
-
const registered = Array.isArray(marketplace?.plugins) && marketplace.plugins.some(
|
|
2621
|
-
(item) => item?.name === "agent-wallet" && item?.source?.source === "local",
|
|
2622
|
-
);
|
|
2623
|
-
if (!registered || !symlinkManifestMatches(pluginTarget, ".codex-plugin/plugin.json", "agent-wallet")) {
|
|
2624
|
-
return null;
|
|
2625
|
-
}
|
|
2626
|
-
return recordManagedIntegration(
|
|
2627
|
-
"codex",
|
|
2628
|
-
{
|
|
2629
|
-
codex_home: resolveCodexHome(env),
|
|
2630
|
-
plugin_target: pluginTarget,
|
|
2631
|
-
marketplace_path: marketplacePath,
|
|
2632
|
-
marketplace_name: String(marketplace.name || "local"),
|
|
2633
|
-
adopted_legacy_install: true,
|
|
2634
|
-
},
|
|
2635
|
-
env,
|
|
2636
|
-
);
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
function legacyClaudeCodeIntegration(env = process.env) {
|
|
2640
|
-
const marketplaceDir = resolveClaudeCodeMarketplaceDir(env);
|
|
2641
|
-
let manifest;
|
|
2642
|
-
try {
|
|
2643
|
-
manifest = readJsonFile(path.join(marketplaceDir, ".claude-plugin", "marketplace.json"));
|
|
2644
|
-
} catch {
|
|
2645
|
-
return null;
|
|
2646
|
-
}
|
|
2647
|
-
const pluginTarget = path.join(marketplaceDir, "plugins", "agent-wallet");
|
|
2648
|
-
const registered = manifest?.name === CLAUDE_CODE_MARKETPLACE_NAME &&
|
|
2649
|
-
Array.isArray(manifest.plugins) && manifest.plugins.some((item) => item?.name === "agent-wallet");
|
|
2650
|
-
if (!registered || !symlinkManifestMatches(pluginTarget, ".claude-plugin/plugin.json", "agent-wallet")) {
|
|
2651
|
-
return null;
|
|
2652
|
-
}
|
|
2653
|
-
return recordManagedIntegration(
|
|
2654
|
-
"claude-code",
|
|
2655
|
-
{
|
|
2656
|
-
marketplace_dir: marketplaceDir,
|
|
2657
|
-
plugin_target: pluginTarget,
|
|
2658
|
-
cache_root: path.resolve(
|
|
2659
|
-
expandHome(env.AGENT_WALLET_CLAUDE_CODE_CACHE_ROOT || "~/.claude/plugins/cache"),
|
|
2660
|
-
),
|
|
2661
|
-
adopted_legacy_install: true,
|
|
2662
|
-
},
|
|
2663
|
-
env,
|
|
2664
|
-
);
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
|
-
function runHostRefresh(command, args, env = process.env) {
|
|
2668
|
-
const binary = commandPath(command);
|
|
2669
|
-
if (!binary) {
|
|
2670
|
-
return {
|
|
2671
|
-
attempted: false,
|
|
2672
|
-
ok: false,
|
|
2673
|
-
error: `${command} CLI not found`,
|
|
2674
|
-
fix: args.join(" "),
|
|
2675
|
-
};
|
|
2676
|
-
}
|
|
2677
|
-
const result = spawnSync(binary, args, { cwd: packageRoot, encoding: "utf8", env });
|
|
2678
|
-
return {
|
|
2679
|
-
attempted: true,
|
|
2680
|
-
ok: result.status === 0,
|
|
2681
|
-
error: result.status === 0 ? "" : (result.stderr || result.stdout || "").trim(),
|
|
2682
|
-
fix: result.status === 0 ? "" : `${command} ${args.join(" ")}`,
|
|
2683
|
-
};
|
|
2684
|
-
}
|
|
2685
|
-
|
|
2686
2524
|
function globalNpmPackageInfo(env = process.env) {
|
|
2687
2525
|
const npmBin = commandPath("npm");
|
|
2688
2526
|
if (!npmBin) return null;
|
|
@@ -2734,145 +2572,12 @@ function refreshGlobalCliIfNeeded(env = process.env) {
|
|
|
2734
2572
|
};
|
|
2735
2573
|
}
|
|
2736
2574
|
|
|
2737
|
-
function
|
|
2738
|
-
|
|
2739
|
-
expandHome(entry.plugin_target || path.join(resolveCodexPluginInstallRoot(env), "agent-wallet")),
|
|
2740
|
-
);
|
|
2741
|
-
const marketplacePath = path.resolve(
|
|
2742
|
-
expandHome(entry.marketplace_path || resolveCodexMarketplacePath(env)),
|
|
2743
|
-
);
|
|
2744
|
-
const link = repairRuntimeSymlink(
|
|
2745
|
-
"codex",
|
|
2746
|
-
pluginTarget,
|
|
2747
|
-
path.join(currentRuntimePath(env), "codex", "plugins", "agent-wallet"),
|
|
2748
|
-
env,
|
|
2749
|
-
{ allowExternal: true },
|
|
2750
|
-
);
|
|
2751
|
-
if (!link.ok) return link;
|
|
2752
|
-
const marketplace = ensureCodexMarketplaceEntry({ marketplacePath, pluginName: "agent-wallet" });
|
|
2753
|
-
const registration = runHostRefresh(
|
|
2754
|
-
"codex",
|
|
2755
|
-
["plugin", "add", `agent-wallet@${marketplace.marketplace_name}`],
|
|
2756
|
-
{ ...env, CODEX_HOME: entry.codex_home || resolveCodexHome(env) },
|
|
2757
|
-
);
|
|
2758
|
-
recordManagedIntegration(
|
|
2759
|
-
"codex",
|
|
2760
|
-
{
|
|
2761
|
-
...entry,
|
|
2762
|
-
plugin_target: pluginTarget,
|
|
2763
|
-
marketplace_path: marketplacePath,
|
|
2764
|
-
marketplace_name: marketplace.marketplace_name,
|
|
2765
|
-
registration_ok: registration.ok,
|
|
2766
|
-
restart_required: true,
|
|
2767
|
-
},
|
|
2768
|
-
env,
|
|
2769
|
-
);
|
|
2770
|
-
return {
|
|
2771
|
-
...link,
|
|
2772
|
-
ok: link.ok && registration.ok,
|
|
2773
|
-
registration,
|
|
2774
|
-
restart_required: true,
|
|
2775
|
-
};
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
|
-
function refreshClaudeCodeIntegration(entry, env = process.env) {
|
|
2779
|
-
const marketplaceDir = path.resolve(
|
|
2780
|
-
expandHome(entry.marketplace_dir || resolveClaudeCodeMarketplaceDir(env)),
|
|
2781
|
-
);
|
|
2782
|
-
const cacheRoot = path.resolve(
|
|
2783
|
-
expandHome(entry.cache_root || env.AGENT_WALLET_CLAUDE_CODE_CACHE_ROOT || "~/.claude/plugins/cache"),
|
|
2784
|
-
);
|
|
2785
|
-
const pluginTarget = path.resolve(
|
|
2786
|
-
expandHome(entry.plugin_target || path.join(marketplaceDir, "plugins", "agent-wallet")),
|
|
2787
|
-
);
|
|
2788
|
-
const link = repairRuntimeSymlink(
|
|
2789
|
-
"claude-code",
|
|
2790
|
-
pluginTarget,
|
|
2791
|
-
path.join(currentRuntimePath(env), "claude-code", "plugins", "agent-wallet"),
|
|
2792
|
-
env,
|
|
2793
|
-
{ allowExternal: true },
|
|
2794
|
-
);
|
|
2795
|
-
if (!link.ok) return link;
|
|
2796
|
-
ensureClaudeCodeMarketplace(
|
|
2797
|
-
marketplaceDir,
|
|
2798
|
-
path.join(currentRuntimePath(env), "claude-code", "plugins", "agent-wallet"),
|
|
2799
|
-
true,
|
|
2800
|
-
);
|
|
2801
|
-
const marketplaceAdd = runHostRefresh(
|
|
2802
|
-
"claude",
|
|
2803
|
-
["plugin", "marketplace", "add", marketplaceDir, "--scope", "user"],
|
|
2804
|
-
env,
|
|
2805
|
-
);
|
|
2806
|
-
const registration = marketplaceAdd.ok
|
|
2807
|
-
? runHostRefresh(
|
|
2808
|
-
"claude",
|
|
2809
|
-
["plugin", "install", `agent-wallet@${CLAUDE_CODE_MARKETPLACE_NAME}`, "--scope", "user"],
|
|
2810
|
-
env,
|
|
2811
|
-
)
|
|
2812
|
-
: { attempted: false, ok: false, error: "marketplace refresh failed", fix: marketplaceAdd.fix };
|
|
2813
|
-
const cachePins = pinClaudeCacheCopies({
|
|
2814
|
-
...env,
|
|
2815
|
-
AGENT_WALLET_CLAUDE_CODE_CACHE_ROOT: cacheRoot,
|
|
2816
|
-
});
|
|
2817
|
-
recordManagedIntegration(
|
|
2818
|
-
"claude-code",
|
|
2819
|
-
{
|
|
2820
|
-
...entry,
|
|
2821
|
-
marketplace_dir: marketplaceDir,
|
|
2822
|
-
plugin_target: pluginTarget,
|
|
2823
|
-
cache_root: cacheRoot,
|
|
2824
|
-
registration_ok: registration.ok,
|
|
2825
|
-
restart_required: true,
|
|
2826
|
-
},
|
|
2827
|
-
env,
|
|
2828
|
-
);
|
|
2829
|
-
return {
|
|
2830
|
-
...link,
|
|
2831
|
-
ok: link.ok && marketplaceAdd.ok && registration.ok,
|
|
2832
|
-
marketplace_add: marketplaceAdd,
|
|
2833
|
-
registration,
|
|
2834
|
-
cache_pins: cachePins,
|
|
2835
|
-
restart_required: true,
|
|
2836
|
-
};
|
|
2575
|
+
function safelyRefreshIntegration(name, callback) {
|
|
2576
|
+
return integrations().safelyRefresh(name, callback);
|
|
2837
2577
|
}
|
|
2838
2578
|
|
|
2839
2579
|
function repairInstalledEditorIntegrations(env = process.env) {
|
|
2840
|
-
|
|
2841
|
-
const currentRoot = currentRuntimePath(env);
|
|
2842
|
-
const hermesEntry = managedIntegration("hermes", env) || legacyHermesIntegration(env);
|
|
2843
|
-
if (hermesEntry) {
|
|
2844
|
-
const hermesTarget = path.resolve(expandHome(hermesEntry.plugin_target));
|
|
2845
|
-
const hermesEnvPath = path.resolve(expandHome(hermesEntry.env_path));
|
|
2846
|
-
const result = repairRuntimeSymlink(
|
|
2847
|
-
"hermes",
|
|
2848
|
-
hermesTarget,
|
|
2849
|
-
path.join(currentRoot, "hermes", "plugins", "agent_wallet"),
|
|
2850
|
-
env,
|
|
2851
|
-
{ allowExternal: true },
|
|
2852
|
-
);
|
|
2853
|
-
result.env_repaired = repairHermesEnv(hermesEnvPath, env);
|
|
2854
|
-
result.restart_required = true;
|
|
2855
|
-
if (result.ok) {
|
|
2856
|
-
recordManagedIntegration(
|
|
2857
|
-
"hermes",
|
|
2858
|
-
{
|
|
2859
|
-
...hermesEntry,
|
|
2860
|
-
plugin_target: hermesTarget,
|
|
2861
|
-
env_path: hermesEnvPath,
|
|
2862
|
-
restart_required: true,
|
|
2863
|
-
},
|
|
2864
|
-
env,
|
|
2865
|
-
);
|
|
2866
|
-
}
|
|
2867
|
-
results.push(result);
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
const codexEntry = managedIntegration("codex", env) || legacyCodexIntegration(env);
|
|
2871
|
-
if (codexEntry) results.push(refreshCodexIntegration(codexEntry, env));
|
|
2872
|
-
|
|
2873
|
-
const claudeEntry = managedIntegration("claude-code", env) || legacyClaudeCodeIntegration(env);
|
|
2874
|
-
if (claudeEntry) results.push(refreshClaudeCodeIntegration(claudeEntry, env));
|
|
2875
|
-
return results;
|
|
2580
|
+
return hostIntegrations(env).refreshAll();
|
|
2876
2581
|
}
|
|
2877
2582
|
|
|
2878
2583
|
const args = process.argv.slice(2);
|