@agentlayer.tech/wallet 0.1.10 → 0.1.12

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.
@@ -0,0 +1,151 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
5
+ PACKAGE_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
6
+
7
+ resolve_python_bin() {
8
+ if [ -n "${OPENCLAW_AGENT_WALLET_PYTHON:-}" ] && [ -x "${OPENCLAW_AGENT_WALLET_PYTHON}" ]; then
9
+ printf "%s" "${OPENCLAW_AGENT_WALLET_PYTHON}"
10
+ return 0
11
+ fi
12
+
13
+ if [ -x "/tmp/agent-wallet-venv/bin/python" ]; then
14
+ printf "%s" "/tmp/agent-wallet-venv/bin/python"
15
+ return 0
16
+ fi
17
+
18
+ if [ -x "$PACKAGE_ROOT/.venv/bin/python" ]; then
19
+ printf "%s" "$PACKAGE_ROOT/.venv/bin/python"
20
+ return 0
21
+ fi
22
+
23
+ if command -v python3 >/dev/null 2>&1; then
24
+ command -v python3
25
+ return 0
26
+ fi
27
+
28
+ command -v python
29
+ }
30
+
31
+ PYTHON_BIN=$(resolve_python_bin)
32
+ export OPENCLAW_AGENT_WALLET_PYTHON="$PYTHON_BIN"
33
+
34
+ has_flag() {
35
+ flag=$1
36
+ shift
37
+ for arg in "$@"; do
38
+ case "$arg" in
39
+ "$flag"|"$flag"=*)
40
+ return 0
41
+ ;;
42
+ esac
43
+ done
44
+ return 1
45
+ }
46
+
47
+ prompt_with_default() {
48
+ label=$1
49
+ default_value=$2
50
+ if [ -t 0 ]; then
51
+ printf "%s [%s]: " "$label" "$default_value" >&2
52
+ read -r value
53
+ if [ -z "${value:-}" ]; then
54
+ printf "%s" "$default_value"
55
+ else
56
+ printf "%s" "$value"
57
+ fi
58
+ return 0
59
+ fi
60
+ printf "%s" "$default_value"
61
+ }
62
+
63
+ normalize_network_value() {
64
+ case $(printf "%s" "$1" | tr '[:upper:]' '[:lower:]') in
65
+ 1|ethereum|eth|mainnet)
66
+ printf "ethereum"
67
+ ;;
68
+ 2|base)
69
+ printf "base"
70
+ ;;
71
+ 3|sepolia)
72
+ printf "sepolia"
73
+ ;;
74
+ 4|base-sepolia|base_sepolia)
75
+ printf "base-sepolia"
76
+ ;;
77
+ *)
78
+ return 1
79
+ ;;
80
+ esac
81
+ }
82
+
83
+ prompt_network_choice() {
84
+ default_value=$1
85
+ if ! [ -t 0 ]; then
86
+ printf "%s" "$default_value"
87
+ return 0
88
+ fi
89
+
90
+ case "$default_value" in
91
+ ethereum) default_hint="1" ;;
92
+ base) default_hint="2" ;;
93
+ sepolia) default_hint="3" ;;
94
+ base-sepolia) default_hint="4" ;;
95
+ *) default_hint="2" ;;
96
+ esac
97
+
98
+ while true; do
99
+ printf "EVM network:\n" >&2
100
+ printf " 1) ethereum\n" >&2
101
+ printf " 2) base\n" >&2
102
+ printf " 3) sepolia\n" >&2
103
+ printf " 4) base-sepolia\n" >&2
104
+ printf "Choose network [%s]: " "$default_hint" >&2
105
+ read -r choice
106
+ if [ -z "${choice:-}" ]; then
107
+ choice=$default_hint
108
+ fi
109
+ if network=$(normalize_network_value "$choice"); then
110
+ printf "%s" "$network"
111
+ return 0
112
+ fi
113
+ printf "Invalid choice. Enter 1, 2, 3, 4, ethereum, base, sepolia, or base-sepolia.\n" >&2
114
+ done
115
+ }
116
+
117
+ DEFAULT_USER_ID=${OPENCLAW_EVM_USER_ID:-${USER:-openclaw-user}-local}
118
+ DEFAULT_NETWORK=${OPENCLAW_EVM_NETWORK:-base}
119
+ DEFAULT_SERVICE_URL=${OPENCLAW_EVM_SERVICE_URL:-http://127.0.0.1:8081}
120
+
121
+ if ! has_flag --user-id "$@"; then
122
+ USER_ID=$(prompt_with_default "OpenClaw user id" "$DEFAULT_USER_ID")
123
+ set -- "$@" --user-id "$USER_ID"
124
+ fi
125
+
126
+ if ! has_flag --network "$@"; then
127
+ NETWORK=$(prompt_network_choice "$DEFAULT_NETWORK")
128
+ set -- "$@" --network "$NETWORK"
129
+ fi
130
+
131
+ if ! has_flag --service-url "$@"; then
132
+ set -- "$@" --service-url "$DEFAULT_SERVICE_URL"
133
+ fi
134
+
135
+ if ! has_flag --config-path "$@" && [ -n "${OPENCLAW_EVM_CONFIG_PATH:-}" ]; then
136
+ set -- "$@" --config-path "$OPENCLAW_EVM_CONFIG_PATH"
137
+ fi
138
+
139
+ if ! has_flag --wdk-wallet-root "$@" && [ -n "${OPENCLAW_EVM_WDK_WALLET_ROOT:-}" ]; then
140
+ set -- "$@" --wdk-wallet-root "$OPENCLAW_EVM_WDK_WALLET_ROOT"
141
+ fi
142
+
143
+ if ! has_flag --python-bin "$@"; then
144
+ set -- "$@" --python-bin "$PYTHON_BIN"
145
+ fi
146
+
147
+ if ! has_flag --package-root "$@"; then
148
+ set -- "$@" --package-root "$PACKAGE_ROOT"
149
+ fi
150
+
151
+ exec "$PYTHON_BIN" "$SCRIPT_DIR/bootstrap_openclaw_evm.py" "$@"
@@ -19,6 +19,7 @@ function printHelp() {
19
19
 
20
20
  Usage:
21
21
  openclaw-agent-wallet install [options]
22
+ openclaw-agent-wallet hermes install [options]
22
23
  openclaw-agent-wallet update [options]
23
24
  openclaw-agent-wallet status
24
25
  openclaw-agent-wallet rollback [--to <version>]
@@ -33,6 +34,7 @@ Common install options:
33
34
 
34
35
  Examples:
35
36
  npx @agentlayer.tech/wallet install --yes
37
+ npx @agentlayer.tech/wallet hermes install --yes
36
38
  npx @agentlayer.tech/wallet install --backend none
37
39
  npx @agentlayer.tech/wallet update --yes
38
40
  npx @agentlayer.tech/wallet status
@@ -65,6 +67,10 @@ function resolveRuntimeBase(env = process.env) {
65
67
  return path.join(resolveOpenclawHome(env), "agent-wallet-runtime");
66
68
  }
67
69
 
70
+ function resolveHermesHome(env = process.env) {
71
+ return path.resolve(expandHome(env.HERMES_HOME || "~/.hermes"));
72
+ }
73
+
68
74
  function releaseRootFor(version, env = process.env) {
69
75
  return path.join(resolveRuntimeBase(env), "releases", version);
70
76
  }
@@ -73,6 +79,21 @@ function currentRuntimePath(env = process.env) {
73
79
  return path.join(resolveRuntimeBase(env), "current");
74
80
  }
75
81
 
82
+ function resolvedCurrentRuntimeRoot(env = process.env) {
83
+ const currentPath = currentRuntimePath(env);
84
+ const currentTarget = readLinkOrNull(currentPath);
85
+ if (currentTarget) {
86
+ return path.resolve(path.dirname(currentPath), currentTarget);
87
+ }
88
+ try {
89
+ const stat = fs.statSync(currentPath);
90
+ if (stat.isDirectory()) return currentPath;
91
+ } catch (error) {
92
+ if (error?.code !== "ENOENT") throw error;
93
+ }
94
+ return "";
95
+ }
96
+
76
97
  function previousRuntimePath(env = process.env) {
77
98
  return path.join(resolveRuntimeBase(env), "previous");
78
99
  }
@@ -282,6 +303,31 @@ function envFileSet(pathname, updates) {
282
303
  }
283
304
  }
284
305
 
306
+ function envFileUnset(pathname, keys) {
307
+ let lines = [];
308
+ try {
309
+ lines = fs.readFileSync(pathname, "utf8").split(/\r?\n/);
310
+ } catch (error) {
311
+ if (error?.code === "ENOENT") return;
312
+ throw error;
313
+ }
314
+ const blocked = new Set(keys);
315
+ const next = [];
316
+ for (const line of lines) {
317
+ const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=/);
318
+ if (match && blocked.has(match[1])) {
319
+ continue;
320
+ }
321
+ if (line.length > 0) next.push(line);
322
+ }
323
+ fs.writeFileSync(pathname, `${next.join("\n")}\n`, { mode: 0o600 });
324
+ try {
325
+ fs.chmodSync(pathname, 0o600);
326
+ } catch {
327
+ // ignored
328
+ }
329
+ }
330
+
285
331
  function readEnvFile(pathname) {
286
332
  try {
287
333
  const result = {};
@@ -297,13 +343,55 @@ function readEnvFile(pathname) {
297
343
  }
298
344
 
299
345
  function currentBootKey(env = process.env) {
300
- const currentPath = currentRuntimePath(env);
301
- const currentTarget = readLinkOrNull(currentPath);
302
- if (!currentTarget) return "";
303
- const currentRoot = path.resolve(path.dirname(currentPath), currentTarget);
346
+ const currentRoot = resolvedCurrentRuntimeRoot(env);
347
+ if (!currentRoot) return "";
304
348
  return readEnvFile(path.join(currentRoot, "agent-wallet", ".env")).AGENT_WALLET_BOOT_KEY || "";
305
349
  }
306
350
 
351
+ function readTextIfExists(pathname) {
352
+ try {
353
+ return fs.readFileSync(pathname, "utf8");
354
+ } catch (error) {
355
+ if (error?.code === "ENOENT") return "";
356
+ throw error;
357
+ }
358
+ }
359
+
360
+ function writeSecretFile(pathname, value) {
361
+ fs.mkdirSync(path.dirname(pathname), { recursive: true });
362
+ fs.writeFileSync(pathname, `${String(value || "").trim()}\n`, { mode: 0o600 });
363
+ try {
364
+ fs.chmodSync(pathname, 0o600);
365
+ } catch {
366
+ // ignored
367
+ }
368
+ }
369
+
370
+ function resolveBootKeyFromFile(env = process.env) {
371
+ const keyFile = String(env.AGENT_WALLET_BOOT_KEY_FILE || "").trim();
372
+ if (!keyFile) return "";
373
+ return readTextIfExists(path.resolve(expandHome(keyFile))).trim();
374
+ }
375
+
376
+ function defaultBootKeyFile(env = process.env) {
377
+ return path.join(resolveRuntimeBase(env), "boot-key");
378
+ }
379
+
380
+ function ensureBootKeyFile(env = process.env) {
381
+ const configuredFile = String(env.AGENT_WALLET_BOOT_KEY_FILE || "").trim();
382
+ const keyFile = configuredFile ? path.resolve(expandHome(configuredFile)) : defaultBootKeyFile(env);
383
+ const existing = readTextIfExists(keyFile).trim();
384
+ if (existing) {
385
+ return { path: keyFile, status: "existing" };
386
+ }
387
+ const bootKey = String(env.AGENT_WALLET_BOOT_KEY || "").trim() || resolveBootKeyFromFile(env) || currentBootKey(env);
388
+ if (!bootKey) {
389
+ return { path: keyFile, status: "missing" };
390
+ }
391
+ writeSecretFile(keyFile, bootKey);
392
+ return { path: keyFile, status: "created" };
393
+ }
394
+
307
395
  function runDoctor() {
308
396
  const requiredPaths = [
309
397
  ["setup.sh", setupPath],
@@ -380,7 +468,7 @@ function buildInstallerEnv(args) {
380
468
  const sealedKeysPath = path.join(resolveOpenclawHome(env), "sealed_keys.json");
381
469
  const sealedKeysExist = fs.existsSync(sealedKeysPath);
382
470
  if (!env.AGENT_WALLET_BOOT_KEY) {
383
- const existingBootKey = currentBootKey(env);
471
+ const existingBootKey = resolveBootKeyFromFile(env) || currentBootKey(env);
384
472
  if (existingBootKey) {
385
473
  env.AGENT_WALLET_BOOT_KEY = existingBootKey;
386
474
  }
@@ -523,6 +611,132 @@ function runRollback(args) {
523
611
  return 0;
524
612
  }
525
613
 
614
+ function resolveHermesPluginSource() {
615
+ const currentRoot = resolvedCurrentRuntimeRoot();
616
+ const candidates = [];
617
+ if (currentRoot) {
618
+ candidates.push(path.join(currentRoot, "hermes", "plugins", "agent_wallet"));
619
+ }
620
+ candidates.push(path.join(packageRoot, "hermes", "plugins", "agent_wallet"));
621
+ for (const source of candidates) {
622
+ if (fs.existsSync(path.join(source, "plugin.yaml"))) {
623
+ return source;
624
+ }
625
+ }
626
+ throw new Error(`Missing Hermes plugin bundle. Checked: ${candidates.join(", ")}`);
627
+ }
628
+
629
+ function resolveAgentWalletPackageRoot(env = process.env) {
630
+ const currentRoot = resolvedCurrentRuntimeRoot(env);
631
+ if (currentRoot) {
632
+ const runtimePackage = path.join(currentRoot, "agent-wallet");
633
+ if (fs.existsSync(path.join(runtimePackage, "agent_wallet", "__init__.py"))) {
634
+ return runtimePackage;
635
+ }
636
+ }
637
+ return path.join(packageRoot, "agent-wallet");
638
+ }
639
+
640
+ function resolveAgentWalletPython(packageRootPath) {
641
+ for (const candidate of [
642
+ process.env.AGENT_WALLET_PYTHON,
643
+ process.env.OPENCLAW_AGENT_WALLET_PYTHON,
644
+ path.join(packageRootPath, ".venv", "bin", "python"),
645
+ path.join(packageRootPath, ".runtime-venv", "bin", "python"),
646
+ commandPath("python3"),
647
+ ]) {
648
+ if (!candidate) continue;
649
+ if (path.isAbsolute(candidate) && !fs.existsSync(candidate)) continue;
650
+ return candidate;
651
+ }
652
+ return "python3";
653
+ }
654
+
655
+ function runHermesInstall(args) {
656
+ const hermesHome = resolveHermesHome();
657
+ const userPluginsDir = path.join(hermesHome, "plugins");
658
+ const pluginSource = resolveHermesPluginSource();
659
+ const pluginTarget = path.join(userPluginsDir, "agent_wallet");
660
+ const force = hasFlag(args, "--force");
661
+ const skipEnable = hasFlag(args, "--skip-enable");
662
+ const hermesBin = commandPath("hermes");
663
+ const agentWalletPackageRoot = resolveAgentWalletPackageRoot();
664
+ const agentWalletPython = resolveAgentWalletPython(agentWalletPackageRoot);
665
+ const hermesEnvPath = path.join(hermesHome, ".env");
666
+ const existingHermesEnv = readEnvFile(hermesEnvPath);
667
+ const bootKeyFile = ensureBootKeyFile({ ...process.env, ...existingHermesEnv });
668
+
669
+ fs.mkdirSync(userPluginsDir, { recursive: true });
670
+ try {
671
+ const existing = fs.lstatSync(pluginTarget);
672
+ if (!existing.isSymbolicLink()) {
673
+ if (!force) {
674
+ throw new Error(`${pluginTarget} exists and is not a symlink. Pass --force to replace it.`);
675
+ }
676
+ fs.rmSync(pluginTarget, { recursive: true, force: true });
677
+ } else {
678
+ fs.unlinkSync(pluginTarget);
679
+ }
680
+ } catch (error) {
681
+ if (error?.code !== "ENOENT") throw error;
682
+ }
683
+ fs.symlinkSync(pluginSource, pluginTarget, "dir");
684
+
685
+ envFileSet(hermesEnvPath, {
686
+ AGENT_WALLET_PACKAGE_ROOT: agentWalletPackageRoot,
687
+ AGENT_WALLET_PYTHON: agentWalletPython,
688
+ AGENT_WALLET_BOOT_KEY_FILE: bootKeyFile.path,
689
+ });
690
+ if (bootKeyFile.status !== "missing") {
691
+ envFileUnset(hermesEnvPath, ["AGENT_WALLET_BOOT_KEY"]);
692
+ }
693
+
694
+ let enable = { attempted: false, ok: false, skipped: skipEnable, error: "" };
695
+ if (!skipEnable) {
696
+ if (!hermesBin) {
697
+ enable = {
698
+ attempted: false,
699
+ ok: false,
700
+ skipped: false,
701
+ error: "Hermes CLI was not found on PATH. Run `hermes plugins enable agent-wallet` after installing Hermes.",
702
+ };
703
+ } else {
704
+ const result = spawnSync(hermesBin, ["plugins", "enable", "agent-wallet"], {
705
+ cwd: packageRoot,
706
+ encoding: "utf8",
707
+ env: { ...process.env, HERMES_HOME: hermesHome },
708
+ });
709
+ enable = {
710
+ attempted: true,
711
+ ok: result.status === 0,
712
+ skipped: false,
713
+ error: result.status === 0 ? "" : (result.stderr || result.stdout || "").trim(),
714
+ };
715
+ }
716
+ }
717
+
718
+ console.log(
719
+ JSON.stringify(
720
+ {
721
+ ok: enable.skipped || enable.ok,
722
+ hermes_home: hermesHome,
723
+ plugin_source: pluginSource,
724
+ plugin_target: pluginTarget,
725
+ env_path: hermesEnvPath,
726
+ agent_wallet_package_root: agentWalletPackageRoot,
727
+ agent_wallet_python: agentWalletPython,
728
+ boot_key_file: bootKeyFile.path,
729
+ boot_key_file_status: bootKeyFile.status,
730
+ hermes_enable: enable,
731
+ restart_required: true,
732
+ },
733
+ null,
734
+ 2,
735
+ ),
736
+ );
737
+ return enable.skipped || enable.ok ? 0 : 1;
738
+ }
739
+
526
740
  const args = process.argv.slice(2);
527
741
  const command = args[0] || "install";
528
742
 
@@ -556,6 +770,16 @@ if (command === "rollback") {
556
770
  process.exit(runRollback(args.slice(1)));
557
771
  }
558
772
 
773
+ if (command === "hermes") {
774
+ const subcommand = args[1] || "install";
775
+ if (subcommand === "install" || subcommand === "setup") {
776
+ process.exit(runHermesInstall(args.slice(2)));
777
+ }
778
+ console.error(`Unknown hermes command: ${subcommand}`);
779
+ console.error("Run `openclaw-agent-wallet hermes install --yes` to connect Hermes Agent.");
780
+ process.exit(2);
781
+ }
782
+
559
783
  if (command.startsWith("-")) {
560
784
  process.exit(runInstall(args, { commandName: "install" }));
561
785
  }
@@ -0,0 +1,54 @@
1
+ # AgentLayer Wallet Hermes Plugin
2
+
3
+ This is a thin Hermes Agent bridge to the existing AgentLayer/OpenClaw wallet backend.
4
+
5
+ It intentionally does not copy the OpenClaw TypeScript extension or reimplement wallet policy. Hermes gets three tools:
6
+
7
+ - `agent_wallet_tools` - lists the underlying wallet tools and schemas from the Python adapter without creating or unlocking a wallet.
8
+ - `agent_wallet_invoke` - forwards one tool call to `python -m agent_wallet.openclaw_cli invoke`.
9
+ - `agent_wallet_approve` - issues a short-lived approval token through `python -m agent_wallet.openclaw_cli issue-approval` after explicit user confirmation of the exact preview summary.
10
+
11
+ OpenClaw remains the primary local environment. This plugin only expands the same backend into Hermes.
12
+
13
+ ## Integration Plan
14
+
15
+ 1. Keep wallet behavior and safety policy in `agent-wallet/`.
16
+ 2. Keep OpenClaw as the primary environment and leave `.openclaw/extensions/agent-wallet` unchanged.
17
+ 3. Register a small Hermes bridge instead of one Hermes tool per wallet operation.
18
+ 4. Use discovery from `OpenClawWalletAdapter.list_tools()` so Hermes sees the current backend schemas without duplicated metadata.
19
+ 5. Forward execution to `agent_wallet.openclaw_cli invoke` so config validation, sealed secrets, approval-token checks, and backend dispatch stay authoritative in Python.
20
+ 6. Forward token issuance to `agent_wallet.openclaw_cli issue-approval` so Hermes can complete preview/approve/execute without learning sealed secrets.
21
+ 7. Cache successful Solana swap previews briefly in Hermes and bind the cached payload digest into the approval token. Execute can then reuse the exact Jupiter preview the user approved instead of re-quoting volatile markets.
22
+ 8. Add broader Hermes ergonomics later only where it improves safety, such as an installer wrapper or read-only status command.
23
+
24
+ ## Install
25
+
26
+ Copy or symlink this directory into a Hermes plugin path:
27
+
28
+ ```bash
29
+ mkdir -p ~/.hermes/plugins
30
+ ln -s /absolute/path/to/openclaw_skill/hermes/plugins/agent_wallet ~/.hermes/plugins/agent_wallet
31
+ ```
32
+
33
+ Set the wallet package root if Hermes is not launched from this repository:
34
+
35
+ ```bash
36
+ export AGENT_WALLET_PACKAGE_ROOT=/absolute/path/to/openclaw_skill/agent-wallet
37
+ export AGENT_WALLET_PYTHON=python3
38
+ ```
39
+
40
+ Then enable or reload plugins in Hermes:
41
+
42
+ ```bash
43
+ hermes plugins
44
+ hermes chat
45
+ ```
46
+
47
+ ## Runtime Notes
48
+
49
+ - Secrets must stay in the existing protected runtime path, especially `~/.openclaw/sealed_keys.json`.
50
+ - Do not pass `privateKey`, `masterKey`, or `approvalSecret` through Hermes tool config.
51
+ - Write-capable wallet tools still require preview first and an `approval_token` bound to the exact `confirmation_summary`.
52
+ - Use `agent_wallet_approve` only after the user explicitly confirms the exact operation. Mainnet execute requires `mainnet_confirmed=true`.
53
+ - Use `agent_wallet_tools` before invoking unfamiliar tool names.
54
+ - Solana swap previews are cached at `~/.hermes/agent_wallet_preview_cache.json` with `0600` permissions for a short window. The cache contains unsigned preview payloads only; signing and approval checks remain in `agent-wallet/`.
@@ -0,0 +1,55 @@
1
+ """Hermes Agent plugin bridge for AgentLayer wallet tools."""
2
+
3
+ from .schemas import (
4
+ AGENT_WALLET_APPROVE,
5
+ AGENT_WALLET_EVM_SETUP,
6
+ AGENT_WALLET_EVM_STATUS,
7
+ AGENT_WALLET_INVOKE,
8
+ AGENT_WALLET_TOOLS,
9
+ )
10
+ from .tools import (
11
+ agent_wallet_approve,
12
+ agent_wallet_evm_setup,
13
+ agent_wallet_evm_status,
14
+ agent_wallet_invoke,
15
+ agent_wallet_tools,
16
+ )
17
+
18
+
19
+ def register(ctx):
20
+ """Register a narrow dispatcher instead of duplicating wallet tools."""
21
+ ctx.register_tool(
22
+ name=AGENT_WALLET_TOOLS["name"],
23
+ toolset="agent_wallet",
24
+ schema=AGENT_WALLET_TOOLS,
25
+ handler=agent_wallet_tools,
26
+ description=AGENT_WALLET_TOOLS["description"],
27
+ )
28
+ ctx.register_tool(
29
+ name=AGENT_WALLET_INVOKE["name"],
30
+ toolset="agent_wallet",
31
+ schema=AGENT_WALLET_INVOKE,
32
+ handler=agent_wallet_invoke,
33
+ description=AGENT_WALLET_INVOKE["description"],
34
+ )
35
+ ctx.register_tool(
36
+ name=AGENT_WALLET_APPROVE["name"],
37
+ toolset="agent_wallet",
38
+ schema=AGENT_WALLET_APPROVE,
39
+ handler=agent_wallet_approve,
40
+ description=AGENT_WALLET_APPROVE["description"],
41
+ )
42
+ ctx.register_tool(
43
+ name=AGENT_WALLET_EVM_STATUS["name"],
44
+ toolset="agent_wallet",
45
+ schema=AGENT_WALLET_EVM_STATUS,
46
+ handler=agent_wallet_evm_status,
47
+ description=AGENT_WALLET_EVM_STATUS["description"],
48
+ )
49
+ ctx.register_tool(
50
+ name=AGENT_WALLET_EVM_SETUP["name"],
51
+ toolset="agent_wallet",
52
+ schema=AGENT_WALLET_EVM_SETUP,
53
+ handler=agent_wallet_evm_setup,
54
+ description=AGENT_WALLET_EVM_SETUP["description"],
55
+ )
@@ -0,0 +1,9 @@
1
+ name: agent-wallet
2
+ version: 0.1.0
3
+ description: Thin Hermes Agent bridge to the existing AgentLayer/OpenClaw wallet backend
4
+ provides_tools:
5
+ - agent_wallet_tools
6
+ - agent_wallet_invoke
7
+ - agent_wallet_approve
8
+ - agent_wallet_evm_status
9
+ - agent_wallet_evm_setup