clawpowers 1.1.4 → 2.0.0
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/CHANGELOG.md +94 -0
- package/LICENSE +44 -0
- package/README.md +204 -228
- package/SECURITY.md +72 -0
- package/dist/index.d.ts +844 -0
- package/dist/index.js +2536 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -44
- package/.claude-plugin/manifest.json +0 -19
- package/.codex/INSTALL.md +0 -36
- package/.cursor-plugin/manifest.json +0 -21
- package/.opencode/INSTALL.md +0 -52
- package/ARCHITECTURE.md +0 -69
- package/bin/clawpowers.js +0 -625
- package/bin/clawpowers.sh +0 -91
- package/docs/demo/clawpowers-demo.cast +0 -197
- package/docs/demo/clawpowers-demo.gif +0 -0
- package/docs/launch-images/25-skills-breakdown.jpg +0 -0
- package/docs/launch-images/clawpowers-vs-superpowers.jpg +0 -0
- package/docs/launch-images/economic-code-optimization.jpg +0 -0
- package/docs/launch-images/native-vs-bridge-2.jpg +0 -0
- package/docs/launch-images/native-vs-bridge.jpg +0 -0
- package/docs/launch-images/post1-hero-lobster.jpg +0 -0
- package/docs/launch-images/post2-dashboard.jpg +0 -0
- package/docs/launch-images/post3-superpowers.jpg +0 -0
- package/docs/launch-images/post4-before-after.jpg +0 -0
- package/docs/launch-images/post5-install-now.jpg +0 -0
- package/docs/launch-images/ultimate-stack.jpg +0 -0
- package/docs/launch-posts.md +0 -76
- package/docs/quickstart-first-transaction.md +0 -204
- package/gemini-extension.json +0 -32
- package/hooks/session-start +0 -205
- package/hooks/session-start.cmd +0 -43
- package/hooks/session-start.js +0 -163
- package/runtime/demo/README.md +0 -78
- package/runtime/demo/x402-mock-server.js +0 -230
- package/runtime/feedback/analyze.js +0 -621
- package/runtime/feedback/analyze.sh +0 -546
- package/runtime/init.js +0 -210
- package/runtime/init.sh +0 -178
- package/runtime/metrics/collector.js +0 -361
- package/runtime/metrics/collector.sh +0 -308
- package/runtime/payments/ledger.js +0 -305
- package/runtime/payments/ledger.sh +0 -262
- package/runtime/payments/pipeline.js +0 -455
- package/runtime/persistence/store.js +0 -433
- package/runtime/persistence/store.sh +0 -303
- package/skill.json +0 -106
- package/skills/agent-bounties/SKILL.md +0 -553
- package/skills/agent-payments/SKILL.md +0 -479
- package/skills/brainstorming/SKILL.md +0 -233
- package/skills/content-pipeline/SKILL.md +0 -282
- package/skills/cross-project-knowledge/SKILL.md +0 -345
- package/skills/dispatching-parallel-agents/SKILL.md +0 -305
- package/skills/economic-code-optimization/SKILL.md +0 -265
- package/skills/executing-plans/SKILL.md +0 -255
- package/skills/finishing-a-development-branch/SKILL.md +0 -260
- package/skills/formal-verification-lite/SKILL.md +0 -441
- package/skills/learn-how-to-learn/SKILL.md +0 -235
- package/skills/market-intelligence/SKILL.md +0 -323
- package/skills/meta-skill-evolution/SKILL.md +0 -325
- package/skills/prospecting/SKILL.md +0 -454
- package/skills/receiving-code-review/SKILL.md +0 -225
- package/skills/requesting-code-review/SKILL.md +0 -206
- package/skills/security-audit/SKILL.md +0 -353
- package/skills/self-healing-code/SKILL.md +0 -369
- package/skills/subagent-driven-development/SKILL.md +0 -244
- package/skills/systematic-debugging/SKILL.md +0 -355
- package/skills/test-driven-development/SKILL.md +0 -416
- package/skills/using-clawpowers/SKILL.md +0 -160
- package/skills/using-git-worktrees/SKILL.md +0 -261
- package/skills/validator/SKILL.md +0 -281
- package/skills/verification-before-completion/SKILL.md +0 -254
- package/skills/writing-plans/SKILL.md +0 -276
- package/skills/writing-skills/SKILL.md +0 -260
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// runtime/payments/pipeline.js — Unified Payment Decision Pipeline
|
|
3
|
-
//
|
|
4
|
-
// Central gate for ALL skill payment decisions. Any skill that encounters a
|
|
5
|
-
// payment boundary (HTTP 402, premium scanner, paid API, x402 endpoint) calls
|
|
6
|
-
// evaluatePayment() here instead of making its own policy judgements.
|
|
7
|
-
//
|
|
8
|
-
// Flow:
|
|
9
|
-
// estimate_cost → check_config → check_policy →
|
|
10
|
-
// (disabled | dry_run | queued | approved | rejected) → log
|
|
11
|
-
//
|
|
12
|
-
// Usage (module):
|
|
13
|
-
// const { evaluatePayment } = require('./pipeline');
|
|
14
|
-
// const result = await evaluatePayment({ skill, reason, amount_usd, asset, chain, recipient, url });
|
|
15
|
-
//
|
|
16
|
-
// Usage (CLI):
|
|
17
|
-
// node pipeline.js evaluate --amount 0.05 --skill security-audit --reason "premium scanner"
|
|
18
|
-
// npx clawpowers payments evaluate --amount 0.05 --skill security-audit --reason "premium scanner"
|
|
19
|
-
'use strict';
|
|
20
|
-
|
|
21
|
-
const fs = require('fs');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const os = require('os');
|
|
24
|
-
const { logPaymentDecision } = require('./ledger');
|
|
25
|
-
|
|
26
|
-
// ─── Config paths ──────────────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
/** Root of the ~/.clawpowers/ runtime directory. */
|
|
29
|
-
const CLAWPOWERS_DIR = process.env.CLAWPOWERS_DIR || path.join(os.homedir(), '.clawpowers');
|
|
30
|
-
|
|
31
|
-
/** Main config file path. */
|
|
32
|
-
const CONFIG_FILE = path.join(CLAWPOWERS_DIR, 'config.json');
|
|
33
|
-
|
|
34
|
-
/** Payment ledger path (JSONL — one record per decision). */
|
|
35
|
-
const PAYMENTS_LEDGER = path.join(CLAWPOWERS_DIR, 'logs', 'payments.jsonl');
|
|
36
|
-
|
|
37
|
-
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Load and parse ~/.clawpowers/config.json.
|
|
41
|
-
* Returns an empty object if the file doesn't exist.
|
|
42
|
-
*
|
|
43
|
-
* @returns {object} Parsed config or {}.
|
|
44
|
-
*/
|
|
45
|
-
function loadConfig() {
|
|
46
|
-
try {
|
|
47
|
-
if (!fs.existsSync(CONFIG_FILE)) return {};
|
|
48
|
-
const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
49
|
-
return JSON.parse(raw);
|
|
50
|
-
} catch (err) {
|
|
51
|
-
process.stderr.write(`[pipeline] Warning: could not read config: ${err.message}\n`);
|
|
52
|
-
return {};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Compute today's total spend (USD) from the payments ledger.
|
|
58
|
-
* Only counts entries with action "approved" or "executed" and today's date.
|
|
59
|
-
*
|
|
60
|
-
* @returns {number} Total USD spent today across all skills and chains.
|
|
61
|
-
*/
|
|
62
|
-
function getTodaySpend() {
|
|
63
|
-
try {
|
|
64
|
-
if (!fs.existsSync(PAYMENTS_LEDGER)) return 0;
|
|
65
|
-
const today = new Date().toISOString().slice(0, 10); // "YYYY-MM-DD"
|
|
66
|
-
const lines = fs.readFileSync(PAYMENTS_LEDGER, 'utf8').split('\n');
|
|
67
|
-
let total = 0;
|
|
68
|
-
for (const line of lines) {
|
|
69
|
-
if (!line.trim()) continue;
|
|
70
|
-
try {
|
|
71
|
-
const r = JSON.parse(line);
|
|
72
|
-
if (
|
|
73
|
-
r.timestamp &&
|
|
74
|
-
r.timestamp.startsWith(today) &&
|
|
75
|
-
(r.policy_result === 'approved' || r.policy_result === 'executed')
|
|
76
|
-
) {
|
|
77
|
-
total += parseFloat(r.required_amount) || 0;
|
|
78
|
-
}
|
|
79
|
-
} catch (_) { /* skip malformed */ }
|
|
80
|
-
}
|
|
81
|
-
return total;
|
|
82
|
-
} catch {
|
|
83
|
-
return 0;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Check if a recipient address appears in the configured allowlist.
|
|
89
|
-
* An empty allowlist means NO recipients are automatically allowed.
|
|
90
|
-
*
|
|
91
|
-
* @param {string} recipient - Recipient address (0x-prefixed).
|
|
92
|
-
* @param {string[]} allowlist - Array of allowed addresses (case-insensitive).
|
|
93
|
-
* @returns {boolean} True if allowed or allowlist is not configured.
|
|
94
|
-
*/
|
|
95
|
-
function isAllowlisted(recipient, allowlist) {
|
|
96
|
-
// If allowlist is undefined/null → not configured → no automatic allowance
|
|
97
|
-
if (!Array.isArray(allowlist)) return false;
|
|
98
|
-
// Empty allowlist → nothing allowed
|
|
99
|
-
if (allowlist.length === 0) return false;
|
|
100
|
-
const lower = (recipient || '').toLowerCase();
|
|
101
|
-
return allowlist.some((a) => (a || '').toLowerCase() === lower);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Returns an ISO 8601 timestamp without milliseconds.
|
|
106
|
-
*/
|
|
107
|
-
|
|
108
|
-
// ─── Core pipeline ─────────────────────────────────────────────────────────
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Payment Decision Pipeline — evaluate whether a payment should be made.
|
|
112
|
-
*
|
|
113
|
-
* Called by any skill when it hits a payment boundary. Reads config, checks
|
|
114
|
-
* policy limits, and returns a decision with a reason. Always logs the decision
|
|
115
|
-
* to the payments ledger regardless of outcome.
|
|
116
|
-
*
|
|
117
|
-
* ### Decision flow
|
|
118
|
-
*
|
|
119
|
-
* ```
|
|
120
|
-
* payments.enabled === false → "disabled"
|
|
121
|
-
* payments.mode === "dry_run" → "dry_run"
|
|
122
|
-
* amount > per_tx_limit → "rejected" (hard limit)
|
|
123
|
-
* today_spend + amount > daily → "rejected" (daily limit)
|
|
124
|
-
* recipient not in allowlist → "queued" (needs approval)
|
|
125
|
-
* amount > require_approval_above → "queued" (needs approval)
|
|
126
|
-
* all checks pass → "approved"
|
|
127
|
-
* ```
|
|
128
|
-
*
|
|
129
|
-
* @param {object} decision - Payment details.
|
|
130
|
-
* @param {string} decision.skill - ClawPowers skill triggering the payment.
|
|
131
|
-
* @param {string} decision.reason - Human-readable reason ("HTTP 402", "premium scanner", …).
|
|
132
|
-
* @param {number} decision.amount_usd - Estimated cost in USD (decimal).
|
|
133
|
-
* @param {string} [decision.asset] - Token/asset symbol (default "USDC").
|
|
134
|
-
* @param {string} [decision.chain] - Chain name (default "base").
|
|
135
|
-
* @param {string} [decision.recipient] - Payment recipient address.
|
|
136
|
-
* @param {string} [decision.url] - URL or service that triggered the boundary.
|
|
137
|
-
*
|
|
138
|
-
* @returns {{
|
|
139
|
-
* action: "disabled"|"dry_run"|"queued"|"approved"|"rejected",
|
|
140
|
-
* reason: string,
|
|
141
|
-
* logged: boolean,
|
|
142
|
-
* amount_usd: number,
|
|
143
|
-
* config_used: object
|
|
144
|
-
* }} Decision result.
|
|
145
|
-
*
|
|
146
|
-
* @example
|
|
147
|
-
* ```javascript
|
|
148
|
-
* const { evaluatePayment } = require('./pipeline');
|
|
149
|
-
* const result = await evaluatePayment({
|
|
150
|
-
* skill: 'security-audit',
|
|
151
|
-
* reason: 'premium scanner API',
|
|
152
|
-
* amount_usd: 0.05,
|
|
153
|
-
* asset: 'USDC',
|
|
154
|
-
* chain: 'base',
|
|
155
|
-
* recipient: '0xSCANNER_RECIPIENT',
|
|
156
|
-
* });
|
|
157
|
-
* if (result.action === 'approved') {
|
|
158
|
-
* // proceed with payment
|
|
159
|
-
* }
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
function evaluatePayment(decision) {
|
|
163
|
-
const {
|
|
164
|
-
skill = 'unknown',
|
|
165
|
-
reason = '',
|
|
166
|
-
amount_usd = 0,
|
|
167
|
-
asset = 'USDC',
|
|
168
|
-
chain = 'base',
|
|
169
|
-
recipient = '',
|
|
170
|
-
url = '',
|
|
171
|
-
} = decision;
|
|
172
|
-
|
|
173
|
-
// ── 1. Load config ────────────────────────────────────────────────────────
|
|
174
|
-
const config = loadConfig();
|
|
175
|
-
const payments = config.payments || {};
|
|
176
|
-
|
|
177
|
-
// ── 2. payments.enabled === false → disabled ──────────────────────────────
|
|
178
|
-
if (payments.enabled === false) {
|
|
179
|
-
const result = {
|
|
180
|
-
action: 'disabled',
|
|
181
|
-
reason: 'Payments disabled in ~/.clawpowers/config.json (payments.enabled = false)',
|
|
182
|
-
logged: false,
|
|
183
|
-
amount_usd,
|
|
184
|
-
config_used: payments,
|
|
185
|
-
};
|
|
186
|
-
result.logged = _logDecision(skill, url, String(amount_usd), asset, chain, 'disabled', result.reason, false);
|
|
187
|
-
return result;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// ── 3. payments.mode === "dry_run" → log what would happen, no payment ────
|
|
191
|
-
if (payments.mode === 'dry_run') {
|
|
192
|
-
const summary =
|
|
193
|
-
`DRY-RUN: skill=${skill} reason="${reason}" would pay ` +
|
|
194
|
-
`$${amount_usd.toFixed ? amount_usd.toFixed(4) : amount_usd} ${asset} on ${chain}` +
|
|
195
|
-
(url ? ` for ${url}` : '');
|
|
196
|
-
console.log(`[pipeline] ${summary}`);
|
|
197
|
-
|
|
198
|
-
const result = {
|
|
199
|
-
action: 'dry_run',
|
|
200
|
-
reason: summary,
|
|
201
|
-
logged: false,
|
|
202
|
-
amount_usd,
|
|
203
|
-
config_used: payments,
|
|
204
|
-
};
|
|
205
|
-
result.logged = _logDecision(skill, url, String(amount_usd), asset, chain, 'dry_run', reason, true);
|
|
206
|
-
return result;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// ── 4. Check per-transaction limit ────────────────────────────────────────
|
|
210
|
-
const perTxLimit = typeof payments.per_tx_limit === 'number' ? payments.per_tx_limit : Infinity;
|
|
211
|
-
if (amount_usd > perTxLimit) {
|
|
212
|
-
const msg = `Amount $${amount_usd} exceeds per_tx_limit $${perTxLimit}`;
|
|
213
|
-
const result = {
|
|
214
|
-
action: 'rejected',
|
|
215
|
-
reason: msg,
|
|
216
|
-
logged: false,
|
|
217
|
-
amount_usd,
|
|
218
|
-
config_used: payments,
|
|
219
|
-
};
|
|
220
|
-
result.logged = _logDecision(skill, url, String(amount_usd), asset, chain, 'rejected', msg, false);
|
|
221
|
-
return result;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// ── 5. Check daily limit ─────────────────────────────────────────────────
|
|
225
|
-
const dailyLimit = typeof payments.daily_limit === 'number' ? payments.daily_limit : Infinity;
|
|
226
|
-
const todaySpend = getTodaySpend();
|
|
227
|
-
if (todaySpend + amount_usd > dailyLimit) {
|
|
228
|
-
const msg =
|
|
229
|
-
`Daily limit would be exceeded: today=$${todaySpend.toFixed(4)}, ` +
|
|
230
|
-
`new=$${amount_usd}, limit=$${dailyLimit}`;
|
|
231
|
-
const result = {
|
|
232
|
-
action: 'rejected',
|
|
233
|
-
reason: msg,
|
|
234
|
-
logged: false,
|
|
235
|
-
amount_usd,
|
|
236
|
-
config_used: payments,
|
|
237
|
-
};
|
|
238
|
-
result.logged = _logDecision(skill, url, String(amount_usd), asset, chain, 'rejected', msg, false);
|
|
239
|
-
return result;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ── 6. Check recipient allowlist ─────────────────────────────────────────
|
|
243
|
-
const allowlist = payments.allowlist; // undefined = not configured
|
|
244
|
-
if (recipient && !isAllowlisted(recipient, allowlist)) {
|
|
245
|
-
// Not in allowlist — queue for approval unless allowlist is intentionally absent
|
|
246
|
-
// (If allowlist key doesn't exist at all, we fall through to approval-above check)
|
|
247
|
-
if (Array.isArray(allowlist)) {
|
|
248
|
-
const msg = `Recipient ${recipient} is not in payments.allowlist`;
|
|
249
|
-
const result = {
|
|
250
|
-
action: 'queued',
|
|
251
|
-
reason: msg,
|
|
252
|
-
logged: false,
|
|
253
|
-
amount_usd,
|
|
254
|
-
config_used: payments,
|
|
255
|
-
};
|
|
256
|
-
result.logged = _logDecision(skill, url, String(amount_usd), asset, chain, 'queued', msg, false);
|
|
257
|
-
return result;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ── 7. Check require_approval_above threshold ─────────────────────────────
|
|
262
|
-
const approvalThreshold = typeof payments.require_approval_above === 'number'
|
|
263
|
-
? payments.require_approval_above
|
|
264
|
-
: Infinity;
|
|
265
|
-
if (amount_usd > approvalThreshold) {
|
|
266
|
-
const msg =
|
|
267
|
-
`Amount $${amount_usd} exceeds require_approval_above threshold $${approvalThreshold}`;
|
|
268
|
-
const result = {
|
|
269
|
-
action: 'queued',
|
|
270
|
-
reason: msg,
|
|
271
|
-
logged: false,
|
|
272
|
-
amount_usd,
|
|
273
|
-
config_used: payments,
|
|
274
|
-
};
|
|
275
|
-
result.logged = _logDecision(skill, url, String(amount_usd), asset, chain, 'queued', msg, false);
|
|
276
|
-
return result;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// ── 8. All checks passed → approved ───────────────────────────────────────
|
|
280
|
-
const approveMsg = `Payment approved: $${amount_usd} ${asset} on ${chain} for ${skill} (${reason})`;
|
|
281
|
-
const result = {
|
|
282
|
-
action: 'approved',
|
|
283
|
-
reason: approveMsg,
|
|
284
|
-
logged: false,
|
|
285
|
-
amount_usd,
|
|
286
|
-
config_used: payments,
|
|
287
|
-
};
|
|
288
|
-
result.logged = _logDecision(skill, url, String(amount_usd), asset, chain, 'approved', reason, true);
|
|
289
|
-
return result;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Internal: write decision to the payments ledger via the ledger module.
|
|
294
|
-
*
|
|
295
|
-
* @param {string} skill
|
|
296
|
-
* @param {string} url
|
|
297
|
-
* @param {string} required_amount
|
|
298
|
-
* @param {string} asset
|
|
299
|
-
* @param {string} chain
|
|
300
|
-
* @param {string} policy_result
|
|
301
|
-
* @param {string} reason
|
|
302
|
-
* @param {boolean} would_have_paid
|
|
303
|
-
* @returns {boolean} Always true (indicates logging was attempted).
|
|
304
|
-
*/
|
|
305
|
-
function _logDecision(skill, url, required_amount, asset, chain, policy_result, reason, would_have_paid) {
|
|
306
|
-
try {
|
|
307
|
-
logPaymentDecision({
|
|
308
|
-
skill,
|
|
309
|
-
type: 'decision',
|
|
310
|
-
url,
|
|
311
|
-
required_amount,
|
|
312
|
-
asset,
|
|
313
|
-
chain,
|
|
314
|
-
policy_result,
|
|
315
|
-
reason,
|
|
316
|
-
would_have_paid,
|
|
317
|
-
});
|
|
318
|
-
return true;
|
|
319
|
-
} catch (err) {
|
|
320
|
-
process.stderr.write(`[pipeline] Warning: could not write to ledger: ${err.message}\n`);
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// ─── CLI ───────────────────────────────────────────────────────────────────
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Print CLI usage to stdout.
|
|
329
|
-
*/
|
|
330
|
-
function printUsage() {
|
|
331
|
-
console.log(`Usage: clawpowers payments evaluate [options]
|
|
332
|
-
|
|
333
|
-
Options:
|
|
334
|
-
--amount <n> Payment amount in USD (required, decimal, e.g. 0.05)
|
|
335
|
-
--skill <name> Skill that triggered the payment (default: "cli")
|
|
336
|
-
--reason <text> Reason for the payment (default: "cli test")
|
|
337
|
-
--asset <symbol> Token symbol (default: USDC)
|
|
338
|
-
--chain <name> Chain name (default: base)
|
|
339
|
-
--recipient <addr> Recipient wallet address (optional)
|
|
340
|
-
--url <url> URL or service requiring payment (optional)
|
|
341
|
-
--json Output raw JSON result
|
|
342
|
-
|
|
343
|
-
Examples:
|
|
344
|
-
node pipeline.js evaluate --amount 0.05 --skill security-audit --reason "premium scanner"
|
|
345
|
-
node pipeline.js evaluate --amount 0.01 --skill prospecting --reason "contact enrichment" --json
|
|
346
|
-
npx clawpowers payments evaluate --amount 0.05 --skill agent-payments --reason "x402 API"
|
|
347
|
-
|
|
348
|
-
Config file: ~/.clawpowers/config.json
|
|
349
|
-
Ledger file: ~/.clawpowers/logs/payments.jsonl`);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* CLI handler for the `evaluate` sub-command.
|
|
354
|
-
* Parses --flags and calls evaluatePayment(), printing the result.
|
|
355
|
-
*
|
|
356
|
-
* @param {string[]} argv - Arguments after "evaluate".
|
|
357
|
-
*/
|
|
358
|
-
function cmdEvaluate(argv) {
|
|
359
|
-
const args = {};
|
|
360
|
-
for (let i = 0; i < argv.length; i++) {
|
|
361
|
-
const flag = argv[i];
|
|
362
|
-
const next = argv[i + 1];
|
|
363
|
-
switch (flag) {
|
|
364
|
-
case '--amount': args.amount_usd = parseFloat(next); i++; break;
|
|
365
|
-
case '--skill': args.skill = next; i++; break;
|
|
366
|
-
case '--reason': args.reason = next; i++; break;
|
|
367
|
-
case '--asset': args.asset = next; i++; break;
|
|
368
|
-
case '--chain': args.chain = next; i++; break;
|
|
369
|
-
case '--recipient': args.recipient = next; i++; break;
|
|
370
|
-
case '--url': args.url = next; i++; break;
|
|
371
|
-
case '--json': args._json = true; break;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (args.amount_usd === undefined || isNaN(args.amount_usd)) {
|
|
376
|
-
process.stderr.write('Error: --amount is required and must be a number\n\n');
|
|
377
|
-
printUsage();
|
|
378
|
-
process.exit(1);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const decision = {
|
|
382
|
-
skill: args.skill || 'cli',
|
|
383
|
-
reason: args.reason || 'cli test',
|
|
384
|
-
amount_usd: args.amount_usd,
|
|
385
|
-
asset: args.asset || 'USDC',
|
|
386
|
-
chain: args.chain || 'base',
|
|
387
|
-
recipient: args.recipient || '',
|
|
388
|
-
url: args.url || '',
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
const result = evaluatePayment(decision);
|
|
392
|
-
|
|
393
|
-
if (args._json) {
|
|
394
|
-
console.log(JSON.stringify(result, null, 2));
|
|
395
|
-
} else {
|
|
396
|
-
console.log('');
|
|
397
|
-
console.log(`Payment Decision Pipeline`);
|
|
398
|
-
console.log(`─────────────────────────`);
|
|
399
|
-
console.log(` Skill: ${decision.skill}`);
|
|
400
|
-
console.log(` Reason: ${decision.reason}`);
|
|
401
|
-
console.log(` Amount: $${decision.amount_usd} ${decision.asset} on ${decision.chain}`);
|
|
402
|
-
if (decision.url) console.log(` URL: ${decision.url}`);
|
|
403
|
-
if (decision.recipient) console.log(` To: ${decision.recipient}`);
|
|
404
|
-
console.log('');
|
|
405
|
-
console.log(` ► Action: ${result.action.toUpperCase()}`);
|
|
406
|
-
console.log(` ► Reason: ${result.reason}`);
|
|
407
|
-
console.log(` ► Logged: ${result.logged ? 'yes' : 'no'}`);
|
|
408
|
-
console.log('');
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Non-zero exit for rejected/disabled so scripts can branch on exit code
|
|
412
|
-
if (result.action === 'rejected') process.exit(2);
|
|
413
|
-
if (result.action === 'disabled') process.exit(3);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Main CLI dispatch.
|
|
418
|
-
* @param {string[]} argv
|
|
419
|
-
*/
|
|
420
|
-
function main(argv) {
|
|
421
|
-
const [cmd, ...rest] = argv;
|
|
422
|
-
|
|
423
|
-
switch (cmd) {
|
|
424
|
-
case 'evaluate':
|
|
425
|
-
case 'eval':
|
|
426
|
-
cmdEvaluate(rest);
|
|
427
|
-
break;
|
|
428
|
-
case 'help':
|
|
429
|
-
case '-h':
|
|
430
|
-
case '--help':
|
|
431
|
-
printUsage();
|
|
432
|
-
break;
|
|
433
|
-
case undefined:
|
|
434
|
-
case '':
|
|
435
|
-
printUsage();
|
|
436
|
-
process.exit(1);
|
|
437
|
-
break;
|
|
438
|
-
default:
|
|
439
|
-
process.stderr.write(`Unknown command: ${cmd}\n`);
|
|
440
|
-
printUsage();
|
|
441
|
-
process.exit(1);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Guard: only run CLI dispatch when invoked directly
|
|
446
|
-
if (require.main === module) {
|
|
447
|
-
try {
|
|
448
|
-
main(process.argv.slice(2));
|
|
449
|
-
} catch (err) {
|
|
450
|
-
process.stderr.write(`Error: ${err.message}\n`);
|
|
451
|
-
process.exit(1);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
module.exports = { evaluatePayment };
|