@agentlayer.tech/wallet 0.1.30 → 0.1.32

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 (39) hide show
  1. package/.openclaw/extensions/agent-wallet/README.md +1 -2
  2. package/.openclaw/extensions/agent-wallet/dist/index.js +6 -340
  3. package/.openclaw/extensions/agent-wallet/index.ts +6 -340
  4. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +0 -43
  5. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  6. package/.openclaw/extensions/agent-wallet/skills/wallet-operator/SKILL.md +1 -3
  7. package/CHANGELOG.md +35 -0
  8. package/README.md +0 -5
  9. package/agent-wallet/.env.example +0 -12
  10. package/agent-wallet/README.md +0 -35
  11. package/agent-wallet/agent_wallet/btc_user_wallets.py +32 -1
  12. package/agent-wallet/agent_wallet/config.py +11 -7
  13. package/agent-wallet/agent_wallet/evm_user_wallets.py +2 -0
  14. package/agent-wallet/agent_wallet/openclaw_adapter.py +1 -655
  15. package/agent-wallet/agent_wallet/openclaw_cli.py +0 -7
  16. package/agent-wallet/agent_wallet/providers/evm_portfolio.py +18 -42
  17. package/agent-wallet/agent_wallet/providers/jupiter.py +1 -307
  18. package/agent-wallet/agent_wallet/providers/wdk_btc_local.py +31 -3
  19. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +37 -3
  20. package/agent-wallet/agent_wallet/transaction_policy.py +0 -262
  21. package/agent-wallet/agent_wallet/wallet_layer/base.py +0 -100
  22. package/agent-wallet/agent_wallet/wallet_layer/solana.py +1 -1118
  23. package/agent-wallet/openclaw.plugin.json +0 -4
  24. package/agent-wallet/pyproject.toml +1 -1
  25. package/agent-wallet/scripts/install_agent_wallet.py +113 -6
  26. package/agent-wallet/scripts/install_openclaw_local_config.py +7 -5
  27. package/agent-wallet/skills/wallet-operator/SKILL.md +1 -5
  28. package/bin/openclaw-agent-wallet.mjs +103 -36
  29. package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +7 -2
  30. package/codex/plugins/agent-wallet/server.py +2 -118
  31. package/hermes/plugins/agent_wallet/tools.py +1 -1
  32. package/package.json +1 -1
  33. package/wdk-btc-wallet/src/local_vault.js +45 -68
  34. package/wdk-btc-wallet/src/server.js +1 -0
  35. package/wdk-evm-wallet/README.md +4 -3
  36. package/wdk-evm-wallet/src/config.js +15 -0
  37. package/wdk-evm-wallet/src/local_vault.js +45 -68
  38. package/wdk-evm-wallet/src/server.js +1 -0
  39. package/agent-wallet/agent_wallet/providers/houdini.py +0 -539
@@ -117,10 +117,6 @@
117
117
  "type": "string",
118
118
  "description": "Optional Jupiter Portfolio API base URL for Jupiter-specific positions and staking data."
119
119
  },
120
- "jupiterLendBaseUrl": {
121
- "type": "string",
122
- "description": "Optional Jupiter Lend API base URL for Earn read and deposit/withdraw flows."
123
- },
124
120
  "jupiterApiKey": {
125
121
  "type": "string",
126
122
  "description": "Optional Jupiter API key if your deployment uses a keyed endpoint."
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "openclaw-agent-wallet"
7
- version = "0.1.30"
7
+ version = "0.1.32"
8
8
  description = "Plugin-friendly wallet backend for OpenClaw agents"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -182,6 +182,7 @@ def build_parser() -> argparse.ArgumentParser:
182
182
  parser.add_argument("--extension-path", default=str(_extension_path()))
183
183
  parser.add_argument("--wdk-btc-root", default=str(_default_wdk_btc_root()))
184
184
  parser.add_argument("--wdk-evm-root", default=str(_default_wdk_evm_root()))
185
+ parser.add_argument("--wdk-evm-service-url", default=EVM_DEFAULT_SERVICE_URL)
185
186
  parser.add_argument("--runtime-root", default=str(_default_runtime_root()))
186
187
  parser.add_argument("--npm-bin", default=_default_npm_bin())
187
188
  parser.add_argument("--plugin-id", default="agent-wallet")
@@ -665,6 +666,9 @@ def _build_next_steps(
665
666
  command.extend(["--extension-path", str(effective_extension_path)])
666
667
  command.extend(["--package-root", str(effective_package_root)])
667
668
  command.extend(["--python-bin", str(python_bin)])
669
+ if _is_evm_backend(args.backend):
670
+ service_url = str(getattr(args, "wdk_evm_service_url", "") or EVM_DEFAULT_SERVICE_URL).strip()
671
+ command.extend(["--wdk-evm-service-url", service_url or EVM_DEFAULT_SERVICE_URL])
668
672
  return command
669
673
 
670
674
 
@@ -672,18 +676,51 @@ def _is_solana_backend(backend: str) -> bool:
672
676
  return backend.strip().lower() in {"solana", "solana_local", "solana-local"}
673
677
 
674
678
 
679
+ def _is_evm_backend(backend: str) -> bool:
680
+ return backend.strip().lower() in {
681
+ "wdk_evm_local",
682
+ "wdk-evm-local",
683
+ "evm_local",
684
+ "evm-local",
685
+ }
686
+
687
+
688
+ EVM_DEFAULT_SERVICE_URL = "http://127.0.0.1:8081"
689
+
690
+
691
+ def _build_evm_onboard_config(args: argparse.Namespace) -> dict[str, object]:
692
+ # The EVM wallet is provisioned on every install (best-effort). Seed creation
693
+ # with a valid EVM network; ensure_user_evm_wallet_ready binds BOTH base and
694
+ # ethereum (one address), so the active --network only matters when EVM is the
695
+ # active backend.
696
+ network = args.network.strip().lower() if _is_evm_backend(args.backend) else "base"
697
+ if network not in {"base", "ethereum"}:
698
+ network = "base"
699
+ service_url = str(getattr(args, "wdk_evm_service_url", "") or EVM_DEFAULT_SERVICE_URL).strip()
700
+ return {
701
+ "backend": "wdk_evm_local",
702
+ "network": network,
703
+ "signOnly": bool(args.sign_only),
704
+ "wdkEvmServiceUrl": service_url or EVM_DEFAULT_SERVICE_URL,
705
+ }
706
+
707
+
675
708
  def _build_solana_onboard_config(args: argparse.Namespace) -> dict[str, object]:
709
+ # Solana is provisioned on every install (both wallets are created), so force
710
+ # the Solana backend/network here -- this must work even when the active
711
+ # backend chosen by the user is EVM or BTC.
712
+ solana_active = _is_solana_backend(args.backend)
676
713
  config: dict[str, object] = {
677
- "backend": args.backend,
678
- "network": args.network,
714
+ "backend": "solana_local",
715
+ "network": "mainnet",
679
716
  "signOnly": bool(args.sign_only),
680
717
  "encryptUserWallets": True,
681
718
  "migratePlaintextUserWallets": True,
682
719
  "refuseMainnetWalletRecreation": True,
683
720
  }
684
- if args.rpc_url.strip():
721
+ if solana_active and args.rpc_url.strip():
685
722
  config["rpcUrl"] = args.rpc_url.strip()
686
- if args.rpc_urls.strip():
723
+ if solana_active and args.rpc_urls.strip():
687
724
  config["rpcUrls"] = [item.strip() for item in args.rpc_urls.split(",") if item.strip()]
688
725
  return config
689
726
 
@@ -708,8 +745,6 @@ def _bootstrap_solana_wallet(
708
745
  package_root: Path,
709
746
  args: argparse.Namespace,
710
747
  ) -> dict[str, object] | None:
711
- if not _is_solana_backend(args.backend):
712
- return None
713
748
  result = subprocess.run(
714
749
  [
715
750
  str(python_bin),
@@ -741,6 +776,60 @@ def _bootstrap_solana_wallet(
741
776
  }
742
777
 
743
778
 
779
+ def _bootstrap_evm_wallet(
780
+ python_bin: Path,
781
+ package_root: Path,
782
+ args: argparse.Namespace,
783
+ wdk_evm_root: Path,
784
+ ) -> dict[str, object]:
785
+ """Provision the local EVM wallet (best-effort).
786
+
787
+ Mirrors _bootstrap_solana_wallet but for wdk_evm_local. Runs the onboard CLI,
788
+ which calls ensure_user_evm_wallet_ready: auto-starts the local Node service,
789
+ creates and seals the wallet password, and binds both base and ethereum.
790
+ Failures here never abort the install -- the lazy runtime path (ensure_ready on
791
+ first EVM use) remains the safety net.
792
+ """
793
+ env = _runtime_env_for_onboard(package_root)
794
+ env["OPENCLAW_EVM_WDK_WALLET_ROOT"] = str(wdk_evm_root)
795
+ try:
796
+ result = subprocess.run(
797
+ [
798
+ str(python_bin),
799
+ "-m",
800
+ "agent_wallet.openclaw_cli",
801
+ "onboard",
802
+ "--user-id",
803
+ args.user_id,
804
+ "--config-json",
805
+ json.dumps(_build_evm_onboard_config(args)),
806
+ ],
807
+ cwd=package_root,
808
+ capture_output=True,
809
+ text=True,
810
+ check=True,
811
+ env=env,
812
+ )
813
+ except subprocess.CalledProcessError as exc:
814
+ detail = (exc.stderr or exc.stdout or str(exc)).strip()
815
+ return {"ok": False, "error": detail[-2000:]}
816
+ try:
817
+ payload = json.loads(result.stdout)
818
+ except json.JSONDecodeError:
819
+ return {"ok": False, "error": "EVM onboard returned non-JSON output."}
820
+ session = dict(payload.get("session") or {})
821
+ wallet_path = str(session.get("wallet_path") or "")
822
+ wallet_id = wallet_path.split("walletId=", 1)[-1] if "walletId=" in wallet_path else None
823
+ return {
824
+ "ok": True,
825
+ "user_id": session.get("user_id") or args.user_id,
826
+ "address": session.get("address"),
827
+ "wallet_id": wallet_id,
828
+ "networks": ["base", "ethereum"],
829
+ "backend": session.get("backend"),
830
+ }
831
+
832
+
744
833
  def main() -> None:
745
834
  args = build_parser().parse_args()
746
835
  source_package_root = Path(args.package_root).expanduser().resolve()
@@ -873,6 +962,7 @@ def main() -> None:
873
962
  configured = False
874
963
  configure_stdout = ""
875
964
  solana_onboard_result: dict[str, object] | None = None
965
+ evm_onboard_result: dict[str, object] | None = None
876
966
  if backend_enabled and not pending_env and not args.dry_run:
877
967
  result = subprocess.run(
878
968
  _build_next_steps(
@@ -893,6 +983,22 @@ def main() -> None:
893
983
  package_root,
894
984
  args,
895
985
  )
986
+ # Both wallets are provisioned on every install. EVM provisioning is
987
+ # best-effort: a failure here must not abort the install, since the lazy
988
+ # runtime path will create the wallet on first EVM use.
989
+ evm_onboard_result = _bootstrap_evm_wallet(
990
+ python_bin,
991
+ package_root,
992
+ args,
993
+ wdk_evm_root,
994
+ )
995
+ if isinstance(evm_onboard_result, dict) and not evm_onboard_result.get("ok"):
996
+ print(
997
+ "warning: the EVM wallet was not provisioned during install; it will "
998
+ "be created automatically on first EVM use. Details: "
999
+ + str(evm_onboard_result.get("error") or "unknown"),
1000
+ file=sys.stderr,
1001
+ )
896
1002
 
897
1003
  print(
898
1004
  json.dumps(
@@ -918,6 +1024,7 @@ def main() -> None:
918
1024
  "configured": configured,
919
1025
  "pending_env": pending_env,
920
1026
  "solana_wallet": solana_onboard_result,
1027
+ "evm_wallet": evm_onboard_result,
921
1028
  "next_configure_command": _build_next_steps(
922
1029
  python_bin,
923
1030
  install_config_script,
@@ -13,7 +13,11 @@ 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
+ from agent_wallet.config import (
17
+ normalize_btc_network,
18
+ normalize_evm_network,
19
+ normalize_solana_network,
20
+ )
17
21
  from agent_wallet.sealed_keys import resolve_sealed_keys_path, seal_keys, unseal_keys
18
22
  from security_utils import write_redacted_backup
19
23
 
@@ -25,10 +29,6 @@ LEGACY_ALLOWLIST_TOOLS = [
25
29
  "set_wallet_backend",
26
30
  "get_wallet_portfolio",
27
31
  "get_solana_token_prices",
28
- "swap_solana_privately",
29
- "continue_solana_private_swap",
30
- "list_pending_solana_private_swaps",
31
- "get_solana_private_swap_status",
32
32
  "get_kamino_lend_markets",
33
33
  "get_kamino_lend_market_reserves",
34
34
  "get_kamino_lend_user_obligations",
@@ -182,6 +182,8 @@ def _normalize_network(backend: str, network: str) -> str:
182
182
  normalized = network.strip().lower()
183
183
  if backend_name in {"wdk_btc_local", "wdk-btc-local", "btc_local", "btc-local"}:
184
184
  return normalize_btc_network(normalized or "bitcoin")
185
+ if backend_name in {"wdk_evm_local", "wdk-evm-local", "evm_local", "evm-local"}:
186
+ return normalize_evm_network(normalized or "ethereum")
185
187
  return normalize_solana_network(normalized or "mainnet")
186
188
 
187
189
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wallet-operator
3
- description: Use when operating OpenClaw wallet tools: balances, transfers, swaps, LI.FI cross-chain swaps, Jupiter swaps, Velora EVM swaps, BTC transfers, staking, Jupiter Earn, Kamino lending, Bags claims/launches, and wallet execution safety.
3
+ description: Use when operating OpenClaw wallet tools: balances, transfers, swaps, LI.FI cross-chain swaps, Jupiter swaps, Velora EVM swaps, BTC transfers, staking, Kamino lending, Bags claims/launches, and wallet execution safety.
4
4
  ---
5
5
 
6
6
  # Wallet Operator
@@ -29,7 +29,6 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
29
29
  - EVM transfers: `transfer_evm_native`, `transfer_evm_token`.
30
30
  - BTC transfer: `transfer_btc`.
31
31
  - Solana staking: `stake_sol_native`, `deactivate_solana_stake`, `withdraw_solana_stake`.
32
- - Jupiter Earn: `jupiter_earn_deposit`, `jupiter_earn_withdraw`.
33
32
  - Kamino: `kamino_lend_deposit`, `kamino_lend_withdraw`, `kamino_lend_borrow`, `kamino_lend_repay`.
34
33
  - Bags: `claim_bags_fees`, `launch_bags_token`.
35
34
 
@@ -107,9 +106,6 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
107
106
  - `stake_sol_native`: `vote_account`, `amount` in SOL, `mode`, `purpose`.
108
107
  - `deactivate_solana_stake`: `stake_account`, `mode`, `purpose`.
109
108
  - `withdraw_solana_stake`: `stake_account`, `amount` in SOL, optional `recipient`, `mode`, `purpose`.
110
- - Before Jupiter Earn writes, use `get_jupiter_earn_tokens`, `get_jupiter_earn_positions`, and `get_jupiter_earn_earnings`.
111
- - `jupiter_earn_deposit`: `asset` mint, `amount_raw`, `mode`, `purpose`.
112
- - `jupiter_earn_withdraw`: `asset` mint, `amount_raw`, `mode`, `purpose`.
113
109
  - Before Kamino writes, use `get_kamino_lend_markets`, `get_kamino_lend_market_reserves`, `get_kamino_lend_user_obligations`, and `get_kamino_lend_user_rewards`.
114
110
  - Kamino write params: `market`, `reserve`, `amount_ui` decimal string, `mode`, `purpose`.
115
111
  - Bags reads: `get_bags_claimable_positions`, `get_bags_fee_analytics`.
@@ -1268,71 +1268,138 @@ function runCodexInstall(args) {
1268
1268
  return add.skipped || add.ok ? 0 : 1;
1269
1269
  }
1270
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");
1271
+ const CLAUDE_CODE_MARKETPLACE_NAME = "agentlayer-local";
1276
1272
 
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");
1273
+ function resolveClaudeCodeMarketplaceDir(env = process.env) {
1274
+ return path.resolve(
1275
+ expandHome(env.AGENT_WALLET_CLAUDE_CODE_MARKETPLACE_DIR || "~/.claude/agentlayer-local"),
1276
+ );
1277
+ }
1278
+
1279
+ function ensureClaudeCodeMarketplace(marketplaceDir, pluginSource, force) {
1280
+ const pluginsDir = path.join(marketplaceDir, "plugins");
1281
+ const pluginLink = path.join(pluginsDir, "agent-wallet");
1282
+ const manifestDir = path.join(marketplaceDir, ".claude-plugin");
1283
+ const manifestPath = path.join(manifestDir, "marketplace.json");
1280
1284
 
1281
- fs.mkdirSync(pluginInstallDir, { recursive: true });
1285
+ fs.mkdirSync(pluginsDir, { recursive: true });
1286
+ fs.mkdirSync(manifestDir, { recursive: true });
1287
+
1288
+ // Symlink plugin source into marketplace plugins dir.
1282
1289
  try {
1283
- const existing = fs.lstatSync(pluginTarget);
1290
+ const existing = fs.lstatSync(pluginLink);
1284
1291
  if (!existing.isSymbolicLink()) {
1285
1292
  if (!force) {
1286
- throw new Error(`${pluginTarget} exists and is not a symlink. Pass --force to replace it.`);
1293
+ throw new Error(
1294
+ `${pluginLink} exists and is not a symlink. Pass --force to replace it.`,
1295
+ );
1287
1296
  }
1288
- fs.rmSync(pluginTarget, { recursive: true, force: true });
1297
+ fs.rmSync(pluginLink, { recursive: true, force: true });
1289
1298
  } else {
1290
- fs.unlinkSync(pluginTarget);
1299
+ fs.unlinkSync(pluginLink);
1291
1300
  }
1292
1301
  } catch (error) {
1293
1302
  if (error?.code !== "ENOENT") throw error;
1294
1303
  }
1295
- fs.symlinkSync(pluginSource, pluginTarget, "dir");
1304
+ fs.symlinkSync(pluginSource, pluginLink, "dir");
1305
+
1306
+ // Write marketplace manifest (Claude Code requires owner + plugins[].source as relative path).
1307
+ const manifest = {
1308
+ name: CLAUDE_CODE_MARKETPLACE_NAME,
1309
+ description: "Local AgentLayer plugins",
1310
+ owner: { name: "AgentLayer" },
1311
+ plugins: [
1312
+ {
1313
+ name: "agent-wallet",
1314
+ displayName: "Agent Wallet",
1315
+ description:
1316
+ "Bridge to the existing local AgentLayer wallet runtime (Solana, Bitcoin, EVM).",
1317
+ category: "development",
1318
+ source: "./plugins/agent-wallet",
1319
+ },
1320
+ ],
1321
+ };
1322
+ writeJsonFile(manifestPath, manifest);
1323
+ return { pluginLink, manifestPath };
1324
+ }
1325
+
1326
+ function runClaudeCodeInstall(args) {
1327
+ const pluginSource = resolveClaudeCodePluginSource();
1328
+ const force = hasFlag(args, "--force");
1329
+ const skipEnable = hasFlag(args, "--skip-enable");
1330
+ const claudeBin = commandPath("claude");
1331
+ const marketplaceDir = resolveClaudeCodeMarketplaceDir();
1332
+
1333
+ const { pluginLink } = ensureClaudeCodeMarketplace(marketplaceDir, pluginSource, force);
1334
+
1335
+ const pluginDirFlag = `claude --plugin-dir ${pluginLink}`;
1296
1336
 
1297
- // Try `claude plugin install <path>` when the CLI is present.
1337
+ // Without the Claude CLI we can only set up the files; the user must register manually.
1338
+ let marketplaceAdd = { attempted: false, ok: false, skipped: skipEnable, error: "" };
1298
1339
  let enable = { attempted: false, ok: false, skipped: skipEnable, error: "" };
1340
+
1299
1341
  if (!skipEnable) {
1300
1342
  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
- };
1343
+ const msg =
1344
+ "Claude Code CLI was not found on PATH. Load the plugin manually with: " + pluginDirFlag;
1345
+ marketplaceAdd = { attempted: false, ok: false, skipped: false, error: msg };
1346
+ enable = { attempted: false, ok: false, skipped: false, error: msg };
1309
1347
  } else {
1310
- const result = spawnSync(claudeBin, ["plugin", "install", pluginTarget, "--scope", "user"], {
1311
- encoding: "utf8",
1312
- stdio: "pipe",
1313
- });
1314
- enable = {
1348
+ // Register the local marketplace (idempotent safe to re-run).
1349
+ const addResult = spawnSync(
1350
+ claudeBin,
1351
+ ["plugin", "marketplace", "add", marketplaceDir, "--scope", "user"],
1352
+ { encoding: "utf8", stdio: "pipe" },
1353
+ );
1354
+ marketplaceAdd = {
1315
1355
  attempted: true,
1316
- ok: result.status === 0,
1356
+ ok: addResult.status === 0,
1317
1357
  skipped: false,
1318
- error: result.status === 0 ? "" : (result.stderr || result.stdout || "").trim(),
1358
+ error: addResult.status === 0 ? "" : (addResult.stderr || addResult.stdout || "").trim(),
1319
1359
  };
1360
+
1361
+ if (marketplaceAdd.ok) {
1362
+ // Install plugin from the now-registered local marketplace.
1363
+ const installResult = spawnSync(
1364
+ claudeBin,
1365
+ ["plugin", "install", `agent-wallet@${CLAUDE_CODE_MARKETPLACE_NAME}`, "--scope", "user"],
1366
+ { encoding: "utf8", stdio: "pipe" },
1367
+ );
1368
+ enable = {
1369
+ attempted: true,
1370
+ ok: installResult.status === 0,
1371
+ skipped: false,
1372
+ error:
1373
+ installResult.status === 0
1374
+ ? ""
1375
+ : (installResult.stderr || installResult.stdout || "").trim(),
1376
+ };
1377
+ } else {
1378
+ enable = {
1379
+ attempted: false,
1380
+ ok: false,
1381
+ skipped: false,
1382
+ error: "Skipped plugin install because marketplace registration failed.",
1383
+ };
1384
+ }
1320
1385
  }
1321
1386
  }
1322
1387
 
1323
- const pluginDirFlag = `claude --plugin-dir ${pluginTarget}`;
1388
+ const ok = enable.skipped || enable.ok;
1389
+ const pluginDirFlagFull = `claude --plugin-dir ${pluginSource}`;
1324
1390
  console.log(
1325
1391
  JSON.stringify(
1326
1392
  {
1327
- ok: enable.skipped || enable.ok,
1393
+ ok,
1328
1394
  plugin_source: pluginSource,
1329
- plugin_target: pluginTarget,
1395
+ marketplace_dir: marketplaceDir,
1396
+ marketplace_add: marketplaceAdd,
1330
1397
  claude_code_install: enable,
1331
- manual_load: pluginDirFlag,
1398
+ manual_load: pluginDirFlagFull,
1332
1399
  restart_required: true,
1333
- note: enable.ok
1400
+ note: ok
1334
1401
  ? "Plugin registered. Restart Claude Code to activate."
1335
- : `If automatic registration failed, load the plugin with: ${pluginDirFlag}`,
1402
+ : `If automatic registration failed, load the plugin with: ${pluginDirFlagFull}`,
1336
1403
  },
1337
1404
  null,
1338
1405
  2,
@@ -6,14 +6,19 @@ PLUGIN_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
6
6
  OPENCLAW_HOME=${OPENCLAW_HOME:-"$HOME/.openclaw"}
7
7
  PACKAGE_ROOT=${AGENT_WALLET_PACKAGE_ROOT:-${OPENCLAW_AGENT_WALLET_PACKAGE_ROOT:-"$OPENCLAW_HOME/agent-wallet-runtime/current/agent-wallet"}}
8
8
 
9
- # Resolve server.py: check sibling codex plugin (same npm package), then local copy
10
- CODEX_SERVER="$PLUGIN_ROOT/../../codex/plugins/agent-wallet/server.py"
9
+ # Resolve server.py. When Claude Code copies this plugin into its cache, the
10
+ # relative sibling paths below no longer resolve, so fall back to the codex
11
+ # plugin copy inside the installed runtime package, which is always present.
11
12
  LOCAL_SERVER="$PLUGIN_ROOT/server.py"
13
+ CODEX_SERVER="$PLUGIN_ROOT/../../codex/plugins/agent-wallet/server.py"
14
+ RUNTIME_CODEX_DIR="$OPENCLAW_HOME/agent-wallet-runtime/current/codex/plugins/agent-wallet"
12
15
 
13
16
  if [ -f "$LOCAL_SERVER" ]; then
14
17
  SERVER_PY="$LOCAL_SERVER"
15
18
  elif [ -f "$CODEX_SERVER" ]; then
16
19
  SERVER_PY=$(CDPATH= cd -- "$PLUGIN_ROOT/../../codex/plugins/agent-wallet" && pwd)/server.py
20
+ elif [ -f "$RUNTIME_CODEX_DIR/server.py" ]; then
21
+ SERVER_PY=$(CDPATH= cd -- "$RUNTIME_CODEX_DIR" && pwd)/server.py
17
22
  else
18
23
  printf '{"error":"agent-wallet server.py not found. Run: npx @agentlayer.tech/wallet install --yes"}\n' >&2
19
24
  exit 1
@@ -46,27 +46,17 @@ HOST_DEFAULT_CONFIG_KEYS = {
46
46
  "jupiterUltraBaseUrl",
47
47
  "jupiterPriceBaseUrl",
48
48
  "jupiterPortfolioBaseUrl",
49
- "jupiterLendBaseUrl",
50
49
  "jupiterApiKey",
51
- "houdiniBaseUrl",
52
- "houdiniApiKey",
53
- "houdiniApiSecret",
54
- "houdiniUserIp",
55
- "houdiniUserAgent",
56
- "houdiniUserTimezone",
57
50
  "kaminoBaseUrl",
58
51
  "kaminoProgramId",
59
52
  }
60
53
  BACKENDS = ("solana_local", "wdk_btc_local", "wdk_evm_local")
61
54
  PREVIEW_CACHE_TTL_SECONDS = 15 * 60
62
- PRIVATE_SWAP_CACHE_TTL_SECONDS = 35 * 60
63
55
  PREVIEW_BOUND_SWAP_TOOLS = {
64
56
  "swap_solana_tokens",
65
- "swap_solana_privately",
66
57
  "flash_trade_open_position",
67
58
  "flash_trade_close_position",
68
59
  }
69
- PRIVATE_SWAP_APPROVAL_TOOL_NAME = "swap_solana_privately"
70
60
  APPROVAL_PREVIEW_TOOL_ALIASES = {
71
61
  "x402_pay_request": "x402_preview_request",
72
62
  }
@@ -81,7 +71,6 @@ selected_solana_network: str | None = None
81
71
  selected_evm_network: str | None = None
82
72
  selected_btc_network: str | None = None
83
73
  approval_preview_cache: dict[str, dict[str, Any]] = {}
84
- private_swap_order_cache: dict[str, dict[str, Any]] = {}
85
74
 
86
75
 
87
76
  class WalletCliError(RuntimeError):
@@ -245,17 +234,10 @@ def _cache_preview_for_approval(user_id: str, tool_name: str, payload: dict[str,
245
234
  _prune_approval_preview_cache()
246
235
  approval_preview_cache[_approval_cache_key(user_id, cache_tool_name)] = {
247
236
  "digest": _preview_digest(data),
248
- "expires_at": time.time()
249
- + (
250
- PRIVATE_SWAP_CACHE_TTL_SECONDS
251
- if cache_tool_name == PRIVATE_SWAP_APPROVAL_TOOL_NAME
252
- else PREVIEW_CACHE_TTL_SECONDS
253
- ),
237
+ "expires_at": time.time() + PREVIEW_CACHE_TTL_SECONDS,
254
238
  "preview": data,
255
239
  "summary": summary,
256
240
  }
257
- if cache_tool_name == PRIVATE_SWAP_APPROVAL_TOOL_NAME:
258
- private_swap_order_cache.pop(_approval_cache_key(user_id, cache_tool_name), None)
259
241
 
260
242
 
261
243
  def _latest_cached_preview(user_id: str, tool_name: str) -> dict[str, Any] | None:
@@ -290,66 +272,6 @@ def _cached_preview_for_token(user_id: str, tool_name: str, token: str) -> dict[
290
272
  return preview if isinstance(preview, dict) else None
291
273
 
292
274
 
293
- def _cache_pending_private_swap_order(
294
- user_id: str,
295
- tool_name: str,
296
- preview: dict[str, Any],
297
- details: dict[str, Any],
298
- ) -> None:
299
- if tool_name != PRIVATE_SWAP_APPROVAL_TOOL_NAME:
300
- return
301
- houdini_id = str(details.get("houdini_id") or "").strip()
302
- deposit_address = str(details.get("deposit_address") or "").strip()
303
- if not houdini_id or not deposit_address:
304
- return
305
- private_swap_order_cache[_approval_cache_key(user_id, tool_name)] = {
306
- "digest": _preview_digest(preview),
307
- "expires_at": time.time() + PRIVATE_SWAP_CACHE_TTL_SECONDS,
308
- "order": {
309
- "multi_id": str(details.get("multi_id") or "").strip() or None,
310
- "houdini_id": houdini_id,
311
- "deposit_address": deposit_address,
312
- "order": details.get("order") if isinstance(details.get("order"), dict) else {},
313
- },
314
- }
315
-
316
-
317
- def _latest_pending_private_swap_order(
318
- user_id: str,
319
- tool_name: str,
320
- preview: dict[str, Any],
321
- ) -> dict[str, Any] | None:
322
- if tool_name != PRIVATE_SWAP_APPROVAL_TOOL_NAME:
323
- return None
324
- cached = private_swap_order_cache.get(_approval_cache_key(user_id, tool_name))
325
- if not cached:
326
- return None
327
- if float(cached.get("expires_at") or 0) <= time.time():
328
- private_swap_order_cache.pop(_approval_cache_key(user_id, tool_name), None)
329
- return None
330
- if cached.get("digest") != _preview_digest(preview):
331
- return None
332
- order = cached.get("order")
333
- return order if isinstance(order, dict) else None
334
-
335
-
336
- def _clear_pending_private_swap_order(user_id: str, tool_name: str) -> None:
337
- if tool_name == PRIVATE_SWAP_APPROVAL_TOOL_NAME:
338
- private_swap_order_cache.pop(_approval_cache_key(user_id, tool_name), None)
339
-
340
-
341
- def _list_pending_private_swap_orders(user_id: str) -> list[dict[str, Any]]:
342
- key = _approval_cache_key(user_id, PRIVATE_SWAP_APPROVAL_TOOL_NAME)
343
- pending = private_swap_order_cache.get(key)
344
- if not pending or float(pending.get("expires_at") or 0) <= time.time():
345
- private_swap_order_cache.pop(key, None)
346
- return []
347
- order = pending.get("order")
348
- if not isinstance(order, dict):
349
- return []
350
- return [{**order, "expires_at_ms": int(float(pending["expires_at"]) * 1000)}]
351
-
352
-
353
275
  def _normalize_wallet_backend(value: Any) -> str:
354
276
  normalized = str(value or "").strip().lower()
355
277
  aliases = {
@@ -651,8 +573,6 @@ def _issue_approval_token(
651
573
  ]
652
574
  if preview_payload.get("is_mainnet") is True:
653
575
  extra_args.append("--mainnet-confirmed")
654
- if tool_name == PRIVATE_SWAP_APPROVAL_TOOL_NAME:
655
- extra_args.extend(["--ttl-seconds", "1800"])
656
576
  payload = _call_wallet_cli("issue-approval", extra_args)
657
577
  token = str(payload.get("approval_token") or "").strip()
658
578
  if not token:
@@ -966,9 +886,6 @@ async def _handle_set_evm_network(params: dict[str, Any]) -> dict[str, Any]:
966
886
 
967
887
 
968
888
  async def _handle_wallet_tool(tool_name: str, params: dict[str, Any]) -> dict[str, Any]:
969
- if tool_name == "list_pending_solana_private_swaps":
970
- return {"orders": _list_pending_private_swap_orders(_user_id())}
971
-
972
889
  config = _base_config(params, tool_name=tool_name)
973
890
  backend = _normalize_wallet_backend(config.get("backend"))
974
891
  if backend == "wdk_evm_local" and params.get("network") is None and selected_evm_network:
@@ -976,24 +893,7 @@ async def _handle_wallet_tool(tool_name: str, params: dict[str, Any]) -> dict[st
976
893
  config["network"] = selected_evm_network
977
894
 
978
895
  effective_params = dict(params)
979
- if tool_name != "continue_solana_private_swap":
980
- _attach_approval_for_execute(tool_name, config, effective_params)
981
- else:
982
- cached = _latest_cached_preview(_user_id(), PRIVATE_SWAP_APPROVAL_TOOL_NAME)
983
- if cached and isinstance(cached.get("preview"), dict):
984
- effective_params["_approved_preview"] = cached["preview"]
985
- effective_params["approval_token"] = _issue_approval_token(
986
- PRIVATE_SWAP_APPROVAL_TOOL_NAME,
987
- config,
988
- cached["preview"],
989
- )
990
- pending = _latest_pending_private_swap_order(
991
- _user_id(), PRIVATE_SWAP_APPROVAL_TOOL_NAME, cached["preview"]
992
- )
993
- if pending and effective_params.get("_resume_private_swap_order") is None:
994
- effective_params["_resume_private_swap_order"] = pending
995
- elif not effective_params.get("approval_token"):
996
- raise RuntimeError(APPROVAL_CONTEXT_MISSING_MESSAGE)
896
+ _attach_approval_for_execute(tool_name, config, effective_params)
997
897
 
998
898
  try:
999
899
  payload = _invoke_tool(tool_name, effective_params, config)
@@ -1001,22 +901,6 @@ async def _handle_wallet_tool(tool_name: str, params: dict[str, Any]) -> dict[st
1001
901
  raise _normalize_approval_context_error(exc) from exc
1002
902
 
1003
903
  _cache_preview_for_approval(_user_id(), tool_name, payload)
1004
- if tool_name == "swap_solana_privately" and payload.get("ok") is True:
1005
- data = payload.get("data")
1006
- approved_preview = effective_params.get("_approved_preview")
1007
- if (
1008
- isinstance(data, dict)
1009
- and data.get("execution_state") == "awaiting_deposit_funding"
1010
- and isinstance(approved_preview, dict)
1011
- ):
1012
- _cache_pending_private_swap_order(_user_id(), tool_name, approved_preview, data)
1013
- elif isinstance(data, dict):
1014
- _clear_pending_private_swap_order(_user_id(), tool_name)
1015
- if tool_name == "continue_solana_private_swap" and payload.get("ok") is True:
1016
- data = payload.get("data")
1017
- if isinstance(data, dict) and data.get("execution_state") == "funding_submitted":
1018
- _clear_pending_private_swap_order(_user_id(), PRIVATE_SWAP_APPROVAL_TOOL_NAME)
1019
-
1020
904
  if payload.get("ok") is False:
1021
905
  raise RuntimeError(str(payload.get("error") or f"{tool_name} failed"))
1022
906
  return payload.get("data", {})
@@ -15,7 +15,7 @@ from typing import Any
15
15
 
16
16
  SECRET_CONFIG_KEYS = {"privateKey", "masterKey", "approvalSecret"}
17
17
  BACKENDS = ("solana_local", "wdk_btc_local", "wdk_evm_local")
18
- PREVIEW_BOUND_SWAP_TOOLS = {"swap_solana_tokens", "swap_solana_privately"}
18
+ PREVIEW_BOUND_SWAP_TOOLS = {"swap_solana_tokens"}
19
19
 
20
20
 
21
21
  def _json(data: dict[str, Any]) -> str:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlayer.tech/wallet",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "NPM installer for the OpenClaw Agent Wallet local runtime.",
5
5
  "type": "module",
6
6
  "repository": {