clawpowers 1.1.4 → 2.2.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.
Files changed (131) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/COMPATIBILITY.md +13 -0
  3. package/KNOWN_LIMITATIONS.md +19 -0
  4. package/LICENSE +44 -0
  5. package/LICENSING.md +10 -0
  6. package/README.md +378 -210
  7. package/SECURITY.md +52 -0
  8. package/dist/index.d.ts +1477 -0
  9. package/dist/index.js +3464 -0
  10. package/dist/index.js.map +1 -0
  11. package/native/Cargo.lock +4863 -0
  12. package/native/Cargo.toml +73 -0
  13. package/native/crates/canonical/Cargo.toml +24 -0
  14. package/native/crates/canonical/src/lib.rs +673 -0
  15. package/native/crates/compression/Cargo.toml +20 -0
  16. package/native/crates/compression/benches/compression_bench.rs +42 -0
  17. package/native/crates/compression/src/lib.rs +393 -0
  18. package/native/crates/evm-eth/Cargo.toml +13 -0
  19. package/native/crates/evm-eth/src/lib.rs +105 -0
  20. package/native/crates/fee/Cargo.toml +15 -0
  21. package/native/crates/fee/src/lib.rs +281 -0
  22. package/native/crates/index/Cargo.toml +16 -0
  23. package/native/crates/index/src/lib.rs +277 -0
  24. package/native/crates/policy/Cargo.toml +17 -0
  25. package/native/crates/policy/src/lib.rs +614 -0
  26. package/native/crates/security/Cargo.toml +22 -0
  27. package/native/crates/security/src/lib.rs +478 -0
  28. package/native/crates/tokens/Cargo.toml +13 -0
  29. package/native/crates/tokens/src/lib.rs +534 -0
  30. package/native/crates/verification/Cargo.toml +23 -0
  31. package/native/crates/verification/src/lib.rs +333 -0
  32. package/native/crates/wallet/Cargo.toml +20 -0
  33. package/native/crates/wallet/src/lib.rs +261 -0
  34. package/native/crates/x402/Cargo.toml +30 -0
  35. package/native/crates/x402/src/lib.rs +423 -0
  36. package/native/ffi/Cargo.toml +34 -0
  37. package/native/ffi/build.rs +4 -0
  38. package/native/ffi/index.node +0 -0
  39. package/native/ffi/src/lib.rs +352 -0
  40. package/native/ffi/tests/integration.rs +354 -0
  41. package/native/pyo3/Cargo.toml +26 -0
  42. package/native/pyo3/pyproject.toml +16 -0
  43. package/native/pyo3/src/lib.rs +407 -0
  44. package/native/pyo3/tests/test_smoke.py +180 -0
  45. package/native/wasm/Cargo.toml +44 -0
  46. package/native/wasm/pkg/.gitignore +6 -0
  47. package/native/wasm/pkg/clawpowers_wasm.d.ts +208 -0
  48. package/native/wasm/pkg/clawpowers_wasm.js +872 -0
  49. package/native/wasm/pkg/clawpowers_wasm_bg.wasm +0 -0
  50. package/native/wasm/pkg/clawpowers_wasm_bg.wasm.d.ts +40 -0
  51. package/native/wasm/pkg/package.json +17 -0
  52. package/native/wasm/pkg-node/.gitignore +6 -0
  53. package/native/wasm/pkg-node/clawpowers_wasm.d.ts +143 -0
  54. package/native/wasm/pkg-node/clawpowers_wasm.js +798 -0
  55. package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm +0 -0
  56. package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm.d.ts +40 -0
  57. package/native/wasm/pkg-node/package.json +13 -0
  58. package/native/wasm/src/lib.rs +433 -0
  59. package/package.json +71 -44
  60. package/src/skills/catalog.ts +435 -0
  61. package/src/skills/executor.ts +56 -0
  62. package/src/skills/index.ts +3 -0
  63. package/src/skills/itp/SKILL.md +112 -0
  64. package/src/skills/loader.ts +193 -0
  65. package/.claude-plugin/manifest.json +0 -19
  66. package/.codex/INSTALL.md +0 -36
  67. package/.cursor-plugin/manifest.json +0 -21
  68. package/.opencode/INSTALL.md +0 -52
  69. package/ARCHITECTURE.md +0 -69
  70. package/bin/clawpowers.js +0 -625
  71. package/bin/clawpowers.sh +0 -91
  72. package/docs/demo/clawpowers-demo.cast +0 -197
  73. package/docs/demo/clawpowers-demo.gif +0 -0
  74. package/docs/launch-images/25-skills-breakdown.jpg +0 -0
  75. package/docs/launch-images/clawpowers-vs-superpowers.jpg +0 -0
  76. package/docs/launch-images/economic-code-optimization.jpg +0 -0
  77. package/docs/launch-images/native-vs-bridge-2.jpg +0 -0
  78. package/docs/launch-images/native-vs-bridge.jpg +0 -0
  79. package/docs/launch-images/post1-hero-lobster.jpg +0 -0
  80. package/docs/launch-images/post2-dashboard.jpg +0 -0
  81. package/docs/launch-images/post3-superpowers.jpg +0 -0
  82. package/docs/launch-images/post4-before-after.jpg +0 -0
  83. package/docs/launch-images/post5-install-now.jpg +0 -0
  84. package/docs/launch-images/ultimate-stack.jpg +0 -0
  85. package/docs/launch-posts.md +0 -76
  86. package/docs/quickstart-first-transaction.md +0 -204
  87. package/gemini-extension.json +0 -32
  88. package/hooks/session-start +0 -205
  89. package/hooks/session-start.cmd +0 -43
  90. package/hooks/session-start.js +0 -163
  91. package/runtime/demo/README.md +0 -78
  92. package/runtime/demo/x402-mock-server.js +0 -230
  93. package/runtime/feedback/analyze.js +0 -621
  94. package/runtime/feedback/analyze.sh +0 -546
  95. package/runtime/init.js +0 -210
  96. package/runtime/init.sh +0 -178
  97. package/runtime/metrics/collector.js +0 -361
  98. package/runtime/metrics/collector.sh +0 -308
  99. package/runtime/payments/ledger.js +0 -305
  100. package/runtime/payments/ledger.sh +0 -262
  101. package/runtime/payments/pipeline.js +0 -455
  102. package/runtime/persistence/store.js +0 -433
  103. package/runtime/persistence/store.sh +0 -303
  104. package/skill.json +0 -106
  105. package/skills/agent-bounties/SKILL.md +0 -553
  106. package/skills/agent-payments/SKILL.md +0 -479
  107. package/skills/brainstorming/SKILL.md +0 -233
  108. package/skills/content-pipeline/SKILL.md +0 -282
  109. package/skills/cross-project-knowledge/SKILL.md +0 -345
  110. package/skills/dispatching-parallel-agents/SKILL.md +0 -305
  111. package/skills/economic-code-optimization/SKILL.md +0 -265
  112. package/skills/executing-plans/SKILL.md +0 -255
  113. package/skills/finishing-a-development-branch/SKILL.md +0 -260
  114. package/skills/formal-verification-lite/SKILL.md +0 -441
  115. package/skills/learn-how-to-learn/SKILL.md +0 -235
  116. package/skills/market-intelligence/SKILL.md +0 -323
  117. package/skills/meta-skill-evolution/SKILL.md +0 -325
  118. package/skills/prospecting/SKILL.md +0 -454
  119. package/skills/receiving-code-review/SKILL.md +0 -225
  120. package/skills/requesting-code-review/SKILL.md +0 -206
  121. package/skills/security-audit/SKILL.md +0 -353
  122. package/skills/self-healing-code/SKILL.md +0 -369
  123. package/skills/subagent-driven-development/SKILL.md +0 -244
  124. package/skills/systematic-debugging/SKILL.md +0 -355
  125. package/skills/test-driven-development/SKILL.md +0 -416
  126. package/skills/using-clawpowers/SKILL.md +0 -160
  127. package/skills/using-git-worktrees/SKILL.md +0 -261
  128. package/skills/validator/SKILL.md +0 -281
  129. package/skills/verification-before-completion/SKILL.md +0 -254
  130. package/skills/writing-plans/SKILL.md +0 -276
  131. package/skills/writing-skills/SKILL.md +0 -260
@@ -1,305 +0,0 @@
1
- #!/usr/bin/env node
2
- // runtime/payments/ledger.js — Payment decision ledger
3
- //
4
- // Records every payment decision (approve, deny, dry-run) to a persistent
5
- // JSONL log at ~/.clawpowers/logs/payments.jsonl. Provides CLI commands to
6
- // review recent decisions and summarize spending by skill, chain, and outcome.
7
- //
8
- // Usage (CLI):
9
- // node ledger.js log [--limit <n>]
10
- // node ledger.js summary
11
- //
12
- // Usage (module):
13
- // const { logPaymentDecision, getPaymentSummary } = require('./ledger');
14
- // await logPaymentDecision({ skill: 'agent-payments', url: '...', ... });
15
- // const summary = getPaymentSummary();
16
- 'use strict';
17
-
18
- const fs = require('fs');
19
- const path = require('path');
20
- const os = require('os');
21
-
22
- // Logs directory — contains payments.jsonl and other audit logs
23
- const LOGS_DIR = path.join(
24
- process.env.CLAWPOWERS_DIR || path.join(os.homedir(), '.clawpowers'),
25
- 'logs'
26
- );
27
-
28
- /** Absolute path to the payment ledger file. */
29
- const LEDGER_FILE = path.join(LOGS_DIR, 'payments.jsonl');
30
-
31
- /**
32
- * Creates the logs directory if it doesn't already exist.
33
- * Mode 0o700 restricts access to the current user only.
34
- */
35
- function ensureLogsDir() {
36
- if (!fs.existsSync(LOGS_DIR)) {
37
- fs.mkdirSync(LOGS_DIR, { recursive: true, mode: 0o700 });
38
- }
39
- }
40
-
41
- /**
42
- * Returns an ISO 8601 timestamp without milliseconds.
43
- *
44
- * @returns {string} e.g. "2026-03-22T21:42:00Z"
45
- */
46
- function isoTimestamp() {
47
- return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
48
- }
49
-
50
- /**
51
- * Records a payment decision to the JSONL ledger.
52
- *
53
- * Each entry captures what happened at a payment gate: the skill that
54
- * encountered the payment requirement, the URL that required payment,
55
- * the amount and asset, the policy evaluation result, and whether payment
56
- * would have been (or was) made.
57
- *
58
- * The entry is appended as a single JSON line to `~/.clawpowers/logs/payments.jsonl`.
59
- * The file is created if it doesn't exist.
60
- *
61
- * @param {object} entry - Payment decision details.
62
- * @param {string} [entry.skill='unknown'] - Skill that triggered the payment gate.
63
- * @param {string} [entry.type='decision'] - Entry type: 'decision' | 'payment' | 'denial'.
64
- * @param {string} [entry.url=''] - Resource URL that required payment.
65
- * @param {string} [entry.required_amount='0'] - Amount required (in smallest asset unit).
66
- * @param {string} [entry.asset='USDC'] - Asset symbol (e.g. 'USDC', 'ETH').
67
- * @param {string} [entry.chain='base'] - Chain name (e.g. 'base', 'base-sepolia').
68
- * @param {string} [entry.policy_result='dry_run'] - Policy outcome: 'dry_run' | 'approved' | 'denied' | 'disabled'.
69
- * @param {string} [entry.reason=''] - Human-readable reason for the policy result.
70
- * @param {boolean} [entry.would_have_paid=false] - Whether payment would have succeeded if live.
71
- * @returns {object} The complete entry as written to the ledger (including timestamp).
72
- */
73
- function logPaymentDecision(entry) {
74
- ensureLogsDir();
75
-
76
- /** @type {object} Full ledger record with timestamp and defaults applied. */
77
- const record = {
78
- timestamp: isoTimestamp(),
79
- skill: entry.skill || 'unknown',
80
- type: entry.type || 'decision',
81
- url: entry.url || '',
82
- required_amount: entry.required_amount || '0',
83
- asset: entry.asset || 'USDC',
84
- chain: entry.chain || 'base',
85
- policy_result: entry.policy_result || 'dry_run',
86
- reason: entry.reason || '',
87
- would_have_paid: Boolean(entry.would_have_paid),
88
- };
89
-
90
- // Append as a single JSON line — safe for concurrent appends
91
- const line = JSON.stringify(record) + '\n';
92
- fs.appendFileSync(LEDGER_FILE, line);
93
- try { fs.chmodSync(LEDGER_FILE, 0o600); } catch (_) { /* non-fatal on Windows */ }
94
-
95
- return record;
96
- }
97
-
98
- /**
99
- * Reads all payment decision records from the ledger.
100
- * Malformed JSON lines are silently skipped.
101
- *
102
- * @returns {object[]} Array of parsed ledger records in chronological order.
103
- */
104
- function loadLedger() {
105
- if (!fs.existsSync(LEDGER_FILE)) return [];
106
-
107
- const content = fs.readFileSync(LEDGER_FILE, 'utf8');
108
- const records = [];
109
- for (const line of content.split('\n')) {
110
- if (!line.trim()) continue;
111
- try {
112
- records.push(JSON.parse(line));
113
- } catch (_) {
114
- // Skip malformed lines — don't crash on a single bad record
115
- }
116
- }
117
- return records;
118
- }
119
-
120
- /**
121
- * Computes a payment summary aggregated by skill, chain, and policy outcome.
122
- *
123
- * Returns totals broken down by:
124
- * - Skill name: which skills hit payment gates most often
125
- * - Chain: which networks were targeted for payment
126
- * - Outcome: how many were dry_run vs approved vs denied vs disabled
127
- *
128
- * @returns {{
129
- * total: number,
130
- * by_skill: Object.<string, number>,
131
- * by_chain: Object.<string, number>,
132
- * by_outcome: Object.<string, number>,
133
- * would_have_paid: number,
134
- * records: object[]
135
- * }} Aggregated summary object.
136
- */
137
- function getPaymentSummary() {
138
- const records = loadLedger();
139
-
140
- /** @type {Object.<string, number>} Count per skill name. */
141
- const by_skill = {};
142
- /** @type {Object.<string, number>} Count per chain name. */
143
- const by_chain = {};
144
- /** @type {Object.<string, number>} Count per policy_result value. */
145
- const by_outcome = {};
146
- let would_have_paid = 0;
147
-
148
- for (const r of records) {
149
- // Tally by skill
150
- by_skill[r.skill] = (by_skill[r.skill] || 0) + 1;
151
- // Tally by chain
152
- by_chain[r.chain] = (by_chain[r.chain] || 0) + 1;
153
- // Tally by outcome
154
- by_outcome[r.policy_result] = (by_outcome[r.policy_result] || 0) + 1;
155
- // Count would-have-paid scenarios (dry-run hits)
156
- if (r.would_have_paid) would_have_paid++;
157
- }
158
-
159
- return {
160
- total: records.length,
161
- by_skill,
162
- by_chain,
163
- by_outcome,
164
- would_have_paid,
165
- records,
166
- };
167
- }
168
-
169
- /**
170
- * `log` CLI command — prints recent payment decisions to stdout.
171
- * Defaults to the last 20 records. Use --limit to change.
172
- *
173
- * @param {string[]} argv - Arguments after 'log'.
174
- */
175
- function cmdLog(argv) {
176
- // Parse optional --limit flag
177
- let limit = 20;
178
- for (let i = 0; i < argv.length; i++) {
179
- if (argv[i] === '--limit' && argv[i + 1]) {
180
- limit = parseInt(argv[i + 1], 10);
181
- i++;
182
- }
183
- }
184
-
185
- const records = loadLedger();
186
- if (records.length === 0) {
187
- console.log('No payment decisions recorded yet.');
188
- console.log(`Ledger location: ${LEDGER_FILE}`);
189
- return;
190
- }
191
-
192
- const slice = records.slice(Math.max(0, records.length - limit));
193
- console.log(`Recent payment decisions (last ${slice.length} of ${records.length}):`);
194
- console.log('');
195
- for (const r of slice) {
196
- const paid = r.would_have_paid ? '[would pay]' : '[would skip]';
197
- console.log(` ${r.timestamp} | ${r.skill} | ${r.policy_result} ${paid}`);
198
- if (r.url) console.log(` URL: ${r.url}`);
199
- if (r.required_amount !== '0') {
200
- console.log(` Amount: ${r.required_amount} ${r.asset} on ${r.chain}`);
201
- }
202
- if (r.reason) console.log(` Reason: ${r.reason}`);
203
- console.log('');
204
- }
205
- }
206
-
207
- /**
208
- * `summary` CLI command — prints aggregated payment totals to stdout.
209
- * Shows totals by skill, chain, and outcome.
210
- */
211
- function cmdSummary() {
212
- const summary = getPaymentSummary();
213
-
214
- if (summary.total === 0) {
215
- console.log('No payment decisions recorded yet.');
216
- console.log(`Ledger location: ${LEDGER_FILE}`);
217
- return;
218
- }
219
-
220
- console.log(`Payment Decision Summary`);
221
- console.log(`========================`);
222
- console.log(`Total decisions: ${summary.total}`);
223
- console.log(`Would have paid: ${summary.would_have_paid}`);
224
- console.log('');
225
-
226
- console.log('By skill:');
227
- for (const [skill, count] of Object.entries(summary.by_skill).sort()) {
228
- console.log(` ${skill}: ${count}`);
229
- }
230
- console.log('');
231
-
232
- console.log('By chain:');
233
- for (const [chain, count] of Object.entries(summary.by_chain).sort()) {
234
- console.log(` ${chain}: ${count}`);
235
- }
236
- console.log('');
237
-
238
- console.log('By outcome:');
239
- for (const [outcome, count] of Object.entries(summary.by_outcome).sort()) {
240
- const pct = Math.round(count / summary.total * 100);
241
- console.log(` ${outcome}: ${count} (${pct}%)`);
242
- }
243
- }
244
-
245
- /**
246
- * Prints usage information for the ledger CLI to stdout.
247
- */
248
- function printUsage() {
249
- console.log(`Usage: ledger.js <command> [options]
250
-
251
- Commands:
252
- log [--limit <n>] Show recent payment decisions (default: last 20)
253
- summary Show totals by skill, chain, and outcome
254
-
255
- Ledger file: ~/.clawpowers/logs/payments.jsonl
256
-
257
- Examples:
258
- ledger.js log
259
- ledger.js log --limit 50
260
- ledger.js summary`);
261
- }
262
-
263
- /**
264
- * CLI dispatch — routes argv to the appropriate command.
265
- *
266
- * @param {string[]} argv - Argument array (typically process.argv.slice(2)).
267
- */
268
- function main(argv) {
269
- const [cmd, ...rest] = argv;
270
-
271
- switch (cmd) {
272
- case 'log':
273
- cmdLog(rest);
274
- break;
275
- case 'summary':
276
- cmdSummary();
277
- break;
278
- case 'help':
279
- case '-h':
280
- case '--help':
281
- printUsage();
282
- break;
283
- case undefined:
284
- case '':
285
- printUsage();
286
- process.exit(1);
287
- break;
288
- default:
289
- process.stderr.write(`Unknown command: ${cmd}\n`);
290
- printUsage();
291
- process.exit(1);
292
- }
293
- }
294
-
295
- // Guard: only run CLI dispatch when invoked directly, not when require()'d
296
- if (require.main === module) {
297
- try {
298
- main(process.argv.slice(2));
299
- } catch (err) {
300
- process.stderr.write(`Error: ${err.message}\n`);
301
- process.exit(1);
302
- }
303
- }
304
-
305
- module.exports = { logPaymentDecision, getPaymentSummary, LEDGER_FILE, LOGS_DIR };
@@ -1,262 +0,0 @@
1
- #!/usr/bin/env bash
2
- # runtime/payments/ledger.sh — Payment decision ledger (bash version)
3
- #
4
- # Records payment decisions to ~/.clawpowers/logs/payments.jsonl.
5
- # Provides CLI commands to review recent decisions and summarize spending.
6
- #
7
- # Usage:
8
- # bash ledger.sh log [--limit <n>]
9
- # bash ledger.sh summary
10
- # bash ledger.sh record --skill <name> --url <url> --amount <amt> \
11
- # --asset <asset> --chain <chain> \
12
- # --policy <result> --reason <text> [--would-pay]
13
- set -euo pipefail
14
-
15
- # Runtime root — override with CLAWPOWERS_DIR for testing
16
- CLAWPOWERS_DIR="${CLAWPOWERS_DIR:-$HOME/.clawpowers}"
17
- LOGS_DIR="$CLAWPOWERS_DIR/logs"
18
- LEDGER_FILE="$LOGS_DIR/payments.jsonl"
19
-
20
- ## === Helpers ===
21
-
22
- # Ensures the logs directory exists with correct permissions.
23
- ensure_logs_dir() {
24
- if [[ ! -d "$LOGS_DIR" ]]; then
25
- mkdir -p "$LOGS_DIR"
26
- chmod 700 "$LOGS_DIR"
27
- fi
28
- }
29
-
30
- # Returns an ISO 8601 timestamp (seconds precision).
31
- iso_timestamp() {
32
- date -u +"%Y-%m-%dT%H:%M:%SZ"
33
- }
34
-
35
- # Escapes a string for safe embedding in a JSON value.
36
- # Handles backslash, double-quote, and common control characters.
37
- json_escape() {
38
- local s="$1"
39
- s="${s//\\/\\\\}" # backslash
40
- s="${s//\"/\\\"}" # double quote
41
- s="${s//$'\t'/\\t}" # tab
42
- s="${s//$'\n'/\\n}" # newline
43
- printf '%s' "$s"
44
- }
45
-
46
- ## === record command ===
47
-
48
- # Appends a single payment decision JSON line to the ledger.
49
- #
50
- # Options:
51
- # --skill <name> Skill that triggered the payment gate
52
- # --type <type> Entry type: decision | payment | denial (default: decision)
53
- # --url <url> Resource URL that required payment
54
- # --amount <n> Required amount in smallest unit (default: 0)
55
- # --asset <sym> Asset symbol: USDC, ETH, etc. (default: USDC)
56
- # --chain <name> Chain name: base, base-sepolia, etc. (default: base)
57
- # --policy <result> Policy outcome: dry_run | approved | denied | disabled
58
- # --reason <text> Human-readable reason for the policy result
59
- # --would-pay Flag: set would_have_paid=true
60
- cmd_record() {
61
- local skill="unknown"
62
- local type="decision"
63
- local url=""
64
- local amount="0"
65
- local asset="USDC"
66
- local chain="base"
67
- local policy="dry_run"
68
- local reason=""
69
- local would_pay="false"
70
-
71
- while [[ $# -gt 0 ]]; do
72
- case "$1" in
73
- --skill) skill="$2"; shift 2 ;;
74
- --type) type="$2"; shift 2 ;;
75
- --url) url="$2"; shift 2 ;;
76
- --amount) amount="$2"; shift 2 ;;
77
- --asset) asset="$2"; shift 2 ;;
78
- --chain) chain="$2"; shift 2 ;;
79
- --policy) policy="$2"; shift 2 ;;
80
- --reason) reason="$2"; shift 2 ;;
81
- --would-pay) would_pay="true"; shift ;;
82
- *) echo "Unknown option: $1" >&2; exit 1 ;;
83
- esac
84
- done
85
-
86
- ensure_logs_dir
87
-
88
- local ts
89
- ts=$(iso_timestamp)
90
-
91
- # Build the JSON line manually (no jq dependency required)
92
- local line
93
- line="{\"timestamp\":\"$(json_escape "$ts")\","
94
- line+="\"skill\":\"$(json_escape "$skill")\","
95
- line+="\"type\":\"$(json_escape "$type")\","
96
- line+="\"url\":\"$(json_escape "$url")\","
97
- line+="\"required_amount\":\"$(json_escape "$amount")\","
98
- line+="\"asset\":\"$(json_escape "$asset")\","
99
- line+="\"chain\":\"$(json_escape "$chain")\","
100
- line+="\"policy_result\":\"$(json_escape "$policy")\","
101
- line+="\"reason\":\"$(json_escape "$reason")\","
102
- line+="\"would_have_paid\":$would_pay}"
103
-
104
- echo "$line" >> "$LEDGER_FILE"
105
- chmod 600 "$LEDGER_FILE" 2>/dev/null || true
106
-
107
- echo "Recorded: $skill → $policy ($(basename "$LEDGER_FILE"))"
108
- }
109
-
110
- ## === log command ===
111
-
112
- # Shows recent payment decisions from the ledger.
113
- #
114
- # Options:
115
- # --limit <n> Maximum records to show (default: 20)
116
- cmd_log() {
117
- local limit=20
118
-
119
- while [[ $# -gt 0 ]]; do
120
- case "$1" in
121
- --limit) limit="$2"; shift 2 ;;
122
- *) echo "Unknown option: $1" >&2; exit 1 ;;
123
- esac
124
- done
125
-
126
- if [[ ! -f "$LEDGER_FILE" ]]; then
127
- echo "No payment decisions recorded yet."
128
- echo "Ledger location: $LEDGER_FILE"
129
- return
130
- fi
131
-
132
- local total
133
- total=$(wc -l < "$LEDGER_FILE" | tr -d ' ')
134
-
135
- if [[ "$total" -eq 0 ]]; then
136
- echo "No payment decisions recorded yet."
137
- return
138
- fi
139
-
140
- echo "Recent payment decisions (last $limit of $total):"
141
- echo ""
142
-
143
- # tail the last N lines and pretty-print each JSON record
144
- tail -n "$limit" "$LEDGER_FILE" | while IFS= read -r line; do
145
- [[ -z "$line" ]] && continue
146
-
147
- # Extract fields using basic shell tools (no jq dependency)
148
- local ts skill policy would_pay url amount asset chain reason
149
- ts=$(echo "$line" | grep -o '"timestamp":"[^"]*"' | cut -d'"' -f4)
150
- skill=$(echo "$line" | grep -o '"skill":"[^"]*"' | cut -d'"' -f4)
151
- policy=$(echo "$line" | grep -o '"policy_result":"[^"]*"' | cut -d'"' -f4)
152
- would_pay=$(echo "$line" | grep -o '"would_have_paid":[a-z]*' | cut -d: -f2)
153
- url=$(echo "$line" | grep -o '"url":"[^"]*"' | cut -d'"' -f4)
154
- amount=$(echo "$line" | grep -o '"required_amount":"[^"]*"' | cut -d'"' -f4)
155
- asset=$(echo "$line" | grep -o '"asset":"[^"]*"' | cut -d'"' -f4)
156
- chain=$(echo "$line" | grep -o '"chain":"[^"]*"' | cut -d'"' -f4)
157
- reason=$(echo "$line" | grep -o '"reason":"[^"]*"' | cut -d'"' -f4)
158
-
159
- local paid_label="[would skip]"
160
- [[ "$would_pay" == "true" ]] && paid_label="[would pay]"
161
-
162
- echo " $ts | $skill | $policy $paid_label"
163
- [[ -n "$url" ]] && echo " URL: $url"
164
- [[ "$amount" != "0" ]] && echo " Amount: $amount $asset on $chain"
165
- [[ -n "$reason" ]] && echo " Reason: $reason"
166
- echo ""
167
- done
168
- }
169
-
170
- ## === summary command ===
171
-
172
- # Shows aggregated payment totals by skill, chain, and outcome.
173
- cmd_summary() {
174
- if [[ ! -f "$LEDGER_FILE" ]]; then
175
- echo "No payment decisions recorded yet."
176
- echo "Ledger location: $LEDGER_FILE"
177
- return
178
- fi
179
-
180
- local total
181
- total=$(grep -c '.' "$LEDGER_FILE" 2>/dev/null || echo 0)
182
-
183
- if [[ "$total" -eq 0 ]]; then
184
- echo "No payment decisions recorded yet."
185
- return
186
- fi
187
-
188
- local would_have_paid
189
- would_have_paid=$(grep -c '"would_have_paid":true' "$LEDGER_FILE" 2>/dev/null || echo 0)
190
-
191
- echo "Payment Decision Summary"
192
- echo "========================"
193
- echo "Total decisions: $total"
194
- echo "Would have paid: $would_have_paid"
195
- echo ""
196
-
197
- echo "By skill:"
198
- grep -o '"skill":"[^"]*"' "$LEDGER_FILE" | \
199
- cut -d'"' -f4 | sort | uniq -c | sort -rn | \
200
- while read -r count skill; do echo " $skill: $count"; done
201
- echo ""
202
-
203
- echo "By chain:"
204
- grep -o '"chain":"[^"]*"' "$LEDGER_FILE" | \
205
- cut -d'"' -f4 | sort | uniq -c | sort -rn | \
206
- while read -r count chain; do echo " $chain: $count"; done
207
- echo ""
208
-
209
- echo "By outcome:"
210
- grep -o '"policy_result":"[^"]*"' "$LEDGER_FILE" | \
211
- cut -d'"' -f4 | sort | uniq -c | sort -rn | \
212
- while read -r count outcome; do echo " $outcome: $count"; done
213
- }
214
-
215
- ## === usage ===
216
-
217
- print_usage() {
218
- echo "Usage: ledger.sh <command> [options]"
219
- echo ""
220
- echo "Commands:"
221
- echo " log [--limit <n>] Show recent payment decisions (default: last 20)"
222
- echo " summary Show totals by skill, chain, and outcome"
223
- echo " record [options] Record a payment decision"
224
- echo ""
225
- echo "record options:"
226
- echo " --skill <name> Skill name (default: unknown)"
227
- echo " --type <type> decision | payment | denial (default: decision)"
228
- echo " --url <url> Resource URL"
229
- echo " --amount <n> Required amount"
230
- echo " --asset <sym> Asset symbol (default: USDC)"
231
- echo " --chain <name> Chain name (default: base)"
232
- echo " --policy <result> dry_run | approved | denied | disabled"
233
- echo " --reason <text> Reason for policy result"
234
- echo " --would-pay Set would_have_paid=true"
235
- echo ""
236
- echo "Ledger file: ~/.clawpowers/logs/payments.jsonl"
237
- }
238
-
239
- ## === main dispatch ===
240
-
241
- main() {
242
- local cmd="${1:-}"
243
- shift || true
244
-
245
- case "$cmd" in
246
- log) cmd_log "$@" ;;
247
- summary) cmd_summary "$@" ;;
248
- record) cmd_record "$@" ;;
249
- help|-h|--help) print_usage ;;
250
- "")
251
- print_usage
252
- exit 1
253
- ;;
254
- *)
255
- echo "Unknown command: $cmd" >&2
256
- print_usage >&2
257
- exit 1
258
- ;;
259
- esac
260
- }
261
-
262
- main "$@"