@agentlayer.tech/wallet 0.1.74 → 0.1.76
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/extensions/agent-wallet/openclaw.plugin.json +1 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/CHANGELOG.md +26 -0
- package/VERSION +1 -1
- package/agent-wallet/AGENTS.md +1 -0
- package/agent-wallet/UPGRADE_COMPATIBILITY.md +36 -0
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/boot_key_recovery.py +10 -1
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/bin/lib/boot-key.mjs +185 -0
- package/bin/lib/integrations.mjs +424 -0
- package/bin/lib/update-transaction.mjs +235 -0
- package/bin/openclaw-agent-wallet.mjs +351 -191
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
- package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
- package/package.json +2 -1
- package/wdk-btc-wallet/package.json +1 -1
- package/wdk-evm-wallet/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "agent-wallet",
|
|
3
3
|
"name": "Agent Wallet",
|
|
4
4
|
"description": "Official OpenClaw plugin bridge for the agent-wallet backends, including Solana, local BTC, and local EVM.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.76",
|
|
6
6
|
"contracts": {
|
|
7
7
|
"tools": [
|
|
8
8
|
"agentlayer_autonomous_approve",
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## v0.1.75 - 2026-07-11
|
|
6
|
+
|
|
7
|
+
- Added a persistent, non-secret ownership registry for OpenClaw, Hermes,
|
|
8
|
+
Codex, and Claude Code integrations. Explicit installs are now distinguishable
|
|
9
|
+
from unrelated external plugins across runtime upgrades.
|
|
10
|
+
|
|
11
|
+
- Fixed `wallet update` so managed framework integrations advance with the
|
|
12
|
+
verified `current` runtime. OpenClaw config and Hermes paths are repaired,
|
|
13
|
+
while Codex and Claude Code registrations and versioned caches are refreshed
|
|
14
|
+
through their host CLIs.
|
|
15
|
+
|
|
16
|
+
- Added guarded adoption for legacy AgentLayer installs. Existing integrations
|
|
17
|
+
are adopted only when their registration and plugin manifests both identify
|
|
18
|
+
AgentLayer; unowned symlinks and external plugins remain untouched.
|
|
19
|
+
|
|
20
|
+
- Added per-framework synchronization details to `wallet status` and
|
|
21
|
+
`wallet doctor`, including failed host registration and restart requirements.
|
|
22
|
+
npm-exec installs also refresh an already-installed stale global CLI without
|
|
23
|
+
making global installation mandatory.
|
|
24
|
+
|
|
25
|
+
- Expanded installer regression coverage for managed upgrades, unmanaged
|
|
26
|
+
integration preservation, global CLI refresh, host registration, and
|
|
27
|
+
hermetic framework/npm homes.
|
|
28
|
+
|
|
29
|
+
## v0.1.74 - 2026-07-10
|
|
30
|
+
|
|
5
31
|
- Added a guarded npm beta-promotion workflow that acceptance-tests the exact
|
|
6
32
|
registry artifact, verifies its integrity, rejects downgrades, serializes with
|
|
7
33
|
publish jobs, and restores the previous `latest` tag if verification fails.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.76
|
package/agent-wallet/AGENTS.md
CHANGED
|
@@ -89,6 +89,7 @@ These instructions apply to the entire `agent-wallet/` tree.
|
|
|
89
89
|
- installer scripts and README examples
|
|
90
90
|
- When changing approval or signing behavior, update the runtime, adapter, CLI bridge, and smoke tests together.
|
|
91
91
|
- If a change affects the OpenClaw bridge, update the TypeScript extension and the Python contract in the same change set.
|
|
92
|
+
- Preserve the direct-upgrade floor and legacy removal policy documented in `UPGRADE_COMPATIBILITY.md`.
|
|
92
93
|
|
|
93
94
|
### Coding style
|
|
94
95
|
- Follow existing Python style and type-hint usage.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Upgrade Compatibility
|
|
2
|
+
|
|
3
|
+
The wallet installer supports direct upgrades from older production releases.
|
|
4
|
+
Compatibility code is removed according to the minimum supported source
|
|
5
|
+
version, not according to elapsed time or a fixed number of releases.
|
|
6
|
+
|
|
7
|
+
## Supported Floor
|
|
8
|
+
|
|
9
|
+
The current regression floor is `0.1.53`. CI must keep a direct upgrade fixture
|
|
10
|
+
at that floor until the project explicitly raises it in release documentation.
|
|
11
|
+
|
|
12
|
+
Raising the floor requires all of the following:
|
|
13
|
+
|
|
14
|
+
- a documented migration path for users below the new floor
|
|
15
|
+
- upgrade fixtures for the new floor and the latest stable release
|
|
16
|
+
- support and telemetry evidence that the retired path is no longer material
|
|
17
|
+
- a release note naming the removed compatibility behavior
|
|
18
|
+
|
|
19
|
+
## Compatibility Paths
|
|
20
|
+
|
|
21
|
+
| Path | Purpose | Removal condition |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| JavaScript boot-key fallback | Updates runtimes that predate the verified Python installer resolver. | The supported floor includes `resolve_boot_key_for_installer`, and direct-upgrade fixtures no longer require the fallback. |
|
|
24
|
+
| Directory runtime-pointer migration | Converts pre-versioned `current/` directories into release entries. | The supported floor guarantees symlink-based runtime pointers. |
|
|
25
|
+
| Hermes legacy adoption | Recognizes an AgentLayer plugin installed before the ownership registry. | The supported floor guarantees an `integrations.json` Hermes ownership entry. |
|
|
26
|
+
| Codex legacy adoption | Recognizes a matching local marketplace registration and AgentLayer manifest. | The supported floor guarantees an `integrations.json` Codex ownership entry. |
|
|
27
|
+
| Claude Code legacy adoption | Recognizes the AgentLayer marketplace and plugin manifest before registry ownership. | The supported floor guarantees an `integrations.json` Claude Code ownership entry. |
|
|
28
|
+
|
|
29
|
+
## Safety Rules
|
|
30
|
+
|
|
31
|
+
- Legacy adoption requires independent registration and manifest evidence.
|
|
32
|
+
- Unknown or externally managed integrations are never adopted automatically.
|
|
33
|
+
- Compatibility paths must not weaken boot-key, sealed-state, approval, or
|
|
34
|
+
signing validation.
|
|
35
|
+
- Every retained path needs a direct-upgrade smoke test or an explicit fixture
|
|
36
|
+
showing why it remains necessary.
|
|
@@ -29,10 +29,19 @@ def export_boot_key() -> str:
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def import_boot_key(value: str) -> dict:
|
|
32
|
-
"""
|
|
32
|
+
"""Validate and write a recorded boot key, then verify the read-back."""
|
|
33
33
|
key = str(value or "").strip()
|
|
34
34
|
if not key:
|
|
35
35
|
raise WalletBackendError("A non-empty boot key is required for import.")
|
|
36
|
+
from agent_wallet.sealed_keys import resolve_sealed_keys_path, unseal_keys
|
|
37
|
+
|
|
38
|
+
if resolve_sealed_keys_path().exists():
|
|
39
|
+
try:
|
|
40
|
+
unseal_keys(key)
|
|
41
|
+
except Exception as exc:
|
|
42
|
+
raise WalletBackendError(
|
|
43
|
+
"The supplied boot key does not unlock the existing sealed wallet state."
|
|
44
|
+
) from exc
|
|
36
45
|
store = resolve_keystore()
|
|
37
46
|
store.set(BOOT_KEY_ITEM, key)
|
|
38
47
|
if store.get(BOOT_KEY_ITEM) != key:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "agent-wallet",
|
|
3
3
|
"name": "Agent Wallet",
|
|
4
4
|
"description": "Plugin-friendly wallet backend for OpenClaw agents with safe wallet tools and runtime instructions across Solana, local BTC, and local EVM.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.76",
|
|
6
6
|
"skills": ["skills/wallet-operator"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
function readEnv(pathname) {
|
|
6
|
+
try {
|
|
7
|
+
const result = {};
|
|
8
|
+
for (const line of fs.readFileSync(pathname, "utf8").split(/\r?\n/)) {
|
|
9
|
+
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
10
|
+
if (match) result[match[1]] = match[2];
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error?.code === "ENOENT") return {};
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readText(pathname) {
|
|
20
|
+
try {
|
|
21
|
+
return fs.readFileSync(pathname, "utf8");
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (error?.code === "ENOENT") return "";
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function writeSecret(pathname, value) {
|
|
29
|
+
fs.mkdirSync(path.dirname(pathname), { recursive: true });
|
|
30
|
+
fs.writeFileSync(pathname, `${String(value || "").trim()}\n`, { mode: 0o600 });
|
|
31
|
+
try {
|
|
32
|
+
fs.chmodSync(pathname, 0o600);
|
|
33
|
+
} catch {
|
|
34
|
+
// Best effort on filesystems without POSIX modes.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createBootKeyManager({
|
|
39
|
+
runtimeBase,
|
|
40
|
+
openclawHome,
|
|
41
|
+
currentRuntimeRoot,
|
|
42
|
+
resolveVenvPython,
|
|
43
|
+
expandHome,
|
|
44
|
+
bridgeTimeoutMs,
|
|
45
|
+
env = process.env,
|
|
46
|
+
}) {
|
|
47
|
+
const defaultFile = path.join(runtimeBase, "boot-key");
|
|
48
|
+
|
|
49
|
+
function currentKey() {
|
|
50
|
+
const root = currentRuntimeRoot();
|
|
51
|
+
if (!root) return "";
|
|
52
|
+
return readEnv(path.join(root, "agent-wallet", ".env")).AGENT_WALLET_BOOT_KEY || "";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function configuredFileKey() {
|
|
56
|
+
const value = String(env.AGENT_WALLET_BOOT_KEY_FILE || "").trim();
|
|
57
|
+
return value ? readText(path.resolve(expandHome(value))).trim() : "";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function ensureFile() {
|
|
61
|
+
const configured = String(env.AGENT_WALLET_BOOT_KEY_FILE || "").trim();
|
|
62
|
+
const keyFile = configured ? path.resolve(expandHome(configured)) : defaultFile;
|
|
63
|
+
if (readText(keyFile).trim()) return { path: keyFile, status: "existing" };
|
|
64
|
+
const key = String(env.AGENT_WALLET_BOOT_KEY || "").trim() || configuredFileKey() || currentKey();
|
|
65
|
+
if (!key) return { path: keyFile, status: "missing" };
|
|
66
|
+
writeSecret(keyFile, key);
|
|
67
|
+
return { path: keyFile, status: "created" };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function readKeystore() {
|
|
71
|
+
const root = currentRuntimeRoot();
|
|
72
|
+
if (!root) return "";
|
|
73
|
+
const python = resolveVenvPython(root);
|
|
74
|
+
if (!python) return "";
|
|
75
|
+
try {
|
|
76
|
+
const result = spawnSync(
|
|
77
|
+
python,
|
|
78
|
+
["-c", "from agent_wallet.config import read_boot_key_from_keystore as r; print(r())"],
|
|
79
|
+
{
|
|
80
|
+
cwd: path.join(root, "agent-wallet"),
|
|
81
|
+
encoding: "utf8",
|
|
82
|
+
timeout: bridgeTimeoutMs,
|
|
83
|
+
env: { ...env, OPENCLAW_HOME: openclawHome },
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
return result.status === 0 ? String(result.stdout || "").trim() : "";
|
|
87
|
+
} catch {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function resolveFromRuntime() {
|
|
93
|
+
const root = currentRuntimeRoot();
|
|
94
|
+
if (!root) return { supported: false, key: "" };
|
|
95
|
+
const python = resolveVenvPython(root);
|
|
96
|
+
if (!python) return { supported: false, key: "" };
|
|
97
|
+
try {
|
|
98
|
+
const result = spawnSync(
|
|
99
|
+
python,
|
|
100
|
+
["-c", "from agent_wallet.config import resolve_boot_key_for_installer as r; print(r())"],
|
|
101
|
+
{
|
|
102
|
+
cwd: path.join(root, "agent-wallet"),
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
timeout: bridgeTimeoutMs,
|
|
105
|
+
env: { ...env, OPENCLAW_HOME: openclawHome },
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
return result.status === 0
|
|
109
|
+
? { supported: true, key: String(result.stdout || "").trim() }
|
|
110
|
+
: { supported: false, key: "" };
|
|
111
|
+
} catch {
|
|
112
|
+
return { supported: false, key: "" };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function resolveLegacy() {
|
|
117
|
+
for (const [source, key] of [
|
|
118
|
+
["legacy_keystore", readKeystore()],
|
|
119
|
+
["current_runtime_env", currentKey()],
|
|
120
|
+
["configured_file", configuredFileKey()],
|
|
121
|
+
["default_file", readText(defaultFile).trim()],
|
|
122
|
+
]) {
|
|
123
|
+
if (key) return { key, source };
|
|
124
|
+
}
|
|
125
|
+
return { key: "", source: "none" };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function provision(releaseRoot, bootKey) {
|
|
129
|
+
const key = String(bootKey || "").trim();
|
|
130
|
+
if (!key) return false;
|
|
131
|
+
const python = resolveVenvPython(releaseRoot);
|
|
132
|
+
if (!python) return false;
|
|
133
|
+
try {
|
|
134
|
+
const result = spawnSync(
|
|
135
|
+
python,
|
|
136
|
+
["-m", "agent_wallet.openclaw_cli", "boot-key-import", "--key-stdin"],
|
|
137
|
+
{
|
|
138
|
+
cwd: path.join(releaseRoot, "agent-wallet"),
|
|
139
|
+
input: key,
|
|
140
|
+
encoding: "utf8",
|
|
141
|
+
timeout: bridgeTimeoutMs,
|
|
142
|
+
env: { ...env, OPENCLAW_HOME: openclawHome },
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
return result.status === 0;
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function verifyWithRuntime(releaseRoot) {
|
|
152
|
+
const sealedPath = path.join(openclawHome, "sealed_keys.json");
|
|
153
|
+
if (!fs.existsSync(sealedPath)) return { ok: true, required: false };
|
|
154
|
+
const key = String(env.AGENT_WALLET_BOOT_KEY || "").trim();
|
|
155
|
+
if (!key) return { ok: false, required: true, error: "no verified boot key is available" };
|
|
156
|
+
const python =
|
|
157
|
+
env.AGENT_WALLET_PYTHON ||
|
|
158
|
+
env.OPENCLAW_AGENT_WALLET_PYTHON ||
|
|
159
|
+
resolveVenvPython(releaseRoot);
|
|
160
|
+
if (!python) {
|
|
161
|
+
if (String(env.AGENT_WALLET_VERIFY_DISABLE || "") === "1") {
|
|
162
|
+
return { ok: true, required: true, skipped: true };
|
|
163
|
+
}
|
|
164
|
+
return { ok: false, required: true, error: "staged Python runtime is unavailable" };
|
|
165
|
+
}
|
|
166
|
+
const result = spawnSync(
|
|
167
|
+
python,
|
|
168
|
+
[
|
|
169
|
+
"-c",
|
|
170
|
+
"from agent_wallet.sealed_keys import unseal_keys; import os; unseal_keys(os.environ['AGENT_WALLET_BOOT_KEY']); print('ok')",
|
|
171
|
+
],
|
|
172
|
+
{
|
|
173
|
+
cwd: path.join(releaseRoot, "agent-wallet"),
|
|
174
|
+
encoding: "utf8",
|
|
175
|
+
timeout: bridgeTimeoutMs,
|
|
176
|
+
env: { ...env, OPENCLAW_HOME: openclawHome },
|
|
177
|
+
},
|
|
178
|
+
);
|
|
179
|
+
return result.status === 0
|
|
180
|
+
? { ok: true, required: true }
|
|
181
|
+
: { ok: false, required: true, error: "selected boot key does not unlock sealed wallet state" };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { ensureFile, resolveFromRuntime, resolveLegacy, provision, verifyWithRuntime };
|
|
185
|
+
}
|