@agentlayer.tech/wallet 0.1.90 → 0.1.91

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