@agentlayer.tech/wallet 0.1.27 → 0.1.30
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/README.md +4 -5
- package/.openclaw/extensions/agent-wallet/dist/index.js +31 -31
- package/.openclaw/extensions/agent-wallet/index.ts +31 -31
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +2 -2
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/CHANGELOG.md +52 -0
- package/README.md +9 -0
- package/agent-wallet/README.md +18 -22
- package/agent-wallet/agent_wallet/bootstrap.py +28 -12
- package/agent-wallet/agent_wallet/btc_user_wallets.py +2 -7
- package/agent-wallet/agent_wallet/config.py +99 -22
- package/agent-wallet/agent_wallet/evm_user_wallets.py +2 -14
- package/agent-wallet/agent_wallet/openclaw_adapter.py +72 -108
- package/agent-wallet/agent_wallet/openclaw_runtime.py +3 -12
- package/agent-wallet/agent_wallet/providers/kamino.py +21 -4
- package/agent-wallet/agent_wallet/providers/solana_rpc.py +0 -23
- package/agent-wallet/agent_wallet/providers/x402.py +198 -18
- package/agent-wallet/agent_wallet/user_wallets.py +4 -3
- package/agent-wallet/agent_wallet/wallet_layer/base.py +3 -3
- package/agent-wallet/agent_wallet/wallet_layer/factory.py +8 -5
- package/agent-wallet/agent_wallet/wallet_layer/solana.py +437 -44
- package/agent-wallet/agent_wallet/wallet_layer/wdk_btc.py +2 -8
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +13 -13
- package/agent-wallet/examples/openclaw_runtime_onboarding.py +1 -1
- package/agent-wallet/examples/openclaw_user_wallet_example.py +1 -1
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +2 -1
- package/agent-wallet/scripts/bootstrap_openclaw_btc.py +3 -5
- package/agent-wallet/scripts/bootstrap_openclaw_evm.py +2 -12
- package/agent-wallet/scripts/build_release_bundle.py +1 -0
- package/agent-wallet/scripts/flash-sdk-bridge/bridge.mjs +1 -4
- package/agent-wallet/scripts/install_agent_wallet.py +1 -0
- package/agent-wallet/scripts/install_openclaw_local_config.py +4 -6
- package/agent-wallet/scripts/manage_openclaw_btc_wallet.py +2 -4
- package/agent-wallet/scripts/manage_openclaw_evm_wallet.py +2 -15
- package/agent-wallet/scripts/reveal_btc_seed.sh +7 -16
- package/agent-wallet/scripts/setup_btc_wallet.sh +7 -16
- package/agent-wallet/scripts/setup_evm_wallet.sh +1 -11
- package/agent-wallet/scripts/switch_openclaw_wallet_network.py +4 -1
- package/agent-wallet/skills/wallet-operator/SKILL.md +0 -1
- package/bin/openclaw-agent-wallet.mjs +289 -0
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +20 -0
- package/claude-code/plugins/agent-wallet/.mcp.json +14 -0
- package/claude-code/plugins/agent-wallet/README.md +65 -0
- package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +34 -0
- package/claude-code/plugins/agent-wallet/skills/wallet-operator/SKILL.md +18 -0
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +38 -0
- package/codex/plugins/agent-wallet/.mcp.json +15 -0
- package/codex/plugins/agent-wallet/README.md +39 -0
- package/codex/plugins/agent-wallet/scripts/run_mcp.sh +21 -0
- package/codex/plugins/agent-wallet/server.py +1077 -0
- package/codex/plugins/agent-wallet/skills/wallet-operator/SKILL.md +18 -0
- package/hermes/plugins/agent_wallet/schemas.py +2 -2
- package/hermes/plugins/agent_wallet/tools.py +17 -3
- package/package.json +6 -1
- package/setup.sh +2 -0
|
@@ -17,6 +17,7 @@ from urllib.request import urlopen
|
|
|
17
17
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
18
18
|
|
|
19
19
|
from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
|
|
20
|
+
from agent_wallet.config import normalize_evm_network
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def _default_config_path() -> Path:
|
|
@@ -44,18 +45,7 @@ def _script_path(name: str) -> Path:
|
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
def _normalize_network(value: str) -> str:
|
|
47
|
-
|
|
48
|
-
aliases = {
|
|
49
|
-
"mainnet": "ethereum",
|
|
50
|
-
"eth": "ethereum",
|
|
51
|
-
"eth-mainnet": "ethereum",
|
|
52
|
-
"base-mainnet": "base",
|
|
53
|
-
"base_sepolia": "base-sepolia",
|
|
54
|
-
}
|
|
55
|
-
network = aliases.get(network, network)
|
|
56
|
-
if network not in {"ethereum", "sepolia", "base", "base-sepolia"}:
|
|
57
|
-
return "ethereum"
|
|
58
|
-
return network
|
|
48
|
+
return normalize_evm_network(value)
|
|
59
49
|
|
|
60
50
|
|
|
61
51
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -264,16 +264,13 @@ function createReadOnlyWallet(web3, owner) {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
function resolveClusterName(network) {
|
|
267
|
-
return
|
|
267
|
+
return "mainnet-beta";
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
function defaultRpcUrlForNetwork(network) {
|
|
271
271
|
if (network === "mainnet") {
|
|
272
272
|
return "https://api.mainnet-beta.solana.com";
|
|
273
273
|
}
|
|
274
|
-
if (network === "devnet") {
|
|
275
|
-
return "https://api.devnet.solana.com";
|
|
276
|
-
}
|
|
277
274
|
return "";
|
|
278
275
|
}
|
|
279
276
|
|
|
@@ -13,6 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
14
14
|
|
|
15
15
|
from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
|
|
16
|
+
from agent_wallet.config import normalize_btc_network, normalize_solana_network
|
|
16
17
|
from agent_wallet.sealed_keys import resolve_sealed_keys_path, seal_keys, unseal_keys
|
|
17
18
|
from security_utils import write_redacted_backup
|
|
18
19
|
|
|
@@ -42,7 +43,6 @@ LEGACY_ALLOWLIST_TOOLS = [
|
|
|
42
43
|
"transfer_spl_token",
|
|
43
44
|
"swap_solana_tokens",
|
|
44
45
|
"close_empty_token_accounts",
|
|
45
|
-
"request_devnet_airdrop",
|
|
46
46
|
"get_flash_trade_markets",
|
|
47
47
|
"get_flash_trade_positions",
|
|
48
48
|
"flash_trade_open_position",
|
|
@@ -181,10 +181,8 @@ def _normalize_network(backend: str, network: str) -> str:
|
|
|
181
181
|
backend_name = backend.strip().lower()
|
|
182
182
|
normalized = network.strip().lower()
|
|
183
183
|
if backend_name in {"wdk_btc_local", "wdk-btc-local", "btc_local", "btc-local"}:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return normalized or "bitcoin"
|
|
187
|
-
return normalized or "devnet"
|
|
184
|
+
return normalize_btc_network(normalized or "bitcoin")
|
|
185
|
+
return normalize_solana_network(normalized or "mainnet")
|
|
188
186
|
|
|
189
187
|
|
|
190
188
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -193,7 +191,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
193
191
|
parser.add_argument("--plugin-id", default="agent-wallet")
|
|
194
192
|
parser.add_argument("--user-id", default="")
|
|
195
193
|
parser.add_argument("--backend", default="solana_local")
|
|
196
|
-
parser.add_argument("--network", default="
|
|
194
|
+
parser.add_argument("--network", default="mainnet")
|
|
197
195
|
parser.add_argument("--rpc-url", default="")
|
|
198
196
|
parser.add_argument("--rpc-urls", default="")
|
|
199
197
|
parser.add_argument("--wdk-btc-service-url", default="")
|
|
@@ -21,13 +21,11 @@ from agent_wallet.btc_user_wallets import ( # noqa: E402
|
|
|
21
21
|
reveal_user_btc_wallet_seed_phrase,
|
|
22
22
|
unlock_user_btc_wallet,
|
|
23
23
|
)
|
|
24
|
+
from agent_wallet.config import normalize_btc_network # noqa: E402
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def _normalize_network(value: str) -> str:
|
|
27
|
-
|
|
28
|
-
if network == "mainnet":
|
|
29
|
-
return "bitcoin"
|
|
30
|
-
return network or "bitcoin"
|
|
28
|
+
return normalize_btc_network(value)
|
|
31
29
|
|
|
32
30
|
|
|
33
31
|
def _read_secret(
|
|
@@ -15,7 +15,7 @@ PACKAGE_ROOT = Path(__file__).resolve().parents[1]
|
|
|
15
15
|
if str(PACKAGE_ROOT) not in sys.path:
|
|
16
16
|
sys.path.insert(0, str(PACKAGE_ROOT))
|
|
17
17
|
|
|
18
|
-
from agent_wallet.config import settings # noqa: E402
|
|
18
|
+
from agent_wallet.config import normalize_evm_network, settings # noqa: E402
|
|
19
19
|
from agent_wallet.evm_user_wallets import ( # noqa: E402
|
|
20
20
|
bind_user_evm_wallet,
|
|
21
21
|
create_user_evm_wallet,
|
|
@@ -29,26 +29,13 @@ from agent_wallet.providers.wdk_evm_local import WdkEvmLocalClient # noqa: E402
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def _normalize_network(value: str) -> str:
|
|
32
|
-
|
|
33
|
-
aliases = {
|
|
34
|
-
"mainnet": "ethereum",
|
|
35
|
-
"eth": "ethereum",
|
|
36
|
-
"eth-mainnet": "ethereum",
|
|
37
|
-
"base-mainnet": "base",
|
|
38
|
-
"base_sepolia": "base-sepolia",
|
|
39
|
-
}
|
|
40
|
-
network = aliases.get(network, network)
|
|
41
|
-
if network not in {"ethereum", "sepolia", "base", "base-sepolia"}:
|
|
42
|
-
return "ethereum"
|
|
43
|
-
return network
|
|
32
|
+
return normalize_evm_network(value)
|
|
44
33
|
|
|
45
34
|
|
|
46
35
|
def _paired_network(network: str) -> str | None:
|
|
47
36
|
mapping = {
|
|
48
37
|
"ethereum": "base",
|
|
49
38
|
"base": "ethereum",
|
|
50
|
-
"sepolia": "base-sepolia",
|
|
51
|
-
"base-sepolia": "sepolia",
|
|
52
39
|
}
|
|
53
40
|
return mapping.get(_normalize_network(network))
|
|
54
41
|
|
|
@@ -65,12 +65,6 @@ normalize_network_value() {
|
|
|
65
65
|
1|mainnet|bitcoin)
|
|
66
66
|
printf "mainnet"
|
|
67
67
|
;;
|
|
68
|
-
2|testnet)
|
|
69
|
-
printf "testnet"
|
|
70
|
-
;;
|
|
71
|
-
3|regtest)
|
|
72
|
-
printf "regtest"
|
|
73
|
-
;;
|
|
74
68
|
*)
|
|
75
69
|
return 1
|
|
76
70
|
;;
|
|
@@ -84,18 +78,11 @@ prompt_network_choice() {
|
|
|
84
78
|
return 0
|
|
85
79
|
fi
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
mainnet|bitcoin) default_hint="1" ;;
|
|
89
|
-
testnet) default_hint="2" ;;
|
|
90
|
-
regtest) default_hint="3" ;;
|
|
91
|
-
*) default_hint="1" ;;
|
|
92
|
-
esac
|
|
81
|
+
default_hint="1"
|
|
93
82
|
|
|
94
83
|
while true; do
|
|
95
84
|
printf "BTC network:\n" >&2
|
|
96
85
|
printf " 1) mainnet\n" >&2
|
|
97
|
-
printf " 2) testnet\n" >&2
|
|
98
|
-
printf " 3) regtest\n" >&2
|
|
99
86
|
printf "Choose network [%s]: " "$default_hint" >&2
|
|
100
87
|
read -r choice
|
|
101
88
|
if [ -z "${choice:-}" ]; then
|
|
@@ -105,12 +92,16 @@ prompt_network_choice() {
|
|
|
105
92
|
printf "%s" "$network"
|
|
106
93
|
return 0
|
|
107
94
|
fi
|
|
108
|
-
printf "Invalid choice. Enter 1,
|
|
95
|
+
printf "Invalid choice. Enter 1, mainnet, or bitcoin.\n" >&2
|
|
109
96
|
done
|
|
110
97
|
}
|
|
111
98
|
|
|
112
99
|
DEFAULT_USER_ID=${OPENCLAW_BTC_USER_ID:-${USER:-openclaw-user}-local}
|
|
113
|
-
DEFAULT_NETWORK=${OPENCLAW_BTC_NETWORK:-mainnet}
|
|
100
|
+
if DEFAULT_NETWORK=$(normalize_network_value "${OPENCLAW_BTC_NETWORK:-mainnet}" 2>/dev/null); then
|
|
101
|
+
:
|
|
102
|
+
else
|
|
103
|
+
DEFAULT_NETWORK=mainnet
|
|
104
|
+
fi
|
|
114
105
|
DEFAULT_SERVICE_URL=${OPENCLAW_BTC_SERVICE_URL:-http://127.0.0.1:8080}
|
|
115
106
|
|
|
116
107
|
if ! has_flag --user-id "$@"; then
|
|
@@ -65,12 +65,6 @@ normalize_network_value() {
|
|
|
65
65
|
1|mainnet|bitcoin)
|
|
66
66
|
printf "mainnet"
|
|
67
67
|
;;
|
|
68
|
-
2|testnet)
|
|
69
|
-
printf "testnet"
|
|
70
|
-
;;
|
|
71
|
-
3|regtest)
|
|
72
|
-
printf "regtest"
|
|
73
|
-
;;
|
|
74
68
|
*)
|
|
75
69
|
return 1
|
|
76
70
|
;;
|
|
@@ -84,18 +78,11 @@ prompt_network_choice() {
|
|
|
84
78
|
return 0
|
|
85
79
|
fi
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
mainnet|bitcoin) default_hint="1" ;;
|
|
89
|
-
testnet) default_hint="2" ;;
|
|
90
|
-
regtest) default_hint="3" ;;
|
|
91
|
-
*) default_hint="1" ;;
|
|
92
|
-
esac
|
|
81
|
+
default_hint="1"
|
|
93
82
|
|
|
94
83
|
while true; do
|
|
95
84
|
printf "BTC network:\n" >&2
|
|
96
85
|
printf " 1) mainnet\n" >&2
|
|
97
|
-
printf " 2) testnet\n" >&2
|
|
98
|
-
printf " 3) regtest\n" >&2
|
|
99
86
|
printf "Choose network [%s]: " "$default_hint" >&2
|
|
100
87
|
read -r choice
|
|
101
88
|
if [ -z "${choice:-}" ]; then
|
|
@@ -105,12 +92,16 @@ prompt_network_choice() {
|
|
|
105
92
|
printf "%s" "$network"
|
|
106
93
|
return 0
|
|
107
94
|
fi
|
|
108
|
-
printf "Invalid choice. Enter 1,
|
|
95
|
+
printf "Invalid choice. Enter 1, mainnet, or bitcoin.\n" >&2
|
|
109
96
|
done
|
|
110
97
|
}
|
|
111
98
|
|
|
112
99
|
DEFAULT_USER_ID=${OPENCLAW_BTC_USER_ID:-${USER:-openclaw-user}-local}
|
|
113
|
-
DEFAULT_NETWORK=${OPENCLAW_BTC_NETWORK:-mainnet}
|
|
100
|
+
if DEFAULT_NETWORK=$(normalize_network_value "${OPENCLAW_BTC_NETWORK:-mainnet}" 2>/dev/null); then
|
|
101
|
+
:
|
|
102
|
+
else
|
|
103
|
+
DEFAULT_NETWORK=mainnet
|
|
104
|
+
fi
|
|
114
105
|
DEFAULT_SERVICE_URL=${OPENCLAW_BTC_SERVICE_URL:-http://127.0.0.1:8080}
|
|
115
106
|
|
|
116
107
|
if ! has_flag --user-id "$@"; then
|
|
@@ -68,12 +68,6 @@ normalize_network_value() {
|
|
|
68
68
|
2|base)
|
|
69
69
|
printf "base"
|
|
70
70
|
;;
|
|
71
|
-
3|sepolia)
|
|
72
|
-
printf "sepolia"
|
|
73
|
-
;;
|
|
74
|
-
4|base-sepolia|base_sepolia)
|
|
75
|
-
printf "base-sepolia"
|
|
76
|
-
;;
|
|
77
71
|
*)
|
|
78
72
|
return 1
|
|
79
73
|
;;
|
|
@@ -90,8 +84,6 @@ prompt_network_choice() {
|
|
|
90
84
|
case "$default_value" in
|
|
91
85
|
ethereum) default_hint="1" ;;
|
|
92
86
|
base) default_hint="2" ;;
|
|
93
|
-
sepolia) default_hint="3" ;;
|
|
94
|
-
base-sepolia) default_hint="4" ;;
|
|
95
87
|
*) default_hint="2" ;;
|
|
96
88
|
esac
|
|
97
89
|
|
|
@@ -99,8 +91,6 @@ prompt_network_choice() {
|
|
|
99
91
|
printf "EVM network:\n" >&2
|
|
100
92
|
printf " 1) ethereum\n" >&2
|
|
101
93
|
printf " 2) base\n" >&2
|
|
102
|
-
printf " 3) sepolia\n" >&2
|
|
103
|
-
printf " 4) base-sepolia\n" >&2
|
|
104
94
|
printf "Choose network [%s]: " "$default_hint" >&2
|
|
105
95
|
read -r choice
|
|
106
96
|
if [ -z "${choice:-}" ]; then
|
|
@@ -110,7 +100,7 @@ prompt_network_choice() {
|
|
|
110
100
|
printf "%s" "$network"
|
|
111
101
|
return 0
|
|
112
102
|
fi
|
|
113
|
-
printf "Invalid choice. Enter 1, 2,
|
|
103
|
+
printf "Invalid choice. Enter 1, 2, ethereum, or base.\n" >&2
|
|
114
104
|
done
|
|
115
105
|
}
|
|
116
106
|
|
|
@@ -5,9 +5,12 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
+
import sys
|
|
8
9
|
from datetime import datetime, timezone
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
12
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
13
|
+
|
|
11
14
|
from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
|
|
12
15
|
from security_utils import write_redacted_backup
|
|
13
16
|
|
|
@@ -20,7 +23,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
20
23
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
21
24
|
parser.add_argument("--config-path", default=str(_default_config_path()))
|
|
22
25
|
parser.add_argument("--plugin-id", default="agent-wallet")
|
|
23
|
-
parser.add_argument("--network", required=True, choices=["mainnet"
|
|
26
|
+
parser.add_argument("--network", required=True, choices=["mainnet"])
|
|
24
27
|
parser.add_argument("--rpc-url", default="")
|
|
25
28
|
parser.add_argument("--rpc-urls", default="")
|
|
26
29
|
parser.add_argument("--sign-only", action=argparse.BooleanOptionalAction, default=None)
|
|
@@ -116,7 +116,6 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
|
|
|
116
116
|
- `claim_bags_fees`: `token_mint`, `mode`, `purpose`.
|
|
117
117
|
- `launch_bags_token`: `name`, `symbol`, `description`, `base_mint`, `claimers`, `basis_points`, `initial_buy_sol`, `mode`, `purpose`; optional socials/image/config type.
|
|
118
118
|
- `close_empty_token_accounts`: `limit`, `mode` (`preview` or `execute`), `purpose`.
|
|
119
|
-
- `request_devnet_airdrop`: `amount`; only outside mainnet.
|
|
120
119
|
|
|
121
120
|
## Approval Flow Template
|
|
122
121
|
|
|
@@ -22,6 +22,8 @@ function printHelp() {
|
|
|
22
22
|
Usage:
|
|
23
23
|
openclaw-agent-wallet install [options]
|
|
24
24
|
openclaw-agent-wallet hermes install [options]
|
|
25
|
+
openclaw-agent-wallet codex install [options]
|
|
26
|
+
openclaw-agent-wallet claude-code install [options]
|
|
25
27
|
openclaw-agent-wallet update [options]
|
|
26
28
|
openclaw-agent-wallet status
|
|
27
29
|
openclaw-agent-wallet rollback [--to <version>]
|
|
@@ -37,6 +39,8 @@ Common install options:
|
|
|
37
39
|
Examples:
|
|
38
40
|
npx @agentlayer.tech/wallet install --yes
|
|
39
41
|
npx @agentlayer.tech/wallet hermes install --yes
|
|
42
|
+
npx @agentlayer.tech/wallet codex install --yes
|
|
43
|
+
npx @agentlayer.tech/wallet claude-code install --yes
|
|
40
44
|
npx @agentlayer.tech/wallet install --backend none
|
|
41
45
|
npx @agentlayer.tech/wallet update --yes
|
|
42
46
|
npx @agentlayer.tech/wallet update --yes --dry-run
|
|
@@ -90,6 +94,10 @@ function resolveHermesHome(env = process.env) {
|
|
|
90
94
|
return path.resolve(expandHome(env.HERMES_HOME || "~/.hermes"));
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
function resolveCodexHome(env = process.env) {
|
|
98
|
+
return path.resolve(expandHome(env.CODEX_HOME || "~/.codex"));
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
function releaseRootFor(version, env = process.env) {
|
|
94
102
|
return path.join(resolveRuntimeBase(env), "releases", version);
|
|
95
103
|
}
|
|
@@ -545,6 +553,20 @@ function readEnvFile(pathname) {
|
|
|
545
553
|
}
|
|
546
554
|
}
|
|
547
555
|
|
|
556
|
+
function readJsonFile(pathname) {
|
|
557
|
+
try {
|
|
558
|
+
return JSON.parse(fs.readFileSync(pathname, "utf8"));
|
|
559
|
+
} catch (error) {
|
|
560
|
+
if (error?.code === "ENOENT") return null;
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function writeJsonFile(pathname, value) {
|
|
566
|
+
fs.mkdirSync(path.dirname(pathname), { recursive: true });
|
|
567
|
+
fs.writeFileSync(pathname, `${JSON.stringify(value, null, 2)}\n`);
|
|
568
|
+
}
|
|
569
|
+
|
|
548
570
|
function currentBootKey(env = process.env) {
|
|
549
571
|
const currentRoot = resolvedCurrentRuntimeRoot(env);
|
|
550
572
|
if (!currentRoot) return "";
|
|
@@ -600,6 +622,8 @@ function runDoctor() {
|
|
|
600
622
|
["setup.sh", setupPath],
|
|
601
623
|
["agent-wallet", path.join(packageRoot, "agent-wallet")],
|
|
602
624
|
["OpenClaw extension", path.join(packageRoot, ".openclaw", "extensions", "agent-wallet")],
|
|
625
|
+
["Codex plugin", path.join(packageRoot, "codex", "plugins", "agent-wallet", ".codex-plugin", "plugin.json")],
|
|
626
|
+
["Claude Code plugin", path.join(packageRoot, "claude-code", "plugins", "agent-wallet", ".claude-plugin", "plugin.json")],
|
|
603
627
|
["wdk-btc-wallet", path.join(packageRoot, "wdk-btc-wallet", "package.json")],
|
|
604
628
|
["wdk-evm-wallet", path.join(packageRoot, "wdk-evm-wallet", "package.json")],
|
|
605
629
|
];
|
|
@@ -961,6 +985,97 @@ function resolveHermesPluginSource() {
|
|
|
961
985
|
throw new Error(`Missing Hermes plugin bundle. Checked: ${candidates.join(", ")}`);
|
|
962
986
|
}
|
|
963
987
|
|
|
988
|
+
function resolveCodexPluginSource() {
|
|
989
|
+
const currentRoot = resolvedCurrentRuntimeRoot();
|
|
990
|
+
const candidates = [];
|
|
991
|
+
if (currentRoot) {
|
|
992
|
+
candidates.push(path.join(currentRoot, "codex", "plugins", "agent-wallet"));
|
|
993
|
+
}
|
|
994
|
+
candidates.push(path.join(packageRoot, "codex", "plugins", "agent-wallet"));
|
|
995
|
+
for (const source of candidates) {
|
|
996
|
+
if (fs.existsSync(path.join(source, ".codex-plugin", "plugin.json"))) {
|
|
997
|
+
return source;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
throw new Error(`Missing Codex plugin bundle. Checked: ${candidates.join(", ")}`);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function resolveClaudeCodePluginSource() {
|
|
1004
|
+
const currentRoot = resolvedCurrentRuntimeRoot();
|
|
1005
|
+
const candidates = [];
|
|
1006
|
+
if (currentRoot) {
|
|
1007
|
+
candidates.push(path.join(currentRoot, "claude-code", "plugins", "agent-wallet"));
|
|
1008
|
+
}
|
|
1009
|
+
candidates.push(path.join(packageRoot, "claude-code", "plugins", "agent-wallet"));
|
|
1010
|
+
for (const source of candidates) {
|
|
1011
|
+
if (fs.existsSync(path.join(source, ".claude-plugin", "plugin.json"))) {
|
|
1012
|
+
return source;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
throw new Error(`Missing Claude Code plugin bundle. Checked: ${candidates.join(", ")}`);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function resolveCodexPluginInstallRoot(env = process.env) {
|
|
1019
|
+
return path.resolve(expandHome(env.AGENT_WALLET_CODEX_PLUGIN_ROOT || "~/plugins"));
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function resolveCodexMarketplacePath(env = process.env) {
|
|
1023
|
+
return path.resolve(
|
|
1024
|
+
expandHome(env.AGENT_WALLET_CODEX_MARKETPLACE_PATH || "~/.agents/plugins/marketplace.json"),
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function ensureCodexMarketplaceEntry({ marketplacePath, pluginName }) {
|
|
1029
|
+
const existing = readJsonFile(marketplacePath);
|
|
1030
|
+
const payload = existing && typeof existing === "object"
|
|
1031
|
+
? existing
|
|
1032
|
+
: {
|
|
1033
|
+
name: "local",
|
|
1034
|
+
interface: {
|
|
1035
|
+
displayName: "Local Plugins",
|
|
1036
|
+
},
|
|
1037
|
+
plugins: [],
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
if (typeof payload.name !== "string" || !payload.name.trim()) {
|
|
1041
|
+
payload.name = "local";
|
|
1042
|
+
}
|
|
1043
|
+
if (!payload.interface || typeof payload.interface !== "object") {
|
|
1044
|
+
payload.interface = { displayName: "Local Plugins" };
|
|
1045
|
+
}
|
|
1046
|
+
if (typeof payload.interface.displayName !== "string" || !payload.interface.displayName.trim()) {
|
|
1047
|
+
payload.interface.displayName = "Local Plugins";
|
|
1048
|
+
}
|
|
1049
|
+
if (!Array.isArray(payload.plugins)) {
|
|
1050
|
+
payload.plugins = [];
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const entry = {
|
|
1054
|
+
name: pluginName,
|
|
1055
|
+
source: {
|
|
1056
|
+
source: "local",
|
|
1057
|
+
path: `./plugins/${pluginName}`,
|
|
1058
|
+
},
|
|
1059
|
+
policy: {
|
|
1060
|
+
installation: "AVAILABLE",
|
|
1061
|
+
authentication: "ON_INSTALL",
|
|
1062
|
+
},
|
|
1063
|
+
category: "Coding",
|
|
1064
|
+
};
|
|
1065
|
+
const index = payload.plugins.findIndex((item) => item && item.name === pluginName);
|
|
1066
|
+
if (index >= 0) {
|
|
1067
|
+
payload.plugins[index] = entry;
|
|
1068
|
+
} else {
|
|
1069
|
+
payload.plugins.push(entry);
|
|
1070
|
+
}
|
|
1071
|
+
writeJsonFile(marketplacePath, payload);
|
|
1072
|
+
return {
|
|
1073
|
+
marketplace_name: payload.name,
|
|
1074
|
+
marketplace_path: marketplacePath,
|
|
1075
|
+
entry,
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
|
|
964
1079
|
function resolveAgentWalletPackageRoot(env = process.env) {
|
|
965
1080
|
const currentRoot = resolvedCurrentRuntimeRoot(env);
|
|
966
1081
|
if (currentRoot) {
|
|
@@ -1072,6 +1187,160 @@ function runHermesInstall(args) {
|
|
|
1072
1187
|
return enable.skipped || enable.ok ? 0 : 1;
|
|
1073
1188
|
}
|
|
1074
1189
|
|
|
1190
|
+
function runCodexInstall(args) {
|
|
1191
|
+
const codexHome = resolveCodexHome();
|
|
1192
|
+
const pluginSource = resolveCodexPluginSource();
|
|
1193
|
+
const pluginRoot = resolveCodexPluginInstallRoot();
|
|
1194
|
+
const pluginTarget = path.join(pluginRoot, "agent-wallet");
|
|
1195
|
+
const marketplacePath = resolveCodexMarketplacePath();
|
|
1196
|
+
const force = hasFlag(args, "--force");
|
|
1197
|
+
const skipEnable = hasFlag(args, "--skip-enable");
|
|
1198
|
+
const codexBin = commandPath("codex");
|
|
1199
|
+
|
|
1200
|
+
fs.mkdirSync(pluginRoot, { recursive: true });
|
|
1201
|
+
try {
|
|
1202
|
+
const existing = fs.lstatSync(pluginTarget);
|
|
1203
|
+
if (!existing.isSymbolicLink()) {
|
|
1204
|
+
if (!force) {
|
|
1205
|
+
throw new Error(`${pluginTarget} exists and is not a symlink. Pass --force to replace it.`);
|
|
1206
|
+
}
|
|
1207
|
+
fs.rmSync(pluginTarget, { recursive: true, force: true });
|
|
1208
|
+
} else {
|
|
1209
|
+
fs.unlinkSync(pluginTarget);
|
|
1210
|
+
}
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
if (error?.code !== "ENOENT") throw error;
|
|
1213
|
+
}
|
|
1214
|
+
fs.symlinkSync(pluginSource, pluginTarget, "dir");
|
|
1215
|
+
|
|
1216
|
+
const marketplace = ensureCodexMarketplaceEntry({
|
|
1217
|
+
marketplacePath,
|
|
1218
|
+
pluginName: "agent-wallet",
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
let add = { attempted: false, ok: false, skipped: skipEnable, error: "" };
|
|
1222
|
+
if (!skipEnable) {
|
|
1223
|
+
if (!codexBin) {
|
|
1224
|
+
add = {
|
|
1225
|
+
attempted: false,
|
|
1226
|
+
ok: false,
|
|
1227
|
+
skipped: false,
|
|
1228
|
+
error: "Codex CLI was not found on PATH. Run `codex plugin add agent-wallet@local` after installing Codex.",
|
|
1229
|
+
};
|
|
1230
|
+
} else {
|
|
1231
|
+
const result = spawnSync(
|
|
1232
|
+
codexBin,
|
|
1233
|
+
["plugin", "add", `agent-wallet@${marketplace.marketplace_name}`],
|
|
1234
|
+
{
|
|
1235
|
+
cwd: packageRoot,
|
|
1236
|
+
encoding: "utf8",
|
|
1237
|
+
env: {
|
|
1238
|
+
...process.env,
|
|
1239
|
+
CODEX_HOME: codexHome,
|
|
1240
|
+
},
|
|
1241
|
+
},
|
|
1242
|
+
);
|
|
1243
|
+
add = {
|
|
1244
|
+
attempted: true,
|
|
1245
|
+
ok: result.status === 0,
|
|
1246
|
+
skipped: false,
|
|
1247
|
+
error: result.status === 0 ? "" : (result.stderr || result.stdout || "").trim(),
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
console.log(
|
|
1253
|
+
JSON.stringify(
|
|
1254
|
+
{
|
|
1255
|
+
ok: add.skipped || add.ok,
|
|
1256
|
+
codex_home: codexHome,
|
|
1257
|
+
plugin_source: pluginSource,
|
|
1258
|
+
plugin_target: pluginTarget,
|
|
1259
|
+
marketplace_path: marketplace.marketplace_path,
|
|
1260
|
+
marketplace_name: marketplace.marketplace_name,
|
|
1261
|
+
codex_add: add,
|
|
1262
|
+
restart_required: true,
|
|
1263
|
+
},
|
|
1264
|
+
null,
|
|
1265
|
+
2,
|
|
1266
|
+
),
|
|
1267
|
+
);
|
|
1268
|
+
return add.skipped || add.ok ? 0 : 1;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
function runClaudeCodeInstall(args) {
|
|
1272
|
+
const pluginSource = resolveClaudeCodePluginSource();
|
|
1273
|
+
const force = hasFlag(args, "--force");
|
|
1274
|
+
const skipEnable = hasFlag(args, "--skip-enable");
|
|
1275
|
+
const claudeBin = commandPath("claude");
|
|
1276
|
+
|
|
1277
|
+
// Symlink the plugin into ~/.claude/plugins/agent-wallet so Claude Code can load it.
|
|
1278
|
+
const pluginInstallDir = path.join(os.homedir(), ".claude", "plugins");
|
|
1279
|
+
const pluginTarget = path.join(pluginInstallDir, "agent-wallet");
|
|
1280
|
+
|
|
1281
|
+
fs.mkdirSync(pluginInstallDir, { recursive: true });
|
|
1282
|
+
try {
|
|
1283
|
+
const existing = fs.lstatSync(pluginTarget);
|
|
1284
|
+
if (!existing.isSymbolicLink()) {
|
|
1285
|
+
if (!force) {
|
|
1286
|
+
throw new Error(`${pluginTarget} exists and is not a symlink. Pass --force to replace it.`);
|
|
1287
|
+
}
|
|
1288
|
+
fs.rmSync(pluginTarget, { recursive: true, force: true });
|
|
1289
|
+
} else {
|
|
1290
|
+
fs.unlinkSync(pluginTarget);
|
|
1291
|
+
}
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
if (error?.code !== "ENOENT") throw error;
|
|
1294
|
+
}
|
|
1295
|
+
fs.symlinkSync(pluginSource, pluginTarget, "dir");
|
|
1296
|
+
|
|
1297
|
+
// Try `claude plugin install <path>` when the CLI is present.
|
|
1298
|
+
let enable = { attempted: false, ok: false, skipped: skipEnable, error: "" };
|
|
1299
|
+
if (!skipEnable) {
|
|
1300
|
+
if (!claudeBin) {
|
|
1301
|
+
enable = {
|
|
1302
|
+
attempted: false,
|
|
1303
|
+
ok: false,
|
|
1304
|
+
skipped: false,
|
|
1305
|
+
error:
|
|
1306
|
+
"Claude Code CLI was not found on PATH. Load the plugin manually with: claude --plugin-dir " +
|
|
1307
|
+
pluginTarget,
|
|
1308
|
+
};
|
|
1309
|
+
} else {
|
|
1310
|
+
const result = spawnSync(claudeBin, ["plugin", "install", pluginTarget, "--scope", "user"], {
|
|
1311
|
+
encoding: "utf8",
|
|
1312
|
+
stdio: "pipe",
|
|
1313
|
+
});
|
|
1314
|
+
enable = {
|
|
1315
|
+
attempted: true,
|
|
1316
|
+
ok: result.status === 0,
|
|
1317
|
+
skipped: false,
|
|
1318
|
+
error: result.status === 0 ? "" : (result.stderr || result.stdout || "").trim(),
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
const pluginDirFlag = `claude --plugin-dir ${pluginTarget}`;
|
|
1324
|
+
console.log(
|
|
1325
|
+
JSON.stringify(
|
|
1326
|
+
{
|
|
1327
|
+
ok: enable.skipped || enable.ok,
|
|
1328
|
+
plugin_source: pluginSource,
|
|
1329
|
+
plugin_target: pluginTarget,
|
|
1330
|
+
claude_code_install: enable,
|
|
1331
|
+
manual_load: pluginDirFlag,
|
|
1332
|
+
restart_required: true,
|
|
1333
|
+
note: enable.ok
|
|
1334
|
+
? "Plugin registered. Restart Claude Code to activate."
|
|
1335
|
+
: `If automatic registration failed, load the plugin with: ${pluginDirFlag}`,
|
|
1336
|
+
},
|
|
1337
|
+
null,
|
|
1338
|
+
2,
|
|
1339
|
+
),
|
|
1340
|
+
);
|
|
1341
|
+
return enable.skipped || enable.ok ? 0 : 1;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1075
1344
|
const args = process.argv.slice(2);
|
|
1076
1345
|
const command = args[0] || "install";
|
|
1077
1346
|
|
|
@@ -1115,6 +1384,26 @@ if (command === "hermes") {
|
|
|
1115
1384
|
process.exit(2);
|
|
1116
1385
|
}
|
|
1117
1386
|
|
|
1387
|
+
if (command === "codex") {
|
|
1388
|
+
const subcommand = args[1] || "install";
|
|
1389
|
+
if (subcommand === "install" || subcommand === "setup") {
|
|
1390
|
+
process.exit(runCodexInstall(args.slice(2)));
|
|
1391
|
+
}
|
|
1392
|
+
console.error(`Unknown codex command: ${subcommand}`);
|
|
1393
|
+
console.error("Run `openclaw-agent-wallet codex install --yes` to connect Codex.");
|
|
1394
|
+
process.exit(2);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
if (command === "claude-code") {
|
|
1398
|
+
const subcommand = args[1] || "install";
|
|
1399
|
+
if (subcommand === "install" || subcommand === "setup") {
|
|
1400
|
+
process.exit(runClaudeCodeInstall(args.slice(2)));
|
|
1401
|
+
}
|
|
1402
|
+
console.error(`Unknown claude-code command: ${subcommand}`);
|
|
1403
|
+
console.error("Run `openclaw-agent-wallet claude-code install --yes` to connect Claude Code.");
|
|
1404
|
+
process.exit(2);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1118
1407
|
if (command.startsWith("-")) {
|
|
1119
1408
|
process.exit(runInstall(args, { commandName: "install" }));
|
|
1120
1409
|
}
|