@agentlayer.tech/wallet 0.1.0
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/AGENTS.md +98 -0
- package/.openclaw/extensions/agent-wallet/README.md +127 -0
- package/.openclaw/extensions/agent-wallet/index.ts +1520 -0
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +184 -0
- package/.openclaw/extensions/agent-wallet/package.json +11 -0
- package/.openclaw/extensions/agent-wallet/skills/wallet-operator/SKILL.md +20 -0
- package/CHANGELOG.md +42 -0
- package/LICENSE +104 -0
- package/README.md +332 -0
- package/RELEASING.md +204 -0
- package/agent-wallet/.env.example +62 -0
- package/agent-wallet/AGENTS.md +129 -0
- package/agent-wallet/README.md +527 -0
- package/agent-wallet/agent_wallet/__init__.py +11 -0
- package/agent-wallet/agent_wallet/approval.py +161 -0
- package/agent-wallet/agent_wallet/bootstrap.py +178 -0
- package/agent-wallet/agent_wallet/btc_user_wallets.py +217 -0
- package/agent-wallet/agent_wallet/config.py +382 -0
- package/agent-wallet/agent_wallet/encrypted_storage.py +161 -0
- package/agent-wallet/agent_wallet/evm_user_wallets.py +370 -0
- package/agent-wallet/agent_wallet/exceptions.py +9 -0
- package/agent-wallet/agent_wallet/file_ops.py +34 -0
- package/agent-wallet/agent_wallet/http_client.py +25 -0
- package/agent-wallet/agent_wallet/models.py +66 -0
- package/agent-wallet/agent_wallet/nonce_registry.py +59 -0
- package/agent-wallet/agent_wallet/openclaw_adapter.py +5128 -0
- package/agent-wallet/agent_wallet/openclaw_cli.py +626 -0
- package/agent-wallet/agent_wallet/openclaw_runtime.py +272 -0
- package/agent-wallet/agent_wallet/plugin_bundle.py +42 -0
- package/agent-wallet/agent_wallet/providers/__init__.py +1 -0
- package/agent-wallet/agent_wallet/providers/bags.py +259 -0
- package/agent-wallet/agent_wallet/providers/evm_portfolio.py +470 -0
- package/agent-wallet/agent_wallet/providers/jupiter.py +567 -0
- package/agent-wallet/agent_wallet/providers/kamino.py +215 -0
- package/agent-wallet/agent_wallet/providers/lifi.py +277 -0
- package/agent-wallet/agent_wallet/providers/solana_rpc.py +470 -0
- package/agent-wallet/agent_wallet/providers/wdk_btc_local.py +114 -0
- package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +205 -0
- package/agent-wallet/agent_wallet/sealed_keys.py +61 -0
- package/agent-wallet/agent_wallet/solana_stake.py +103 -0
- package/agent-wallet/agent_wallet/solana_tx.py +93 -0
- package/agent-wallet/agent_wallet/spending_limits.py +101 -0
- package/agent-wallet/agent_wallet/transaction_policy.py +518 -0
- package/agent-wallet/agent_wallet/user_wallets.py +355 -0
- package/agent-wallet/agent_wallet/validation.py +31 -0
- package/agent-wallet/agent_wallet/wallet_layer/__init__.py +1 -0
- package/agent-wallet/agent_wallet/wallet_layer/base.py +808 -0
- package/agent-wallet/agent_wallet/wallet_layer/base58.py +44 -0
- package/agent-wallet/agent_wallet/wallet_layer/factory.py +102 -0
- package/agent-wallet/agent_wallet/wallet_layer/solana.py +4252 -0
- package/agent-wallet/agent_wallet/wallet_layer/wdk_btc.py +272 -0
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +1628 -0
- package/agent-wallet/examples/bootstrap_wallet.py +21 -0
- package/agent-wallet/examples/openclaw_runtime_onboarding.py +28 -0
- package/agent-wallet/examples/openclaw_user_wallet_example.py +31 -0
- package/agent-wallet/examples/openclaw_wallet_adapter_example.py +33 -0
- package/agent-wallet/openclaw.plugin.json +138 -0
- package/agent-wallet/pyproject.toml +31 -0
- package/agent-wallet/scripts/bootstrap_openclaw_btc.py +278 -0
- package/agent-wallet/scripts/build_release_bundle.py +188 -0
- package/agent-wallet/scripts/finalize_openclaw_local_wallet_config.py +121 -0
- package/agent-wallet/scripts/install_agent_wallet.py +505 -0
- package/agent-wallet/scripts/install_openclaw_local_config.py +226 -0
- package/agent-wallet/scripts/install_openclaw_sealed_keys.py +105 -0
- package/agent-wallet/scripts/manage_openclaw_btc_wallet.py +244 -0
- package/agent-wallet/scripts/reveal_btc_seed.sh +130 -0
- package/agent-wallet/scripts/security_utils.py +37 -0
- package/agent-wallet/scripts/setup_btc_wallet.sh +146 -0
- package/agent-wallet/scripts/switch_openclaw_wallet_network.py +106 -0
- package/agent-wallet/skills/wallet-operator/SKILL.md +128 -0
- package/bin/openclaw-agent-wallet.mjs +487 -0
- package/install-from-github.sh +134 -0
- package/package.json +61 -0
- package/setup.sh +40 -0
- package/wdk-btc-wallet/README.md +325 -0
- package/wdk-btc-wallet/bootstrap.sh +22 -0
- package/wdk-btc-wallet/package-lock.json +1839 -0
- package/wdk-btc-wallet/package.json +18 -0
- package/wdk-btc-wallet/run-local.sh +21 -0
- package/wdk-btc-wallet/src/config.js +160 -0
- package/wdk-btc-wallet/src/json.js +35 -0
- package/wdk-btc-wallet/src/local_vault.js +432 -0
- package/wdk-btc-wallet/src/network_state.js +84 -0
- package/wdk-btc-wallet/src/server.js +257 -0
- package/wdk-btc-wallet/src/wdk_btc_wallet.js +332 -0
- package/wdk-evm-wallet/README.md +183 -0
- package/wdk-evm-wallet/bootstrap.sh +8 -0
- package/wdk-evm-wallet/package-lock.json +2340 -0
- package/wdk-evm-wallet/package.json +23 -0
- package/wdk-evm-wallet/run-local.sh +12 -0
- package/wdk-evm-wallet/src/config.js +274 -0
- package/wdk-evm-wallet/src/json.js +35 -0
- package/wdk-evm-wallet/src/local_vault.js +430 -0
- package/wdk-evm-wallet/src/network_state.js +92 -0
- package/wdk-evm-wallet/src/server.js +575 -0
- package/wdk-evm-wallet/src/wdk_evm_wallet.js +4981 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Build a release bundle tarball for the OpenClaw wallet/plugin runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import io
|
|
7
|
+
import json
|
|
8
|
+
import subprocess
|
|
9
|
+
import tarfile
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
|
+
from pathlib import Path, PurePosixPath
|
|
12
|
+
|
|
13
|
+
DEFAULT_BUNDLE_PREFIX = "openclaw-agent-wallet-bundle"
|
|
14
|
+
INCLUDED_ROOT_FILES = [
|
|
15
|
+
".env.example",
|
|
16
|
+
".gitignore",
|
|
17
|
+
"AGENTS.md",
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"README.md",
|
|
21
|
+
"RELEASING.md",
|
|
22
|
+
"install-from-github.sh",
|
|
23
|
+
"requirements.txt",
|
|
24
|
+
"setup.sh",
|
|
25
|
+
]
|
|
26
|
+
INCLUDED_TOP_LEVEL_DIRS = [
|
|
27
|
+
".openclaw",
|
|
28
|
+
"agent-a2a-gateway",
|
|
29
|
+
"agent-wallet",
|
|
30
|
+
"wdk-btc-wallet",
|
|
31
|
+
"wdk-evm-wallet",
|
|
32
|
+
]
|
|
33
|
+
EXCLUDED_EXACT_RELATIVE_PATHS = {
|
|
34
|
+
".openclaw/extensions-local",
|
|
35
|
+
".openclaw/openclaw.local.example.json",
|
|
36
|
+
}
|
|
37
|
+
EXCLUDED_DIR_NAMES = {
|
|
38
|
+
".git",
|
|
39
|
+
".venv",
|
|
40
|
+
".pytest_cache",
|
|
41
|
+
".ruff_cache",
|
|
42
|
+
"extensions-local",
|
|
43
|
+
"graphify-out",
|
|
44
|
+
"__pycache__",
|
|
45
|
+
"dist",
|
|
46
|
+
"node_modules",
|
|
47
|
+
}
|
|
48
|
+
EXCLUDED_FILE_NAMES = {
|
|
49
|
+
".DS_Store",
|
|
50
|
+
}
|
|
51
|
+
EXCLUDED_SUFFIXES = {
|
|
52
|
+
".pyc",
|
|
53
|
+
".pyo",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _repo_root() -> Path:
|
|
58
|
+
return Path(__file__).resolve().parents[2]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _default_output_dir() -> Path:
|
|
62
|
+
return _repo_root() / "dist"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _git_version(root: Path) -> str:
|
|
66
|
+
try:
|
|
67
|
+
result = subprocess.run(
|
|
68
|
+
["git", "-C", str(root), "describe", "--tags", "--always", "--dirty"],
|
|
69
|
+
capture_output=True,
|
|
70
|
+
text=True,
|
|
71
|
+
check=True,
|
|
72
|
+
)
|
|
73
|
+
version = result.stdout.strip()
|
|
74
|
+
if version:
|
|
75
|
+
return version.replace("/", "-")
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
return datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _bundle_root_name(version: str) -> str:
|
|
82
|
+
return f"{DEFAULT_BUNDLE_PREFIX}-{version}"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _should_skip(path: Path, source_root: Path) -> bool:
|
|
86
|
+
relative = path.relative_to(source_root)
|
|
87
|
+
relative_text = relative.as_posix()
|
|
88
|
+
if relative_text in EXCLUDED_EXACT_RELATIVE_PATHS:
|
|
89
|
+
return True
|
|
90
|
+
for part in relative.parts:
|
|
91
|
+
if part in EXCLUDED_DIR_NAMES:
|
|
92
|
+
return True
|
|
93
|
+
if path.name in EXCLUDED_FILE_NAMES:
|
|
94
|
+
return True
|
|
95
|
+
return any(path.name.endswith(suffix) for suffix in EXCLUDED_SUFFIXES)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _iter_paths(base_path: Path, source_root: Path) -> list[Path]:
|
|
99
|
+
if not base_path.exists():
|
|
100
|
+
return []
|
|
101
|
+
if base_path.is_file():
|
|
102
|
+
return [] if _should_skip(base_path, source_root) else [base_path]
|
|
103
|
+
|
|
104
|
+
collected: list[Path] = []
|
|
105
|
+
for path in sorted(base_path.rglob("*")):
|
|
106
|
+
if _should_skip(path, source_root):
|
|
107
|
+
continue
|
|
108
|
+
collected.append(path)
|
|
109
|
+
return collected
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _normalize_tar_name(bundle_root: str, source_root: Path, path: Path) -> str:
|
|
113
|
+
relative = PurePosixPath(path.relative_to(source_root).as_posix())
|
|
114
|
+
return str(PurePosixPath(bundle_root) / relative)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _build_manifest(source_root: Path, version: str, bundle_root: str) -> bytes:
|
|
118
|
+
payload = {
|
|
119
|
+
"bundle_prefix": DEFAULT_BUNDLE_PREFIX,
|
|
120
|
+
"bundle_root": bundle_root,
|
|
121
|
+
"version": version,
|
|
122
|
+
"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
123
|
+
"source_root": str(source_root),
|
|
124
|
+
"included_root_files": INCLUDED_ROOT_FILES,
|
|
125
|
+
"included_top_level_dirs": INCLUDED_TOP_LEVEL_DIRS,
|
|
126
|
+
"excluded_dir_names": sorted(EXCLUDED_DIR_NAMES),
|
|
127
|
+
"excluded_file_names": sorted(EXCLUDED_FILE_NAMES),
|
|
128
|
+
"excluded_suffixes": sorted(EXCLUDED_SUFFIXES),
|
|
129
|
+
"excluded_exact_relative_paths": sorted(EXCLUDED_EXACT_RELATIVE_PATHS),
|
|
130
|
+
}
|
|
131
|
+
return (json.dumps(payload, indent=2) + "\n").encode("utf-8")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
135
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
136
|
+
parser.add_argument("--source-root", default=str(_repo_root()))
|
|
137
|
+
parser.add_argument("--output-dir", default=str(_default_output_dir()))
|
|
138
|
+
parser.add_argument("--output-file", default="")
|
|
139
|
+
parser.add_argument("--version", default="")
|
|
140
|
+
parser.add_argument("--bundle-root", default="")
|
|
141
|
+
return parser
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def main() -> None:
|
|
145
|
+
args = build_parser().parse_args()
|
|
146
|
+
source_root = Path(args.source_root).expanduser().resolve()
|
|
147
|
+
output_dir = Path(args.output_dir).expanduser().resolve()
|
|
148
|
+
version = args.version.strip() or _git_version(source_root)
|
|
149
|
+
bundle_root = args.bundle_root.strip() or _bundle_root_name(version)
|
|
150
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
151
|
+
output_path = (
|
|
152
|
+
Path(args.output_file).expanduser().resolve()
|
|
153
|
+
if args.output_file.strip()
|
|
154
|
+
else output_dir / f"{bundle_root}.tar.gz"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
included_paths: list[Path] = []
|
|
158
|
+
for relative in INCLUDED_ROOT_FILES + INCLUDED_TOP_LEVEL_DIRS:
|
|
159
|
+
included_paths.extend(_iter_paths(source_root / relative, source_root))
|
|
160
|
+
|
|
161
|
+
manifest_bytes = _build_manifest(source_root, version, bundle_root)
|
|
162
|
+
with tarfile.open(output_path, "w:gz") as archive:
|
|
163
|
+
for path in included_paths:
|
|
164
|
+
archive.add(path, arcname=_normalize_tar_name(bundle_root, source_root, path), recursive=False)
|
|
165
|
+
|
|
166
|
+
manifest_info = tarfile.TarInfo(name=f"{bundle_root}/bundle-manifest.json")
|
|
167
|
+
manifest_info.size = len(manifest_bytes)
|
|
168
|
+
manifest_info.mtime = int(datetime.now(timezone.utc).timestamp())
|
|
169
|
+
archive.addfile(manifest_info, io.BytesIO(manifest_bytes))
|
|
170
|
+
|
|
171
|
+
print(
|
|
172
|
+
json.dumps(
|
|
173
|
+
{
|
|
174
|
+
"ok": True,
|
|
175
|
+
"source_root": str(source_root),
|
|
176
|
+
"output_path": str(output_path),
|
|
177
|
+
"bundle_root": bundle_root,
|
|
178
|
+
"version": version,
|
|
179
|
+
"included_root_files": INCLUDED_ROOT_FILES,
|
|
180
|
+
"included_top_level_dirs": INCLUDED_TOP_LEVEL_DIRS,
|
|
181
|
+
},
|
|
182
|
+
indent=2,
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
main()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Finalize an OpenClaw agent-wallet config with a persistent master key."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import secrets
|
|
9
|
+
import sys
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
14
|
+
|
|
15
|
+
from agent_wallet.file_ops import atomic_write_text, chmod_if_exists
|
|
16
|
+
from agent_wallet.sealed_keys import resolve_sealed_keys_path, seal_keys, unseal_keys
|
|
17
|
+
from agent_wallet.user_wallets import resolve_user_wallet_path, rotate_user_wallet_encryption
|
|
18
|
+
from security_utils import write_redacted_backup
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _default_config_path() -> Path:
|
|
22
|
+
return Path(os.path.expanduser("~/.openclaw/openclaw.json"))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
26
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
27
|
+
parser.add_argument("--config-path", default=str(_default_config_path()))
|
|
28
|
+
parser.add_argument("--plugin-id", default="agent-wallet")
|
|
29
|
+
parser.add_argument("--user-id")
|
|
30
|
+
parser.add_argument("--network")
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--current-master-key",
|
|
33
|
+
default="",
|
|
34
|
+
help="Deprecated insecure path. Use AGENT_WALLET_CURRENT_MASTER_KEY env instead.",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--new-master-key",
|
|
38
|
+
default="",
|
|
39
|
+
help="Deprecated insecure path. Use AGENT_WALLET_NEW_MASTER_KEY env instead.",
|
|
40
|
+
)
|
|
41
|
+
return parser
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main() -> None:
|
|
45
|
+
args = build_parser().parse_args()
|
|
46
|
+
config_path = Path(args.config_path).expanduser()
|
|
47
|
+
data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
48
|
+
plugin_config = data["plugins"]["entries"][args.plugin_id]["config"]
|
|
49
|
+
|
|
50
|
+
backup_path = config_path.with_name(
|
|
51
|
+
f"{config_path.name}.bak.agent-wallet-finalize.{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}"
|
|
52
|
+
)
|
|
53
|
+
write_redacted_backup(backup_path, data)
|
|
54
|
+
|
|
55
|
+
existing_master_key = str(plugin_config.get("masterKey") or "").strip()
|
|
56
|
+
if existing_master_key:
|
|
57
|
+
raise SystemExit(
|
|
58
|
+
"masterKey stored in config is no longer supported. Remove it and keep runtime secrets in sealed_keys.json."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
boot_key = os.getenv("AGENT_WALLET_BOOT_KEY", "").strip()
|
|
62
|
+
if not boot_key:
|
|
63
|
+
raise SystemExit("AGENT_WALLET_BOOT_KEY is required.")
|
|
64
|
+
|
|
65
|
+
sealed_keys_path = resolve_sealed_keys_path()
|
|
66
|
+
sealed_secrets = unseal_keys(boot_key) if sealed_keys_path.exists() else {}
|
|
67
|
+
current_master_key = args.current_master_key.strip() or os.getenv("AGENT_WALLET_CURRENT_MASTER_KEY", "").strip()
|
|
68
|
+
if not current_master_key:
|
|
69
|
+
current_master_key = str(sealed_secrets.get("master_key") or "").strip()
|
|
70
|
+
new_master_key_arg = args.new_master_key.strip() or os.getenv("AGENT_WALLET_NEW_MASTER_KEY", "").strip()
|
|
71
|
+
if args.current_master_key.strip() or args.new_master_key.strip():
|
|
72
|
+
raise SystemExit(
|
|
73
|
+
"Passing master keys via command-line arguments is insecure. Use AGENT_WALLET_CURRENT_MASTER_KEY / AGENT_WALLET_NEW_MASTER_KEY environment variables instead."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
user_id = args.user_id or str(plugin_config.get("userId") or "").strip()
|
|
77
|
+
if not user_id:
|
|
78
|
+
raise SystemExit("user_id is required either as an argument or in plugin config.")
|
|
79
|
+
|
|
80
|
+
network = args.network or str(plugin_config.get("network") or "mainnet").strip() or "mainnet"
|
|
81
|
+
wallet_path = resolve_user_wallet_path(user_id, network=network)
|
|
82
|
+
new_master_key = new_master_key_arg or secrets.token_hex(32)
|
|
83
|
+
|
|
84
|
+
rotated_wallet = False
|
|
85
|
+
if wallet_path.exists():
|
|
86
|
+
if not current_master_key:
|
|
87
|
+
raise SystemExit(
|
|
88
|
+
"AGENT_WALLET_CURRENT_MASTER_KEY is required to rotate an existing encrypted user wallet."
|
|
89
|
+
)
|
|
90
|
+
rotate_user_wallet_encryption(
|
|
91
|
+
user_id,
|
|
92
|
+
network=network,
|
|
93
|
+
current_master_key=current_master_key,
|
|
94
|
+
new_master_key=new_master_key,
|
|
95
|
+
)
|
|
96
|
+
rotated_wallet = True
|
|
97
|
+
|
|
98
|
+
plugin_config.pop("masterKey", None)
|
|
99
|
+
atomic_write_text(config_path, json.dumps(data, indent=2) + "\n", mode=0o600)
|
|
100
|
+
chmod_if_exists(config_path, 0o600)
|
|
101
|
+
seal_keys(boot_key, {**sealed_secrets, "master_key": new_master_key})
|
|
102
|
+
|
|
103
|
+
print(
|
|
104
|
+
json.dumps(
|
|
105
|
+
{
|
|
106
|
+
"ok": True,
|
|
107
|
+
"config_path": str(config_path),
|
|
108
|
+
"backup_path": str(backup_path),
|
|
109
|
+
"master_key_present": True,
|
|
110
|
+
"rotated_wallet": rotated_wallet,
|
|
111
|
+
"user_id": user_id,
|
|
112
|
+
"network": network,
|
|
113
|
+
"sealed_keys_path": str(resolve_sealed_keys_path()),
|
|
114
|
+
},
|
|
115
|
+
indent=2,
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
main()
|