clawmoney 0.15.39 → 0.15.41
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/dist/commands/wallet.js +17 -9
- package/dist/promote/auto-verify.js +4 -2
- package/dist/utils/awal.d.ts +26 -0
- package/dist/utils/awal.js +64 -0
- package/package.json +1 -1
package/dist/commands/wallet.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import { awalExec } from '../utils/awal.js';
|
|
3
|
+
import { awalExec, awalExecSafe } from '../utils/awal.js';
|
|
4
4
|
import { apiGet } from '../utils/api.js';
|
|
5
5
|
import { loadConfig, saveConfig } from '../utils/config.js';
|
|
6
6
|
// Base mainnet USDC contract + balanceOf(address) ABI selector.
|
|
@@ -43,7 +43,8 @@ async function readBaseUsdcBalance(walletAddress, timeoutMs = 8000) {
|
|
|
43
43
|
export async function walletStatusCommand() {
|
|
44
44
|
const spinner = ora('Getting wallet status...').start();
|
|
45
45
|
try {
|
|
46
|
-
|
|
46
|
+
// Read-only, safe to auto-retry after killing a wedged awal.
|
|
47
|
+
const result = await awalExecSafe(['status'], { timeoutMs: 8_000 });
|
|
47
48
|
spinner.succeed('Wallet Status');
|
|
48
49
|
console.log('');
|
|
49
50
|
const data = result.data;
|
|
@@ -117,10 +118,11 @@ export async function walletBalanceCommand() {
|
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
if (!walletAddress) {
|
|
120
|
-
// Last resort: cold-start awal.
|
|
121
|
-
//
|
|
121
|
+
// Last resort: cold-start awal. Uses awalExecSafe so a wedged
|
|
122
|
+
// Electron is automatically killed + retried before surfacing
|
|
123
|
+
// the error. Capped at 6s per attempt.
|
|
122
124
|
try {
|
|
123
|
-
const awalResult = await
|
|
125
|
+
const awalResult = await awalExecSafe(['address'], { timeoutMs: 6_000 });
|
|
124
126
|
const data = awalResult.data;
|
|
125
127
|
if (typeof data?.address === 'string' && data.address) {
|
|
126
128
|
walletAddress = data.address;
|
|
@@ -166,14 +168,19 @@ export async function walletBalanceCommand() {
|
|
|
166
168
|
console.log(` ${chalk.yellow('unavailable')} ${chalk.dim('(' + (onchainError ?? 'unknown') + ')')}`);
|
|
167
169
|
}
|
|
168
170
|
console.log('');
|
|
169
|
-
console.log(chalk.bold(' Relay
|
|
171
|
+
console.log(chalk.bold(' Pending payout (Relay)'));
|
|
170
172
|
if (relayRows && relayRows.length > 0) {
|
|
173
|
+
// "Earned lifetime" is vanity — the only thing that matters for
|
|
174
|
+
// the wallet view is: how much is already in the on-chain wallet
|
|
175
|
+
// (shown above as USDC), and how much is still owed to the user
|
|
176
|
+
// (pending = earned - withdrawn). Those two numbers together
|
|
177
|
+
// tell the full story; showing "earned" separately would double-
|
|
178
|
+
// count the wallet USDC that came from relay payouts.
|
|
171
179
|
const earned = relayRows.reduce((s, p) => s + (p.total_earned_usd ?? 0), 0);
|
|
172
180
|
const withdrawn = relayRows.reduce((s, p) => s + (p.total_withdrawn_usd ?? 0), 0);
|
|
173
181
|
const pending = Math.max(0, earned - withdrawn);
|
|
174
182
|
const requests = relayRows.reduce((s, p) => s + (p.total_requests ?? 0), 0);
|
|
175
|
-
console.log(` ${chalk.dim('
|
|
176
|
-
console.log(` ${chalk.dim('Pending payout:').padEnd(22)} ${chalk.green('$' + pending.toFixed(2))}`);
|
|
183
|
+
console.log(` ${chalk.dim('Amount:').padEnd(22)} ${chalk.green('$' + pending.toFixed(2))}`);
|
|
177
184
|
console.log(chalk.dim(` (${relayRows.length} provider${relayRows.length === 1 ? "" : "s"} · ${requests} request${requests === 1 ? "" : "s"} served)`));
|
|
178
185
|
}
|
|
179
186
|
else if (relayRows && relayRows.length === 0) {
|
|
@@ -187,7 +194,8 @@ export async function walletBalanceCommand() {
|
|
|
187
194
|
export async function walletAddressCommand() {
|
|
188
195
|
const spinner = ora('Getting wallet address...').start();
|
|
189
196
|
try {
|
|
190
|
-
|
|
197
|
+
// Read-only, safe to auto-retry on awal wedge.
|
|
198
|
+
const result = await awalExecSafe(['address'], { timeoutMs: 8_000 });
|
|
191
199
|
spinner.succeed('Wallet Address');
|
|
192
200
|
console.log('');
|
|
193
201
|
const data = result.data;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { apiGet, apiPost } from "../utils/api.js";
|
|
2
|
-
import { awalExec } from "../utils/awal.js";
|
|
2
|
+
import { awalExec, awalExecSafe } from "../utils/awal.js";
|
|
3
3
|
import { requireConfig } from "../utils/config.js";
|
|
4
4
|
const POLL_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
|
|
5
5
|
const MAX_PER_CYCLE = 3;
|
|
@@ -11,7 +11,9 @@ function log(msg) {
|
|
|
11
11
|
}
|
|
12
12
|
async function getUsdcBalance() {
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
// Read-only balance check inside a long-running daemon loop —
|
|
15
|
+
// must auto-recover if awal wedges during the day.
|
|
16
|
+
const result = await awalExecSafe(["balance"], { timeoutMs: 10_000 });
|
|
15
17
|
const base = result.data.base;
|
|
16
18
|
const balances = base?.balances;
|
|
17
19
|
const usdc = balances?.USDC;
|
package/dist/utils/awal.d.ts
CHANGED
|
@@ -17,3 +17,29 @@ export declare function awalExecInteractive(args: string[]): Promise<string>;
|
|
|
17
17
|
* Check if awal is installed and available.
|
|
18
18
|
*/
|
|
19
19
|
export declare function isAwalAvailable(): Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Kill a wedged awal server process using the pattern documented in
|
|
22
|
+
* clawmoney-skill's SKILL.md. Reads the pid from `awal status --json`
|
|
23
|
+
* and SIGKILLs it. Safe to call even when awal isn't running (the
|
|
24
|
+
* inner command fails, kill -9 gets nothing, we swallow both).
|
|
25
|
+
*
|
|
26
|
+
* awal's Electron "Payments MCP" wrapper occasionally wedges on
|
|
27
|
+
* macOS (GPU render hang, stdin/stdout pipe full, or upstream
|
|
28
|
+
* Coinbase MCP endpoint unreachable). A hard kill lets the next
|
|
29
|
+
* `awal status` cold-start a fresh process.
|
|
30
|
+
*/
|
|
31
|
+
export declare function killAwal(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Execute an awal command with timeout + one automatic retry on
|
|
34
|
+
* failure. On the first failure we kill any wedged awal process
|
|
35
|
+
* (using the SKILL-documented kill pattern) and re-run.
|
|
36
|
+
*
|
|
37
|
+
* Safe ONLY for READ operations (status, address, balance, etc).
|
|
38
|
+
* Do NOT use for writes (send, x402 pay, auth verify) — those
|
|
39
|
+
* either cost money twice on retry, or consume a single-use OTP
|
|
40
|
+
* and fail the second time. For writes, use awalExec directly
|
|
41
|
+
* and let the failure surface to the user.
|
|
42
|
+
*/
|
|
43
|
+
export declare function awalExecSafe(args: string[], opts?: {
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
}): Promise<AwalResult>;
|
package/dist/utils/awal.js
CHANGED
|
@@ -98,3 +98,67 @@ export async function isAwalAvailable() {
|
|
|
98
98
|
return false;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Kill a wedged awal server process using the pattern documented in
|
|
103
|
+
* clawmoney-skill's SKILL.md. Reads the pid from `awal status --json`
|
|
104
|
+
* and SIGKILLs it. Safe to call even when awal isn't running (the
|
|
105
|
+
* inner command fails, kill -9 gets nothing, we swallow both).
|
|
106
|
+
*
|
|
107
|
+
* awal's Electron "Payments MCP" wrapper occasionally wedges on
|
|
108
|
+
* macOS (GPU render hang, stdin/stdout pipe full, or upstream
|
|
109
|
+
* Coinbase MCP endpoint unreachable). A hard kill lets the next
|
|
110
|
+
* `awal status` cold-start a fresh process.
|
|
111
|
+
*/
|
|
112
|
+
export async function killAwal() {
|
|
113
|
+
await new Promise((resolve) => {
|
|
114
|
+
const child = spawn('sh', [
|
|
115
|
+
'-c',
|
|
116
|
+
'kill -9 $(npx awal status --json 2>/dev/null | grep -o \'"pid":[0-9]*\' | grep -o \'[0-9]*\') 2>/dev/null',
|
|
117
|
+
], { stdio: 'ignore', shell: false });
|
|
118
|
+
child.on('exit', () => resolve());
|
|
119
|
+
child.on('error', () => resolve());
|
|
120
|
+
});
|
|
121
|
+
// Give the OS a moment to reclaim the process + named pipes.
|
|
122
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Execute an awal command with timeout + one automatic retry on
|
|
126
|
+
* failure. On the first failure we kill any wedged awal process
|
|
127
|
+
* (using the SKILL-documented kill pattern) and re-run.
|
|
128
|
+
*
|
|
129
|
+
* Safe ONLY for READ operations (status, address, balance, etc).
|
|
130
|
+
* Do NOT use for writes (send, x402 pay, auth verify) — those
|
|
131
|
+
* either cost money twice on retry, or consume a single-use OTP
|
|
132
|
+
* and fail the second time. For writes, use awalExec directly
|
|
133
|
+
* and let the failure surface to the user.
|
|
134
|
+
*/
|
|
135
|
+
export async function awalExecSafe(args, opts = {}) {
|
|
136
|
+
const timeoutMs = opts.timeoutMs ?? 10_000;
|
|
137
|
+
const runWithTimeout = () => new Promise((resolve, reject) => {
|
|
138
|
+
const timer = setTimeout(() => {
|
|
139
|
+
reject(new Error(`awal ${args.join(' ')} timed out after ${timeoutMs}ms`));
|
|
140
|
+
}, timeoutMs);
|
|
141
|
+
awalExec(args).then((v) => {
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
resolve(v);
|
|
144
|
+
}, (e) => {
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
reject(e);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
try {
|
|
150
|
+
return await runWithTimeout();
|
|
151
|
+
}
|
|
152
|
+
catch (firstErr) {
|
|
153
|
+
// First attempt failed — kill any wedged server and retry once.
|
|
154
|
+
await killAwal();
|
|
155
|
+
try {
|
|
156
|
+
return await runWithTimeout();
|
|
157
|
+
}
|
|
158
|
+
catch (secondErr) {
|
|
159
|
+
// Preserve the first error too — it's usually the more
|
|
160
|
+
// informative one (the retry typically just times out again).
|
|
161
|
+
throw new Error(`awal ${args.join(' ')} failed after retry: ${secondErr.message} (initial: ${firstErr.message})`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|