@agentlayer.tech/wallet 0.1.90 → 0.1.92

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.
@@ -5,8 +5,15 @@ import crypto from "node:crypto";
5
5
  import fs from "node:fs";
6
6
  import os from "node:os";
7
7
  import path from "node:path";
8
+ import readline from "node:readline/promises";
8
9
  import { fileURLToPath } from "node:url";
9
10
  import { createBootKeyManager } from "./lib/boot-key.mjs";
11
+ import {
12
+ buildInstallPlan,
13
+ detectHosts,
14
+ stripUniversalInstallerArgs,
15
+ } from "./lib/host-detection.mjs";
16
+ import { stopLocalEvmDaemonSync } from "./lib/evm-daemon.mjs";
10
17
  import { createHostIntegrationManager, createIntegrationManager } from "./lib/integrations.mjs";
11
18
  import { createUpdateTransactionManager } from "./lib/update-transaction.mjs";
12
19
 
@@ -33,6 +40,7 @@ function printHelp() {
33
40
 
34
41
  Usage:
35
42
  openclaw-agent-wallet install [options]
43
+ openclaw-agent-wallet detect [--json]
36
44
  openclaw-agent-wallet hermes install [options]
37
45
  openclaw-agent-wallet codex install [options]
38
46
  openclaw-agent-wallet claude-code install [options]
@@ -44,12 +52,20 @@ Usage:
44
52
 
45
53
  Common install options:
46
54
  --yes Generate local runtime secrets when missing.
55
+ --hosts <list> Hosts to install: detected, all, none, or comma-separated names.
56
+ --exclude <list> Detected/managed hosts to leave untouched.
57
+ --runtime-only Install/update the shared runtime without adding host plugins.
58
+ --no-prompt Accept the automatic host selection in interactive terminals.
47
59
  --no-auto-secrets Do not generate runtime secrets automatically.
48
60
  --backend <backend> solana_local, wdk_btc_local, wdk_evm_local, or none.
49
61
  --network <network> devnet, mainnet, base, ethereum, bitcoin, etc.
62
+ --invite <code> Bind a welcome invite to the local Base address.
50
63
 
51
64
  Examples:
52
65
  npx @agentlayer.tech/wallet install --yes
66
+ npx @agentlayer.tech/wallet install --yes --hosts codex,claude-code
67
+ npx @agentlayer.tech/wallet detect --json
68
+ npx @agentlayer.tech/wallet install --yes --invite alw_...
53
69
  npx @agentlayer.tech/wallet hermes install --yes
54
70
  npx @agentlayer.tech/wallet codex install --yes
55
71
  npx @agentlayer.tech/wallet claude-code install --yes
@@ -322,6 +338,32 @@ function runWithCliTelemetry(fn, { startEvent, successEvent, failedEvent, comman
322
338
  return code;
323
339
  }
324
340
 
341
+ async function runWithCliTelemetryAsync(
342
+ fn,
343
+ { startEvent, successEvent, failedEvent, commandName, host = "", args = [] },
344
+ ) {
345
+ recordCliTelemetry(startEvent, { commandName, host, ok: true, args, flush: false });
346
+ let code;
347
+ try {
348
+ code = await fn();
349
+ } catch (error) {
350
+ recordCliTelemetry(failedEvent, {
351
+ commandName,
352
+ host,
353
+ ok: false,
354
+ args,
355
+ });
356
+ throw error;
357
+ }
358
+ recordCliTelemetry(code === 0 ? successEvent : failedEvent, {
359
+ commandName,
360
+ host,
361
+ ok: code === 0,
362
+ args,
363
+ });
364
+ return code;
365
+ }
366
+
325
367
  // Shared with the Python runtime (agent_wallet/update_check.py): the cache lives
326
368
  // under OPENCLAW_HOME/agent-wallet-runtime regardless of OPENCLAW_INSTALL_ROOT.
327
369
  function updateCheckCachePath(env = process.env) {
@@ -458,6 +500,134 @@ function hostIntegrations(env = process.env) {
458
500
  });
459
501
  }
460
502
 
503
+ function managedHostNames(env = process.env) {
504
+ const registry = integrations(env).readRegistry();
505
+ return Object.entries(registry.integrations || {})
506
+ .filter(([, entry]) => entry?.managed === true)
507
+ .map(([name]) => name);
508
+ }
509
+
510
+ function runtimeInstalled(env = process.env) {
511
+ try {
512
+ const stat = fs.lstatSync(currentRuntimePath(env));
513
+ return stat.isSymbolicLink() || stat.isDirectory();
514
+ } catch (error) {
515
+ if (error?.code === "ENOENT") return false;
516
+ throw error;
517
+ }
518
+ }
519
+
520
+ function universalInstallPlan(args, env = process.env) {
521
+ const detections = detectHosts({
522
+ env,
523
+ commandPath,
524
+ });
525
+ const managedHosts = managedHostNames(env);
526
+ const plan = buildInstallPlan({
527
+ args,
528
+ detections: detections.map((entry) => ({
529
+ ...entry,
530
+ managed: managedHosts.includes(entry.name),
531
+ })),
532
+ managedHosts,
533
+ runtimeInstalled: runtimeInstalled(env),
534
+ });
535
+ const universalSelectionExplicit =
536
+ hasFlag(args, "--hosts") ||
537
+ hasFlag(args, "--exclude") ||
538
+ hasFlag(args, "--runtime-only") ||
539
+ hasFlag(args, "--managed-only");
540
+ if (
541
+ !plan.runtime_installed_before &&
542
+ !universalSelectionExplicit &&
543
+ hasFlag(args, "--config-path")
544
+ ) {
545
+ plan.selected_hosts = ["openclaw"];
546
+ plan.selection_reason = "explicit_openclaw_config";
547
+ }
548
+ return plan;
549
+ }
550
+
551
+ function runDetect(args = [], env = process.env) {
552
+ let plan;
553
+ try {
554
+ plan = universalInstallPlan(args, env);
555
+ } catch (error) {
556
+ console.error(error.message);
557
+ return 2;
558
+ }
559
+ console.log(
560
+ JSON.stringify(
561
+ {
562
+ schema_version: 1,
563
+ ok: true,
564
+ runtime_base: resolveRuntimeBase(env),
565
+ runtime_installed: plan.runtime_installed_before,
566
+ detected_hosts: plan.detected_hosts,
567
+ managed_hosts: plan.managed_hosts,
568
+ default_selected_hosts: plan.selected_hosts,
569
+ selection_reason: plan.selection_reason,
570
+ hosts: plan.detections,
571
+ },
572
+ null,
573
+ 2,
574
+ ),
575
+ );
576
+ return 0;
577
+ }
578
+
579
+ async function promptForInstallPlan(plan, args) {
580
+ const shouldPrompt =
581
+ !plan.runtime_installed_before &&
582
+ plan.selection_reason === "fresh_install_detected" &&
583
+ !hasFlag(args, "--yes") &&
584
+ !hasFlag(args, "--no-prompt") &&
585
+ process.stdin.isTTY &&
586
+ process.stdout.isTTY;
587
+ if (!shouldPrompt) return plan;
588
+
589
+ const excluded = new Set(plan.excluded_hosts);
590
+ const selected = new Set(plan.selected_hosts);
591
+ const terminal = readline.createInterface({
592
+ input: process.stdin,
593
+ output: process.stdout,
594
+ });
595
+ try {
596
+ console.log("AgentLayer detected these agent frameworks:");
597
+ for (const host of plan.detections.filter((entry) => entry.detected && !excluded.has(entry.name))) {
598
+ const answer = (
599
+ await terminal.question(
600
+ ` Install AgentLayer for ${host.display_name}? ${selected.has(host.name) ? "[Y/n]" : "[y/N]"} `,
601
+ )
602
+ ).trim().toLowerCase();
603
+ if (answer) {
604
+ if (["y", "yes"].includes(answer)) selected.add(host.name);
605
+ if (["n", "no"].includes(answer)) selected.delete(host.name);
606
+ }
607
+ }
608
+ } finally {
609
+ terminal.close();
610
+ }
611
+ return {
612
+ ...plan,
613
+ selection_reason: "interactive",
614
+ selected_hosts: plan.detections
615
+ .map((entry) => entry.name)
616
+ .filter((name) => selected.has(name)),
617
+ };
618
+ }
619
+
620
+ async function runUniversalInstall(args, options = {}) {
621
+ try {
622
+ const initialPlan = universalInstallPlan(args);
623
+ const installPlan = await promptForInstallPlan(initialPlan, args);
624
+ return runInstall(args, { ...options, installPlan });
625
+ } catch (error) {
626
+ console.error(error.message);
627
+ return 2;
628
+ }
629
+ }
630
+
461
631
  function writeUpdateJournal(state, details = {}, env = process.env) {
462
632
  updateTransactions(env).writeJournal(state, details);
463
633
  }
@@ -855,7 +1025,13 @@ function withoutCliOnlyArgs(args) {
855
1025
  const output = [];
856
1026
  for (let index = 0; index < args.length; index += 1) {
857
1027
  const value = args[index];
858
- if (value === "--yes" || value === "--auto-secrets" || value === "--no-auto-secrets") {
1028
+ if (
1029
+ value === "--yes" ||
1030
+ value === "--auto-secrets" ||
1031
+ value === "--no-auto-secrets" ||
1032
+ value === "--force" ||
1033
+ value === "--skip-enable"
1034
+ ) {
859
1035
  continue;
860
1036
  }
861
1037
  if (value === "--to") {
@@ -867,7 +1043,7 @@ function withoutCliOnlyArgs(args) {
867
1043
  }
868
1044
  output.push(value);
869
1045
  }
870
- return output;
1046
+ return stripUniversalInstallerArgs(output);
871
1047
  }
872
1048
 
873
1049
  function extractTrailingJson(text) {
@@ -1060,6 +1236,15 @@ function provisionBootKeyToKeystore(releaseRoot, env, bootKey) {
1060
1236
  return bootKeys(env).provision(releaseRoot, bootKey);
1061
1237
  }
1062
1238
 
1239
+ function printMacOSKeychainNotice(env = process.env) {
1240
+ if (process.platform !== "darwin") return;
1241
+ const preference = String(env.AGENT_WALLET_KEYSTORE_BACKEND || "auto").trim().toLowerCase();
1242
+ if (["plain", "plaintext", "plaintext-file", "file"].includes(preference)) return;
1243
+ console.error(
1244
+ "Security notice: AgentLayer uses your macOS login Keychain for the wallet boot key by default. macOS may ask for Keychain access; approve it only if you initiated this install or update.",
1245
+ );
1246
+ }
1247
+
1063
1248
  function resolveVenvPython(releaseRoot) {
1064
1249
  const candidates = [
1065
1250
  path.join(releaseRoot, "agent-wallet", ".venv", "bin", "python"),
@@ -1395,7 +1580,158 @@ function buildInstallerEnv(args) {
1395
1580
  return { env, generated, bootKeySource };
1396
1581
  }
1397
1582
 
1398
- function runInstallUnlocked(args, { commandName = "install" } = {}) {
1583
+ function openclawHostCommandArgs(args, env = process.env) {
1584
+ const runtimeRoot = currentRuntimePath(env);
1585
+ const walletRoot = path.join(runtimeRoot, "agent-wallet");
1586
+ const pythonBin = resolveVenvPython(runtimeRoot) || resolveAgentWalletPython(walletRoot);
1587
+ const commandArgs = [
1588
+ path.join(walletRoot, "scripts", "install_openclaw_local_config.py"),
1589
+ "--config-path",
1590
+ path.resolve(
1591
+ expandHome(parseFlagValue(args, "--config-path") || path.join(resolveOpenclawHome(env), "openclaw.json")),
1592
+ ),
1593
+ "--plugin-id",
1594
+ parseFlagValue(args, "--plugin-id") || "agent-wallet",
1595
+ "--user-id",
1596
+ parseFlagValue(args, "--user-id") || "",
1597
+ "--backend",
1598
+ parseFlagValue(args, "--backend") || "solana_local",
1599
+ "--network",
1600
+ parseFlagValue(args, "--network") || "mainnet",
1601
+ hasFlag(args, "--sign-only") ? "--sign-only" : "--no-sign-only",
1602
+ "--extension-path",
1603
+ path.join(runtimeRoot, ".openclaw", "extensions", "agent-wallet"),
1604
+ "--package-root",
1605
+ walletRoot,
1606
+ "--python-bin",
1607
+ pythonBin,
1608
+ ];
1609
+ for (const [flag, targetFlag] of [
1610
+ ["--rpc-url", "--rpc-url"],
1611
+ ["--rpc-urls", "--rpc-urls"],
1612
+ ["--wdk-evm-service-url", "--wdk-evm-service-url"],
1613
+ ]) {
1614
+ const value = parseFlagValue(args, flag);
1615
+ if (value) commandArgs.push(targetFlag, value);
1616
+ }
1617
+ return { pythonBin, commandArgs, configPath: commandArgs[2] };
1618
+ }
1619
+
1620
+ function runOpenclawHostInstall(args, env = process.env) {
1621
+ const { pythonBin, commandArgs, configPath } = openclawHostCommandArgs(args, env);
1622
+ const previousConfig = fs.existsSync(configPath) ? fs.readFileSync(configPath) : null;
1623
+ if (previousConfig === null) {
1624
+ writeJsonFileAtomic(configPath, {
1625
+ plugins: { entries: {} },
1626
+ tools: { alsoAllow: [] },
1627
+ });
1628
+ }
1629
+ const result = spawnSync(pythonBin, commandArgs, {
1630
+ cwd: packageRoot,
1631
+ encoding: "utf8",
1632
+ env,
1633
+ });
1634
+ if (result.error || (result.status ?? 1) !== 0) {
1635
+ if (previousConfig === null) {
1636
+ fs.rmSync(configPath, { force: true });
1637
+ } else {
1638
+ fs.writeFileSync(configPath, previousConfig, { mode: 0o600 });
1639
+ }
1640
+ return {
1641
+ name: "openclaw",
1642
+ attempted: true,
1643
+ ok: false,
1644
+ error: result.error?.message || (result.stderr || result.stdout || "").trim(),
1645
+ rolled_back: true,
1646
+ restart_required: false,
1647
+ };
1648
+ }
1649
+ let details = {};
1650
+ try {
1651
+ details = extractTrailingJson(result.stdout);
1652
+ } catch {
1653
+ details = { config_path: configPath };
1654
+ }
1655
+ recordManagedIntegration(
1656
+ "openclaw",
1657
+ {
1658
+ config_path: configPath,
1659
+ extension_path: path.join(currentRuntimePath(env), ".openclaw", "extensions", "agent-wallet"),
1660
+ package_root: path.join(currentRuntimePath(env), "agent-wallet"),
1661
+ restart_required: true,
1662
+ },
1663
+ env,
1664
+ );
1665
+ return {
1666
+ name: "openclaw",
1667
+ attempted: true,
1668
+ ok: true,
1669
+ ...details,
1670
+ restart_required: true,
1671
+ };
1672
+ }
1673
+
1674
+ function runEditorHostInstall(name, args, env = process.env) {
1675
+ const binaryName = name === "claude-code" ? "claude" : name;
1676
+ const hostArgs = [
1677
+ name,
1678
+ "install",
1679
+ ...(hasFlag(args, "--force") ? ["--force"] : []),
1680
+ ...(!commandPath(binaryName) || hasFlag(args, "--skip-enable") ? ["--skip-enable"] : []),
1681
+ ];
1682
+ const result = spawnSync(process.execPath, [cliPath, ...hostArgs], {
1683
+ cwd: packageRoot,
1684
+ encoding: "utf8",
1685
+ env,
1686
+ });
1687
+ let details = {};
1688
+ try {
1689
+ details = extractTrailingJson(result.stdout);
1690
+ } catch {
1691
+ // Preserve the host adapter's error below when it did not emit JSON.
1692
+ }
1693
+ return {
1694
+ name,
1695
+ attempted: true,
1696
+ ok: !result.error && (result.status ?? 1) === 0,
1697
+ ...details,
1698
+ error:
1699
+ result.error?.message ||
1700
+ ((result.status ?? 1) === 0 ? "" : (result.stderr || result.stdout || "").trim()),
1701
+ };
1702
+ }
1703
+
1704
+ function applyHostInstallPlan(plan, args, env = process.env) {
1705
+ const selected = new Set(plan.selected_hosts);
1706
+ const managed = new Set(plan.managed_hosts);
1707
+ const managedSelected = plan.selected_hosts.filter((name) => managed.has(name));
1708
+ const adoptLegacy =
1709
+ plan.runtime_installed_before &&
1710
+ ["managed_only", "existing_runtime_managed_only"].includes(plan.selection_reason);
1711
+ const refreshed = repairInstalledEditorIntegrations(
1712
+ env,
1713
+ adoptLegacy ? null : managedSelected,
1714
+ );
1715
+ const installed = [];
1716
+
1717
+ for (const name of plan.selected_hosts) {
1718
+ if (managed.has(name)) continue;
1719
+ installed.push(
1720
+ name === "openclaw"
1721
+ ? runOpenclawHostInstall(args, env)
1722
+ : runEditorHostInstall(name, args, env),
1723
+ );
1724
+ }
1725
+
1726
+ return {
1727
+ ok: [...refreshed, ...installed].every((entry) => entry?.ok !== false),
1728
+ selected_hosts: [...selected],
1729
+ refreshed,
1730
+ installed,
1731
+ };
1732
+ }
1733
+
1734
+ function runInstallUnlocked(args, { commandName = "install", installPlan = null } = {}) {
1399
1735
  if (!fs.existsSync(setupPath)) {
1400
1736
  console.error(`Missing bundled setup.sh at ${setupPath}`);
1401
1737
  return 1;
@@ -1407,8 +1743,10 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1407
1743
  : releaseRootFor(packageVersion);
1408
1744
  const currentPath = currentRuntimePath();
1409
1745
  const previousPath = previousRuntimePath();
1746
+ const hostPlan = installPlan || universalInstallPlan(args);
1410
1747
  const installerArgs = withoutCliOnlyArgs(args);
1411
1748
  const dryRun = hasFlag(args, "--dry-run");
1749
+ if (!dryRun) printMacOSKeychainNotice(process.env);
1412
1750
  const recovery = dryRun
1413
1751
  ? { attempted: false, ok: true, reason: "dry run" }
1414
1752
  : recoverInterruptedUpdate(process.env);
@@ -1425,6 +1763,9 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1425
1763
  if (!hasFlag(installerArgs, "--install-from-runtime")) {
1426
1764
  installerArgs.push("--install-from-runtime");
1427
1765
  }
1766
+ if (!hasFlag(installerArgs, "--configure-openclaw") && !hasFlag(installerArgs, "--no-configure-openclaw")) {
1767
+ installerArgs.push("--no-configure-openclaw");
1768
+ }
1428
1769
 
1429
1770
  let installerEnv;
1430
1771
  try {
@@ -1450,7 +1791,8 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1450
1791
  }
1451
1792
  const result = spawnSync("sh", [setupPath, ...installerArgs], {
1452
1793
  cwd: packageRoot,
1453
- stdio: "inherit",
1794
+ stdio: dryRun ? "pipe" : "inherit",
1795
+ encoding: dryRun ? "utf8" : undefined,
1454
1796
  env,
1455
1797
  });
1456
1798
 
@@ -1463,6 +1805,10 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1463
1805
  return 1;
1464
1806
  }
1465
1807
  if ((result.status ?? 1) !== 0) {
1808
+ if (dryRun) {
1809
+ if (result.stderr) process.stderr.write(result.stderr);
1810
+ if (result.stdout) process.stderr.write(result.stdout);
1811
+ }
1466
1812
  const failedRoot = failStagingRuntime(stagingRoot, `installer exited with ${result.status ?? 1}`);
1467
1813
  if (!dryRun) {
1468
1814
  writeUpdateJournal(
@@ -1475,6 +1821,27 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1475
1821
  }
1476
1822
 
1477
1823
  if (dryRun) {
1824
+ if (result.stderr) process.stderr.write(result.stderr);
1825
+ let installerPlan;
1826
+ try {
1827
+ installerPlan = extractTrailingJson(result.stdout);
1828
+ } catch (error) {
1829
+ if (result.stdout) process.stderr.write(result.stdout);
1830
+ console.error(error.message);
1831
+ return 1;
1832
+ }
1833
+ console.log(
1834
+ JSON.stringify(
1835
+ {
1836
+ ...installerPlan,
1837
+ command: commandName,
1838
+ dry_run: true,
1839
+ host_plan: hostPlan,
1840
+ },
1841
+ null,
1842
+ 2,
1843
+ ),
1844
+ );
1478
1845
  return 0;
1479
1846
  }
1480
1847
 
@@ -1631,20 +1998,13 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1631
1998
  { ...readUpdateJournal(env), release_root: releaseRoot, previous_runtime: previousTarget },
1632
1999
  env,
1633
2000
  );
2001
+ // Daemon restart is advisory lifecycle cleanup, not part of the atomic
2002
+ // runtime commit. Record the successful update before waiting on it.
2003
+ const evmDaemonStop = stopLocalEvmDaemonSync({ env });
1634
2004
 
1635
- recordManagedIntegration(
1636
- "openclaw",
1637
- {
1638
- config_path: path.resolve(
1639
- expandHome(parseFlagValue(args, "--config-path") || path.join(resolveOpenclawHome(env), "openclaw.json")),
1640
- ),
1641
- extension_path: path.join(currentPath, ".openclaw", "extensions", "agent-wallet"),
1642
- package_root: path.join(currentPath, "agent-wallet"),
1643
- },
1644
- env,
1645
- );
1646
-
1647
- const integrationRefresh = repairInstalledEditorIntegrations(env);
2005
+ const integrationRegistryRecovery = integrations(env).recoverCorruptRegistry();
2006
+ const hostInstallation = applyHostInstallPlan(hostPlan, args, env);
2007
+ const hostInstallFailed = hostInstallation.installed.some((entry) => entry?.ok === false);
1648
2008
  const globalCliRefresh = safelyRefreshIntegration(
1649
2009
  "global-cli",
1650
2010
  () => refreshGlobalCliIfNeeded(env),
@@ -1661,7 +2021,7 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1661
2021
  console.error(
1662
2022
  JSON.stringify(
1663
2023
  {
1664
- ok: true,
2024
+ ok: !hostInstallFailed,
1665
2025
  command: commandName,
1666
2026
  version: packageVersion,
1667
2027
  runtime_root: releaseRoot,
@@ -1672,14 +2032,24 @@ function runInstallUnlocked(args, { commandName = "install" } = {}) {
1672
2032
  staged: Boolean(stagingRoot),
1673
2033
  release_state: "verified",
1674
2034
  recovery,
1675
- integration_refresh: integrationRefresh,
2035
+ host_plan: hostPlan,
2036
+ host_installation: hostInstallation,
2037
+ integration_registry_recovery: integrationRegistryRecovery,
2038
+ integration_refresh: hostInstallation.refreshed,
1676
2039
  global_cli_refresh: globalCliRefresh,
2040
+ evm_daemon_stop: evmDaemonStop,
2041
+ ...(hostInstallFailed
2042
+ ? {
2043
+ category: "host_install_failed",
2044
+ message: "The shared runtime is active, but one or more selected host plugins failed to install.",
2045
+ }
2046
+ : {}),
1677
2047
  },
1678
2048
  null,
1679
2049
  2,
1680
2050
  ),
1681
2051
  );
1682
- return 0;
2052
+ return hostInstallFailed ? 1 : 0;
1683
2053
  }
1684
2054
 
1685
2055
  function runInstall(args, options = {}) {
@@ -1717,10 +2087,11 @@ function resolveUpdatePackageSpec(env = process.env) {
1717
2087
  }
1718
2088
 
1719
2089
  function runDelegatedInstallForUpdate(args, { captureOutput = false } = {}) {
2090
+ const installArgs = hasFlag(args, "--managed-only") ? args : [...args, "--managed-only"];
1720
2091
  const localCliPath = String(process.env[UPDATE_CLI_PATH_ENV] || "").trim();
1721
2092
  if (localCliPath) {
1722
2093
  const meta = resolveCliPackageMeta(localCliPath);
1723
- const result = spawnSync("node", [localCliPath, "install", ...args], {
2094
+ const result = spawnSync("node", [localCliPath, "install", ...installArgs], {
1724
2095
  cwd: packageRoot,
1725
2096
  stdio: captureOutput ? "pipe" : "inherit",
1726
2097
  encoding: captureOutput ? "utf8" : undefined,
@@ -1743,7 +2114,7 @@ function runDelegatedInstallForUpdate(args, { captureOutput = false } = {}) {
1743
2114
  const binCommand = primaryBinCommand();
1744
2115
  const result = spawnSync(
1745
2116
  npmBin,
1746
- ["exec", "--yes", `--package=${packageSpec}`, binCommand, "--", "install", ...args],
2117
+ ["exec", "--yes", `--package=${packageSpec}`, binCommand, "--", "install", ...installArgs],
1747
2118
  {
1748
2119
  cwd: packageRoot,
1749
2120
  stdio: captureOutput ? "pipe" : "inherit",
@@ -1902,12 +2273,14 @@ function runRollback(args) {
1902
2273
  switchSymlink(previousRuntimePath(), releaseRootFor(current));
1903
2274
  }
1904
2275
  switchSymlink(currentPath, target);
2276
+ const evmDaemonStop = stopLocalEvmDaemonSync();
1905
2277
  console.log(
1906
2278
  JSON.stringify(
1907
2279
  {
1908
2280
  ok: true,
1909
2281
  active_version: activeVersion(),
1910
2282
  current_runtime: currentPath,
2283
+ evm_daemon_stop: evmDaemonStop,
1911
2284
  },
1912
2285
  null,
1913
2286
  2,
@@ -2576,8 +2949,8 @@ function safelyRefreshIntegration(name, callback) {
2576
2949
  return integrations().safelyRefresh(name, callback);
2577
2950
  }
2578
2951
 
2579
- function repairInstalledEditorIntegrations(env = process.env) {
2580
- return hostIntegrations(env).refreshAll();
2952
+ function repairInstalledEditorIntegrations(env = process.env, names = null) {
2953
+ return hostIntegrations(env).refreshAll(names);
2581
2954
  }
2582
2955
 
2583
2956
  const args = process.argv.slice(2);
@@ -2614,11 +2987,15 @@ if (command === "status") {
2614
2987
  process.exit(runStatus(args.slice(1)));
2615
2988
  }
2616
2989
 
2990
+ if (command === "detect") {
2991
+ process.exit(runDetect(args.slice(1)));
2992
+ }
2993
+
2617
2994
  if (command === "install" || command === "setup") {
2618
2995
  const commandArgs = args.slice(1);
2619
2996
  process.exit(
2620
- runWithCliTelemetry(
2621
- () => runInstall(commandArgs, { commandName: "install" }),
2997
+ await runWithCliTelemetryAsync(
2998
+ () => runUniversalInstall(commandArgs, { commandName: "install" }),
2622
2999
  {
2623
3000
  startEvent: "install_start",
2624
3001
  successEvent: "install_success",
@@ -2721,8 +3098,8 @@ if (command === "claude-code") {
2721
3098
 
2722
3099
  if (command.startsWith("-")) {
2723
3100
  process.exit(
2724
- runWithCliTelemetry(
2725
- () => runInstall(args, { commandName: "install" }),
3101
+ await runWithCliTelemetryAsync(
3102
+ () => runUniversalInstall(args, { commandName: "install" }),
2726
3103
  {
2727
3104
  startEvent: "install_start",
2728
3105
  successEvent: "install_success",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-wallet",
3
3
  "displayName": "Agent Wallet",
4
- "version": "0.1.90",
4
+ "version": "0.1.92",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-wallet",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "description": "Codex plugin bridge for the AgentLayer wallet runtime.",
5
5
  "author": {
6
6
  "name": "AgentLayer"
@@ -1,5 +1,5 @@
1
1
  name: agent-wallet
2
- version: 0.1.90
2
+ version: 0.1.92
3
3
  description: Thin Hermes Agent bridge to the existing AgentLayer/OpenClaw wallet backend
4
4
  provides_tools:
5
5
  - agent_wallet_tools
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentlayer.tech/wallet",
3
- "version": "0.1.90",
4
- "description": "NPM installer for the OpenClaw Agent Wallet local runtime.",
3
+ "version": "0.1.92",
4
+ "description": "Universal AgentLayer wallet installer for OpenClaw, Codex, Claude Code, and Hermes.",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -15,7 +15,7 @@
15
15
  "wallet": "./bin/openclaw-agent-wallet.mjs"
16
16
  },
17
17
  "scripts": {
18
- "check": "node --check bin/openclaw-agent-wallet.mjs",
18
+ "check": "node --check bin/openclaw-agent-wallet.mjs && node --check bin/lib/evm-daemon.mjs && node --test bin/lib/host-detection.test.mjs bin/lib/evm-daemon.test.mjs",
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",
@@ -23,6 +23,8 @@
23
23
  "version:sync": "node scripts/sync_version.mjs",
24
24
  "release:local": "node scripts/release_local.mjs",
25
25
  "test:npm-installer": "python3 agent-wallet/tests/smoke_npm_installer.py",
26
+ "test:universal-installer": "node --test bin/lib/host-detection.test.mjs && python3 agent-wallet/tests/smoke_universal_installer.py",
27
+ "test:published-floor-upgrade": "python3 agent-wallet/tests/e2e_install_upgrade_from_published_npm.py",
26
28
  "pack:dry-run": "npm pack --dry-run"
27
29
  },
28
30
  "files": [
@@ -74,6 +76,7 @@
74
76
  "!codex/**/*.pyc",
75
77
  "!agent-wallet/.pytest_cache/**",
76
78
  "!agent-wallet/.runtime-venv/**",
79
+ "!bin/**/*.test.mjs",
77
80
  "!**/node_modules/**",
78
81
  "!**/*.tgz",
79
82
  "!**/.DS_Store"
@@ -81,6 +84,8 @@
81
84
  "keywords": [
82
85
  "openclaw",
83
86
  "codex",
87
+ "claude-code",
88
+ "hermes",
84
89
  "agent-wallet",
85
90
  "wallet",
86
91
  "solana",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wdk-btc-wallet",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Separate BTC-only wallet service built on Tether WDK.",