@agentlayer.tech/wallet 0.1.90 → 0.1.92
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 +35 -0
- package/README.md +86 -467
- package/VERSION +1 -1
- package/agent-wallet/README.md +26 -1
- package/agent-wallet/UPGRADE_COMPATIBILITY.md +2 -1
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/boot_key_migration.py +10 -21
- package/agent-wallet/agent_wallet/config.py +20 -0
- package/agent-wallet/agent_wallet/evm_user_wallets.py +176 -25
- package/agent-wallet/agent_wallet/keystore.py +112 -17
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/install_agent_wallet.py +197 -11
- package/agent-wallet/scripts/install_openclaw_local_config.py +4 -1
- package/agent-wallet/scripts/install_openclaw_sealed_keys.py +6 -2
- package/bin/lib/evm-daemon.mjs +375 -0
- package/bin/lib/host-detection.mjs +236 -0
- package/bin/lib/integrations.mjs +69 -5
- package/bin/openclaw-agent-wallet.mjs +405 -28
- 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 +8 -3
- package/wdk-btc-wallet/package.json +1 -1
- package/wdk-evm-wallet/package.json +3 -2
- package/wdk-evm-wallet/src/server.js +24 -3
- package/wdk-evm-wallet/src/shutdown.js +63 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export const HOST_NAMES = Object.freeze([
|
|
6
|
+
"openclaw",
|
|
7
|
+
"codex",
|
|
8
|
+
"claude-code",
|
|
9
|
+
"hermes",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const HOST_ALIASES = Object.freeze({
|
|
13
|
+
openclaw: "openclaw",
|
|
14
|
+
codex: "codex",
|
|
15
|
+
claude: "claude-code",
|
|
16
|
+
"claude-code": "claude-code",
|
|
17
|
+
claudecode: "claude-code",
|
|
18
|
+
hermes: "hermes",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function expandHome(value, env) {
|
|
22
|
+
const home = env.HOME || os.homedir();
|
|
23
|
+
if (value === "~") return home;
|
|
24
|
+
if (value.startsWith("~/")) return path.join(home, value.slice(2));
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function existingEvidence(candidates, exists = fs.existsSync) {
|
|
29
|
+
return candidates.filter((candidate) => {
|
|
30
|
+
try {
|
|
31
|
+
return exists(candidate);
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function detectedHost(name, displayName, binary, configCandidates, commandPath, exists) {
|
|
39
|
+
const binaryPath = commandPath(binary);
|
|
40
|
+
const configPaths = existingEvidence(configCandidates, exists);
|
|
41
|
+
const evidence = [
|
|
42
|
+
...(binaryPath ? [{ type: "cli", path: binaryPath }] : []),
|
|
43
|
+
...configPaths.map((configPath) => ({ type: "config", path: configPath })),
|
|
44
|
+
];
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
display_name: displayName,
|
|
48
|
+
detected: evidence.length > 0,
|
|
49
|
+
confidence: evidence.length > 0 ? "high" : "none",
|
|
50
|
+
evidence,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function detectHosts({
|
|
55
|
+
env = process.env,
|
|
56
|
+
commandPath,
|
|
57
|
+
exists = fs.existsSync,
|
|
58
|
+
} = {}) {
|
|
59
|
+
if (typeof commandPath !== "function") {
|
|
60
|
+
throw new TypeError("detectHosts requires commandPath(name)");
|
|
61
|
+
}
|
|
62
|
+
const home = env.HOME || os.homedir();
|
|
63
|
+
const openclawHome = path.resolve(expandHome(env.OPENCLAW_HOME || "~/.openclaw", env));
|
|
64
|
+
const codexHome = path.resolve(expandHome(env.CODEX_HOME || "~/.codex", env));
|
|
65
|
+
const hermesHome = path.resolve(expandHome(env.HERMES_HOME || "~/.hermes", env));
|
|
66
|
+
const claudeHome = path.resolve(expandHome(env.CLAUDE_CONFIG_DIR || "~/.claude", env));
|
|
67
|
+
|
|
68
|
+
return [
|
|
69
|
+
detectedHost(
|
|
70
|
+
"openclaw",
|
|
71
|
+
"OpenClaw",
|
|
72
|
+
"openclaw",
|
|
73
|
+
[path.join(openclawHome, "openclaw.json")],
|
|
74
|
+
commandPath,
|
|
75
|
+
exists,
|
|
76
|
+
),
|
|
77
|
+
detectedHost(
|
|
78
|
+
"codex",
|
|
79
|
+
"Codex",
|
|
80
|
+
"codex",
|
|
81
|
+
[path.join(codexHome, "config.toml")],
|
|
82
|
+
commandPath,
|
|
83
|
+
exists,
|
|
84
|
+
),
|
|
85
|
+
detectedHost(
|
|
86
|
+
"claude-code",
|
|
87
|
+
"Claude Code",
|
|
88
|
+
"claude",
|
|
89
|
+
[
|
|
90
|
+
path.join(claudeHome, "settings.json"),
|
|
91
|
+
path.join(home, ".claude.json"),
|
|
92
|
+
],
|
|
93
|
+
commandPath,
|
|
94
|
+
exists,
|
|
95
|
+
),
|
|
96
|
+
detectedHost(
|
|
97
|
+
"hermes",
|
|
98
|
+
"Hermes",
|
|
99
|
+
"hermes",
|
|
100
|
+
[
|
|
101
|
+
path.join(hermesHome, "config.yaml"),
|
|
102
|
+
path.join(hermesHome, "config.yml"),
|
|
103
|
+
path.join(hermesHome, "settings.json"),
|
|
104
|
+
],
|
|
105
|
+
commandPath,
|
|
106
|
+
exists,
|
|
107
|
+
),
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function flagValues(args, name) {
|
|
112
|
+
const values = [];
|
|
113
|
+
const prefix = `${name}=`;
|
|
114
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
115
|
+
const value = args[index];
|
|
116
|
+
if (value === name) {
|
|
117
|
+
const next = args[index + 1] || "";
|
|
118
|
+
if (!next || next.startsWith("--")) {
|
|
119
|
+
throw new Error(`${name} requires a value.`);
|
|
120
|
+
}
|
|
121
|
+
values.push(next);
|
|
122
|
+
index += 1;
|
|
123
|
+
} else if (value.startsWith(prefix)) {
|
|
124
|
+
values.push(value.slice(prefix.length));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return values;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function normalizeHostToken(value) {
|
|
131
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
132
|
+
return HOST_ALIASES[normalized] || normalized;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function parseHostSet(values, { detected, managed }) {
|
|
136
|
+
const selected = new Set();
|
|
137
|
+
for (const rawValue of values) {
|
|
138
|
+
for (const rawToken of String(rawValue).split(",")) {
|
|
139
|
+
const token = normalizeHostToken(rawToken);
|
|
140
|
+
if (!token) continue;
|
|
141
|
+
if (token === "none" || token === "runtime-only") {
|
|
142
|
+
selected.clear();
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (token === "all") {
|
|
146
|
+
HOST_NAMES.forEach((name) => selected.add(name));
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (token === "detected") {
|
|
150
|
+
detected.forEach((name) => selected.add(name));
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (token === "managed") {
|
|
154
|
+
managed.forEach((name) => selected.add(name));
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (!HOST_NAMES.includes(token)) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Unknown host '${rawToken}'. Expected: ${HOST_NAMES.join(", ")}, detected, managed, all, or none.`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
selected.add(token);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return selected;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function buildInstallPlan({
|
|
169
|
+
args,
|
|
170
|
+
detections,
|
|
171
|
+
managedHosts = [],
|
|
172
|
+
runtimeInstalled,
|
|
173
|
+
}) {
|
|
174
|
+
const detected = detections.filter((entry) => entry.detected).map((entry) => entry.name);
|
|
175
|
+
const managed = managedHosts.filter((name) => HOST_NAMES.includes(name));
|
|
176
|
+
const hostValues = flagValues(args, "--hosts");
|
|
177
|
+
const explicitHosts = hostValues.length > 0;
|
|
178
|
+
const runtimeOnly = args.includes("--runtime-only");
|
|
179
|
+
const managedOnly = args.includes("--managed-only");
|
|
180
|
+
|
|
181
|
+
let selected;
|
|
182
|
+
let selectionReason;
|
|
183
|
+
if (runtimeOnly) {
|
|
184
|
+
selected = new Set();
|
|
185
|
+
selectionReason = "runtime_only";
|
|
186
|
+
} else if (managedOnly) {
|
|
187
|
+
selected = new Set(managed);
|
|
188
|
+
selectionReason = "managed_only";
|
|
189
|
+
} else if (explicitHosts) {
|
|
190
|
+
selected = parseHostSet(hostValues, { detected, managed });
|
|
191
|
+
selectionReason = "explicit";
|
|
192
|
+
} else if (runtimeInstalled) {
|
|
193
|
+
selected = new Set(managed);
|
|
194
|
+
selectionReason = "existing_runtime_managed_only";
|
|
195
|
+
} else {
|
|
196
|
+
selected = new Set(detected);
|
|
197
|
+
selectionReason = "fresh_install_detected";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const excluded = parseHostSet(flagValues(args, "--exclude"), { detected, managed });
|
|
201
|
+
excluded.forEach((name) => selected.delete(name));
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
schema_version: 1,
|
|
205
|
+
runtime_installed_before: Boolean(runtimeInstalled),
|
|
206
|
+
selection_reason: selectionReason,
|
|
207
|
+
explicit_hosts: explicitHosts,
|
|
208
|
+
detected_hosts: detected,
|
|
209
|
+
managed_hosts: managed,
|
|
210
|
+
selected_hosts: HOST_NAMES.filter((name) => selected.has(name)),
|
|
211
|
+
excluded_hosts: HOST_NAMES.filter((name) => excluded.has(name)),
|
|
212
|
+
detections,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function stripUniversalInstallerArgs(args) {
|
|
217
|
+
const output = [];
|
|
218
|
+
const valueFlags = new Set(["--hosts", "--exclude"]);
|
|
219
|
+
const booleanFlags = new Set([
|
|
220
|
+
"--runtime-only",
|
|
221
|
+
"--managed-only",
|
|
222
|
+
"--no-prompt",
|
|
223
|
+
"--json",
|
|
224
|
+
]);
|
|
225
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
226
|
+
const value = args[index];
|
|
227
|
+
if (booleanFlags.has(value)) continue;
|
|
228
|
+
if (valueFlags.has(value)) {
|
|
229
|
+
index += 1;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if ([...valueFlags].some((name) => value.startsWith(`${name}=`))) continue;
|
|
233
|
+
output.push(value);
|
|
234
|
+
}
|
|
235
|
+
return output;
|
|
236
|
+
}
|
package/bin/lib/integrations.mjs
CHANGED
|
@@ -81,6 +81,24 @@ export function createIntegrationManager({ runtimeBase, packageVersion, activeVe
|
|
|
81
81
|
return registry.integrations[name];
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function recoverCorruptRegistry() {
|
|
85
|
+
const registry = readRegistry();
|
|
86
|
+
if (!registry.registry_error) {
|
|
87
|
+
return { recovered: false, backup: null };
|
|
88
|
+
}
|
|
89
|
+
const corruptBackup = quarantineCorruptRegistry(registry);
|
|
90
|
+
const recovered = emptyRegistry();
|
|
91
|
+
if (corruptBackup) {
|
|
92
|
+
recovered.recovered_corrupt_registry = path.basename(corruptBackup);
|
|
93
|
+
}
|
|
94
|
+
recovered.updated_at = new Date().toISOString();
|
|
95
|
+
writeJsonAtomic(registryPath, recovered);
|
|
96
|
+
return {
|
|
97
|
+
recovered: true,
|
|
98
|
+
backup: corruptBackup,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
84
102
|
function managed(name) {
|
|
85
103
|
const entry = readRegistry().integrations[name];
|
|
86
104
|
return entry?.managed === true ? entry : null;
|
|
@@ -140,7 +158,16 @@ export function createIntegrationManager({ runtimeBase, packageVersion, activeVe
|
|
|
140
158
|
.filter(Boolean);
|
|
141
159
|
}
|
|
142
160
|
|
|
143
|
-
return {
|
|
161
|
+
return {
|
|
162
|
+
registryPath,
|
|
163
|
+
readRegistry,
|
|
164
|
+
record,
|
|
165
|
+
recoverCorruptRegistry,
|
|
166
|
+
managed,
|
|
167
|
+
status,
|
|
168
|
+
safelyRefresh,
|
|
169
|
+
refreshAll,
|
|
170
|
+
};
|
|
144
171
|
}
|
|
145
172
|
|
|
146
173
|
export function createHostIntegrationManager({
|
|
@@ -190,6 +217,37 @@ export function createHostIntegrationManager({
|
|
|
190
217
|
}
|
|
191
218
|
}
|
|
192
219
|
|
|
220
|
+
function adoptOpenclaw() {
|
|
221
|
+
const configPath = path.join(openclawHome, "openclaw.json");
|
|
222
|
+
let config;
|
|
223
|
+
try {
|
|
224
|
+
config = readJson(configPath);
|
|
225
|
+
} catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
const walletEntry = config?.plugins?.entries?.["agent-wallet"];
|
|
229
|
+
const packageRoot = walletEntry?.config?.packageRoot;
|
|
230
|
+
const loadPaths = Array.isArray(config?.plugins?.load?.paths)
|
|
231
|
+
? config.plugins.load.paths
|
|
232
|
+
: [];
|
|
233
|
+
const ownedPackage =
|
|
234
|
+
typeof packageRoot === "string" &&
|
|
235
|
+
packageRoot.trim() &&
|
|
236
|
+
runtimeOwnedTarget(packageRoot);
|
|
237
|
+
const ownedExtension = loadPaths.some(
|
|
238
|
+
(item) =>
|
|
239
|
+
String(item || "").replaceAll("\\", "/").endsWith("/.openclaw/extensions/agent-wallet") &&
|
|
240
|
+
runtimeOwnedTarget(String(item)),
|
|
241
|
+
);
|
|
242
|
+
if (!walletEntry || (!ownedPackage && !ownedExtension)) return null;
|
|
243
|
+
return registry.record("openclaw", {
|
|
244
|
+
config_path: configPath,
|
|
245
|
+
extension_path: path.join(currentRuntimePath, ".openclaw", "extensions", "agent-wallet"),
|
|
246
|
+
package_root: path.join(currentRuntimePath, "agent-wallet"),
|
|
247
|
+
adopted_legacy_install: true,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
193
251
|
function adoptHermes() {
|
|
194
252
|
const pluginTarget = path.join(hermesHome, "plugins", "agent_wallet");
|
|
195
253
|
const target = symlinkTarget(pluginTarget);
|
|
@@ -268,7 +326,7 @@ export function createHostIntegrationManager({
|
|
|
268
326
|
}
|
|
269
327
|
|
|
270
328
|
function refreshOpenclaw() {
|
|
271
|
-
const entry = registry.managed("openclaw");
|
|
329
|
+
const entry = registry.managed("openclaw") || adoptOpenclaw();
|
|
272
330
|
if (!entry) {
|
|
273
331
|
return { name: "openclaw", attempted: false, ok: true, repaired: false, reason: "not managed" };
|
|
274
332
|
}
|
|
@@ -411,13 +469,19 @@ export function createHostIntegrationManager({
|
|
|
411
469
|
};
|
|
412
470
|
}
|
|
413
471
|
|
|
414
|
-
function refreshAll() {
|
|
415
|
-
|
|
472
|
+
function refreshAll(names = null) {
|
|
473
|
+
const selected = names ? new Set(names) : null;
|
|
474
|
+
const refreshers = {
|
|
416
475
|
openclaw: refreshOpenclaw,
|
|
417
476
|
hermes: refreshHermes,
|
|
418
477
|
codex: refreshCodex,
|
|
419
478
|
"claude-code": refreshClaudeCode,
|
|
420
|
-
}
|
|
479
|
+
};
|
|
480
|
+
return registry.refreshAll(
|
|
481
|
+
Object.fromEntries(
|
|
482
|
+
Object.entries(refreshers).filter(([name]) => !selected || selected.has(name)),
|
|
483
|
+
),
|
|
484
|
+
);
|
|
421
485
|
}
|
|
422
486
|
|
|
423
487
|
return { refreshAll };
|