genesis-ai-cli 14.4.1 → 14.5.1

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.
@@ -486,8 +486,8 @@ class AgenticToolExecutor {
486
486
  async handleMemory(args) {
487
487
  const memory = this.memory;
488
488
  // Normalize action - support common aliases
489
+ const action = String(args.action || '').toLowerCase();
489
490
  const normalizedAction = (() => {
490
- const action = args.action?.toLowerCase() || '';
491
491
  if (['store', 'save', 'remember', 'add', 'write'].includes(action))
492
492
  return 'store';
493
493
  if (['search', 'query', 'find', 'retrieve', 'recall', 'get', 'read', 'load', 'fetch'].includes(action))
@@ -496,24 +496,26 @@ class AgenticToolExecutor {
496
496
  return 'list';
497
497
  return action;
498
498
  })();
499
+ // v14.5.1: Accept multiple parameter names for content
500
+ const content = String(args.content || args.text || args.message || args.data || args.value || args.input || '');
501
+ const query = String(args.query || args.search || args.q || content || '');
502
+ const tags = String(args.tags || args.tag || args.categories || '');
499
503
  switch (normalizedAction) {
500
504
  case 'store':
501
- if (!args.content)
502
- return 'Missing content to store';
505
+ if (!content)
506
+ return 'Missing content to store. Use: {"action":"store","content":"text to remember"}';
503
507
  // Use remember for simple episodic storage
504
508
  memory.remember({
505
- what: args.content,
506
- tags: args.tags?.split(',').map(t => t.trim()) || [],
509
+ what: content,
510
+ tags: tags ? tags.split(',').map(t => t.trim()) : [],
507
511
  });
508
- return 'Stored in memory';
512
+ return `Stored in memory: "${content.slice(0, 100)}${content.length > 100 ? '...' : ''}"`;
509
513
  case 'search':
510
- // If no query but has content, use content as query
511
- const searchQuery = args.query || args.content;
512
- if (!searchQuery)
513
- return 'Missing search query';
514
- const results = await memory.recall(searchQuery, { limit: 5 });
514
+ if (!query)
515
+ return 'Missing search query. Use: {"action":"search","query":"what to find"}';
516
+ const results = await memory.recall(query, { limit: 5 });
515
517
  if (!results || results.length === 0) {
516
- return 'No memories found matching query';
518
+ return `No memories found matching: "${query}"`;
517
519
  }
518
520
  return results.map((r, i) => {
519
521
  const text = typeof r === 'string' ? r : (r.content || r.text || JSON.stringify(r));
@@ -523,7 +525,7 @@ class AgenticToolExecutor {
523
525
  const stats = memory.getStats();
524
526
  return `Memory stats: ${JSON.stringify(stats, null, 2)}`;
525
527
  default:
526
- return `Unknown memory action: ${args.action}. Valid actions: store, search, list (or aliases: save, retrieve, recall, get, etc.)`;
528
+ return `Unknown memory action: "${action}". Valid actions: store, search, list (aliases: save, retrieve, recall, get, remember)`;
527
529
  }
528
530
  }
529
531
  }
@@ -572,26 +574,52 @@ ${(0, ui_js_1.muted)('Claude Code-like capabilities:')}
572
574
  buildSystemPrompt() {
573
575
  return `You are Genesis, an autonomous AI agent with full agentic capabilities.
574
576
 
575
- You have access to these tools:
577
+ ## Available Tools
576
578
  ${AGENTIC_TOOLS.map(t => `- ${t.name}: ${t.description}`).join('\n')}
577
579
 
578
- Current working directory: ${this.config.cwd}
579
- Current date: ${new Date().toISOString().split('T')[0]}
580
+ ## Tool Call Format
581
+ When you need to use a tool, respond with ONLY a JSON block:
582
+ \`\`\`tool
583
+ {"name": "ToolName", "arguments": {"param": "value"}}
584
+ \`\`\`
580
585
 
581
- Guidelines:
582
- 1. Use tools proactively to accomplish tasks
583
- 2. Read files before editing them
584
- 3. Use Glob/Grep to explore codebases
585
- 4. Execute bash commands for git, npm, etc.
586
- 5. Break complex tasks into steps
587
- 6. Store important information in memory
586
+ ## Common Tool Examples
587
+
588
+ Read a file:
589
+ \`\`\`tool
590
+ {"name": "Read", "arguments": {"path": "package.json"}}
591
+ \`\`\`
588
592
 
589
- When you need to use a tool, respond with a JSON tool call in this format:
593
+ Search for files:
590
594
  \`\`\`tool
591
- {"name": "ToolName", "arguments": {...}}
595
+ {"name": "Glob", "arguments": {"pattern": "**/*.ts"}}
592
596
  \`\`\`
593
597
 
594
- You can make multiple tool calls in sequence. After each tool result, continue your work.`;
598
+ Run bash command:
599
+ \`\`\`tool
600
+ {"name": "Bash", "arguments": {"command": "npm test"}}
601
+ \`\`\`
602
+
603
+ Store in memory:
604
+ \`\`\`tool
605
+ {"name": "Memory", "arguments": {"action": "store", "content": "Important finding..."}}
606
+ \`\`\`
607
+
608
+ Search memory:
609
+ \`\`\`tool
610
+ {"name": "Memory", "arguments": {"action": "search", "query": "what I learned about..."}}
611
+ \`\`\`
612
+
613
+ ## Context
614
+ Working directory: ${this.config.cwd}
615
+ Date: ${new Date().toISOString().split('T')[0]}
616
+
617
+ ## Guidelines
618
+ 1. Use tools to accomplish tasks - don't just explain what you would do
619
+ 2. Read files before editing them
620
+ 3. Use Glob to find files, Grep to search content
621
+ 4. After using tools, provide a summary of findings or results
622
+ 5. When task is complete, provide a final answer WITHOUT a tool block`;
595
623
  }
596
624
  async chatLoop() {
597
625
  return new Promise((resolve) => {
@@ -697,7 +725,7 @@ ${colorize('Commands:', 'cyan')}
697
725
  try {
698
726
  let continueLoop = true;
699
727
  let iterations = 0;
700
- const maxIterations = 20;
728
+ const maxIterations = 30; // v14.5.1: Increased from 20
701
729
  let currentPrompt = fullPrompt;
702
730
  while (continueLoop && iterations < maxIterations) {
703
731
  iterations++;
@@ -731,9 +759,18 @@ ${colorize('Commands:', 'cyan')}
731
759
  // Add to conversation history
732
760
  this.conversationHistory.push(`Assistant: ${content}`);
733
761
  this.conversationHistory.push(`Tool (${toolCall.name}): ${result}`);
734
- // Continue with tool result
735
- currentPrompt = `Tool result for ${toolCall.name}:\n\`\`\`\n${result}\n\`\`\`\n\nContinue with your task.`;
736
- spinner.start();
762
+ // v14.5.1: Check if there's meaningful text outside the tool block
763
+ const textOutsideTool = content.replace(/```tool\s*\n[\s\S]*?\n```/g, '').trim();
764
+ if (textOutsideTool.length > 100) {
765
+ // LLM provided explanation alongside tool - this is the final response
766
+ console.log('\n' + this.formatResponse(content));
767
+ continueLoop = false;
768
+ }
769
+ else {
770
+ // Continue with tool result
771
+ currentPrompt = `Tool result for ${toolCall.name}:\n\`\`\`\n${result}\n\`\`\`\n\nContinue with your task. When done, provide a summary without tool calls.`;
772
+ spinner.start();
773
+ }
737
774
  }
738
775
  catch {
739
776
  // Not a valid tool call, treat as regular response
@@ -1,12 +1,19 @@
1
1
  /**
2
- * Bounty Hunter — DeWork / Layer3 / Immunefi
2
+ * Bounty Hunter — Multi-Platform Autonomous Bounty Discovery
3
+ *
4
+ * v14.5.0: Added Algora, Gitcoin, GitHub bounty sources
5
+ *
6
+ * Autonomously discovers and completes bounties on multiple platforms:
7
+ * - DeWork: Web3 DAO bounties (GraphQL API)
8
+ * - Algora: GitHub-native bounties (REST API) - BEST FOR AI
9
+ * - Gitcoin: OSS grants and bounties (REST API)
10
+ * - GitHub: Issues with bounty labels (Search API)
3
11
  *
4
- * Autonomously discovers and completes bounties on Web3 platforms.
5
12
  * Revenue model: per-bounty payouts (typically $50-$10,000+).
6
13
  *
7
14
  * Requirements:
8
15
  * - Capital: $0 (zero capital needed)
9
- * - Identity: Wallet only (wallet-based identity on all platforms)
16
+ * - Identity: Wallet/GitHub account
10
17
  * - Revenue: $500-$5,000/month depending on bounty availability
11
18
  *
12
19
  * Bounty types:
@@ -21,10 +28,11 @@
21
28
  * 2. Evaluate expected value (reward × success probability)
22
29
  * 3. Execute bounty work using kernel task system
23
30
  * 4. Submit and track payout
31
+ * 5. Feed results back to RSI for skill improvement
24
32
  */
25
33
  export interface Bounty {
26
34
  id: string;
27
- platform: 'dework' | 'layer3' | 'immunefi' | 'code4rena' | 'gitcoin';
35
+ platform: 'dework' | 'algora' | 'gitcoin' | 'github' | 'layer3' | 'immunefi' | 'code4rena';
28
36
  title: string;
29
37
  description: string;
30
38
  reward: number;
@@ -37,6 +45,13 @@ export interface Bounty {
37
45
  tags: string[];
38
46
  discovered: number;
39
47
  status: 'open' | 'claimed' | 'submitted' | 'completed' | 'rejected' | 'expired';
48
+ /** v14.5.0: Source-specific metadata */
49
+ sourceMetadata?: {
50
+ org?: string;
51
+ repo?: string;
52
+ issueNumber?: number;
53
+ githubUrl?: string;
54
+ };
40
55
  }
41
56
  export interface BountySubmission {
42
57
  bountyId: string;
@@ -114,6 +129,7 @@ export declare class BountyHunter {
114
129
  private isViable;
115
130
  private estimateSuccessProbability;
116
131
  private scanPlatform;
132
+ private mapGitcoinDifficulty;
117
133
  private inferDifficulty;
118
134
  private inferCategory;
119
135
  private mapCurrency;
@@ -1,13 +1,20 @@
1
1
  "use strict";
2
2
  /**
3
- * Bounty Hunter — DeWork / Layer3 / Immunefi
3
+ * Bounty Hunter — Multi-Platform Autonomous Bounty Discovery
4
+ *
5
+ * v14.5.0: Added Algora, Gitcoin, GitHub bounty sources
6
+ *
7
+ * Autonomously discovers and completes bounties on multiple platforms:
8
+ * - DeWork: Web3 DAO bounties (GraphQL API)
9
+ * - Algora: GitHub-native bounties (REST API) - BEST FOR AI
10
+ * - Gitcoin: OSS grants and bounties (REST API)
11
+ * - GitHub: Issues with bounty labels (Search API)
4
12
  *
5
- * Autonomously discovers and completes bounties on Web3 platforms.
6
13
  * Revenue model: per-bounty payouts (typically $50-$10,000+).
7
14
  *
8
15
  * Requirements:
9
16
  * - Capital: $0 (zero capital needed)
10
- * - Identity: Wallet only (wallet-based identity on all platforms)
17
+ * - Identity: Wallet/GitHub account
11
18
  * - Revenue: $500-$5,000/month depending on bounty availability
12
19
  *
13
20
  * Bounty types:
@@ -22,6 +29,7 @@
22
29
  * 2. Evaluate expected value (reward × success probability)
23
30
  * 3. Execute bounty work using kernel task system
24
31
  * 4. Submit and track payout
32
+ * 5. Feed results back to RSI for skill improvement
25
33
  */
26
34
  Object.defineProperty(exports, "__esModule", { value: true });
27
35
  exports.BountyHunter = void 0;
@@ -29,6 +37,9 @@ exports.getBountyHunter = getBountyHunter;
29
37
  exports.resetBountyHunter = resetBountyHunter;
30
38
  const fiber_js_1 = require("../fiber.js");
31
39
  const dework_js_1 = require("../live/connectors/dework.js");
40
+ const algora_js_1 = require("../live/connectors/algora.js");
41
+ const gitcoin_js_1 = require("../live/connectors/gitcoin.js");
42
+ const github_bounties_js_1 = require("../live/connectors/github-bounties.js");
32
43
  const revenue_tracker_js_1 = require("../live/revenue-tracker.js");
33
44
  // ============================================================================
34
45
  // Bounty Hunter
@@ -41,7 +52,8 @@ class BountyHunter {
41
52
  lastScan = 0;
42
53
  constructor(config) {
43
54
  this.config = {
44
- platforms: config?.platforms ?? ['dework', 'layer3', 'immunefi'],
55
+ // v14.5.0: Added algora, gitcoin, github as primary platforms
56
+ platforms: config?.platforms ?? ['algora', 'github', 'gitcoin', 'dework'],
45
57
  categories: config?.categories ?? ['code', 'audit', 'content', 'research'],
46
58
  minReward: config?.minReward ?? 50,
47
59
  maxDifficulty: config?.maxDifficulty ?? 'hard',
@@ -253,11 +265,12 @@ class BountyHunter {
253
265
  }
254
266
  async scanPlatform(platform) {
255
267
  try {
256
- // Use real DeWork connector for dework platform
268
+ // ══════════════════════════════════════════════════════════════════════
269
+ // DEWORK - Web3 DAO bounties
270
+ // ══════════════════════════════════════════════════════════════════════
257
271
  if (platform === 'dework') {
258
272
  const connector = (0, dework_js_1.getDeworkConnector)();
259
273
  const tags = this.config.categories.map(c => {
260
- // Map our categories to DeWork tags
261
274
  const tagMap = {
262
275
  code: ['solidity', 'typescript', 'smart-contract', 'rust'],
263
276
  audit: ['security', 'audit', 'bug-bounty'],
@@ -286,8 +299,99 @@ class BountyHunter {
286
299
  status: 'open',
287
300
  }));
288
301
  }
302
+ // ══════════════════════════════════════════════════════════════════════
303
+ // ALGORA - GitHub-native bounties (BEST API for AI agents)
304
+ // ══════════════════════════════════════════════════════════════════════
305
+ if (platform === 'algora') {
306
+ const connector = (0, algora_js_1.getAlgoraConnector)();
307
+ const algoraBounties = await connector.scanBounties();
308
+ return algoraBounties
309
+ .filter(b => (b.reward / 100) >= this.config.minReward) // Algora uses cents
310
+ .map((b) => ({
311
+ id: `algora:${b.id}`,
312
+ platform: 'algora',
313
+ title: b.title,
314
+ description: b.description || '',
315
+ reward: b.reward / 100, // Convert from cents to dollars
316
+ currency: 'USD',
317
+ difficulty: this.inferDifficulty(b.reward / 100),
318
+ category: 'code', // Algora is primarily code bounties
319
+ deadline: b.deadline ? new Date(b.deadline).getTime() : undefined,
320
+ submissionUrl: b.url,
321
+ protocol: b.org,
322
+ tags: b.tags,
323
+ discovered: Date.now(),
324
+ status: 'open',
325
+ sourceMetadata: {
326
+ org: b.org,
327
+ repo: b.repo,
328
+ githubUrl: b.issueUrl,
329
+ },
330
+ }));
331
+ }
332
+ // ══════════════════════════════════════════════════════════════════════
333
+ // GITCOIN - OSS grants and bounties
334
+ // ══════════════════════════════════════════════════════════════════════
335
+ if (platform === 'gitcoin') {
336
+ const connector = (0, gitcoin_js_1.getGitcoinConnector)();
337
+ const gitcoinBounties = await connector.scanBounties({
338
+ isOpen: true,
339
+ keywords: this.config.categories,
340
+ });
341
+ return gitcoinBounties
342
+ .filter(b => b.reward >= this.config.minReward)
343
+ .map((b) => ({
344
+ id: `gitcoin:${b.id}`,
345
+ platform: 'gitcoin',
346
+ title: b.title,
347
+ description: b.description || '',
348
+ reward: b.reward,
349
+ currency: this.mapCurrency(b.currency),
350
+ difficulty: this.mapGitcoinDifficulty(b.experienceLevel),
351
+ category: this.inferCategory(b.tags),
352
+ deadline: b.deadline ? new Date(b.deadline).getTime() : undefined,
353
+ submissionUrl: b.url,
354
+ protocol: b.projectType,
355
+ tags: b.tags,
356
+ discovered: Date.now(),
357
+ status: 'open',
358
+ sourceMetadata: {
359
+ githubUrl: b.githubUrl,
360
+ },
361
+ }));
362
+ }
363
+ // ══════════════════════════════════════════════════════════════════════
364
+ // GITHUB - Issues with bounty labels
365
+ // ══════════════════════════════════════════════════════════════════════
366
+ if (platform === 'github') {
367
+ const connector = (0, github_bounties_js_1.getGitHubBountyConnector)();
368
+ const githubBounties = await connector.scanBounties();
369
+ return githubBounties
370
+ .filter(b => b.reward >= this.config.minReward && b.status === 'open')
371
+ .map((b) => ({
372
+ id: b.id,
373
+ platform: 'github',
374
+ title: b.title,
375
+ description: b.description || '',
376
+ reward: b.reward,
377
+ currency: this.mapCurrency(b.currency),
378
+ difficulty: this.inferDifficulty(b.reward),
379
+ category: 'code', // GitHub bounties are typically code
380
+ deadline: undefined,
381
+ submissionUrl: b.url,
382
+ protocol: `${b.owner}/${b.repo}`,
383
+ tags: b.tags,
384
+ discovered: Date.now(),
385
+ status: 'open',
386
+ sourceMetadata: {
387
+ org: b.owner,
388
+ repo: b.repo,
389
+ issueNumber: b.issueNumber,
390
+ githubUrl: b.url,
391
+ },
392
+ }));
393
+ }
289
394
  // For other platforms (layer3, immunefi), return empty for now
290
- // Can add more connectors later
291
395
  console.log(`[BountyHunter] Platform ${platform} not yet connected to real API`);
292
396
  return [];
293
397
  }
@@ -296,6 +400,14 @@ class BountyHunter {
296
400
  return [];
297
401
  }
298
402
  }
403
+ mapGitcoinDifficulty(level) {
404
+ switch (level) {
405
+ case 'beginner': return 'easy';
406
+ case 'intermediate': return 'medium';
407
+ case 'advanced': return 'hard';
408
+ default: return 'medium';
409
+ }
410
+ }
299
411
  inferDifficulty(reward) {
300
412
  if (reward < 100)
301
413
  return 'easy';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Algora API Connector
3
+ *
4
+ * Real connector for bounty discovery using Algora REST API.
5
+ * Algora is GitHub-native with full CRUD API - best for AI agents.
6
+ *
7
+ * API Reference: https://api.docs.algora.io/bounties
8
+ * - GET /api/orgs/{org}/bounties - Public, no auth required
9
+ * - GET /api/bounties/{id} - Get specific bounty
10
+ *
11
+ * Amount format: Cents (100 = $1.00)
12
+ */
13
+ export interface AlgoraBounty {
14
+ id: string;
15
+ title: string;
16
+ reward: number;
17
+ currency: string;
18
+ tags: string[];
19
+ deadline: string | null;
20
+ url: string;
21
+ description?: string;
22
+ status: 'open' | 'claimed' | 'completed' | 'cancelled';
23
+ org: string;
24
+ repo?: string;
25
+ issueUrl?: string;
26
+ }
27
+ export interface AlgoraConnector {
28
+ scanBounties(orgs?: string[]): Promise<AlgoraBounty[]>;
29
+ getBountyDetails(id: string): Promise<AlgoraBounty | null>;
30
+ searchBounties(query: string): Promise<AlgoraBounty[]>;
31
+ }
32
+ export declare function getAlgoraConnector(): AlgoraConnector;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ /**
3
+ * Algora API Connector
4
+ *
5
+ * Real connector for bounty discovery using Algora REST API.
6
+ * Algora is GitHub-native with full CRUD API - best for AI agents.
7
+ *
8
+ * API Reference: https://api.docs.algora.io/bounties
9
+ * - GET /api/orgs/{org}/bounties - Public, no auth required
10
+ * - GET /api/bounties/{id} - Get specific bounty
11
+ *
12
+ * Amount format: Cents (100 = $1.00)
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getAlgoraConnector = getAlgoraConnector;
16
+ const ALGORA_API = 'https://console.algora.io/api';
17
+ const TIMEOUT_MS = 15000;
18
+ const RATE_LIMIT_MS = 500;
19
+ let lastRequestTime = 0;
20
+ async function rateLimit() {
21
+ const now = Date.now();
22
+ const elapsed = now - lastRequestTime;
23
+ if (elapsed < RATE_LIMIT_MS) {
24
+ await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_MS - elapsed));
25
+ }
26
+ lastRequestTime = Date.now();
27
+ }
28
+ async function fetchWithTimeout(url, options, timeoutMs) {
29
+ const controller = new AbortController();
30
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
31
+ try {
32
+ const response = await fetch(url, {
33
+ ...options,
34
+ signal: controller.signal,
35
+ });
36
+ return response;
37
+ }
38
+ finally {
39
+ clearTimeout(timeout);
40
+ }
41
+ }
42
+ function mapApiBounty(b, org) {
43
+ return {
44
+ id: b.id,
45
+ title: b.title,
46
+ reward: b.amount_cents, // Keep in cents
47
+ currency: b.currency || 'USD',
48
+ tags: b.skills || [],
49
+ deadline: b.expires_at || null,
50
+ url: b.url || `https://console.algora.io/${org}/bounties/${b.id}`,
51
+ description: b.title, // Algora doesn't always include description in list
52
+ status: mapStatus(b.status),
53
+ org: org,
54
+ repo: b.repo_slug,
55
+ issueUrl: b.issue_url,
56
+ };
57
+ }
58
+ function mapStatus(status) {
59
+ switch (status?.toLowerCase()) {
60
+ case 'open':
61
+ case 'available':
62
+ return 'open';
63
+ case 'claimed':
64
+ case 'in_progress':
65
+ case 'assigned':
66
+ return 'claimed';
67
+ case 'completed':
68
+ case 'paid':
69
+ case 'rewarded':
70
+ return 'completed';
71
+ case 'cancelled':
72
+ case 'expired':
73
+ case 'closed':
74
+ return 'cancelled';
75
+ default:
76
+ return 'open';
77
+ }
78
+ }
79
+ /**
80
+ * Scan bounties from specific orgs
81
+ */
82
+ async function scanBounties(orgs) {
83
+ const targetOrgs = orgs || [
84
+ // Popular orgs with active bounties
85
+ 'calcom',
86
+ 'twentyhq',
87
+ 'formbricks',
88
+ 'triggerdotdev',
89
+ 'dubinc',
90
+ 'infisical',
91
+ 'documenso',
92
+ 'latitude-dev',
93
+ 'boxyhq',
94
+ ];
95
+ const allBounties = [];
96
+ for (const org of targetOrgs) {
97
+ await rateLimit();
98
+ try {
99
+ const response = await fetchWithTimeout(`${ALGORA_API}/orgs/${org}/bounties?limit=50`, {
100
+ method: 'GET',
101
+ headers: {
102
+ 'Accept': 'application/json',
103
+ },
104
+ }, TIMEOUT_MS);
105
+ if (!response.ok) {
106
+ if (response.status === 404) {
107
+ // Org not found or no bounties, skip silently
108
+ continue;
109
+ }
110
+ console.warn(`[AlgoraConnector] API error for ${org}:`, response.status);
111
+ continue;
112
+ }
113
+ const data = await response.json();
114
+ if (data.items && Array.isArray(data.items)) {
115
+ const bounties = data.items
116
+ .filter(b => b.status === 'open' || b.status === 'available')
117
+ .map(b => mapApiBounty(b, org));
118
+ allBounties.push(...bounties);
119
+ }
120
+ }
121
+ catch (error) {
122
+ if (error instanceof Error && error.name === 'AbortError') {
123
+ console.warn(`[AlgoraConnector] Timeout for ${org}`);
124
+ }
125
+ else {
126
+ console.warn(`[AlgoraConnector] Error for ${org}:`, error);
127
+ }
128
+ }
129
+ }
130
+ console.log(`[AlgoraConnector] Found ${allBounties.length} open bounties from ${targetOrgs.length} orgs`);
131
+ return allBounties;
132
+ }
133
+ /**
134
+ * Get details of a specific bounty
135
+ */
136
+ async function getBountyDetails(id) {
137
+ await rateLimit();
138
+ try {
139
+ const response = await fetchWithTimeout(`${ALGORA_API}/bounties/${id}`, {
140
+ method: 'GET',
141
+ headers: {
142
+ 'Accept': 'application/json',
143
+ },
144
+ }, TIMEOUT_MS);
145
+ if (!response.ok) {
146
+ console.warn(`[AlgoraConnector] Bounty ${id} not found:`, response.status);
147
+ return null;
148
+ }
149
+ const data = await response.json();
150
+ return mapApiBounty(data, data.org_slug);
151
+ }
152
+ catch (error) {
153
+ console.warn(`[AlgoraConnector] Error getting bounty ${id}:`, error);
154
+ return null;
155
+ }
156
+ }
157
+ /**
158
+ * Search bounties by keyword (searches across all orgs)
159
+ */
160
+ async function searchBounties(query) {
161
+ // Algora doesn't have a public search endpoint, so we scan popular orgs
162
+ // and filter locally
163
+ const allBounties = await scanBounties();
164
+ const lowerQuery = query.toLowerCase();
165
+ return allBounties.filter(b => b.title.toLowerCase().includes(lowerQuery) ||
166
+ b.tags.some(t => t.toLowerCase().includes(lowerQuery)));
167
+ }
168
+ function getAlgoraConnector() {
169
+ return {
170
+ scanBounties,
171
+ getBountyDetails,
172
+ searchBounties,
173
+ };
174
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Gitcoin API Connector
3
+ *
4
+ * Read-only connector for bounty discovery using Gitcoin REST API.
5
+ * API Reference: https://github.com/gitcoinco/web/blob/master/docs/API.md
6
+ *
7
+ * Note: Gitcoin has deprecated bounties in favor of Grants.
8
+ * This connector focuses on grants discovery for research opportunities.
9
+ */
10
+ export interface GitcoinBounty {
11
+ id: string;
12
+ title: string;
13
+ reward: number;
14
+ currency: string;
15
+ tags: string[];
16
+ deadline: string | null;
17
+ url: string;
18
+ description?: string;
19
+ status: 'open' | 'started' | 'submitted' | 'done' | 'cancelled';
20
+ experienceLevel: 'beginner' | 'intermediate' | 'advanced';
21
+ projectType: string;
22
+ githubUrl?: string;
23
+ }
24
+ export interface GitcoinConnector {
25
+ scanBounties(filters?: GitcoinFilters): Promise<GitcoinBounty[]>;
26
+ getBountyDetails(id: string): Promise<GitcoinBounty | null>;
27
+ }
28
+ export interface GitcoinFilters {
29
+ isOpen?: boolean;
30
+ experienceLevel?: string;
31
+ projectLength?: string;
32
+ bountyType?: string;
33
+ keywords?: string[];
34
+ }
35
+ export declare function getGitcoinConnector(): GitcoinConnector;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ /**
3
+ * Gitcoin API Connector
4
+ *
5
+ * Read-only connector for bounty discovery using Gitcoin REST API.
6
+ * API Reference: https://github.com/gitcoinco/web/blob/master/docs/API.md
7
+ *
8
+ * Note: Gitcoin has deprecated bounties in favor of Grants.
9
+ * This connector focuses on grants discovery for research opportunities.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.getGitcoinConnector = getGitcoinConnector;
13
+ const GITCOIN_API = 'https://gitcoin.co/api/v0.1';
14
+ const TIMEOUT_MS = 15000;
15
+ const RATE_LIMIT_MS = 1000;
16
+ let lastRequestTime = 0;
17
+ async function rateLimit() {
18
+ const now = Date.now();
19
+ const elapsed = now - lastRequestTime;
20
+ if (elapsed < RATE_LIMIT_MS) {
21
+ await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_MS - elapsed));
22
+ }
23
+ lastRequestTime = Date.now();
24
+ }
25
+ async function fetchWithTimeout(url, options, timeoutMs) {
26
+ const controller = new AbortController();
27
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
28
+ try {
29
+ const response = await fetch(url, {
30
+ ...options,
31
+ signal: controller.signal,
32
+ });
33
+ return response;
34
+ }
35
+ finally {
36
+ clearTimeout(timeout);
37
+ }
38
+ }
39
+ function mapApiBounty(b) {
40
+ return {
41
+ id: String(b.pk),
42
+ title: b.title,
43
+ reward: b.value_true || 0,
44
+ currency: b.token_name || 'USD',
45
+ tags: b.keywords || [],
46
+ deadline: b.expires_date,
47
+ url: b.url || `https://gitcoin.co/issue/${b.pk}`,
48
+ description: b.issue_description?.slice(0, 500),
49
+ status: mapStatus(b.status),
50
+ experienceLevel: mapExperienceLevel(b.experience_level),
51
+ projectType: b.project_type || 'unknown',
52
+ githubUrl: b.github_url,
53
+ };
54
+ }
55
+ function mapStatus(status) {
56
+ switch (status?.toLowerCase()) {
57
+ case 'open':
58
+ return 'open';
59
+ case 'started':
60
+ case 'work_started':
61
+ return 'started';
62
+ case 'submitted':
63
+ case 'work_submitted':
64
+ return 'submitted';
65
+ case 'done':
66
+ case 'completed':
67
+ return 'done';
68
+ case 'cancelled':
69
+ case 'expired':
70
+ return 'cancelled';
71
+ default:
72
+ return 'open';
73
+ }
74
+ }
75
+ function mapExperienceLevel(level) {
76
+ switch (level?.toLowerCase()) {
77
+ case 'beginner':
78
+ case 'easy':
79
+ return 'beginner';
80
+ case 'intermediate':
81
+ case 'medium':
82
+ return 'intermediate';
83
+ case 'advanced':
84
+ case 'hard':
85
+ case 'expert':
86
+ return 'advanced';
87
+ default:
88
+ return 'intermediate';
89
+ }
90
+ }
91
+ /**
92
+ * Scan open bounties with optional filters
93
+ */
94
+ async function scanBounties(filters) {
95
+ await rateLimit();
96
+ const params = new URLSearchParams();
97
+ params.set('is_open', filters?.isOpen !== false ? 'true' : 'false');
98
+ params.set('order_by', '-value_true'); // Highest value first
99
+ params.set('limit', '50');
100
+ if (filters?.experienceLevel) {
101
+ params.set('experience_level', filters.experienceLevel);
102
+ }
103
+ if (filters?.projectLength) {
104
+ params.set('project_length', filters.projectLength);
105
+ }
106
+ if (filters?.bountyType) {
107
+ params.set('bounty_type', filters.bountyType);
108
+ }
109
+ if (filters?.keywords && filters.keywords.length > 0) {
110
+ params.set('keywords', filters.keywords.join(','));
111
+ }
112
+ try {
113
+ const response = await fetchWithTimeout(`${GITCOIN_API}/bounties/?${params.toString()}`, {
114
+ method: 'GET',
115
+ headers: {
116
+ 'Accept': 'application/json',
117
+ },
118
+ }, TIMEOUT_MS);
119
+ if (!response.ok) {
120
+ console.warn(`[GitcoinConnector] API error:`, response.status);
121
+ return [];
122
+ }
123
+ const data = await response.json();
124
+ if (!Array.isArray(data)) {
125
+ console.warn('[GitcoinConnector] Unexpected response format');
126
+ return [];
127
+ }
128
+ const bounties = data
129
+ .filter(b => b.status === 'open')
130
+ .map(mapApiBounty);
131
+ console.log(`[GitcoinConnector] Found ${bounties.length} open bounties`);
132
+ return bounties;
133
+ }
134
+ catch (error) {
135
+ if (error instanceof Error && error.name === 'AbortError') {
136
+ console.warn('[GitcoinConnector] Request timeout');
137
+ }
138
+ else {
139
+ console.warn('[GitcoinConnector] Request failed:', error);
140
+ }
141
+ return [];
142
+ }
143
+ }
144
+ /**
145
+ * Get details of a specific bounty
146
+ */
147
+ async function getBountyDetails(id) {
148
+ await rateLimit();
149
+ try {
150
+ const response = await fetchWithTimeout(`${GITCOIN_API}/bounties/${id}/`, {
151
+ method: 'GET',
152
+ headers: {
153
+ 'Accept': 'application/json',
154
+ },
155
+ }, TIMEOUT_MS);
156
+ if (!response.ok) {
157
+ console.warn(`[GitcoinConnector] Bounty ${id} not found:`, response.status);
158
+ return null;
159
+ }
160
+ const data = await response.json();
161
+ return mapApiBounty(data);
162
+ }
163
+ catch (error) {
164
+ console.warn(`[GitcoinConnector] Error getting bounty ${id}:`, error);
165
+ return null;
166
+ }
167
+ }
168
+ function getGitcoinConnector() {
169
+ return {
170
+ scanBounties,
171
+ getBountyDetails,
172
+ };
173
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * GitHub Bounty Connector
3
+ *
4
+ * Discovers bounties from GitHub issues with bounty labels.
5
+ * Uses GitHub Search API to find issues with common bounty labels:
6
+ * - "bounty"
7
+ * - "reward"
8
+ * - "help wanted" + "good first issue" (filtered by reward mentions)
9
+ *
10
+ * Requires GITHUB_TOKEN for higher rate limits.
11
+ */
12
+ export interface GitHubBounty {
13
+ id: string;
14
+ title: string;
15
+ reward: number;
16
+ currency: string;
17
+ tags: string[];
18
+ deadline: string | null;
19
+ url: string;
20
+ description: string;
21
+ status: 'open' | 'assigned' | 'closed';
22
+ repo: string;
23
+ owner: string;
24
+ issueNumber: number;
25
+ }
26
+ export interface GitHubBountyConnector {
27
+ scanBounties(labels?: string[]): Promise<GitHubBounty[]>;
28
+ getBountyDetails(owner: string, repo: string, issueNumber: number): Promise<GitHubBounty | null>;
29
+ }
30
+ export declare function getGitHubBountyConnector(): GitHubBountyConnector;
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ /**
3
+ * GitHub Bounty Connector
4
+ *
5
+ * Discovers bounties from GitHub issues with bounty labels.
6
+ * Uses GitHub Search API to find issues with common bounty labels:
7
+ * - "bounty"
8
+ * - "reward"
9
+ * - "help wanted" + "good first issue" (filtered by reward mentions)
10
+ *
11
+ * Requires GITHUB_TOKEN for higher rate limits.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.getGitHubBountyConnector = getGitHubBountyConnector;
15
+ const GITHUB_API = 'https://api.github.com';
16
+ const TIMEOUT_MS = 15000;
17
+ const RATE_LIMIT_MS = 1000;
18
+ let lastRequestTime = 0;
19
+ async function rateLimit() {
20
+ const now = Date.now();
21
+ const elapsed = now - lastRequestTime;
22
+ if (elapsed < RATE_LIMIT_MS) {
23
+ await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_MS - elapsed));
24
+ }
25
+ lastRequestTime = Date.now();
26
+ }
27
+ async function fetchWithTimeout(url, options, timeoutMs) {
28
+ const controller = new AbortController();
29
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
30
+ try {
31
+ const response = await fetch(url, {
32
+ ...options,
33
+ signal: controller.signal,
34
+ });
35
+ return response;
36
+ }
37
+ finally {
38
+ clearTimeout(timeout);
39
+ }
40
+ }
41
+ /**
42
+ * Extract reward amount from text
43
+ * Supports: $100, 100 USD, 100 USDC, 0.1 ETH, etc.
44
+ */
45
+ function extractReward(text) {
46
+ // Match patterns like: $500, 500 USD, 500 USDC, 0.1 ETH
47
+ const patterns = [
48
+ /\$\s*(\d+(?:,\d{3})*(?:\.\d{2})?)/i, // $500 or $1,000.00
49
+ /(\d+(?:,\d{3})*(?:\.\d{2})?)\s*(?:USD|USDC|DAI)/i, // 500 USD
50
+ /(\d+(?:\.\d+)?)\s*ETH/i, // 0.1 ETH
51
+ /bounty[:\s]+\$?\s*(\d+(?:,\d{3})*)/i, // bounty: $500
52
+ /reward[:\s]+\$?\s*(\d+(?:,\d{3})*)/i, // reward: 500
53
+ ];
54
+ for (const pattern of patterns) {
55
+ const match = text.match(pattern);
56
+ if (match) {
57
+ const amountStr = match[1].replace(/,/g, '');
58
+ const amount = parseFloat(amountStr);
59
+ // Determine currency
60
+ let currency = 'USD';
61
+ if (text.toLowerCase().includes('eth')) {
62
+ currency = 'ETH';
63
+ // Convert ETH to USD estimate (rough)
64
+ return { amount: amount * 2000, currency: 'ETH' };
65
+ }
66
+ if (text.toLowerCase().includes('usdc')) {
67
+ currency = 'USDC';
68
+ }
69
+ return { amount, currency };
70
+ }
71
+ }
72
+ return { amount: 0, currency: 'USD' };
73
+ }
74
+ function mapIssueToBoounty(issue) {
75
+ // Extract repo info from repository_url
76
+ const repoMatch = issue.repository_url.match(/repos\/([^/]+)\/([^/]+)$/);
77
+ const owner = repoMatch?.[1] || 'unknown';
78
+ const repo = repoMatch?.[2] || 'unknown';
79
+ // Extract reward from title and body
80
+ const textToSearch = `${issue.title} ${issue.body || ''}`;
81
+ const { amount, currency } = extractReward(textToSearch);
82
+ // Determine status
83
+ let status = 'open';
84
+ if (issue.state === 'closed') {
85
+ status = 'closed';
86
+ }
87
+ else if (issue.assignees && issue.assignees.length > 0) {
88
+ status = 'assigned';
89
+ }
90
+ return {
91
+ id: `github:${owner}/${repo}#${issue.number}`,
92
+ title: issue.title,
93
+ reward: amount,
94
+ currency,
95
+ tags: issue.labels.map(l => l.name),
96
+ deadline: null, // GitHub issues don't have deadlines
97
+ url: issue.html_url,
98
+ description: (issue.body || '').slice(0, 500),
99
+ status,
100
+ repo,
101
+ owner,
102
+ issueNumber: issue.number,
103
+ };
104
+ }
105
+ /**
106
+ * Scan for bounty issues on GitHub
107
+ */
108
+ async function scanBounties(labels) {
109
+ const targetLabels = labels || ['bounty', 'reward', 'cash-reward', 'paid'];
110
+ const allBounties = [];
111
+ for (const label of targetLabels) {
112
+ await rateLimit();
113
+ try {
114
+ // Search for open issues with bounty-related labels
115
+ const query = `is:issue is:open label:"${label}"`;
116
+ const params = new URLSearchParams({
117
+ q: query,
118
+ sort: 'updated',
119
+ order: 'desc',
120
+ per_page: '30',
121
+ });
122
+ const headers = {
123
+ 'Accept': 'application/vnd.github.v3+json',
124
+ 'User-Agent': 'Genesis-AI-Bounty-Hunter',
125
+ };
126
+ // Use GitHub token if available for higher rate limits
127
+ const token = process.env.GITHUB_TOKEN;
128
+ if (token) {
129
+ headers['Authorization'] = `token ${token}`;
130
+ }
131
+ const response = await fetchWithTimeout(`${GITHUB_API}/search/issues?${params.toString()}`, {
132
+ method: 'GET',
133
+ headers,
134
+ }, TIMEOUT_MS);
135
+ if (!response.ok) {
136
+ if (response.status === 403) {
137
+ console.warn('[GitHubBountyConnector] Rate limited, try setting GITHUB_TOKEN');
138
+ }
139
+ else {
140
+ console.warn(`[GitHubBountyConnector] API error:`, response.status);
141
+ }
142
+ continue;
143
+ }
144
+ const data = await response.json();
145
+ if (data.items && Array.isArray(data.items)) {
146
+ const bounties = data.items
147
+ .filter(issue => issue.state === 'open')
148
+ .map(mapIssueToBoounty)
149
+ // Filter out zero-reward issues (no bounty amount found)
150
+ .filter(b => b.reward > 0);
151
+ allBounties.push(...bounties);
152
+ }
153
+ }
154
+ catch (error) {
155
+ if (error instanceof Error && error.name === 'AbortError') {
156
+ console.warn(`[GitHubBountyConnector] Timeout for label: ${label}`);
157
+ }
158
+ else {
159
+ console.warn(`[GitHubBountyConnector] Error for label ${label}:`, error);
160
+ }
161
+ }
162
+ }
163
+ // Deduplicate by URL
164
+ const uniqueBounties = Array.from(new Map(allBounties.map(b => [b.url, b])).values());
165
+ console.log(`[GitHubBountyConnector] Found ${uniqueBounties.length} bounties with rewards`);
166
+ return uniqueBounties;
167
+ }
168
+ /**
169
+ * Get details of a specific issue
170
+ */
171
+ async function getBountyDetails(owner, repo, issueNumber) {
172
+ await rateLimit();
173
+ try {
174
+ const headers = {
175
+ 'Accept': 'application/vnd.github.v3+json',
176
+ 'User-Agent': 'Genesis-AI-Bounty-Hunter',
177
+ };
178
+ const token = process.env.GITHUB_TOKEN;
179
+ if (token) {
180
+ headers['Authorization'] = `token ${token}`;
181
+ }
182
+ const response = await fetchWithTimeout(`${GITHUB_API}/repos/${owner}/${repo}/issues/${issueNumber}`, {
183
+ method: 'GET',
184
+ headers,
185
+ }, TIMEOUT_MS);
186
+ if (!response.ok) {
187
+ console.warn(`[GitHubBountyConnector] Issue not found:`, response.status);
188
+ return null;
189
+ }
190
+ const issue = await response.json();
191
+ // Add repository_url for mapping
192
+ issue.repository_url = `https://api.github.com/repos/${owner}/${repo}`;
193
+ return mapIssueToBoounty(issue);
194
+ }
195
+ catch (error) {
196
+ console.warn(`[GitHubBountyConnector] Error getting issue:`, error);
197
+ return null;
198
+ }
199
+ }
200
+ function getGitHubBountyConnector() {
201
+ return {
202
+ scanBounties,
203
+ getBountyDetails,
204
+ };
205
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-ai-cli",
3
- "version": "14.4.1",
3
+ "version": "14.5.1",
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",