genesis-ai-cli 14.6.4 → 14.7.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.
@@ -13,7 +13,7 @@
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.getAlgoraConnector = getAlgoraConnector;
16
- const ALGORA_API = 'https://console.algora.io/api';
16
+ const ALGORA_API = 'https://algora.io/api';
17
17
  const TIMEOUT_MS = 15000;
18
18
  const RATE_LIMIT_MS = 500;
19
19
  let lastRequestTime = 0;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Genesis v14.7 — Earnings Tracker
3
+ *
4
+ * Persists all bounty earnings to state/earnings.json.
5
+ * Tracks attempts, successes, failures, and total revenue.
6
+ */
7
+ export interface BountyAttempt {
8
+ bountyId: string;
9
+ platform: string;
10
+ title: string;
11
+ reward: number;
12
+ currency: string;
13
+ startedAt: string;
14
+ completedAt?: string;
15
+ status: 'in_progress' | 'submitted' | 'accepted' | 'rejected' | 'abandoned';
16
+ prUrl?: string;
17
+ payout?: number;
18
+ feedback?: string;
19
+ timeSpentMinutes?: number;
20
+ costIncurred?: number;
21
+ }
22
+ export interface EarningsData {
23
+ version: string;
24
+ lastUpdated: string;
25
+ summary: {
26
+ totalAttempts: number;
27
+ totalAccepted: number;
28
+ totalRejected: number;
29
+ totalAbandoned: number;
30
+ totalEarned: number;
31
+ totalCost: number;
32
+ netProfit: number;
33
+ successRate: number;
34
+ avgReward: number;
35
+ bestBounty: number;
36
+ };
37
+ attempts: BountyAttempt[];
38
+ dailyStats: Array<{
39
+ date: string;
40
+ attempts: number;
41
+ accepted: number;
42
+ earned: number;
43
+ cost: number;
44
+ }>;
45
+ }
46
+ export declare class EarningsTracker {
47
+ private dataPath;
48
+ private data;
49
+ constructor(statePath?: string);
50
+ /**
51
+ * Record a new bounty attempt
52
+ */
53
+ startAttempt(bounty: {
54
+ id: string;
55
+ platform: string;
56
+ title: string;
57
+ reward: number;
58
+ currency: string;
59
+ }): BountyAttempt;
60
+ /**
61
+ * Mark attempt as submitted (PR created)
62
+ */
63
+ markSubmitted(bountyId: string, prUrl: string): void;
64
+ /**
65
+ * Record successful payout
66
+ */
67
+ recordSuccess(bountyId: string, payout: number, timeSpentMinutes?: number, costIncurred?: number): void;
68
+ /**
69
+ * Record rejection
70
+ */
71
+ recordRejection(bountyId: string, feedback?: string, costIncurred?: number): void;
72
+ /**
73
+ * Mark attempt as abandoned
74
+ */
75
+ abandonAttempt(bountyId: string, reason?: string): void;
76
+ getSummary(): EarningsData['summary'];
77
+ getRecentAttempts(limit?: number): BountyAttempt[];
78
+ getAttemptsByStatus(status: BountyAttempt['status']): BountyAttempt[];
79
+ getDailyStats(days?: number): EarningsData['dailyStats'];
80
+ getTotalEarned(): number;
81
+ getSuccessRate(): number;
82
+ private load;
83
+ private save;
84
+ private createEmpty;
85
+ private findAttempt;
86
+ private updateDailyStats;
87
+ }
88
+ export declare function getEarningsTracker(statePath?: string): EarningsTracker;
89
+ export declare function resetEarningsTracker(): void;
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ /**
3
+ * Genesis v14.7 — Earnings Tracker
4
+ *
5
+ * Persists all bounty earnings to state/earnings.json.
6
+ * Tracks attempts, successes, failures, and total revenue.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.EarningsTracker = void 0;
10
+ exports.getEarningsTracker = getEarningsTracker;
11
+ exports.resetEarningsTracker = resetEarningsTracker;
12
+ const fs_1 = require("fs");
13
+ const path_1 = require("path");
14
+ // ============================================================================
15
+ // Earnings Tracker
16
+ // ============================================================================
17
+ class EarningsTracker {
18
+ dataPath;
19
+ data;
20
+ constructor(statePath) {
21
+ this.dataPath = statePath || (0, path_1.join)(process.cwd(), 'state', 'earnings.json');
22
+ this.data = this.load();
23
+ }
24
+ // ==========================================================================
25
+ // Core Operations
26
+ // ==========================================================================
27
+ /**
28
+ * Record a new bounty attempt
29
+ */
30
+ startAttempt(bounty) {
31
+ const attempt = {
32
+ bountyId: bounty.id,
33
+ platform: bounty.platform,
34
+ title: bounty.title,
35
+ reward: bounty.reward,
36
+ currency: bounty.currency,
37
+ startedAt: new Date().toISOString(),
38
+ status: 'in_progress',
39
+ };
40
+ this.data.attempts.push(attempt);
41
+ this.data.summary.totalAttempts++;
42
+ this.save();
43
+ console.log(`[EarningsTracker] Started attempt: ${bounty.title} ($${bounty.reward})`);
44
+ return attempt;
45
+ }
46
+ /**
47
+ * Mark attempt as submitted (PR created)
48
+ */
49
+ markSubmitted(bountyId, prUrl) {
50
+ const attempt = this.findAttempt(bountyId);
51
+ if (!attempt) {
52
+ console.warn(`[EarningsTracker] Attempt not found: ${bountyId}`);
53
+ return;
54
+ }
55
+ attempt.status = 'submitted';
56
+ attempt.prUrl = prUrl;
57
+ this.save();
58
+ console.log(`[EarningsTracker] Submitted: ${attempt.title}`);
59
+ }
60
+ /**
61
+ * Record successful payout
62
+ */
63
+ recordSuccess(bountyId, payout, timeSpentMinutes, costIncurred) {
64
+ const attempt = this.findAttempt(bountyId);
65
+ if (!attempt) {
66
+ console.warn(`[EarningsTracker] Attempt not found: ${bountyId}`);
67
+ return;
68
+ }
69
+ attempt.status = 'accepted';
70
+ attempt.completedAt = new Date().toISOString();
71
+ attempt.payout = payout;
72
+ attempt.timeSpentMinutes = timeSpentMinutes;
73
+ attempt.costIncurred = costIncurred || 0;
74
+ // Update summary
75
+ this.data.summary.totalAccepted++;
76
+ this.data.summary.totalEarned += payout;
77
+ this.data.summary.totalCost += attempt.costIncurred;
78
+ this.data.summary.netProfit = this.data.summary.totalEarned - this.data.summary.totalCost;
79
+ this.data.summary.successRate = this.data.summary.totalAccepted / this.data.summary.totalAttempts;
80
+ this.data.summary.avgReward = this.data.summary.totalEarned / this.data.summary.totalAccepted;
81
+ this.data.summary.bestBounty = Math.max(this.data.summary.bestBounty, payout);
82
+ // Update daily stats
83
+ this.updateDailyStats(payout, attempt.costIncurred, true);
84
+ this.save();
85
+ console.log(`[EarningsTracker] ✅ Payout received: $${payout} for ${attempt.title}`);
86
+ console.log(`[EarningsTracker] Total earned: $${this.data.summary.totalEarned.toFixed(2)}`);
87
+ }
88
+ /**
89
+ * Record rejection
90
+ */
91
+ recordRejection(bountyId, feedback, costIncurred) {
92
+ const attempt = this.findAttempt(bountyId);
93
+ if (!attempt) {
94
+ console.warn(`[EarningsTracker] Attempt not found: ${bountyId}`);
95
+ return;
96
+ }
97
+ attempt.status = 'rejected';
98
+ attempt.completedAt = new Date().toISOString();
99
+ attempt.feedback = feedback;
100
+ attempt.costIncurred = costIncurred || 0;
101
+ // Update summary
102
+ this.data.summary.totalRejected++;
103
+ this.data.summary.totalCost += attempt.costIncurred;
104
+ this.data.summary.netProfit = this.data.summary.totalEarned - this.data.summary.totalCost;
105
+ this.data.summary.successRate = this.data.summary.totalAccepted / this.data.summary.totalAttempts;
106
+ // Update daily stats
107
+ this.updateDailyStats(0, attempt.costIncurred, false);
108
+ this.save();
109
+ console.log(`[EarningsTracker] ❌ Rejected: ${attempt.title}`);
110
+ if (feedback) {
111
+ console.log(`[EarningsTracker] Feedback: ${feedback}`);
112
+ }
113
+ }
114
+ /**
115
+ * Mark attempt as abandoned
116
+ */
117
+ abandonAttempt(bountyId, reason) {
118
+ const attempt = this.findAttempt(bountyId);
119
+ if (!attempt)
120
+ return;
121
+ attempt.status = 'abandoned';
122
+ attempt.completedAt = new Date().toISOString();
123
+ attempt.feedback = reason;
124
+ this.data.summary.totalAbandoned++;
125
+ this.save();
126
+ console.log(`[EarningsTracker] Abandoned: ${attempt.title}`);
127
+ }
128
+ // ==========================================================================
129
+ // Queries
130
+ // ==========================================================================
131
+ getSummary() {
132
+ return { ...this.data.summary };
133
+ }
134
+ getRecentAttempts(limit = 10) {
135
+ return this.data.attempts.slice(-limit).reverse();
136
+ }
137
+ getAttemptsByStatus(status) {
138
+ return this.data.attempts.filter(a => a.status === status);
139
+ }
140
+ getDailyStats(days = 7) {
141
+ return this.data.dailyStats.slice(-days);
142
+ }
143
+ getTotalEarned() {
144
+ return this.data.summary.totalEarned;
145
+ }
146
+ getSuccessRate() {
147
+ return this.data.summary.successRate;
148
+ }
149
+ // ==========================================================================
150
+ // Persistence
151
+ // ==========================================================================
152
+ load() {
153
+ if (!(0, fs_1.existsSync)(this.dataPath)) {
154
+ return this.createEmpty();
155
+ }
156
+ try {
157
+ const raw = (0, fs_1.readFileSync)(this.dataPath, 'utf-8');
158
+ return JSON.parse(raw);
159
+ }
160
+ catch (error) {
161
+ console.warn('[EarningsTracker] Failed to load earnings data, creating new');
162
+ return this.createEmpty();
163
+ }
164
+ }
165
+ save() {
166
+ this.data.lastUpdated = new Date().toISOString();
167
+ try {
168
+ const dir = (0, path_1.dirname)(this.dataPath);
169
+ if (!(0, fs_1.existsSync)(dir)) {
170
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
171
+ }
172
+ (0, fs_1.writeFileSync)(this.dataPath, JSON.stringify(this.data, null, 2));
173
+ }
174
+ catch (error) {
175
+ console.error('[EarningsTracker] Failed to save earnings data:', error);
176
+ }
177
+ }
178
+ createEmpty() {
179
+ return {
180
+ version: '14.7.0',
181
+ lastUpdated: new Date().toISOString(),
182
+ summary: {
183
+ totalAttempts: 0,
184
+ totalAccepted: 0,
185
+ totalRejected: 0,
186
+ totalAbandoned: 0,
187
+ totalEarned: 0,
188
+ totalCost: 0,
189
+ netProfit: 0,
190
+ successRate: 0,
191
+ avgReward: 0,
192
+ bestBounty: 0,
193
+ },
194
+ attempts: [],
195
+ dailyStats: [],
196
+ };
197
+ }
198
+ findAttempt(bountyId) {
199
+ return this.data.attempts.find(a => a.bountyId === bountyId);
200
+ }
201
+ updateDailyStats(earned, cost, accepted) {
202
+ const today = new Date().toISOString().split('T')[0];
203
+ let todayStats = this.data.dailyStats.find(s => s.date === today);
204
+ if (!todayStats) {
205
+ todayStats = { date: today, attempts: 0, accepted: 0, earned: 0, cost: 0 };
206
+ this.data.dailyStats.push(todayStats);
207
+ }
208
+ todayStats.attempts++;
209
+ if (accepted)
210
+ todayStats.accepted++;
211
+ todayStats.earned += earned;
212
+ todayStats.cost += cost;
213
+ // Keep only last 90 days
214
+ if (this.data.dailyStats.length > 90) {
215
+ this.data.dailyStats = this.data.dailyStats.slice(-90);
216
+ }
217
+ }
218
+ }
219
+ exports.EarningsTracker = EarningsTracker;
220
+ // ============================================================================
221
+ // Factory
222
+ // ============================================================================
223
+ let trackerInstance = null;
224
+ function getEarningsTracker(statePath) {
225
+ if (!trackerInstance) {
226
+ trackerInstance = new EarningsTracker(statePath);
227
+ }
228
+ return trackerInstance;
229
+ }
230
+ function resetEarningsTracker() {
231
+ trackerInstance = null;
232
+ }
@@ -36,3 +36,7 @@ export { retry, retryOrThrow, withTimeout, sleep, CircuitBreaker, RateLimiter, g
36
36
  export type { RetryConfig, RetryResult, CircuitBreakerConfig, } from './retry.js';
37
37
  export { DefiExecutor, getDefiExecutor, resetDefiExecutor, } from './defi-executor.js';
38
38
  export type { DefiOperation, ProtocolConfig, ExecuteResult, } from './defi-executor.js';
39
+ export { PRPipeline, getPRPipeline, createPRPipeline, } from './pr-pipeline.js';
40
+ export type { PRSubmission, CodeChange, PRPipelineConfig, } from './pr-pipeline.js';
41
+ export { EarningsTracker, getEarningsTracker, resetEarningsTracker, } from './earnings-tracker.js';
42
+ export type { BountyAttempt, EarningsData, } from './earnings-tracker.js';
@@ -7,7 +7,7 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.retry = exports.usdToEth = exports.ethToUsd = exports.getEthPrice = exports.resetPriceFeed = exports.getPriceFeed = exports.installSignalHandlers = exports.getRecoveryInfo = exports.isEmergencyActive = exports.gracefulShutdown = exports.triggerEmergency = exports.getEmergencyManager = exports.resetPositionTracker = exports.getPositionTracker = exports.PositionTracker = exports.resetGasManager = exports.getGasManager = exports.GasManager = exports.getStatusLine = exports.isHealthy = exports.formatStatus = exports.getSystemStatus = exports.checkHealth = exports.createRevenueAlertHandler = exports.createBalanceAlertHandler = exports.resetAlertSystem = exports.getAlertSystem = exports.AlertSystem = exports.resetRevenueTracker = exports.getRevenueTracker = exports.RevenueTracker = exports.resetBalanceMonitor = exports.getBalanceMonitor = exports.BalanceMonitor = exports.createPaymentWatcher = exports.PaymentWatcher = exports.getPaymentVerifier = exports.PaymentVerifier = exports.getDefiConnector = exports.getCloudflareConnector = exports.getDeworkConnector = exports.StatePersistence = exports.resetStatePersistence = exports.getStatePersistence = exports.resetWallet = exports.getLiveWallet = exports.getBootResult = exports.isLive = exports.boot = exports.bootLiveEconomy = void 0;
10
- exports.resetDefiExecutor = exports.getDefiExecutor = exports.DefiExecutor = exports.throttle = exports.debounce = exports.getRateLimiter = exports.getCircuitBreaker = exports.RateLimiter = exports.CircuitBreaker = exports.sleep = exports.withTimeout = exports.retryOrThrow = void 0;
10
+ exports.resetEarningsTracker = exports.getEarningsTracker = exports.EarningsTracker = exports.createPRPipeline = exports.getPRPipeline = exports.PRPipeline = exports.resetDefiExecutor = exports.getDefiExecutor = exports.DefiExecutor = exports.throttle = exports.debounce = exports.getRateLimiter = exports.getCircuitBreaker = exports.RateLimiter = exports.CircuitBreaker = exports.sleep = exports.withTimeout = exports.retryOrThrow = void 0;
11
11
  // Boot sequence
12
12
  var boot_js_1 = require("./boot.js");
13
13
  Object.defineProperty(exports, "bootLiveEconomy", { enumerable: true, get: function () { return boot_js_1.bootLiveEconomy; } });
@@ -100,3 +100,13 @@ var defi_executor_js_1 = require("./defi-executor.js");
100
100
  Object.defineProperty(exports, "DefiExecutor", { enumerable: true, get: function () { return defi_executor_js_1.DefiExecutor; } });
101
101
  Object.defineProperty(exports, "getDefiExecutor", { enumerable: true, get: function () { return defi_executor_js_1.getDefiExecutor; } });
102
102
  Object.defineProperty(exports, "resetDefiExecutor", { enumerable: true, get: function () { return defi_executor_js_1.resetDefiExecutor; } });
103
+ // v14.7: PR Pipeline for bounty submissions
104
+ var pr_pipeline_js_1 = require("./pr-pipeline.js");
105
+ Object.defineProperty(exports, "PRPipeline", { enumerable: true, get: function () { return pr_pipeline_js_1.PRPipeline; } });
106
+ Object.defineProperty(exports, "getPRPipeline", { enumerable: true, get: function () { return pr_pipeline_js_1.getPRPipeline; } });
107
+ Object.defineProperty(exports, "createPRPipeline", { enumerable: true, get: function () { return pr_pipeline_js_1.createPRPipeline; } });
108
+ // v14.7: Earnings Tracker for revenue persistence
109
+ var earnings_tracker_js_1 = require("./earnings-tracker.js");
110
+ Object.defineProperty(exports, "EarningsTracker", { enumerable: true, get: function () { return earnings_tracker_js_1.EarningsTracker; } });
111
+ Object.defineProperty(exports, "getEarningsTracker", { enumerable: true, get: function () { return earnings_tracker_js_1.getEarningsTracker; } });
112
+ Object.defineProperty(exports, "resetEarningsTracker", { enumerable: true, get: function () { return earnings_tracker_js_1.resetEarningsTracker; } });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Genesis v14.7 — PR Submission Pipeline
3
+ *
4
+ * Autonomous workflow to complete bounties:
5
+ * 1. Fork repository
6
+ * 2. Create branch
7
+ * 3. Generate/apply code changes
8
+ * 4. Submit PR with formatted description
9
+ * 5. Monitor for review feedback
10
+ *
11
+ * Uses GitHub MCP for all git operations.
12
+ */
13
+ import type { Bounty } from '../generators/bounty-hunter.js';
14
+ export interface PRSubmission {
15
+ bountyId: string;
16
+ prUrl: string;
17
+ prNumber: number;
18
+ repo: string;
19
+ branch: string;
20
+ status: 'submitted' | 'reviewing' | 'changes_requested' | 'merged' | 'closed';
21
+ submittedAt: Date;
22
+ lastChecked?: Date;
23
+ feedback?: string[];
24
+ }
25
+ export interface CodeChange {
26
+ path: string;
27
+ content: string;
28
+ operation: 'create' | 'update' | 'delete';
29
+ }
30
+ export interface PRPipelineConfig {
31
+ githubUsername: string;
32
+ prTitlePrefix?: string;
33
+ autoPush?: boolean;
34
+ dryRun?: boolean;
35
+ }
36
+ export declare class PRPipeline {
37
+ private mcp;
38
+ private config;
39
+ private submissions;
40
+ constructor(config: PRPipelineConfig);
41
+ /**
42
+ * Execute the full bounty completion pipeline
43
+ */
44
+ submitBounty(bounty: Bounty, changes: CodeChange[], description: string): Promise<PRSubmission | null>;
45
+ private forkRepository;
46
+ private createBranch;
47
+ private applyChange;
48
+ private createPullRequest;
49
+ /**
50
+ * Check status of submitted PRs
51
+ */
52
+ checkPRStatus(bountyId: string): Promise<PRSubmission | null>;
53
+ /**
54
+ * Check all submitted PRs
55
+ */
56
+ checkAllPRs(): Promise<PRSubmission[]>;
57
+ private generateBranchName;
58
+ private generatePRTitle;
59
+ private generatePRBody;
60
+ getSubmission(bountyId: string): PRSubmission | undefined;
61
+ getAllSubmissions(): PRSubmission[];
62
+ getStats(): {
63
+ total: number;
64
+ submitted: number;
65
+ reviewing: number;
66
+ merged: number;
67
+ closed: number;
68
+ };
69
+ }
70
+ export declare function getPRPipeline(config?: PRPipelineConfig): PRPipeline;
71
+ export declare function createPRPipeline(config: PRPipelineConfig): PRPipeline;
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ /**
3
+ * Genesis v14.7 — PR Submission Pipeline
4
+ *
5
+ * Autonomous workflow to complete bounties:
6
+ * 1. Fork repository
7
+ * 2. Create branch
8
+ * 3. Generate/apply code changes
9
+ * 4. Submit PR with formatted description
10
+ * 5. Monitor for review feedback
11
+ *
12
+ * Uses GitHub MCP for all git operations.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.PRPipeline = void 0;
16
+ exports.getPRPipeline = getPRPipeline;
17
+ exports.createPRPipeline = createPRPipeline;
18
+ const index_js_1 = require("../../mcp/index.js");
19
+ // ============================================================================
20
+ // PR Pipeline
21
+ // ============================================================================
22
+ class PRPipeline {
23
+ mcp = (0, index_js_1.getMCPClient)();
24
+ config;
25
+ submissions = new Map();
26
+ constructor(config) {
27
+ this.config = {
28
+ prTitlePrefix: config.prTitlePrefix ?? '[Genesis]',
29
+ autoPush: config.autoPush ?? true,
30
+ dryRun: config.dryRun ?? false,
31
+ ...config,
32
+ };
33
+ }
34
+ // ==========================================================================
35
+ // Main Pipeline
36
+ // ==========================================================================
37
+ /**
38
+ * Execute the full bounty completion pipeline
39
+ */
40
+ async submitBounty(bounty, changes, description) {
41
+ const meta = bounty.sourceMetadata;
42
+ if (!meta?.org || !meta?.repo) {
43
+ console.error('[PRPipeline] Bounty missing source metadata:', bounty.id);
44
+ return null;
45
+ }
46
+ const owner = meta.org;
47
+ const repo = meta.repo;
48
+ const branchName = this.generateBranchName(bounty);
49
+ console.log(`[PRPipeline] Starting submission for: ${bounty.title}`);
50
+ console.log(`[PRPipeline] Target: ${owner}/${repo}`);
51
+ try {
52
+ // Step 1: Fork the repository
53
+ console.log('[PRPipeline] Step 1: Forking repository...');
54
+ const forkResult = await this.forkRepository(owner, repo);
55
+ if (!forkResult.success) {
56
+ console.error('[PRPipeline] Fork failed:', forkResult.error);
57
+ return null;
58
+ }
59
+ // Step 2: Create branch
60
+ console.log(`[PRPipeline] Step 2: Creating branch ${branchName}...`);
61
+ const branchResult = await this.createBranch(this.config.githubUsername, repo, branchName);
62
+ if (!branchResult.success) {
63
+ console.error('[PRPipeline] Branch creation failed:', branchResult.error);
64
+ return null;
65
+ }
66
+ // Step 3: Apply changes
67
+ console.log(`[PRPipeline] Step 3: Applying ${changes.length} changes...`);
68
+ for (const change of changes) {
69
+ const changeResult = await this.applyChange(this.config.githubUsername, repo, branchName, change);
70
+ if (!changeResult.success) {
71
+ console.error(`[PRPipeline] Failed to apply change to ${change.path}:`, changeResult.error);
72
+ return null;
73
+ }
74
+ }
75
+ // Step 4: Create PR
76
+ console.log('[PRPipeline] Step 4: Creating pull request...');
77
+ const prTitle = this.generatePRTitle(bounty);
78
+ const prBody = this.generatePRBody(bounty, description);
79
+ const prResult = await this.createPullRequest(owner, repo, prTitle, prBody, `${this.config.githubUsername}:${branchName}`, 'main');
80
+ if (!prResult.success || !prResult.data) {
81
+ console.error('[PRPipeline] PR creation failed:', prResult.error);
82
+ return null;
83
+ }
84
+ // Record submission
85
+ const submission = {
86
+ bountyId: bounty.id,
87
+ prUrl: prResult.data.url,
88
+ prNumber: prResult.data.number,
89
+ repo: `${owner}/${repo}`,
90
+ branch: branchName,
91
+ status: 'submitted',
92
+ submittedAt: new Date(),
93
+ };
94
+ this.submissions.set(bounty.id, submission);
95
+ console.log(`[PRPipeline] ✅ PR submitted: ${submission.prUrl}`);
96
+ return submission;
97
+ }
98
+ catch (error) {
99
+ console.error('[PRPipeline] Pipeline error:', error);
100
+ return null;
101
+ }
102
+ }
103
+ // ==========================================================================
104
+ // GitHub Operations via MCP
105
+ // ==========================================================================
106
+ async forkRepository(owner, repo) {
107
+ if (this.config.dryRun) {
108
+ console.log(`[PRPipeline] [DRY RUN] Would fork ${owner}/${repo}`);
109
+ return { success: true };
110
+ }
111
+ const result = await this.mcp.call('github', 'fork_repository', {
112
+ owner,
113
+ repo,
114
+ });
115
+ if (!result.success) {
116
+ // Fork might already exist, try to continue
117
+ if (result.error?.includes('already exists')) {
118
+ return { success: true };
119
+ }
120
+ return { success: false, error: result.error };
121
+ }
122
+ return { success: true };
123
+ }
124
+ async createBranch(owner, repo, branchName) {
125
+ if (this.config.dryRun) {
126
+ console.log(`[PRPipeline] [DRY RUN] Would create branch ${branchName}`);
127
+ return { success: true };
128
+ }
129
+ // First get the default branch SHA
130
+ const repoResult = await this.mcp.call('github', 'get_file_contents', {
131
+ owner,
132
+ repo,
133
+ path: '',
134
+ });
135
+ // Create branch from main
136
+ const result = await this.mcp.call('github', 'create_branch', {
137
+ owner,
138
+ repo,
139
+ branch: branchName,
140
+ from_branch: 'main',
141
+ });
142
+ if (!result.success) {
143
+ // Branch might already exist
144
+ if (result.error?.includes('already exists')) {
145
+ return { success: true };
146
+ }
147
+ return { success: false, error: result.error };
148
+ }
149
+ return { success: true };
150
+ }
151
+ async applyChange(owner, repo, branch, change) {
152
+ if (this.config.dryRun) {
153
+ console.log(`[PRPipeline] [DRY RUN] Would ${change.operation} ${change.path}`);
154
+ return { success: true };
155
+ }
156
+ const result = await this.mcp.call('github', 'create_or_update_file', {
157
+ owner,
158
+ repo,
159
+ path: change.path,
160
+ content: change.content,
161
+ message: `[Genesis] ${change.operation}: ${change.path}`,
162
+ branch,
163
+ });
164
+ if (!result.success) {
165
+ return { success: false, error: result.error };
166
+ }
167
+ return { success: true };
168
+ }
169
+ async createPullRequest(owner, repo, title, body, head, base) {
170
+ if (this.config.dryRun) {
171
+ console.log(`[PRPipeline] [DRY RUN] Would create PR: ${title}`);
172
+ return { success: true, data: { url: 'https://github.com/dry-run', number: 0 } };
173
+ }
174
+ const result = await this.mcp.call('github', 'create_pull_request', {
175
+ owner,
176
+ repo,
177
+ title,
178
+ body,
179
+ head,
180
+ base,
181
+ });
182
+ if (!result.success) {
183
+ return { success: false, error: result.error };
184
+ }
185
+ return {
186
+ success: true,
187
+ data: {
188
+ url: result.data?.html_url || result.data?.url,
189
+ number: result.data?.number,
190
+ },
191
+ };
192
+ }
193
+ // ==========================================================================
194
+ // PR Monitoring
195
+ // ==========================================================================
196
+ /**
197
+ * Check status of submitted PRs
198
+ */
199
+ async checkPRStatus(bountyId) {
200
+ const submission = this.submissions.get(bountyId);
201
+ if (!submission)
202
+ return null;
203
+ const [owner, repo] = submission.repo.split('/');
204
+ const result = await this.mcp.call('github', 'get_pull_request', {
205
+ owner,
206
+ repo,
207
+ pull_number: submission.prNumber,
208
+ });
209
+ if (!result.success) {
210
+ console.warn(`[PRPipeline] Failed to check PR status:`, result.error);
211
+ return submission;
212
+ }
213
+ const pr = result.data;
214
+ // Update status based on PR state
215
+ if (pr.merged) {
216
+ submission.status = 'merged';
217
+ }
218
+ else if (pr.state === 'closed') {
219
+ submission.status = 'closed';
220
+ }
221
+ else if (pr.review_comments > 0 || pr.requested_changes) {
222
+ submission.status = 'changes_requested';
223
+ }
224
+ else {
225
+ submission.status = 'reviewing';
226
+ }
227
+ submission.lastChecked = new Date();
228
+ // Get review comments
229
+ const commentsResult = await this.mcp.call('github', 'get_pull_request_comments', {
230
+ owner,
231
+ repo,
232
+ pull_number: submission.prNumber,
233
+ });
234
+ if (commentsResult.success && commentsResult.data) {
235
+ submission.feedback = commentsResult.data.map((c) => c.body).slice(-5);
236
+ }
237
+ return submission;
238
+ }
239
+ /**
240
+ * Check all submitted PRs
241
+ */
242
+ async checkAllPRs() {
243
+ const updated = [];
244
+ for (const [bountyId] of this.submissions) {
245
+ const result = await this.checkPRStatus(bountyId);
246
+ if (result) {
247
+ updated.push(result);
248
+ }
249
+ }
250
+ return updated;
251
+ }
252
+ // ==========================================================================
253
+ // Helpers
254
+ // ==========================================================================
255
+ generateBranchName(bounty) {
256
+ const slug = bounty.title
257
+ .toLowerCase()
258
+ .replace(/[^a-z0-9]+/g, '-')
259
+ .slice(0, 40);
260
+ const timestamp = Date.now().toString(36);
261
+ return `genesis/${slug}-${timestamp}`;
262
+ }
263
+ generatePRTitle(bounty) {
264
+ return `${this.config.prTitlePrefix} ${bounty.title}`;
265
+ }
266
+ generatePRBody(bounty, description) {
267
+ const meta = bounty.sourceMetadata;
268
+ const issueRef = meta?.issueNumber ? `\n\nCloses #${meta.issueNumber}` : '';
269
+ return `## Summary
270
+
271
+ ${description}
272
+
273
+ ## Related Issue
274
+
275
+ ${bounty.submissionUrl || 'N/A'}${issueRef}
276
+
277
+ ## Changes
278
+
279
+ This PR was generated autonomously by Genesis AI to complete a bounty.
280
+
281
+ ---
282
+
283
+ 🤖 *Generated by [Genesis AI](https://github.com/rossignoliluca/genesis) v14.7*
284
+ `;
285
+ }
286
+ // ==========================================================================
287
+ // State Access
288
+ // ==========================================================================
289
+ getSubmission(bountyId) {
290
+ return this.submissions.get(bountyId);
291
+ }
292
+ getAllSubmissions() {
293
+ return [...this.submissions.values()];
294
+ }
295
+ getStats() {
296
+ const all = this.getAllSubmissions();
297
+ return {
298
+ total: all.length,
299
+ submitted: all.filter(s => s.status === 'submitted').length,
300
+ reviewing: all.filter(s => s.status === 'reviewing').length,
301
+ merged: all.filter(s => s.status === 'merged').length,
302
+ closed: all.filter(s => s.status === 'closed').length,
303
+ };
304
+ }
305
+ }
306
+ exports.PRPipeline = PRPipeline;
307
+ // ============================================================================
308
+ // Factory
309
+ // ============================================================================
310
+ let pipelineInstance = null;
311
+ function getPRPipeline(config) {
312
+ if (!pipelineInstance && config) {
313
+ pipelineInstance = new PRPipeline(config);
314
+ }
315
+ if (!pipelineInstance) {
316
+ throw new Error('PRPipeline not initialized. Call with config first.');
317
+ }
318
+ return pipelineInstance;
319
+ }
320
+ function createPRPipeline(config) {
321
+ return new PRPipeline(config);
322
+ }
package/dist/src/index.js CHANGED
@@ -1698,6 +1698,93 @@ ${c('Examples:', 'cyan')}
1698
1698
  }
1699
1699
  }
1700
1700
  // ============================================================================
1701
+ // Bounty Command (v14.7: Autonomous Bounty Hunting)
1702
+ // ============================================================================
1703
+ async function cmdBounty(subcommand, options) {
1704
+ const { getBountyHunter } = await import('./economy/generators/bounty-hunter.js');
1705
+ const { getEarningsTracker } = await import('./economy/live/earnings-tracker.js');
1706
+ const hunter = getBountyHunter();
1707
+ const earnings = getEarningsTracker();
1708
+ switch (subcommand) {
1709
+ case 'scan':
1710
+ // Scan for new bounties
1711
+ console.log(c('Scanning for bounties...', 'cyan'));
1712
+ const discovered = await hunter.scan();
1713
+ console.log(`Found ${discovered.length} new bounties:`);
1714
+ discovered.slice(0, 10).forEach((b, i) => {
1715
+ console.log(` ${i + 1}. [$${b.reward}] ${b.title.slice(0, 50)} (${b.platform})`);
1716
+ });
1717
+ break;
1718
+ case 'list':
1719
+ // List cached bounties
1720
+ const stats = hunter.getStats();
1721
+ console.log(c('Bounty Statistics:', 'cyan'));
1722
+ console.log(` Discovered: ${stats.bountiesDiscovered}`);
1723
+ console.log(` Claimed: ${stats.bountiesClaimed}`);
1724
+ console.log(` Submitted: ${stats.bountiesSubmitted}`);
1725
+ console.log(` Accepted: ${stats.bountiesAccepted}`);
1726
+ console.log(` Success Rate: ${(stats.successRate * 100).toFixed(1)}%`);
1727
+ console.log(` Total Earned: $${stats.totalEarned.toFixed(2)}`);
1728
+ break;
1729
+ case 'earnings':
1730
+ // Show earnings summary
1731
+ const summary = earnings.getSummary();
1732
+ console.log(c('Earnings Summary:', 'cyan'));
1733
+ console.log(` Total Attempts: ${summary.totalAttempts}`);
1734
+ console.log(` Accepted: ${summary.totalAccepted}`);
1735
+ console.log(` Rejected: ${summary.totalRejected}`);
1736
+ console.log(` Total Earned: ${c('$' + summary.totalEarned.toFixed(2), 'green')}`);
1737
+ console.log(` Total Cost: $${summary.totalCost.toFixed(2)}`);
1738
+ console.log(` Net Profit: ${c('$' + summary.netProfit.toFixed(2), summary.netProfit >= 0 ? 'green' : 'red')}`);
1739
+ console.log(` Success Rate: ${(summary.successRate * 100).toFixed(1)}%`);
1740
+ console.log(` Best Bounty: $${summary.bestBounty.toFixed(2)}`);
1741
+ break;
1742
+ case 'select':
1743
+ // Select best bounty to work on
1744
+ const best = hunter.selectBest();
1745
+ if (best) {
1746
+ console.log(c('Best bounty to work on:', 'cyan'));
1747
+ console.log(` Title: ${best.title}`);
1748
+ console.log(` Reward: $${best.reward}`);
1749
+ console.log(` Platform: ${best.platform}`);
1750
+ console.log(` Category: ${best.category}`);
1751
+ console.log(` Difficulty: ${best.difficulty}`);
1752
+ console.log(` URL: ${best.submissionUrl || 'N/A'}`);
1753
+ }
1754
+ else {
1755
+ console.log('No suitable bounties found. Run "genesis bounty scan" first.');
1756
+ }
1757
+ break;
1758
+ case 'claim': {
1759
+ // Claim a bounty (start working on it)
1760
+ const bountyId = options.id;
1761
+ if (!bountyId) {
1762
+ console.error('Usage: genesis bounty claim --id <bounty-id>');
1763
+ process.exit(1);
1764
+ }
1765
+ const claimed = await hunter.claim(bountyId);
1766
+ if (claimed) {
1767
+ console.log(c('Bounty claimed! Start working on it.', 'green'));
1768
+ }
1769
+ else {
1770
+ console.error('Failed to claim bounty. Check the ID.');
1771
+ }
1772
+ break;
1773
+ }
1774
+ default:
1775
+ console.log(c('Genesis Bounty Hunter', 'cyan'));
1776
+ console.log('');
1777
+ console.log('Usage:');
1778
+ console.log(' genesis bounty scan Scan for new bounties');
1779
+ console.log(' genesis bounty list Show bounty statistics');
1780
+ console.log(' genesis bounty earnings Show earnings summary');
1781
+ console.log(' genesis bounty select Select best bounty to work on');
1782
+ console.log(' genesis bounty claim --id <id> Claim a bounty');
1783
+ console.log('');
1784
+ console.log('Supported platforms: Algora, GitHub, Gitcoin, DeWork');
1785
+ }
1786
+ }
1787
+ // ============================================================================
1701
1788
  // Agents Command (v10.4.2: Parallel Agent Execution)
1702
1789
  // ============================================================================
1703
1790
  async function cmdAgents(subcommand, options) {
@@ -2057,6 +2144,10 @@ async function main() {
2057
2144
  // v14.2: Agentic chat interface with Claude Code-like capabilities
2058
2145
  await cmdAgentic(positional, options);
2059
2146
  break;
2147
+ case 'bounty':
2148
+ // v14.7: Autonomous bounty hunting
2149
+ await cmdBounty(positional, options);
2150
+ break;
2060
2151
  default:
2061
2152
  console.error(c(`Unknown command: ${command}`, 'red'));
2062
2153
  console.log('Use "genesis help" for usage information');
@@ -614,8 +614,9 @@ class RealMCPClient {
614
614
  this.mode = this.config.mode;
615
615
  this.manager = new MCPConnectionManager(this.config.timeout, this.config.logCalls);
616
616
  }
617
- async call(server, tool, params, options = {}, _isRetry = false) {
617
+ async call(server, tool, params, options = {}, retryCount = 0) {
618
618
  const startTime = Date.now();
619
+ const maxRetries = options.retries ?? 3;
619
620
  if (this.config.onCall) {
620
621
  this.config.onCall(server, tool, params);
621
622
  }
@@ -637,14 +638,22 @@ class RealMCPClient {
637
638
  }
638
639
  catch (error) {
639
640
  const errorMessage = error instanceof Error ? error.message : String(error);
640
- // v7.18: Try fallback for web search tools
641
- if (!_isRetry) {
641
+ // v14.6.4: Retry with exponential backoff for transient errors
642
+ const isTransient = this.isTransientError(errorMessage);
643
+ if (retryCount < maxRetries && isTransient) {
644
+ const backoffMs = Math.min(1000 * Math.pow(2, retryCount) + Math.random() * 100, 30000);
645
+ console.log(`[MCP] ${server}.${tool} transient error, retry ${retryCount + 1}/${maxRetries} in ${Math.round(backoffMs)}ms`);
646
+ await new Promise(r => setTimeout(r, backoffMs));
647
+ return this.call(server, tool, params, options, retryCount + 1);
648
+ }
649
+ // v7.18: Try fallback for web search tools (only on first attempt)
650
+ if (retryCount === 0) {
642
651
  const fallback = getNextFallbackServer(server, tool);
643
652
  if (fallback) {
644
653
  console.log(`[MCP] ${server}.${tool} failed (${isRateLimitError(errorMessage) ? 'rate limit' : 'error'}), trying ${fallback.server}.${fallback.tool}...`);
645
654
  // Adapt params for the new tool if needed
646
655
  const adaptedParams = this.adaptParamsForFallback(tool, fallback.tool, params);
647
- return this.call(fallback.server, fallback.tool, adaptedParams, options, true);
656
+ return this.call(fallback.server, fallback.tool, adaptedParams, options, 0);
648
657
  }
649
658
  }
650
659
  const result = {
@@ -662,6 +671,27 @@ class RealMCPClient {
662
671
  return result;
663
672
  }
664
673
  }
674
+ /**
675
+ * Check if an error is transient and should be retried.
676
+ */
677
+ isTransientError(errorMessage) {
678
+ const transientPatterns = [
679
+ 'ECONNRESET',
680
+ 'ETIMEDOUT',
681
+ 'ECONNREFUSED',
682
+ 'socket hang up',
683
+ '429',
684
+ 'rate limit',
685
+ 'too many requests',
686
+ 'timeout',
687
+ 'ENOTFOUND',
688
+ 'EAI_AGAIN',
689
+ 'network error',
690
+ 'connection reset',
691
+ ];
692
+ const lowerError = errorMessage.toLowerCase();
693
+ return transientPatterns.some(p => lowerError.includes(p.toLowerCase()));
694
+ }
665
695
  /**
666
696
  * Adapt parameters when falling back to a different web search provider.
667
697
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-ai-cli",
3
- "version": "14.6.4",
3
+ "version": "14.7.0",
4
4
  "description": "Fully Autonomous AI System with RSI (Recursive Self-Improvement) - Self-funding, Self-deploying, Production Memory, A2A Protocol & Governance",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",