Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ Payment skill for AI agents (Claude Code, OpenClaw, OpenCode) to make and receiv
- `get-address/` - Get wallet address and balance
- `pay/` - Make payments
- `payment-config/` - Configuration management
- `x402curl/` - HTTP client with 402 payment handling
- `skill/` - Skill files for Claude Code
- `claude/skills/payment/` - Skill files for Claude Code
- `openclaw/skills/payment/` - Skill files for OpenClaw
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ All personal data is stored in the skill root directory (the parent of the `scri
└── password.txt # Wallet password (auto-generated, 600 permissions)
```

The data directory is determined at runtime via `std::env::current_exe()` — each binary resolves paths relative to its own location (`../` from the `scripts/` directory).
The data directory is resolved at runtime:
- `PAYMENT_SKILL_DATA_DIR` if set
- otherwise, `../` from the `scripts/` directory in installed skill layouts
- otherwise, current working directory during local `cargo run` development

## Skill Directory Structure

Expand Down Expand Up @@ -171,4 +174,4 @@ The project uses GitHub Actions to build binaries for all supported platforms. S

## License

See [LICENSE](LICENSE) for details.
License is declared as MIT in `Cargo.toml`. The project owner should provide the canonical license file.
51 changes: 44 additions & 7 deletions payment-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,55 @@ pub use config::Config;
pub use error::{Error, Result};
pub use wallet::{Wallet, WalletInfo};

use std::path::PathBuf;
use std::path::{Path, PathBuf};

/// Get the default data directory (parent of the directory containing the executable).
/// Resolve the default data directory.
///
/// Given the installed layout where binaries live in `scripts/`, this returns the
/// skill root directory (e.g., `~/.openclaw/skills/payment/`).
/// Resolution order:
/// 1. `PAYMENT_SKILL_DATA_DIR` environment variable (if set)
/// 2. Installed layout: if executable is in `.../scripts/`, use its parent
/// 3. Local development (`cargo run` from `target/{debug,release}`): use current working directory
/// 4. Fallback: executable directory
pub fn default_data_dir() -> PathBuf {
if let Ok(dir) = std::env::var("PAYMENT_SKILL_DATA_DIR") {
let trimmed = dir.trim();
if !trimmed.is_empty() {
return PathBuf::from(trimmed);
}
}

let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));

std::env::current_exe()
.ok()
.and_then(|exe| exe.parent().map(|p| p.to_path_buf()))
.and_then(|dir| dir.parent().map(|p| p.to_path_buf()))
.unwrap_or_else(|| PathBuf::from("."))
.and_then(|exe| resolve_data_dir_from_exe(&exe, &cwd))
.unwrap_or(cwd)
}

fn resolve_data_dir_from_exe(exe: &Path, cwd: &Path) -> Option<PathBuf> {
let exe_dir = exe.parent()?;

// Installed skill layout: <skill-root>/scripts/<binary>
if exe_dir.file_name().and_then(|n| n.to_str()) == Some("scripts") {
return exe_dir.parent().map(|p| p.to_path_buf());
}

// Local cargo layout: <workspace>/target/{debug,release}/<binary>
let is_target_profile = matches!(
exe_dir.file_name().and_then(|n| n.to_str()),
Some("debug" | "release")
);
let is_under_target = exe_dir
.parent()
.and_then(|p| p.file_name())
.and_then(|n| n.to_str())
== Some("target");

if is_target_profile && is_under_target {
return Some(cwd.to_path_buf());
}

Some(exe_dir.to_path_buf())
}

/// Get the default wallet path (data_dir/wallet.json)
Expand Down