@agentlayer.tech/wallet 0.1.18 → 0.1.20
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 +0 -7
- package/.openclaw/extensions/agent-wallet/README.md +3 -2
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/README.md +111 -3
- package/RELEASING.md +5 -15
- package/agent-wallet/README.md +3 -0
- package/agent-wallet/agent_wallet/config.py +11 -0
- package/agent-wallet/agent_wallet/evm_user_wallets.py +310 -2
- package/agent-wallet/agent_wallet/openclaw_runtime.py +10 -41
- package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +52 -0
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/build_release_bundle.py +1 -0
- package/agent-wallet/scripts/flash-sdk-bridge/bridge.mjs +21 -11
- package/agent-wallet/scripts/install_agent_wallet.py +250 -14
- package/agent-wallet/scripts/install_openclaw_local_config.py +20 -51
- package/agent-wallet/scripts/install_openclaw_sealed_keys.py +9 -1
- package/bin/openclaw-agent-wallet.mjs +282 -24
- package/package.json +1 -2
- package/.openclaw/extensions/pay-bridge/README.md +0 -38
- package/.openclaw/extensions/pay-bridge/core.mjs +0 -287
- package/.openclaw/extensions/pay-bridge/dist/core.mjs +0 -287
- package/.openclaw/extensions/pay-bridge/dist/index.js +0 -196
- package/.openclaw/extensions/pay-bridge/index.ts +0 -196
- package/.openclaw/extensions/pay-bridge/openclaw.plugin.json +0 -34
- package/.openclaw/extensions/pay-bridge/package.json +0 -49
- package/.openclaw/extensions/pay-bridge/skills/pay-operator/SKILL.md +0 -20
- package/.openclaw/extensions/pay-bridge/smoke_pay_bridge.mjs +0 -38
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import hashlib
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
9
|
+
import platform
|
|
8
10
|
import shutil
|
|
9
11
|
import subprocess
|
|
10
12
|
import sys
|
|
@@ -114,6 +116,21 @@ def _resolve_sealed_keys_path() -> Path:
|
|
|
114
116
|
return _resolve_openclaw_home() / "sealed_keys.json"
|
|
115
117
|
|
|
116
118
|
|
|
119
|
+
def _runtime_base_for(runtime_root: Path) -> Path:
|
|
120
|
+
resolved = runtime_root.expanduser().resolve()
|
|
121
|
+
if resolved.parent.name == "releases":
|
|
122
|
+
return resolved.parent.parent
|
|
123
|
+
return resolved.parent
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _shared_runtime_root(runtime_root: Path) -> Path:
|
|
127
|
+
return _runtime_base_for(runtime_root) / "shared"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _shared_dependency_links_supported() -> bool:
|
|
131
|
+
return os.name != "nt"
|
|
132
|
+
|
|
133
|
+
|
|
117
134
|
def _atomic_write_text(path: Path, content: str, *, mode: int = 0o600) -> None:
|
|
118
135
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
119
136
|
fd, temp_path = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
|
|
@@ -139,6 +156,21 @@ def _chmod_if_exists(path: Path, mode: int = 0o600) -> None:
|
|
|
139
156
|
return
|
|
140
157
|
|
|
141
158
|
|
|
159
|
+
def _sha256_text(parts: list[str]) -> str:
|
|
160
|
+
digest = hashlib.sha256()
|
|
161
|
+
for part in parts:
|
|
162
|
+
digest.update(part.encode("utf-8"))
|
|
163
|
+
digest.update(b"\0")
|
|
164
|
+
return digest.hexdigest()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _file_text_or_empty(path: Path) -> str:
|
|
168
|
+
try:
|
|
169
|
+
return path.read_text(encoding="utf-8")
|
|
170
|
+
except FileNotFoundError:
|
|
171
|
+
return ""
|
|
172
|
+
|
|
173
|
+
|
|
142
174
|
def build_parser() -> argparse.ArgumentParser:
|
|
143
175
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
144
176
|
parser.add_argument("--config-path", default=str(_default_config_path()))
|
|
@@ -201,6 +233,9 @@ def _ignore_runtime_entries(_directory: str, names: list[str]) -> set[str]:
|
|
|
201
233
|
keep_dist = ".openclaw" in directory.parts and "extensions" in directory.parts
|
|
202
234
|
ignored: set[str] = set()
|
|
203
235
|
for name in names:
|
|
236
|
+
if name == "pay-bridge" and directory.parts[-2:] == (".openclaw", "extensions"):
|
|
237
|
+
ignored.add(name)
|
|
238
|
+
continue
|
|
204
239
|
if name == "dist" and keep_dist:
|
|
205
240
|
continue
|
|
206
241
|
if name in EXCLUDED_RUNTIME_DIR_NAMES:
|
|
@@ -269,6 +304,14 @@ def _sync_runtime_tree(source_root: Path, runtime_root: Path) -> dict[str, objec
|
|
|
269
304
|
def _ensure_env_file(env_path: Path, env_example_path: Path) -> bool:
|
|
270
305
|
if env_path.exists():
|
|
271
306
|
return False
|
|
307
|
+
if not env_example_path.exists():
|
|
308
|
+
source_candidate = _package_root() / ".env.example"
|
|
309
|
+
if source_candidate.exists():
|
|
310
|
+
env_example_path = source_candidate
|
|
311
|
+
else:
|
|
312
|
+
raise SystemExit(
|
|
313
|
+
f"Missing env example template at '{env_example_path}'."
|
|
314
|
+
)
|
|
272
315
|
env_path.parent.mkdir(parents=True, exist_ok=True)
|
|
273
316
|
shutil.copyfile(env_example_path, env_path)
|
|
274
317
|
_chmod_if_exists(env_path, 0o600)
|
|
@@ -363,8 +406,95 @@ def _ensure_python_wrapper(venv_path: Path) -> Path:
|
|
|
363
406
|
return wrapper
|
|
364
407
|
|
|
365
408
|
|
|
366
|
-
def
|
|
409
|
+
def _python_runtime_fingerprint(package_root: Path, python_bin: Path) -> str:
|
|
410
|
+
version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
411
|
+
return _sha256_text(
|
|
412
|
+
[
|
|
413
|
+
f"python-bin:{python_bin}",
|
|
414
|
+
f"python-version:{version}",
|
|
415
|
+
f"platform:{platform.system()}",
|
|
416
|
+
f"machine:{platform.machine()}",
|
|
417
|
+
_file_text_or_empty(package_root / "pyproject.toml"),
|
|
418
|
+
]
|
|
419
|
+
)[:24]
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _python_runtime_plan(
|
|
423
|
+
venv_path: Path,
|
|
424
|
+
package_root: Path,
|
|
425
|
+
runtime_root: Path,
|
|
426
|
+
) -> dict[str, object]:
|
|
427
|
+
if _shared_dependency_links_supported():
|
|
428
|
+
fingerprint = _python_runtime_fingerprint(package_root, Path(sys.executable))
|
|
429
|
+
shared_root = _shared_runtime_root(runtime_root) / "python" / fingerprint
|
|
430
|
+
shared_venv_path = shared_root / "venv"
|
|
431
|
+
shared_wrapper = _venv_python_wrapper(shared_venv_path)
|
|
432
|
+
return {
|
|
433
|
+
"shared": True,
|
|
434
|
+
"fingerprint": fingerprint,
|
|
435
|
+
"shared_root": str(shared_root),
|
|
436
|
+
"venv_path": str(shared_venv_path),
|
|
437
|
+
"release_link_path": str(venv_path),
|
|
438
|
+
"python_bin": str(venv_path / shared_wrapper.relative_to(shared_venv_path)),
|
|
439
|
+
"action": "reuse" if shared_venv_path.exists() else "create",
|
|
440
|
+
"exists": shared_venv_path.exists(),
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
"shared": False,
|
|
444
|
+
"fingerprint": None,
|
|
445
|
+
"shared_root": None,
|
|
446
|
+
"venv_path": str(venv_path),
|
|
447
|
+
"release_link_path": str(venv_path),
|
|
448
|
+
"python_bin": str(_venv_python_wrapper(venv_path)),
|
|
449
|
+
"action": "install",
|
|
450
|
+
"exists": _venv_python(venv_path).exists(),
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _replace_with_directory_symlink(link_path: Path, target_path: Path) -> None:
|
|
455
|
+
target_resolved = target_path.resolve()
|
|
456
|
+
if link_path.is_symlink():
|
|
457
|
+
existing_target = link_path.resolve()
|
|
458
|
+
if existing_target == target_resolved:
|
|
459
|
+
return
|
|
460
|
+
link_path.unlink()
|
|
461
|
+
elif link_path.exists():
|
|
462
|
+
if link_path.is_dir():
|
|
463
|
+
shutil.rmtree(link_path)
|
|
464
|
+
else:
|
|
465
|
+
link_path.unlink()
|
|
466
|
+
link_path.parent.mkdir(parents=True, exist_ok=True)
|
|
467
|
+
link_path.symlink_to(target_resolved, target_is_directory=True)
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def _ensure_python_runtime(
|
|
471
|
+
venv_path: Path,
|
|
472
|
+
package_root: Path,
|
|
473
|
+
runtime_root: Path,
|
|
474
|
+
) -> tuple[Path, bool, dict[str, object]]:
|
|
367
475
|
created = False
|
|
476
|
+
plan = _python_runtime_plan(venv_path, package_root, runtime_root)
|
|
477
|
+
if bool(plan["shared"]):
|
|
478
|
+
shared_root = Path(str(plan["shared_root"]))
|
|
479
|
+
shared_venv_path = Path(str(plan["venv_path"]))
|
|
480
|
+
python_bin = _venv_python(shared_venv_path)
|
|
481
|
+
if not python_bin.exists():
|
|
482
|
+
venv.EnvBuilder(with_pip=True).create(shared_venv_path)
|
|
483
|
+
created = True
|
|
484
|
+
subprocess.run(
|
|
485
|
+
[str(python_bin), "-m", "pip", "install", "-e", str(package_root)],
|
|
486
|
+
check=True,
|
|
487
|
+
)
|
|
488
|
+
shared_wrapper = _ensure_python_wrapper(shared_venv_path)
|
|
489
|
+
_replace_with_directory_symlink(venv_path, shared_venv_path)
|
|
490
|
+
plan["action"] = "create" if created else "reuse"
|
|
491
|
+
plan["exists"] = True
|
|
492
|
+
return (
|
|
493
|
+
venv_path / shared_wrapper.relative_to(shared_venv_path),
|
|
494
|
+
created,
|
|
495
|
+
plan,
|
|
496
|
+
)
|
|
497
|
+
|
|
368
498
|
python_bin = _venv_python(venv_path)
|
|
369
499
|
if not python_bin.exists():
|
|
370
500
|
venv.EnvBuilder(with_pip=True).create(venv_path)
|
|
@@ -374,10 +504,79 @@ def _ensure_python_runtime(venv_path: Path, package_root: Path) -> tuple[Path, b
|
|
|
374
504
|
[str(python_bin), "-m", "pip", "install", "-e", str(package_root)],
|
|
375
505
|
check=True,
|
|
376
506
|
)
|
|
377
|
-
return
|
|
507
|
+
return (
|
|
508
|
+
_ensure_python_wrapper(venv_path),
|
|
509
|
+
created,
|
|
510
|
+
plan,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _node_version() -> str:
|
|
515
|
+
result = subprocess.run(
|
|
516
|
+
["node", "--version"],
|
|
517
|
+
capture_output=True,
|
|
518
|
+
text=True,
|
|
519
|
+
check=True,
|
|
520
|
+
)
|
|
521
|
+
return result.stdout.strip()
|
|
522
|
+
|
|
378
523
|
|
|
524
|
+
def _node_runtime_fingerprint(project_root: Path) -> str:
|
|
525
|
+
return _sha256_text(
|
|
526
|
+
[
|
|
527
|
+
f"node-version:{_node_version()}",
|
|
528
|
+
f"platform:{platform.system()}",
|
|
529
|
+
f"machine:{platform.machine()}",
|
|
530
|
+
_file_text_or_empty(project_root / "package.json"),
|
|
531
|
+
_file_text_or_empty(project_root / "package-lock.json"),
|
|
532
|
+
]
|
|
533
|
+
)[:24]
|
|
379
534
|
|
|
380
|
-
|
|
535
|
+
|
|
536
|
+
def _node_runtime_plan(project_root: Path, runtime_root: Path) -> dict[str, object]:
|
|
537
|
+
package_json = project_root / "package.json"
|
|
538
|
+
package_lock = project_root / "package-lock.json"
|
|
539
|
+
command = ["npm", "ci"] if package_lock.exists() else ["npm", "install"]
|
|
540
|
+
if _shared_dependency_links_supported():
|
|
541
|
+
fingerprint = _node_runtime_fingerprint(project_root)
|
|
542
|
+
shared_project_root = _shared_runtime_root(runtime_root) / "node" / project_root.name / fingerprint
|
|
543
|
+
shared_node_modules = shared_project_root / "node_modules"
|
|
544
|
+
return {
|
|
545
|
+
"project_root": str(project_root),
|
|
546
|
+
"package_json": str(package_json),
|
|
547
|
+
"package_lock": str(package_lock) if package_lock.exists() else None,
|
|
548
|
+
"command": command,
|
|
549
|
+
"shared": True,
|
|
550
|
+
"fingerprint": fingerprint,
|
|
551
|
+
"shared_root": str(shared_project_root),
|
|
552
|
+
"node_modules_path": str(shared_node_modules),
|
|
553
|
+
"release_link_path": str(project_root / "node_modules"),
|
|
554
|
+
"action": "reuse" if shared_node_modules.exists() else "create",
|
|
555
|
+
"exists": shared_node_modules.exists(),
|
|
556
|
+
}
|
|
557
|
+
return {
|
|
558
|
+
"project_root": str(project_root),
|
|
559
|
+
"package_json": str(package_json),
|
|
560
|
+
"package_lock": str(package_lock) if package_lock.exists() else None,
|
|
561
|
+
"command": command,
|
|
562
|
+
"shared": False,
|
|
563
|
+
"fingerprint": None,
|
|
564
|
+
"shared_root": None,
|
|
565
|
+
"node_modules_path": str(project_root / "node_modules"),
|
|
566
|
+
"release_link_path": str(project_root / "node_modules"),
|
|
567
|
+
"action": "install",
|
|
568
|
+
"exists": (project_root / "node_modules").exists(),
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def _copy_if_exists(source: Path, target: Path) -> None:
|
|
573
|
+
if not source.exists():
|
|
574
|
+
return
|
|
575
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
576
|
+
shutil.copy2(source, target)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def _ensure_node_runtime(npm_bin: str, project_root: Path, runtime_root: Path) -> dict[str, object]:
|
|
381
580
|
package_json = project_root / "package.json"
|
|
382
581
|
if not package_json.exists():
|
|
383
582
|
raise SystemExit(f"Missing package.json for Node runtime at '{project_root}'.")
|
|
@@ -391,14 +590,30 @@ def _ensure_node_runtime(npm_bin: str, project_root: Path) -> dict[str, object]:
|
|
|
391
590
|
env["NPM_CONFIG_CACHE"] = cache_dir
|
|
392
591
|
env["npm_config_cache"] = cache_dir
|
|
393
592
|
Path(cache_dir).expanduser().mkdir(parents=True, exist_ok=True)
|
|
593
|
+
plan = _node_runtime_plan(project_root, runtime_root)
|
|
594
|
+
if bool(plan["shared"]):
|
|
595
|
+
shared_project_root = Path(str(plan["shared_root"]))
|
|
596
|
+
shared_node_modules = Path(str(plan["node_modules_path"]))
|
|
597
|
+
created = False
|
|
598
|
+
if not shared_node_modules.exists():
|
|
599
|
+
shared_project_root.mkdir(parents=True, exist_ok=True)
|
|
600
|
+
_copy_if_exists(package_json, shared_project_root / "package.json")
|
|
601
|
+
_copy_if_exists(package_lock, shared_project_root / "package-lock.json")
|
|
602
|
+
_copy_if_exists(project_root / ".npmrc", shared_project_root / ".npmrc")
|
|
603
|
+
subprocess.run(command, cwd=shared_project_root, check=True, env=env)
|
|
604
|
+
created = True
|
|
605
|
+
_replace_with_directory_symlink(project_root / "node_modules", shared_node_modules)
|
|
606
|
+
plan["cache_dir"] = cache_dir
|
|
607
|
+
plan["created"] = created
|
|
608
|
+
plan["action"] = "create" if created else "reuse"
|
|
609
|
+
plan["exists"] = True
|
|
610
|
+
return plan
|
|
611
|
+
|
|
394
612
|
subprocess.run(command, cwd=project_root, check=True, env=env)
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
"command": command,
|
|
400
|
-
"cache_dir": cache_dir,
|
|
401
|
-
}
|
|
613
|
+
plan["cache_dir"] = cache_dir
|
|
614
|
+
plan["created"] = True
|
|
615
|
+
plan["exists"] = True
|
|
616
|
+
return plan
|
|
402
617
|
|
|
403
618
|
|
|
404
619
|
def _pending_env_names() -> list[str]:
|
|
@@ -523,13 +738,28 @@ def main() -> None:
|
|
|
523
738
|
python_bin = Path(sys.executable)
|
|
524
739
|
venv_created = False
|
|
525
740
|
existing_wrapper = _venv_python_wrapper(venv_path)
|
|
741
|
+
python_runtime: dict[str, object] = {
|
|
742
|
+
"shared": False,
|
|
743
|
+
"fingerprint": None,
|
|
744
|
+
"shared_root": None,
|
|
745
|
+
"venv_path": str(venv_path),
|
|
746
|
+
"release_link_path": str(venv_path),
|
|
747
|
+
"python_bin": str(_venv_python_wrapper(venv_path)),
|
|
748
|
+
"action": "skipped",
|
|
749
|
+
"exists": existing_wrapper.exists(),
|
|
750
|
+
}
|
|
526
751
|
if args.skip_python_setup and args.install_from_runtime and existing_wrapper.exists():
|
|
527
752
|
python_bin = existing_wrapper
|
|
528
753
|
elif not args.skip_python_setup:
|
|
529
754
|
if not args.dry_run:
|
|
530
|
-
python_bin, venv_created = _ensure_python_runtime(
|
|
755
|
+
python_bin, venv_created, python_runtime = _ensure_python_runtime(
|
|
756
|
+
venv_path,
|
|
757
|
+
package_root,
|
|
758
|
+
runtime_root,
|
|
759
|
+
)
|
|
531
760
|
else:
|
|
532
|
-
|
|
761
|
+
python_runtime = _python_runtime_plan(venv_path, package_root, runtime_root)
|
|
762
|
+
python_bin = Path(str(python_runtime["python_bin"]))
|
|
533
763
|
|
|
534
764
|
node_runtime = {
|
|
535
765
|
"skipped": bool(args.skip_node_setup),
|
|
@@ -545,17 +775,22 @@ def main() -> None:
|
|
|
545
775
|
if args.dry_run:
|
|
546
776
|
node_runtime["projects"] = [
|
|
547
777
|
{
|
|
548
|
-
|
|
778
|
+
**_node_runtime_plan(project_root, runtime_root),
|
|
549
779
|
"command": [
|
|
550
780
|
args.npm_bin,
|
|
551
781
|
"ci" if (project_root / "package-lock.json").exists() else "install",
|
|
552
782
|
],
|
|
783
|
+
"cache_dir": (
|
|
784
|
+
os.environ.get("OPENCLAW_AGENT_WALLET_NPM_CACHE")
|
|
785
|
+
or str(_resolve_openclaw_home() / "npm-cache")
|
|
786
|
+
),
|
|
787
|
+
"created": False,
|
|
553
788
|
}
|
|
554
789
|
for project_root in node_projects
|
|
555
790
|
]
|
|
556
791
|
else:
|
|
557
792
|
node_runtime["projects"] = [
|
|
558
|
-
_ensure_node_runtime(args.npm_bin, project_root)
|
|
793
|
+
_ensure_node_runtime(args.npm_bin, project_root, runtime_root)
|
|
559
794
|
for project_root in node_projects
|
|
560
795
|
]
|
|
561
796
|
|
|
@@ -597,6 +832,7 @@ def main() -> None:
|
|
|
597
832
|
"install_from_runtime": bool(args.install_from_runtime),
|
|
598
833
|
"python_bin": str(python_bin),
|
|
599
834
|
"venv_created": venv_created,
|
|
835
|
+
"python_runtime": python_runtime,
|
|
600
836
|
"node_runtime": node_runtime,
|
|
601
837
|
"runtime_sync": runtime_sync,
|
|
602
838
|
"configured": configured,
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
-
import
|
|
8
|
+
import secrets
|
|
9
9
|
import sys
|
|
10
10
|
from datetime import datetime, timezone
|
|
11
11
|
from pathlib import Path
|
|
@@ -49,15 +49,6 @@ OPTIONAL_TOOLS = [
|
|
|
49
49
|
"flash_trade_close_position",
|
|
50
50
|
]
|
|
51
51
|
|
|
52
|
-
PAY_BRIDGE_PLUGIN_ID = "pay-bridge"
|
|
53
|
-
PAY_BRIDGE_TOOLS = [
|
|
54
|
-
"pay_status",
|
|
55
|
-
"pay_wallet_info",
|
|
56
|
-
"pay_search_services",
|
|
57
|
-
"pay_get_service_endpoints",
|
|
58
|
-
"pay_api_request",
|
|
59
|
-
]
|
|
60
|
-
|
|
61
52
|
X402_TOOLS = [
|
|
62
53
|
"x402_search_services",
|
|
63
54
|
"x402_get_service_details",
|
|
@@ -104,13 +95,6 @@ def _default_extension_path() -> Path:
|
|
|
104
95
|
return _repo_root() / ".openclaw" / "extensions" / "agent-wallet"
|
|
105
96
|
|
|
106
97
|
|
|
107
|
-
def _default_pay_bridge_extension_path() -> Path:
|
|
108
|
-
runtime_root = _trusted_runtime_root()
|
|
109
|
-
if runtime_root is not None:
|
|
110
|
-
return runtime_root / ".openclaw" / "extensions" / PAY_BRIDGE_PLUGIN_ID
|
|
111
|
-
return _repo_root() / ".openclaw" / "extensions" / PAY_BRIDGE_PLUGIN_ID
|
|
112
|
-
|
|
113
|
-
|
|
114
98
|
def _default_package_root() -> Path:
|
|
115
99
|
runtime_root = _trusted_runtime_root()
|
|
116
100
|
if runtime_root is not None:
|
|
@@ -133,14 +117,6 @@ def _default_python_bin() -> str:
|
|
|
133
117
|
return sys.executable
|
|
134
118
|
|
|
135
119
|
|
|
136
|
-
def _default_pay_binary() -> str:
|
|
137
|
-
explicit = os.getenv("OPENCLAW_PAY_BINARY", "").strip()
|
|
138
|
-
if explicit:
|
|
139
|
-
return explicit
|
|
140
|
-
resolved = shutil.which("pay")
|
|
141
|
-
return resolved or "pay"
|
|
142
|
-
|
|
143
|
-
|
|
144
120
|
def _default_user_id() -> str:
|
|
145
121
|
return f"{os.getenv('USER', 'openclaw-user')}-local"
|
|
146
122
|
|
|
@@ -178,10 +154,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
178
154
|
default=True,
|
|
179
155
|
)
|
|
180
156
|
parser.add_argument("--extension-path", default=str(_default_extension_path()))
|
|
181
|
-
parser.add_argument("--pay-bridge-extension-path", default=str(_default_pay_bridge_extension_path()))
|
|
182
157
|
parser.add_argument("--package-root", default=str(_default_package_root()))
|
|
183
158
|
parser.add_argument("--python-bin", default=_default_python_bin())
|
|
184
|
-
parser.add_argument("--pay-binary", default=_default_pay_binary())
|
|
185
159
|
parser.add_argument("--write-master-key", action=argparse.BooleanOptionalAction, default=False)
|
|
186
160
|
return parser
|
|
187
161
|
|
|
@@ -191,12 +165,15 @@ def _collect_sealed_secret_updates() -> dict[str, str]:
|
|
|
191
165
|
master_key = os.getenv("AGENT_WALLET_MASTER_KEY", "").strip()
|
|
192
166
|
approval_secret = os.getenv("AGENT_WALLET_APPROVAL_SECRET", "").strip()
|
|
193
167
|
private_key = os.getenv("SOLANA_AGENT_PRIVATE_KEY", "").strip()
|
|
168
|
+
evm_wallet_password = os.getenv("WDK_EVM_WALLET_PASSWORD", "").strip()
|
|
194
169
|
if master_key:
|
|
195
170
|
updates["master_key"] = master_key
|
|
196
171
|
if approval_secret:
|
|
197
172
|
updates["approval_secret"] = approval_secret
|
|
198
173
|
if private_key:
|
|
199
174
|
updates["private_key"] = private_key
|
|
175
|
+
if evm_wallet_password:
|
|
176
|
+
updates["wdk_evm_wallet_password"] = evm_wallet_password
|
|
200
177
|
return updates
|
|
201
178
|
|
|
202
179
|
|
|
@@ -205,10 +182,12 @@ def _maybe_install_sealed_keys() -> str | None:
|
|
|
205
182
|
if not boot_key:
|
|
206
183
|
return None
|
|
207
184
|
updates = _collect_sealed_secret_updates()
|
|
208
|
-
if not updates:
|
|
209
|
-
return None
|
|
210
185
|
sealed_path = resolve_sealed_keys_path()
|
|
211
186
|
existing = unseal_keys(boot_key) if sealed_path.exists() else {}
|
|
187
|
+
if "wdk_evm_wallet_password" not in existing and "wdk_evm_wallet_password" not in updates:
|
|
188
|
+
updates["wdk_evm_wallet_password"] = secrets.token_urlsafe(24)
|
|
189
|
+
if not updates:
|
|
190
|
+
return None
|
|
212
191
|
return str(seal_keys(boot_key, {**existing, **updates}))
|
|
213
192
|
|
|
214
193
|
|
|
@@ -255,17 +234,14 @@ def main() -> None:
|
|
|
255
234
|
allow = plugins.setdefault("allow", [])
|
|
256
235
|
if args.plugin_id not in allow:
|
|
257
236
|
allow.append(args.plugin_id)
|
|
258
|
-
|
|
259
|
-
allow.append(PAY_BRIDGE_PLUGIN_ID)
|
|
237
|
+
allow[:] = [item for item in allow if item != "pay-bridge"]
|
|
260
238
|
|
|
261
239
|
load = plugins.setdefault("load", {})
|
|
262
240
|
paths = load.setdefault("paths", [])
|
|
263
241
|
extension_path_text = str(Path(args.extension_path).expanduser().resolve())
|
|
264
242
|
if extension_path_text not in paths:
|
|
265
243
|
paths.append(extension_path_text)
|
|
266
|
-
|
|
267
|
-
if pay_bridge_extension_path_text not in paths:
|
|
268
|
-
paths.append(pay_bridge_extension_path_text)
|
|
244
|
+
paths[:] = [item for item in paths if "extensions/pay-bridge" not in str(item)]
|
|
269
245
|
|
|
270
246
|
entries = plugins.setdefault("entries", {})
|
|
271
247
|
effective_network = _normalize_network(args.backend, args.network)
|
|
@@ -318,25 +294,19 @@ def main() -> None:
|
|
|
318
294
|
"enabled": True,
|
|
319
295
|
"config": plugin_config,
|
|
320
296
|
}
|
|
321
|
-
|
|
322
|
-
existing_pay_config = (
|
|
323
|
-
dict(existing_pay_entry.get("config"))
|
|
324
|
-
if isinstance(existing_pay_entry.get("config"), dict)
|
|
325
|
-
else {}
|
|
326
|
-
)
|
|
327
|
-
pay_bridge_config = {
|
|
328
|
-
**existing_pay_config,
|
|
329
|
-
"payBinary": args.pay_binary.strip() or _default_pay_binary(),
|
|
330
|
-
"requireHttps": bool(existing_pay_config.get("requireHttps", True)),
|
|
331
|
-
}
|
|
332
|
-
entries[PAY_BRIDGE_PLUGIN_ID] = {
|
|
333
|
-
"enabled": True,
|
|
334
|
-
"config": pay_bridge_config,
|
|
335
|
-
}
|
|
297
|
+
entries.pop("pay-bridge", None)
|
|
336
298
|
|
|
337
299
|
tools = data.setdefault("tools", {})
|
|
338
300
|
also_allow = tools.setdefault("alsoAllow", [])
|
|
339
|
-
|
|
301
|
+
removed_pay_tools = {
|
|
302
|
+
"pay_status",
|
|
303
|
+
"pay_wallet_info",
|
|
304
|
+
"pay_search_services",
|
|
305
|
+
"pay_get_service_endpoints",
|
|
306
|
+
"pay_api_request",
|
|
307
|
+
}
|
|
308
|
+
also_allow[:] = [tool_name for tool_name in also_allow if tool_name not in removed_pay_tools]
|
|
309
|
+
for tool_name in OPTIONAL_TOOLS + X402_TOOLS:
|
|
340
310
|
if tool_name not in also_allow:
|
|
341
311
|
also_allow.append(tool_name)
|
|
342
312
|
|
|
@@ -352,7 +322,6 @@ def main() -> None:
|
|
|
352
322
|
"config_path": str(config_path),
|
|
353
323
|
"backup_path": str(backup_path),
|
|
354
324
|
"extension_path": extension_path_text,
|
|
355
|
-
"pay_bridge_extension_path": pay_bridge_extension_path_text,
|
|
356
325
|
"python_bin": args.python_bin,
|
|
357
326
|
"package_root": plugin_config["packageRoot"],
|
|
358
327
|
"plugin_id": args.plugin_id,
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
+
import secrets as py_secrets
|
|
8
9
|
import sys
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
@@ -49,12 +50,15 @@ def _collect_secret_updates() -> dict[str, str]:
|
|
|
49
50
|
master_key = os.getenv("AGENT_WALLET_MASTER_KEY", "").strip()
|
|
50
51
|
approval_secret = os.getenv("AGENT_WALLET_APPROVAL_SECRET", "").strip()
|
|
51
52
|
private_key = os.getenv("SOLANA_AGENT_PRIVATE_KEY", "").strip()
|
|
53
|
+
evm_wallet_password = os.getenv("WDK_EVM_WALLET_PASSWORD", "").strip()
|
|
52
54
|
if master_key:
|
|
53
55
|
updates["master_key"] = master_key
|
|
54
56
|
if approval_secret:
|
|
55
57
|
updates["approval_secret"] = approval_secret
|
|
56
58
|
if private_key:
|
|
57
59
|
updates["private_key"] = private_key
|
|
60
|
+
if evm_wallet_password:
|
|
61
|
+
updates["wdk_evm_wallet_password"] = evm_wallet_password
|
|
58
62
|
return updates
|
|
59
63
|
|
|
60
64
|
|
|
@@ -80,6 +84,10 @@ def main() -> None:
|
|
|
80
84
|
sealed_path = resolve_sealed_keys_path()
|
|
81
85
|
existing = unseal_keys(boot_key) if sealed_path.exists() and not args.replace else {}
|
|
82
86
|
secrets = {**existing, **updates}
|
|
87
|
+
generated_keys: list[str] = []
|
|
88
|
+
if "wdk_evm_wallet_password" not in secrets:
|
|
89
|
+
secrets["wdk_evm_wallet_password"] = py_secrets.token_urlsafe(24)
|
|
90
|
+
generated_keys.append("wdk_evm_wallet_password")
|
|
83
91
|
if not secrets:
|
|
84
92
|
raise SystemExit(
|
|
85
93
|
"No secrets provided. Set AGENT_WALLET_MASTER_KEY, AGENT_WALLET_APPROVAL_SECRET, "
|
|
@@ -93,7 +101,7 @@ def main() -> None:
|
|
|
93
101
|
"ok": True,
|
|
94
102
|
"path": str(path),
|
|
95
103
|
"stored_keys": sorted(secrets.keys()),
|
|
96
|
-
"updated_keys": sorted(updates.keys()),
|
|
104
|
+
"updated_keys": sorted(set(updates.keys()) | set(generated_keys)),
|
|
97
105
|
"replaced": bool(args.replace),
|
|
98
106
|
},
|
|
99
107
|
indent=2,
|