clawpowers 1.1.0 → 1.1.2

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/README.md CHANGED
@@ -27,7 +27,7 @@ ClawPowers gives your coding agent superpowers that go beyond instructions. Whil
27
27
  | Windows native support | ✅ | ❌ |
28
28
  | Zero dependencies | ✅ | ✅ |
29
29
 
30
- **24 skills.** 14 cover everything static frameworks do (TDD, subagent dev, debugging, planning, code review, git worktrees). 6 go where they can't — payments, security, content, prospecting, market intelligence, and metacognitive learning.
30
+ **25 skills.** 14 cover everything static frameworks do (TDD, subagent dev, debugging, planning, code review, git worktrees). 6 go where they can't — payments, security, content, prospecting, market intelligence, and metacognitive learning. 4 are things no other framework even attempts — self-healing code, agents that rewrite their own methodology, cross-project knowledge transfer, and property-based formal verification.
31
31
 
32
32
  ## Requirements
33
33
 
@@ -214,11 +214,23 @@ Static frameworks stop at coding methodology. ClawPowers includes skills for:
214
214
  | `market-intelligence` | Competitive analysis, trend detection, opportunity scoring | Requires web access, data aggregation, persistent tracking |
215
215
  | `prospecting` | Lead generation, contact enrichment, CRM sync | Requires API calls (Exa, Apollo), structured output |
216
216
 
217
+ ### RSI Intelligence Layer (4 skills)
218
+
219
+ These skills don't exist in any other framework. They require runtime execution, persistent state, and self-modification capabilities that static prompt collections can never deliver.
220
+
221
+ | Skill | What It Does | Why This Changes Everything |
222
+ |-------|-------------|----------------------------|
223
+ | `meta-skill-evolution` | Every 50 tasks, analyzes outcome patterns, identifies the weakest skill, surgically rewrites its methodology, version bumps | Your agent's coding discipline improves autonomously over time. After 30 days it's measurably better than any static install |
224
+ | `self-healing-code` | On test failure: captures error → builds hypothesis tree → generates 2+ patches → applies with coverage guard → auto-commits winner | 3-cycle max with rollback. Turns red tests into green tests without human intervention |
225
+ | `cross-project-knowledge` | Persistent pattern library across ALL repos. Bug fixes, architecture decisions, and performance optimizations transfer between projects | Agent working on Project B benefits from everything learned on Projects A, C, D. Knowledge compounds |
226
+ | `formal-verification-lite` | Property-based testing with fast-check (JS), Hypothesis (Python), QuickCheck (Haskell). 5 property templates, 1000+ examples per property | Goes beyond "tests pass" to "tests actually prove correctness." Catches edge cases unit tests miss |
227
+ | `economic-code-optimization` | Autonomously spends micro-budgets on premium models, cloud GPUs, expert reviews when ROI justifies it. Tracks every cent and learns optimal spend ratios | Agents literally invest in their own performance. Spending efficiency improves over time via RSI feedback loop |
228
+
217
229
  ## Architecture
218
230
 
219
231
  ```
220
232
  clawpowers/
221
- ├── skills/ # 20 skill directories, each with SKILL.md
233
+ ├── skills/ # 25 skill directories, each with SKILL.md
222
234
  ├── runtime/
223
235
  │ ├── persistence/ # Cross-session state (store.js + store.sh)
224
236
  │ ├── metrics/ # Outcome tracking (collector.js + collector.sh)
@@ -382,6 +394,10 @@ Built by [AI Agent Economy](https://github.com/up2itnow0822) — the team behind
382
394
 
383
395
  We welcome contributions. Unlike some frameworks, we don't dismiss legitimate skill proposals with one-word responses. Open an issue or PR — every submission gets a genuine technical review.
384
396
 
397
+ ## Patent Notice
398
+
399
+ **Patent Pending** — The underlying financial infrastructure (agentwallet-sdk, agentpay-mcp) is covered by USPTO provisional patent application filed March 2026: "Non-Custodial Multi-Chain Financial Infrastructure System for Autonomous AI Agents."
400
+
385
401
  ## License
386
402
 
387
403
  MIT
package/bin/clawpowers.js CHANGED
@@ -25,6 +25,7 @@ const ANALYZE_JS = path.join(REPO_ROOT, 'runtime', 'feedback', 'analyze.js');
25
25
  const STORE_JS = path.join(REPO_ROOT, 'runtime', 'persistence', 'store.js');
26
26
  const COLLECTOR_JS = path.join(REPO_ROOT, 'runtime', 'metrics', 'collector.js');
27
27
  const SESSION_JS = path.join(REPO_ROOT, 'hooks', 'session-start.js');
28
+ const LEDGER_JS = path.join(REPO_ROOT, 'runtime', 'payments', 'ledger.js');
28
29
 
29
30
  /**
30
31
  * Prints the top-level command usage to stdout.
@@ -41,6 +42,8 @@ Commands:
41
42
  metrics <cmd> Record or query skill execution metrics
42
43
  analyze [opts] RSI feedback analysis of skill performance
43
44
  store <cmd> Key-value state store operations
45
+ payments <cmd> Payment setup, log, and summary commands
46
+ demo <cmd> Run interactive demos (e.g. x402 mock merchant)
44
47
 
45
48
  Examples:
46
49
  npx clawpowers init
@@ -50,6 +53,10 @@ Examples:
50
53
  npx clawpowers analyze --skill systematic-debugging
51
54
  npx clawpowers store set "my:key" "my value"
52
55
  npx clawpowers store get "my:key"
56
+ npx clawpowers payments setup
57
+ npx clawpowers payments log
58
+ npx clawpowers payments summary
59
+ npx clawpowers demo x402
53
60
 
54
61
  Run 'npx clawpowers <command> help' for command-specific help.`);
55
62
  }
@@ -356,6 +363,216 @@ function delegateToNode(script, args) {
356
363
  process.exit(result.status || 0);
357
364
  }
358
365
 
366
+ /**
367
+ * `clawpowers payments setup` — Interactive wallet activation wizard.
368
+ *
369
+ * Guides the user through enabling or configuring the payment subsystem.
370
+ * Never asks for private keys — those belong in .env.
371
+ * Reads and writes ~/.clawpowers/config.json (created by init if missing).
372
+ *
373
+ * Wizard steps:
374
+ * 1. Explain that payments are optional
375
+ * 2. Ask user to choose a mode: disabled / dry run / live
376
+ * 3. If dry run: update config payments.mode = "dry_run", payments.enabled = true
377
+ * 4. If live: ask for per_tx_limit and daily_limit, then update config
378
+ * 5. Confirm where logs are stored
379
+ */
380
+ function cmdPaymentsSetup() {
381
+ const readline = require('readline');
382
+ const CLAWPOWERS_DIR = process.env.CLAWPOWERS_DIR || path.join(os.homedir(), '.clawpowers');
383
+ const configFile = path.join(CLAWPOWERS_DIR, 'config.json');
384
+
385
+ // Load existing config or fall back to defaults
386
+ let config = {};
387
+ if (fs.existsSync(configFile)) {
388
+ try {
389
+ config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
390
+ } catch (_) {
391
+ config = {};
392
+ }
393
+ }
394
+ // Ensure payments section exists with safe defaults
395
+ if (!config.payments) {
396
+ config.payments = {
397
+ enabled: false,
398
+ mode: 'dry_run',
399
+ per_tx_limit_usd: 0,
400
+ daily_limit_usd: 0,
401
+ weekly_limit_usd: 0,
402
+ allowlist: [],
403
+ require_approval_above_usd: 0,
404
+ };
405
+ }
406
+
407
+ const logsPath = path.join(CLAWPOWERS_DIR, 'logs', 'payments.jsonl');
408
+
409
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
410
+
411
+ const ask = (question) => new Promise((resolve) => rl.question(question, resolve));
412
+
413
+ async function run() {
414
+ console.log('');
415
+ console.log('╔══════════════════════════════════════════════════╗');
416
+ console.log('║ ClawPowers Payment Setup Wizard ║');
417
+ console.log('╚══════════════════════════════════════════════════╝');
418
+ console.log('');
419
+ console.log('Payments are optional. ClawPowers works without a wallet.');
420
+ console.log('When enabled, agents can autonomously pay for premium APIs');
421
+ console.log('and services using the x402 protocol.');
422
+ console.log('');
423
+ console.log('⚠ Private keys belong in .env — this wizard never asks for them.');
424
+ console.log('');
425
+ console.log('Choose a mode:');
426
+ console.log(' [1] Keep disabled (default — no payments, no wallet required)');
427
+ console.log(' [2] Enable Dry Run (detect payment gates, log what WOULD happen)');
428
+ console.log(' [3] Enable Live (real payments within configured limits)');
429
+ console.log('');
430
+
431
+ const choice = (await ask('Enter choice [1/2/3]: ')).trim();
432
+
433
+ if (choice === '1' || choice === '') {
434
+ config.payments.enabled = false;
435
+ config.payments.mode = 'dry_run';
436
+ console.log('');
437
+ console.log('✓ Payments remain disabled. No wallet required.');
438
+
439
+ } else if (choice === '2') {
440
+ config.payments.enabled = true;
441
+ config.payments.mode = 'dry_run';
442
+ console.log('');
443
+ console.log('✓ Dry Run mode enabled.');
444
+ console.log(' Agents will detect payment requirements and log what would happen.');
445
+ console.log(' No funds will move. Review the log after 10+ cycles, then');
446
+ console.log(' run this wizard again to go live with confidence.');
447
+
448
+ } else if (choice === '3') {
449
+ console.log('');
450
+ console.log('Live payment mode requires spending limits to protect your wallet.');
451
+ console.log('');
452
+
453
+ const perTxRaw = (await ask('Per-transaction limit (USD, e.g. 0.10): ')).trim();
454
+ const dailyRaw = (await ask('Daily limit (USD, e.g. 5.00): ')).trim();
455
+
456
+ const perTxLimit = parseFloat(perTxRaw) || 0;
457
+ const dailyLimit = parseFloat(dailyRaw) || 0;
458
+
459
+ config.payments.enabled = true;
460
+ config.payments.mode = 'live';
461
+ config.payments.per_tx_limit_usd = perTxLimit;
462
+ config.payments.daily_limit_usd = dailyLimit;
463
+
464
+ console.log('');
465
+ console.log(`✓ Live payments enabled.`);
466
+ console.log(` Per-transaction limit: $${perTxLimit.toFixed(2)}`);
467
+ console.log(` Daily limit: $${dailyLimit.toFixed(2)}`);
468
+ console.log('');
469
+ console.log(' Add your private key or mnemonic to ~/.clawpowers/.env');
470
+ console.log(' (never commit this file — it is gitignored by default)');
471
+
472
+ } else {
473
+ console.log('');
474
+ console.log('Invalid choice. No changes made.');
475
+ rl.close();
476
+ return;
477
+ }
478
+
479
+ // Save updated config
480
+ if (!fs.existsSync(CLAWPOWERS_DIR)) {
481
+ fs.mkdirSync(CLAWPOWERS_DIR, { recursive: true, mode: 0o700 });
482
+ }
483
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
484
+
485
+ console.log('');
486
+ console.log(`✓ Configuration saved to: ${configFile}`);
487
+ console.log(` All payment logs are stored locally at: ${logsPath}`);
488
+ console.log('');
489
+ console.log(' Review logs with: npx clawpowers payments log');
490
+ console.log(' See summary with: npx clawpowers payments summary');
491
+ console.log('');
492
+
493
+ rl.close();
494
+ }
495
+
496
+ run().catch((err) => {
497
+ process.stderr.write(`Error: ${err.message}\n`);
498
+ rl.close();
499
+ process.exit(1);
500
+ });
501
+ }
502
+
503
+ /**
504
+ * `clawpowers payments <subcmd> [args]` — Payment ledger and setup commands.
505
+ *
506
+ * Subcommands:
507
+ * setup — Interactive payment mode wizard
508
+ * log — Show recent payment decisions
509
+ * summary — Show payment totals by skill, chain, outcome
510
+ *
511
+ * @param {string[]} args - Remaining argv after 'payments'.
512
+ */
513
+ function cmdPayments(args) {
514
+ const [subcmd, ...rest] = args;
515
+
516
+ if (!subcmd || subcmd === 'help' || subcmd === '--help' || subcmd === '-h') {
517
+ console.log(`Usage: clawpowers payments <command> [options]
518
+
519
+ Commands:
520
+ setup Interactive payment mode wizard
521
+ log [--limit <n>] Show recent payment decisions (default: last 20)
522
+ summary Show totals by skill, chain, and outcome`);
523
+ return;
524
+ }
525
+
526
+ switch (subcmd) {
527
+ case 'setup':
528
+ cmdPaymentsSetup();
529
+ break;
530
+ case 'log':
531
+ requireModule(LEDGER_JS).cmdLog ? requireModule(LEDGER_JS).cmdLog(rest)
532
+ : delegateToNode(LEDGER_JS, ['log', ...rest]);
533
+ break;
534
+ case 'summary':
535
+ requireModule(LEDGER_JS).cmdSummary ? requireModule(LEDGER_JS).cmdSummary()
536
+ : delegateToNode(LEDGER_JS, ['summary']);
537
+ break;
538
+ default:
539
+ process.stderr.write(`Unknown payments subcommand: ${subcmd}\n`);
540
+ process.stderr.write('Run: npx clawpowers payments help\n');
541
+ process.exit(1);
542
+ }
543
+ }
544
+
545
+ /**
546
+ * `clawpowers demo x402` — Start the x402 mock merchant server for demo purposes.
547
+ * Delegates to runtime/demo/x402-mock-server.js.
548
+ *
549
+ * @param {string[]} args - Remaining argv after 'demo'.
550
+ */
551
+ function cmdDemo(args) {
552
+ const [subcmd] = args;
553
+
554
+ if (!subcmd || subcmd === 'help' || subcmd === '--help' || subcmd === '-h') {
555
+ console.log(`Usage: clawpowers demo <command>
556
+
557
+ Commands:
558
+ x402 Start the x402 mock merchant server`);
559
+ return;
560
+ }
561
+
562
+ if (subcmd === 'x402') {
563
+ const MOCK_SERVER_JS = path.join(REPO_ROOT, 'runtime', 'demo', 'x402-mock-server.js');
564
+ if (!fs.existsSync(MOCK_SERVER_JS)) {
565
+ process.stderr.write(`Error: runtime module not found: ${MOCK_SERVER_JS}\n`);
566
+ process.exit(1);
567
+ }
568
+ delegateToNode(MOCK_SERVER_JS, []);
569
+ } else {
570
+ process.stderr.write(`Unknown demo subcommand: ${subcmd}\n`);
571
+ process.stderr.write('Run: npx clawpowers demo help\n');
572
+ process.exit(1);
573
+ }
574
+ }
575
+
359
576
  // ============================================================
360
577
  // Main dispatch — parse the first positional argument as the command
361
578
  // ============================================================
@@ -363,16 +580,18 @@ const [cmd, ...args] = process.argv.slice(2);
363
580
 
364
581
  try {
365
582
  switch (cmd) {
366
- case 'init': cmdInit(); break;
367
- case 'status': cmdStatus(); break;
368
- case 'update': cmdUpdate(); break;
369
- case 'inject': cmdInject(); break;
370
- case 'metrics': cmdMetrics(args); break;
371
- case 'analyze': cmdAnalyze(args); break;
372
- case 'store': cmdStore(args); break;
583
+ case 'init': cmdInit(); break;
584
+ case 'status': cmdStatus(); break;
585
+ case 'update': cmdUpdate(); break;
586
+ case 'inject': cmdInject(); break;
587
+ case 'metrics': cmdMetrics(args); break;
588
+ case 'analyze': cmdAnalyze(args); break;
589
+ case 'store': cmdStore(args); break;
590
+ case 'payments': cmdPayments(args); break;
591
+ case 'demo': cmdDemo(args); break;
373
592
  case 'help':
374
593
  case '-h':
375
- case '--help': printUsage(); break;
594
+ case '--help': printUsage(); break;
376
595
  // No command or empty string: show usage and exit 1 (non-zero for scripts)
377
596
  case undefined:
378
597
  case '':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawpowers",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "The skills framework that actually does something — runtime execution, persistent memory, self-improvement, and autonomous payments for coding agents.",
5
5
  "license": "MIT",
6
6
  "author": "AI Agent Economy <https://github.com/up2itnow0822>",
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+ // runtime/demo/x402-mock-server.js — x402 Mock Merchant Demo Server
3
+ //
4
+ // Starts a local HTTP server that demonstrates the x402 payment-required flow:
5
+ // 1. GET /api/premium-data without payment header → 402 Payment Required
6
+ // 2. GET /api/premium-data with 'x-payment: mock-proof' header → 200 OK with data
7
+ //
8
+ // This server is for DEMO and DEVELOPMENT ONLY — it does not process real payments.
9
+ //
10
+ // Usage:
11
+ // node runtime/demo/x402-mock-server.js
12
+ // npx clawpowers demo x402
13
+ //
14
+ // The server picks a random available port and prints instructions on startup.
15
+ // Press Ctrl+C to stop.
16
+ 'use strict';
17
+
18
+ const http = require('http');
19
+ const os = require('os');
20
+
21
+ // Track requests for demo visibility
22
+ let requestCount = 0;
23
+
24
+ /**
25
+ * Returns an ISO 8601 timestamp without milliseconds.
26
+ *
27
+ * @returns {string} e.g. "2026-03-22T21:42:00Z"
28
+ */
29
+ function isoTimestamp() {
30
+ return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
31
+ }
32
+
33
+ /**
34
+ * Logs a request to stdout with timestamp and key details.
35
+ * Allows the demo runner to see exactly what is happening.
36
+ *
37
+ * @param {string} method - HTTP method (GET, POST, etc.)
38
+ * @param {string} url - Request URL path
39
+ * @param {number} status - Response HTTP status code
40
+ * @param {string} [note=''] - Optional human-readable note about this request
41
+ */
42
+ function logRequest(method, url, status, note = '') {
43
+ const ts = isoTimestamp();
44
+ const noteStr = note ? ` — ${note}` : '';
45
+ console.log(` [${ts}] ${method} ${url} → ${status}${noteStr}`);
46
+ }
47
+
48
+ /**
49
+ * Builds the x402 Payment Required response body for the mock merchant.
50
+ * This mimics the response a real x402-compliant API would return.
51
+ *
52
+ * @param {number} port - The port this server is running on (embedded in the resource URL).
53
+ * @returns {object} x402 payment requirements object.
54
+ */
55
+ function buildPaymentRequired(port) {
56
+ return {
57
+ x402Version: 1,
58
+ accepts: [
59
+ {
60
+ scheme: 'exact',
61
+ network: 'base-sepolia',
62
+ maxAmountRequired: '100000',
63
+ resource: `http://localhost:${port}/api/premium-data`,
64
+ asset: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
65
+ payTo: '0xff86829393C6C26A4EC122bE0Cc3E466Ef876AdD',
66
+ },
67
+ ],
68
+ error: 'Payment Required',
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Builds the mock premium data response returned after (simulated) payment.
74
+ *
75
+ * @returns {object} Mock API data payload.
76
+ */
77
+ function buildPremiumData() {
78
+ return {
79
+ status: 'ok',
80
+ data: {
81
+ market: 'AGENT/USDC',
82
+ price: '4.20',
83
+ volume_24h: '1234567.89',
84
+ change_24h: '+12.3%',
85
+ source: 'mock-premium-api',
86
+ paid_with: 'x402',
87
+ timestamp: isoTimestamp(),
88
+ },
89
+ message: 'Payment accepted. Here is your premium data.',
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Main HTTP request handler for the mock x402 merchant.
95
+ *
96
+ * Routes:
97
+ * GET /api/premium-data — Returns 402 or 200 depending on x-payment header
98
+ * GET / — Returns a simple HTML help page
99
+ * All others — Returns 404
100
+ *
101
+ * @param {http.IncomingMessage} req - Incoming HTTP request
102
+ * @param {http.ServerResponse} res - HTTP response object
103
+ */
104
+ function handleRequest(req, res) {
105
+ requestCount++;
106
+ const url = req.url || '/';
107
+ const method = req.method || 'GET';
108
+
109
+ // CORS headers — allow curl and browser requests during demo
110
+ res.setHeader('Access-Control-Allow-Origin', '*');
111
+ res.setHeader('Access-Control-Allow-Headers', 'x-payment, content-type');
112
+
113
+ if (url === '/api/premium-data' && method === 'GET') {
114
+ const paymentHeader = req.headers['x-payment'];
115
+
116
+ if (!paymentHeader) {
117
+ // No payment header — return 402 Payment Required with x402 spec body
118
+ const body = JSON.stringify(buildPaymentRequired(server.address().port), null, 2);
119
+ res.setHeader('Content-Type', 'application/json');
120
+ res.writeHead(402);
121
+ res.end(body);
122
+ logRequest(method, url, 402, 'No x-payment header — returning payment requirements');
123
+ } else {
124
+ // Payment header present — simulate successful payment verification
125
+ const body = JSON.stringify(buildPremiumData(), null, 2);
126
+ res.setHeader('Content-Type', 'application/json');
127
+ res.writeHead(200);
128
+ res.end(body);
129
+ logRequest(method, url, 200, `x-payment: ${paymentHeader} — payment accepted, returning data`);
130
+ }
131
+
132
+ } else if (url === '/' && method === 'GET') {
133
+ // Help page — shows curl examples
134
+ const port = server.address().port;
135
+ const body = [
136
+ '<html><body><pre>',
137
+ 'x402 Mock Merchant Demo',
138
+ '=======================',
139
+ '',
140
+ 'Try these curl commands:',
141
+ '',
142
+ `# Step 1: Request without payment (returns 402)`,
143
+ `curl -i http://localhost:${port}/api/premium-data`,
144
+ '',
145
+ `# Step 2: Request with payment header (returns 200 + data)`,
146
+ `curl -i -H "x-payment: mock-proof" http://localhost:${port}/api/premium-data`,
147
+ '</pre></body></html>',
148
+ ].join('\n');
149
+ res.setHeader('Content-Type', 'text/html');
150
+ res.writeHead(200);
151
+ res.end(body);
152
+ logRequest(method, url, 200, 'help page');
153
+
154
+ } else {
155
+ res.setHeader('Content-Type', 'application/json');
156
+ res.writeHead(404);
157
+ res.end(JSON.stringify({ error: 'Not Found', path: url }));
158
+ logRequest(method, url, 404);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Finds a random available port by binding to port 0 and reading what the OS assigned.
164
+ * Returns a Promise that resolves to the chosen port number.
165
+ *
166
+ * @returns {Promise<number>} Available port number.
167
+ */
168
+ function getAvailablePort() {
169
+ return new Promise((resolve, reject) => {
170
+ const tmp = http.createServer();
171
+ tmp.listen(0, '127.0.0.1', () => {
172
+ const port = tmp.address().port;
173
+ tmp.close(() => resolve(port));
174
+ });
175
+ tmp.on('error', reject);
176
+ });
177
+ }
178
+
179
+ // The server object is referenced inside handleRequest for the port — declare before use
180
+ /** @type {http.Server} */
181
+ const server = http.createServer(handleRequest);
182
+
183
+ /**
184
+ * Main entry point: picks a port, starts the server, and prints instructions.
185
+ * Waits for Ctrl+C (SIGINT) or SIGTERM to shut down cleanly.
186
+ */
187
+ async function main() {
188
+ let port;
189
+ try {
190
+ port = await getAvailablePort();
191
+ } catch (_) {
192
+ // If dynamic port detection fails, fall back to a fixed high port
193
+ port = 18402;
194
+ }
195
+
196
+ server.listen(port, '127.0.0.1', () => {
197
+ console.log('');
198
+ console.log('╔══════════════════════════════════════════════════════════════╗');
199
+ console.log('║ x402 Mock Merchant — Demo Server ║');
200
+ console.log('╚══════════════════════════════════════════════════════════════╝');
201
+ console.log('');
202
+ console.log(` Mock x402 merchant running on http://localhost:${port}`);
203
+ console.log('');
204
+ console.log(' Try:');
205
+ console.log(` curl http://localhost:${port}/api/premium-data`);
206
+ console.log('');
207
+ console.log(' The server will return 402 (Payment Required) on first request.');
208
+ console.log('');
209
+ console.log(` curl -H 'x-payment: mock-proof' http://localhost:${port}/api/premium-data`);
210
+ console.log('');
211
+ console.log(" Send with header 'x-payment: mock-proof' to simulate payment.");
212
+ console.log('');
213
+ console.log(' Request log:');
214
+ });
215
+
216
+ // Handle Ctrl+C and process termination signals cleanly
217
+ const shutdown = () => {
218
+ console.log('');
219
+ console.log(` Shutting down after ${requestCount} request(s). Goodbye.`);
220
+ server.close(() => process.exit(0));
221
+ };
222
+
223
+ process.on('SIGINT', shutdown);
224
+ process.on('SIGTERM', shutdown);
225
+ }
226
+
227
+ main().catch((err) => {
228
+ process.stderr.write(`Error starting mock server: ${err.message}\n`);
229
+ process.exit(1);
230
+ });
package/runtime/init.js CHANGED
@@ -13,7 +13,7 @@ const fs = require('fs');
13
13
  const path = require('path');
14
14
  const os = require('os');
15
15
 
16
- const VERSION = '1.0.0';
16
+ const VERSION = '1.1.1';
17
17
 
18
18
  // Runtime root — override with CLAWPOWERS_DIR env var for testing or custom locations
19
19
  const CLAWPOWERS_DIR = process.env.CLAWPOWERS_DIR || path.join(os.homedir(), '.clawpowers');
@@ -96,6 +96,43 @@ function writeReadme() {
96
96
  }
97
97
  }
98
98
 
99
+ /**
100
+ * Default configuration written to ~/.clawpowers/config.json on first init.
101
+ * Users can edit this file to enable payments, telemetry, or change skill behavior.
102
+ * Never overwritten once created — user settings are always preserved.
103
+ */
104
+ const DEFAULT_CONFIG = {
105
+ version: VERSION,
106
+ payments: {
107
+ enabled: false,
108
+ mode: 'dry_run',
109
+ per_tx_limit_usd: 0,
110
+ daily_limit_usd: 0,
111
+ weekly_limit_usd: 0,
112
+ allowlist: [],
113
+ require_approval_above_usd: 0,
114
+ },
115
+ telemetry: {
116
+ enabled: false,
117
+ },
118
+ skills: {
119
+ auto_load: true,
120
+ },
121
+ };
122
+
123
+ /**
124
+ * Writes the default config.json to CLAWPOWERS_DIR on first initialization.
125
+ * No-op if config.json already exists — user settings are always preserved.
126
+ * The config file is written with mode 0o600 (owner read/write only).
127
+ */
128
+ function writeConfig() {
129
+ const configFile = path.join(CLAWPOWERS_DIR, 'config.json');
130
+ if (!fs.existsSync(configFile)) {
131
+ const content = JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n';
132
+ fs.writeFileSync(configFile, content, { mode: 0o600 });
133
+ }
134
+ }
135
+
99
136
  /**
100
137
  * Updates the version stamp in .version after initialization.
101
138
  * Currently a no-op placeholder for actual schema migrations; the version
@@ -139,6 +176,7 @@ function main() {
139
176
  const created = createStructure();
140
177
  writeVersion();
141
178
  writeReadme();
179
+ writeConfig();
142
180
 
143
181
  // Only run migrations when .version exists (i.e., after writeVersion)
144
182
  if (fs.existsSync(path.join(CLAWPOWERS_DIR, '.version'))) {
@@ -169,4 +207,4 @@ if (require.main === module) {
169
207
  }
170
208
  }
171
209
 
172
- module.exports = { main, CLAWPOWERS_DIR, VERSION };
210
+ module.exports = { main, CLAWPOWERS_DIR, VERSION, writeConfig, DEFAULT_CONFIG };