@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.
Files changed (27) hide show
  1. package/.openclaw/AGENTS.md +0 -7
  2. package/.openclaw/extensions/agent-wallet/README.md +3 -2
  3. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  4. package/README.md +111 -3
  5. package/RELEASING.md +5 -15
  6. package/agent-wallet/README.md +3 -0
  7. package/agent-wallet/agent_wallet/config.py +11 -0
  8. package/agent-wallet/agent_wallet/evm_user_wallets.py +310 -2
  9. package/agent-wallet/agent_wallet/openclaw_runtime.py +10 -41
  10. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +52 -0
  11. package/agent-wallet/pyproject.toml +1 -1
  12. package/agent-wallet/scripts/build_release_bundle.py +1 -0
  13. package/agent-wallet/scripts/flash-sdk-bridge/bridge.mjs +21 -11
  14. package/agent-wallet/scripts/install_agent_wallet.py +250 -14
  15. package/agent-wallet/scripts/install_openclaw_local_config.py +20 -51
  16. package/agent-wallet/scripts/install_openclaw_sealed_keys.py +9 -1
  17. package/bin/openclaw-agent-wallet.mjs +282 -24
  18. package/package.json +1 -2
  19. package/.openclaw/extensions/pay-bridge/README.md +0 -38
  20. package/.openclaw/extensions/pay-bridge/core.mjs +0 -287
  21. package/.openclaw/extensions/pay-bridge/dist/core.mjs +0 -287
  22. package/.openclaw/extensions/pay-bridge/dist/index.js +0 -196
  23. package/.openclaw/extensions/pay-bridge/index.ts +0 -196
  24. package/.openclaw/extensions/pay-bridge/openclaw.plugin.json +0 -34
  25. package/.openclaw/extensions/pay-bridge/package.json +0 -49
  26. package/.openclaw/extensions/pay-bridge/skills/pay-operator/SKILL.md +0 -20
  27. 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 _ensure_python_runtime(venv_path: Path, package_root: Path) -> tuple[Path, bool]:
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 _ensure_python_wrapper(venv_path), created
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
- def _ensure_node_runtime(npm_bin: str, project_root: Path) -> dict[str, object]:
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
- return {
396
- "project_root": str(project_root),
397
- "package_json": str(package_json),
398
- "package_lock": str(package_lock) if package_lock.exists() else None,
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(venv_path, package_root)
755
+ python_bin, venv_created, python_runtime = _ensure_python_runtime(
756
+ venv_path,
757
+ package_root,
758
+ runtime_root,
759
+ )
531
760
  else:
532
- python_bin = _venv_python_wrapper(venv_path)
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
- "project_root": str(project_root),
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 shutil
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
- if PAY_BRIDGE_PLUGIN_ID not in allow:
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
- pay_bridge_extension_path_text = str(Path(args.pay_bridge_extension_path).expanduser().resolve())
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
- existing_pay_entry = entries.get(PAY_BRIDGE_PLUGIN_ID) if isinstance(entries.get(PAY_BRIDGE_PLUGIN_ID), dict) else {}
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
- for tool_name in OPTIONAL_TOOLS + PAY_BRIDGE_TOOLS + X402_TOOLS:
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,