genesis-ai-cli 11.0.1 → 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/daemon/index.js +79 -0
- package/dist/src/daemon/types.d.ts +13 -0
- package/dist/src/daemon/types.js +8 -0
- package/dist/src/payments/index.d.ts +1 -0
- package/dist/src/payments/index.js +6 -1
- package/dist/src/payments/stripe-client.d.ts +136 -0
- package/dist/src/payments/stripe-client.js +153 -0
- package/dist/src/services/revenue-loop.d.ts +103 -0
- package/dist/src/services/revenue-loop.js +243 -0
- package/package.json +1 -1
package/dist/src/daemon/index.js
CHANGED
|
@@ -450,6 +450,85 @@ class Daemon {
|
|
|
450
450
|
tags: ['system', 'self-improvement', 'autopoiesis'],
|
|
451
451
|
});
|
|
452
452
|
}
|
|
453
|
+
// v11.1: Competitive Intelligence scan
|
|
454
|
+
if (this.config.competitiveIntel?.enabled && this.config.competitiveIntel.competitors.length > 0) {
|
|
455
|
+
this.scheduler.schedule({
|
|
456
|
+
name: 'compintel-scan',
|
|
457
|
+
description: 'Competitive Intelligence: scan competitors and generate insights',
|
|
458
|
+
schedule: { type: 'interval', intervalMs: this.config.competitiveIntel.checkIntervalMs },
|
|
459
|
+
priority: 'normal',
|
|
460
|
+
timeout: 120000, // 2 minutes max per scan
|
|
461
|
+
handler: async (ctx) => {
|
|
462
|
+
ctx.logger.info('Running CompIntel scan...');
|
|
463
|
+
const startTime = Date.now();
|
|
464
|
+
try {
|
|
465
|
+
const { createCompetitiveIntelService } = await import('../services/competitive-intel.js');
|
|
466
|
+
const { createRevenueLoop } = await import('../services/revenue-loop.js');
|
|
467
|
+
const ciConfig = this.config.competitiveIntel;
|
|
468
|
+
// Check subscription if required
|
|
469
|
+
if (ciConfig.requireSubscription && ciConfig.customerId) {
|
|
470
|
+
const revenueLoop = createRevenueLoop();
|
|
471
|
+
const sub = await revenueLoop.checkSubscription(ciConfig.customerId);
|
|
472
|
+
if (!sub.valid) {
|
|
473
|
+
ctx.logger.warn(`CompIntel scan skipped: ${sub.reason}`);
|
|
474
|
+
return {
|
|
475
|
+
success: true,
|
|
476
|
+
duration: Date.now() - startTime,
|
|
477
|
+
output: { skipped: true, reason: sub.reason },
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
ctx.logger.info(`Subscription valid: plan=${sub.plan}, competitors=${sub.maxCompetitors}`);
|
|
481
|
+
}
|
|
482
|
+
// Run scan
|
|
483
|
+
const service = createCompetitiveIntelService({
|
|
484
|
+
competitors: ciConfig.competitors,
|
|
485
|
+
});
|
|
486
|
+
const changes = await service.checkAll();
|
|
487
|
+
ctx.logger.info(`Scan complete: ${changes.length} changes detected`);
|
|
488
|
+
// Generate digest if changes found
|
|
489
|
+
let digest;
|
|
490
|
+
if (changes.length > 0) {
|
|
491
|
+
digest = await service.generateDigest(24);
|
|
492
|
+
ctx.logger.info(`Digest: ${digest.keyInsights.length} insights, ${digest.recommendations.length} recommendations`);
|
|
493
|
+
// Emit event for downstream consumers
|
|
494
|
+
this.emit({
|
|
495
|
+
type: 'task_completed',
|
|
496
|
+
timestamp: new Date(),
|
|
497
|
+
data: {
|
|
498
|
+
task: 'compintel-scan',
|
|
499
|
+
changes: changes.length,
|
|
500
|
+
insights: digest.keyInsights,
|
|
501
|
+
recommendations: digest.recommendations,
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
success: true,
|
|
507
|
+
duration: Date.now() - startTime,
|
|
508
|
+
output: {
|
|
509
|
+
competitors: ciConfig.competitors.length,
|
|
510
|
+
changes: changes.length,
|
|
511
|
+
digest: digest ? {
|
|
512
|
+
insights: digest.keyInsights.length,
|
|
513
|
+
recommendations: digest.recommendations.length,
|
|
514
|
+
} : null,
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
520
|
+
ctx.logger.error(`CompIntel scan failed: ${error.message}`);
|
|
521
|
+
return {
|
|
522
|
+
success: false,
|
|
523
|
+
duration: Date.now() - startTime,
|
|
524
|
+
error,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
tags: ['revenue', 'compintel', 'scan'],
|
|
529
|
+
});
|
|
530
|
+
this.log(`CompIntel daemon: monitoring ${this.config.competitiveIntel.competitors.length} competitors every ${(this.config.competitiveIntel.checkIntervalMs / 3600000).toFixed(1)}h`);
|
|
531
|
+
}
|
|
453
532
|
}
|
|
454
533
|
buildMaintenanceContext() {
|
|
455
534
|
return {
|
|
@@ -194,6 +194,18 @@ export interface SelfImprovementDaemonConfig {
|
|
|
194
194
|
minPhiThreshold: number;
|
|
195
195
|
maxImprovementsPerCycle: number;
|
|
196
196
|
}
|
|
197
|
+
export interface CompIntelDaemonConfig {
|
|
198
|
+
enabled: boolean;
|
|
199
|
+
checkIntervalMs: number;
|
|
200
|
+
digestIntervalMs: number;
|
|
201
|
+
competitors: Array<{
|
|
202
|
+
name: string;
|
|
203
|
+
domain: string;
|
|
204
|
+
pages?: string[];
|
|
205
|
+
}>;
|
|
206
|
+
requireSubscription: boolean;
|
|
207
|
+
customerId?: string;
|
|
208
|
+
}
|
|
197
209
|
export interface DaemonConfig {
|
|
198
210
|
enabled: boolean;
|
|
199
211
|
heartbeatIntervalMs: number;
|
|
@@ -207,6 +219,7 @@ export interface DaemonConfig {
|
|
|
207
219
|
maintenance: MaintenanceConfig;
|
|
208
220
|
dream: DreamConfig;
|
|
209
221
|
selfImprovement: SelfImprovementDaemonConfig;
|
|
222
|
+
competitiveIntel: CompIntelDaemonConfig;
|
|
210
223
|
logLevel: 'debug' | 'info' | 'warn' | 'error';
|
|
211
224
|
logToFile: boolean;
|
|
212
225
|
logFilePath?: string;
|
package/dist/src/daemon/types.js
CHANGED
|
@@ -53,6 +53,14 @@ exports.DEFAULT_DAEMON_CONFIG = {
|
|
|
53
53
|
minPhiThreshold: 0.3, // Require consciousness
|
|
54
54
|
maxImprovementsPerCycle: 3, // Conservative default
|
|
55
55
|
},
|
|
56
|
+
// v11.1: Competitive Intelligence
|
|
57
|
+
competitiveIntel: {
|
|
58
|
+
enabled: false, // Opt-in (needs competitors configured)
|
|
59
|
+
checkIntervalMs: 6 * 60 * 60 * 1000, // 6 hours
|
|
60
|
+
digestIntervalMs: 24 * 60 * 60 * 1000, // 24 hours
|
|
61
|
+
competitors: [],
|
|
62
|
+
requireSubscription: false, // Free by default (set true for paid mode)
|
|
63
|
+
},
|
|
56
64
|
logLevel: 'info',
|
|
57
65
|
logToFile: false,
|
|
58
66
|
};
|
|
@@ -8,3 +8,4 @@ export * from './types.js';
|
|
|
8
8
|
export { PaymentService, getPaymentService, resetPaymentService, type CreateTransactionParams, type CreateSubscriptionParams, type RecordUsageParams, type PaymentServiceStats, } from './payment-service.js';
|
|
9
9
|
export { RevenueTracker, getRevenueTracker, resetRevenueTracker, type RevenueMetrics, type CostEntry, type ProfitTarget, } from './revenue-tracker.js';
|
|
10
10
|
export { PaymentAPI, getPaymentAPI, processPaymentCommand, type APIResponse, type APIRequest, } from './api.js';
|
|
11
|
+
export { StripeClient, getStripeClient, resetStripeClient, type StripeProduct, type StripePrice, type StripePaymentLink, type StripeSubscription, type StripeCheckoutSession, type StripeClientConfig, } from './stripe-client.js';
|
|
@@ -20,7 +20,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
20
20
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.processPaymentCommand = exports.getPaymentAPI = exports.PaymentAPI = exports.resetRevenueTracker = exports.getRevenueTracker = exports.RevenueTracker = exports.resetPaymentService = exports.getPaymentService = exports.PaymentService = void 0;
|
|
23
|
+
exports.resetStripeClient = exports.getStripeClient = exports.StripeClient = exports.processPaymentCommand = exports.getPaymentAPI = exports.PaymentAPI = exports.resetRevenueTracker = exports.getRevenueTracker = exports.RevenueTracker = exports.resetPaymentService = exports.getPaymentService = exports.PaymentService = void 0;
|
|
24
24
|
// Types
|
|
25
25
|
__exportStar(require("./types.js"), exports);
|
|
26
26
|
// Payment Service
|
|
@@ -38,3 +38,8 @@ var api_js_1 = require("./api.js");
|
|
|
38
38
|
Object.defineProperty(exports, "PaymentAPI", { enumerable: true, get: function () { return api_js_1.PaymentAPI; } });
|
|
39
39
|
Object.defineProperty(exports, "getPaymentAPI", { enumerable: true, get: function () { return api_js_1.getPaymentAPI; } });
|
|
40
40
|
Object.defineProperty(exports, "processPaymentCommand", { enumerable: true, get: function () { return api_js_1.processPaymentCommand; } });
|
|
41
|
+
// Stripe Client
|
|
42
|
+
var stripe_client_js_1 = require("./stripe-client.js");
|
|
43
|
+
Object.defineProperty(exports, "StripeClient", { enumerable: true, get: function () { return stripe_client_js_1.StripeClient; } });
|
|
44
|
+
Object.defineProperty(exports, "getStripeClient", { enumerable: true, get: function () { return stripe_client_js_1.getStripeClient; } });
|
|
45
|
+
Object.defineProperty(exports, "resetStripeClient", { enumerable: true, get: function () { return stripe_client_js_1.resetStripeClient; } });
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Genesis v11.0 - Stripe Direct Client
|
|
3
|
+
*
|
|
4
|
+
* Zero-dependency Stripe integration using native fetch.
|
|
5
|
+
* Handles products, prices, payment links, and subscriptions
|
|
6
|
+
* for the Competitive Intelligence revenue loop.
|
|
7
|
+
*
|
|
8
|
+
* Uses Stripe REST API v2024-12-18.
|
|
9
|
+
*/
|
|
10
|
+
export interface StripeProduct {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
active: boolean;
|
|
15
|
+
metadata?: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export interface StripePrice {
|
|
18
|
+
id: string;
|
|
19
|
+
product: string;
|
|
20
|
+
unit_amount: number;
|
|
21
|
+
currency: string;
|
|
22
|
+
recurring?: {
|
|
23
|
+
interval: 'month' | 'year';
|
|
24
|
+
interval_count: number;
|
|
25
|
+
};
|
|
26
|
+
active: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface StripePaymentLink {
|
|
29
|
+
id: string;
|
|
30
|
+
url: string;
|
|
31
|
+
active: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface StripeSubscription {
|
|
34
|
+
id: string;
|
|
35
|
+
customer: string;
|
|
36
|
+
status: 'active' | 'past_due' | 'canceled' | 'incomplete' | 'trialing' | 'unpaid';
|
|
37
|
+
current_period_start: number;
|
|
38
|
+
current_period_end: number;
|
|
39
|
+
items: {
|
|
40
|
+
data: Array<{
|
|
41
|
+
price: {
|
|
42
|
+
id: string;
|
|
43
|
+
product: string;
|
|
44
|
+
unit_amount: number;
|
|
45
|
+
};
|
|
46
|
+
}>;
|
|
47
|
+
};
|
|
48
|
+
metadata?: Record<string, string>;
|
|
49
|
+
}
|
|
50
|
+
export interface StripeCheckoutSession {
|
|
51
|
+
id: string;
|
|
52
|
+
url: string;
|
|
53
|
+
payment_status: 'paid' | 'unpaid' | 'no_payment_required';
|
|
54
|
+
subscription?: string;
|
|
55
|
+
customer?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface StripeClientConfig {
|
|
58
|
+
apiKey: string;
|
|
59
|
+
apiVersion?: string;
|
|
60
|
+
}
|
|
61
|
+
export declare class StripeClient {
|
|
62
|
+
private apiKey;
|
|
63
|
+
private baseUrl;
|
|
64
|
+
private apiVersion;
|
|
65
|
+
constructor(config?: StripeClientConfig);
|
|
66
|
+
createProduct(params: {
|
|
67
|
+
name: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
metadata?: Record<string, string>;
|
|
70
|
+
}): Promise<StripeProduct>;
|
|
71
|
+
getProduct(id: string): Promise<StripeProduct>;
|
|
72
|
+
listProducts(params?: {
|
|
73
|
+
active?: boolean;
|
|
74
|
+
limit?: number;
|
|
75
|
+
}): Promise<{
|
|
76
|
+
data: StripeProduct[];
|
|
77
|
+
}>;
|
|
78
|
+
createPrice(params: {
|
|
79
|
+
product: string;
|
|
80
|
+
unit_amount: number;
|
|
81
|
+
currency: string;
|
|
82
|
+
recurring?: {
|
|
83
|
+
interval: 'month' | 'year';
|
|
84
|
+
};
|
|
85
|
+
metadata?: Record<string, string>;
|
|
86
|
+
}): Promise<StripePrice>;
|
|
87
|
+
listPrices(params?: {
|
|
88
|
+
product?: string;
|
|
89
|
+
active?: boolean;
|
|
90
|
+
}): Promise<{
|
|
91
|
+
data: StripePrice[];
|
|
92
|
+
}>;
|
|
93
|
+
createPaymentLink(params: {
|
|
94
|
+
line_items: Array<{
|
|
95
|
+
price: string;
|
|
96
|
+
quantity: number;
|
|
97
|
+
}>;
|
|
98
|
+
metadata?: Record<string, string>;
|
|
99
|
+
after_completion?: {
|
|
100
|
+
type: 'redirect';
|
|
101
|
+
redirect: {
|
|
102
|
+
url: string;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
}): Promise<StripePaymentLink>;
|
|
106
|
+
createCheckoutSession(params: {
|
|
107
|
+
mode: 'subscription' | 'payment';
|
|
108
|
+
line_items: Array<{
|
|
109
|
+
price: string;
|
|
110
|
+
quantity: number;
|
|
111
|
+
}>;
|
|
112
|
+
success_url: string;
|
|
113
|
+
cancel_url?: string;
|
|
114
|
+
metadata?: Record<string, string>;
|
|
115
|
+
}): Promise<StripeCheckoutSession>;
|
|
116
|
+
getSubscription(id: string): Promise<StripeSubscription>;
|
|
117
|
+
listSubscriptions(params?: {
|
|
118
|
+
customer?: string;
|
|
119
|
+
status?: string;
|
|
120
|
+
price?: string;
|
|
121
|
+
limit?: number;
|
|
122
|
+
}): Promise<{
|
|
123
|
+
data: StripeSubscription[];
|
|
124
|
+
}>;
|
|
125
|
+
isConfigured(): boolean;
|
|
126
|
+
isTestMode(): boolean;
|
|
127
|
+
private request;
|
|
128
|
+
/**
|
|
129
|
+
* Convert nested object to Stripe's form-encoded format.
|
|
130
|
+
* Stripe expects: line_items[0][price]=xxx&line_items[0][quantity]=1
|
|
131
|
+
*/
|
|
132
|
+
private toFormData;
|
|
133
|
+
private toQueryString;
|
|
134
|
+
}
|
|
135
|
+
export declare function getStripeClient(config?: StripeClientConfig): StripeClient;
|
|
136
|
+
export declare function resetStripeClient(): void;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genesis v11.0 - Stripe Direct Client
|
|
4
|
+
*
|
|
5
|
+
* Zero-dependency Stripe integration using native fetch.
|
|
6
|
+
* Handles products, prices, payment links, and subscriptions
|
|
7
|
+
* for the Competitive Intelligence revenue loop.
|
|
8
|
+
*
|
|
9
|
+
* Uses Stripe REST API v2024-12-18.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.StripeClient = void 0;
|
|
13
|
+
exports.getStripeClient = getStripeClient;
|
|
14
|
+
exports.resetStripeClient = resetStripeClient;
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Stripe Client
|
|
17
|
+
// ============================================================================
|
|
18
|
+
class StripeClient {
|
|
19
|
+
apiKey;
|
|
20
|
+
baseUrl = 'https://api.stripe.com/v1';
|
|
21
|
+
apiVersion;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.apiKey = config?.apiKey || process.env.STRIPE_API_KEY || process.env.STRIPE_SECRET_KEY || '';
|
|
24
|
+
this.apiVersion = config?.apiVersion || '2023-10-16';
|
|
25
|
+
if (!this.apiKey) {
|
|
26
|
+
console.warn('[Stripe] No API key configured. Set STRIPE_API_KEY env var.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// ==========================================================================
|
|
30
|
+
// Products
|
|
31
|
+
// ==========================================================================
|
|
32
|
+
async createProduct(params) {
|
|
33
|
+
return this.request('/products', params);
|
|
34
|
+
}
|
|
35
|
+
async getProduct(id) {
|
|
36
|
+
return this.request(`/products/${id}`, undefined, 'GET');
|
|
37
|
+
}
|
|
38
|
+
async listProducts(params) {
|
|
39
|
+
const query = params ? '?' + this.toQueryString(params) : '';
|
|
40
|
+
return this.request(`/products${query}`, undefined, 'GET');
|
|
41
|
+
}
|
|
42
|
+
// ==========================================================================
|
|
43
|
+
// Prices
|
|
44
|
+
// ==========================================================================
|
|
45
|
+
async createPrice(params) {
|
|
46
|
+
return this.request('/prices', params);
|
|
47
|
+
}
|
|
48
|
+
async listPrices(params) {
|
|
49
|
+
const query = params ? '?' + this.toQueryString(params) : '';
|
|
50
|
+
return this.request(`/prices${query}`, undefined, 'GET');
|
|
51
|
+
}
|
|
52
|
+
// ==========================================================================
|
|
53
|
+
// Payment Links
|
|
54
|
+
// ==========================================================================
|
|
55
|
+
async createPaymentLink(params) {
|
|
56
|
+
return this.request('/payment_links', params);
|
|
57
|
+
}
|
|
58
|
+
// ==========================================================================
|
|
59
|
+
// Checkout Sessions
|
|
60
|
+
// ==========================================================================
|
|
61
|
+
async createCheckoutSession(params) {
|
|
62
|
+
return this.request('/checkout/sessions', params);
|
|
63
|
+
}
|
|
64
|
+
// ==========================================================================
|
|
65
|
+
// Subscriptions
|
|
66
|
+
// ==========================================================================
|
|
67
|
+
async getSubscription(id) {
|
|
68
|
+
return this.request(`/subscriptions/${id}`, undefined, 'GET');
|
|
69
|
+
}
|
|
70
|
+
async listSubscriptions(params) {
|
|
71
|
+
const query = params ? '?' + this.toQueryString(params) : '';
|
|
72
|
+
return this.request(`/subscriptions${query}`, undefined, 'GET');
|
|
73
|
+
}
|
|
74
|
+
// ==========================================================================
|
|
75
|
+
// Utility
|
|
76
|
+
// ==========================================================================
|
|
77
|
+
isConfigured() {
|
|
78
|
+
return this.apiKey.length > 0;
|
|
79
|
+
}
|
|
80
|
+
isTestMode() {
|
|
81
|
+
return this.apiKey.includes('_test_');
|
|
82
|
+
}
|
|
83
|
+
// ==========================================================================
|
|
84
|
+
// Private: HTTP Layer
|
|
85
|
+
// ==========================================================================
|
|
86
|
+
async request(path, body, method = 'POST') {
|
|
87
|
+
const headers = {
|
|
88
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
89
|
+
'Stripe-Version': this.apiVersion,
|
|
90
|
+
};
|
|
91
|
+
const options = { method, headers };
|
|
92
|
+
if (body && method === 'POST') {
|
|
93
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
94
|
+
options.body = this.toFormData(body);
|
|
95
|
+
}
|
|
96
|
+
const resp = await fetch(`${this.baseUrl}${path}`, options);
|
|
97
|
+
if (!resp.ok) {
|
|
98
|
+
const err = await resp.json().catch(() => ({}));
|
|
99
|
+
const msg = err?.error?.message || `Stripe API error: ${resp.status}`;
|
|
100
|
+
throw new Error(`[Stripe] ${msg}`);
|
|
101
|
+
}
|
|
102
|
+
return resp.json();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Convert nested object to Stripe's form-encoded format.
|
|
106
|
+
* Stripe expects: line_items[0][price]=xxx&line_items[0][quantity]=1
|
|
107
|
+
*/
|
|
108
|
+
toFormData(obj, prefix = '') {
|
|
109
|
+
const parts = [];
|
|
110
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
111
|
+
if (value === undefined || value === null)
|
|
112
|
+
continue;
|
|
113
|
+
const fullKey = prefix ? `${prefix}[${key}]` : key;
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
value.forEach((item, i) => {
|
|
116
|
+
if (typeof item === 'object') {
|
|
117
|
+
parts.push(this.toFormData(item, `${fullKey}[${i}]`));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
parts.push(`${encodeURIComponent(`${fullKey}[${i}]`)}=${encodeURIComponent(String(item))}`);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else if (typeof value === 'object') {
|
|
125
|
+
parts.push(this.toFormData(value, fullKey));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
parts.push(`${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return parts.filter(Boolean).join('&');
|
|
132
|
+
}
|
|
133
|
+
toQueryString(obj) {
|
|
134
|
+
return Object.entries(obj)
|
|
135
|
+
.filter(([, v]) => v !== undefined)
|
|
136
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
|
|
137
|
+
.join('&');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.StripeClient = StripeClient;
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// Singleton
|
|
143
|
+
// ============================================================================
|
|
144
|
+
let stripeInstance = null;
|
|
145
|
+
function getStripeClient(config) {
|
|
146
|
+
if (!stripeInstance) {
|
|
147
|
+
stripeInstance = new StripeClient(config);
|
|
148
|
+
}
|
|
149
|
+
return stripeInstance;
|
|
150
|
+
}
|
|
151
|
+
function resetStripeClient() {
|
|
152
|
+
stripeInstance = null;
|
|
153
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Genesis v11.0 - Revenue Loop
|
|
3
|
+
*
|
|
4
|
+
* Closes the loop: CompIntel → Stripe → Revenue.
|
|
5
|
+
* This is the first autonomous revenue-generating pipeline:
|
|
6
|
+
*
|
|
7
|
+
* 1. Creates Stripe product + price for CompIntel subscription
|
|
8
|
+
* 2. Generates payment link customers can use
|
|
9
|
+
* 3. Verifies subscription before running scans
|
|
10
|
+
* 4. Tracks revenue generated
|
|
11
|
+
*
|
|
12
|
+
* The world's first verified autonomous revenue-generating agent.
|
|
13
|
+
*/
|
|
14
|
+
import { StripeProduct, StripePrice, StripePaymentLink } from '../payments/stripe-client.js';
|
|
15
|
+
export interface RevenueLoopConfig {
|
|
16
|
+
/** CompIntel plans and pricing (in cents) */
|
|
17
|
+
plans: {
|
|
18
|
+
starter: {
|
|
19
|
+
price: number;
|
|
20
|
+
competitors: number;
|
|
21
|
+
interval: 'month' | 'year';
|
|
22
|
+
};
|
|
23
|
+
pro: {
|
|
24
|
+
price: number;
|
|
25
|
+
competitors: number;
|
|
26
|
+
interval: 'month' | 'year';
|
|
27
|
+
};
|
|
28
|
+
enterprise: {
|
|
29
|
+
price: number;
|
|
30
|
+
competitors: number;
|
|
31
|
+
interval: 'month' | 'year';
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/** Stripe product metadata */
|
|
35
|
+
productName?: string;
|
|
36
|
+
productDescription?: string;
|
|
37
|
+
/** Success URL after payment */
|
|
38
|
+
successUrl?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface RevenueLoopState {
|
|
41
|
+
product?: StripeProduct;
|
|
42
|
+
prices: Record<string, StripePrice>;
|
|
43
|
+
paymentLinks: Record<string, StripePaymentLink>;
|
|
44
|
+
totalRevenue: number;
|
|
45
|
+
activeSubscriptions: number;
|
|
46
|
+
scansDelivered: number;
|
|
47
|
+
digestsGenerated: number;
|
|
48
|
+
}
|
|
49
|
+
export interface SubscriptionCheck {
|
|
50
|
+
valid: boolean;
|
|
51
|
+
plan?: string;
|
|
52
|
+
maxCompetitors?: number;
|
|
53
|
+
expiresAt?: number;
|
|
54
|
+
reason?: string;
|
|
55
|
+
}
|
|
56
|
+
export declare const DEFAULT_REVENUE_CONFIG: RevenueLoopConfig;
|
|
57
|
+
export declare class RevenueLoop {
|
|
58
|
+
private config;
|
|
59
|
+
private stripe;
|
|
60
|
+
private state;
|
|
61
|
+
constructor(config?: Partial<RevenueLoopConfig>);
|
|
62
|
+
/**
|
|
63
|
+
* Initialize the revenue loop by creating Stripe product, prices, and payment links.
|
|
64
|
+
* Idempotent: checks for existing product before creating.
|
|
65
|
+
*/
|
|
66
|
+
setup(): Promise<{
|
|
67
|
+
product: StripeProduct;
|
|
68
|
+
prices: Record<string, StripePrice>;
|
|
69
|
+
paymentLinks: Record<string, StripePaymentLink>;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Check if a customer has an active CompIntel subscription.
|
|
73
|
+
*/
|
|
74
|
+
checkSubscription(customerId: string): Promise<SubscriptionCheck>;
|
|
75
|
+
/**
|
|
76
|
+
* Run a paid CompIntel scan for a customer.
|
|
77
|
+
* Checks subscription, runs scan, tracks revenue.
|
|
78
|
+
*/
|
|
79
|
+
runPaidScan(customerId: string, competitors: Array<{
|
|
80
|
+
name: string;
|
|
81
|
+
domain: string;
|
|
82
|
+
pages?: string[];
|
|
83
|
+
}>): Promise<{
|
|
84
|
+
success: boolean;
|
|
85
|
+
changes?: any[];
|
|
86
|
+
digest?: any;
|
|
87
|
+
subscription?: SubscriptionCheck;
|
|
88
|
+
error?: string;
|
|
89
|
+
}>;
|
|
90
|
+
/**
|
|
91
|
+
* Get the payment URL for a plan.
|
|
92
|
+
*/
|
|
93
|
+
getPaymentUrl(plan: 'starter' | 'pro' | 'enterprise'): string;
|
|
94
|
+
/**
|
|
95
|
+
* Get all payment links.
|
|
96
|
+
*/
|
|
97
|
+
getPaymentLinks(): Record<string, string>;
|
|
98
|
+
getStats(): RevenueLoopState;
|
|
99
|
+
private ensureProduct;
|
|
100
|
+
private ensurePrice;
|
|
101
|
+
}
|
|
102
|
+
export declare function getRevenueLoop(config?: Partial<RevenueLoopConfig>): RevenueLoop;
|
|
103
|
+
export declare function createRevenueLoop(config?: Partial<RevenueLoopConfig>): RevenueLoop;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genesis v11.0 - Revenue Loop
|
|
4
|
+
*
|
|
5
|
+
* Closes the loop: CompIntel → Stripe → Revenue.
|
|
6
|
+
* This is the first autonomous revenue-generating pipeline:
|
|
7
|
+
*
|
|
8
|
+
* 1. Creates Stripe product + price for CompIntel subscription
|
|
9
|
+
* 2. Generates payment link customers can use
|
|
10
|
+
* 3. Verifies subscription before running scans
|
|
11
|
+
* 4. Tracks revenue generated
|
|
12
|
+
*
|
|
13
|
+
* The world's first verified autonomous revenue-generating agent.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.RevenueLoop = exports.DEFAULT_REVENUE_CONFIG = void 0;
|
|
17
|
+
exports.getRevenueLoop = getRevenueLoop;
|
|
18
|
+
exports.createRevenueLoop = createRevenueLoop;
|
|
19
|
+
const stripe_client_js_1 = require("../payments/stripe-client.js");
|
|
20
|
+
const competitive_intel_js_1 = require("./competitive-intel.js");
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Default Config
|
|
23
|
+
// ============================================================================
|
|
24
|
+
exports.DEFAULT_REVENUE_CONFIG = {
|
|
25
|
+
plans: {
|
|
26
|
+
starter: { price: 4900, competitors: 3, interval: 'month' }, // $49/mo
|
|
27
|
+
pro: { price: 9900, competitors: 10, interval: 'month' }, // $99/mo
|
|
28
|
+
enterprise: { price: 19900, competitors: 25, interval: 'month' }, // $199/mo
|
|
29
|
+
},
|
|
30
|
+
productName: 'Genesis Competitive Intelligence',
|
|
31
|
+
productDescription: 'AI-powered competitive monitoring with strategic insights. Not just "page changed" — understand WHY and what it means for your business.',
|
|
32
|
+
successUrl: 'https://genesis-ai.dev/welcome',
|
|
33
|
+
};
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Revenue Loop
|
|
36
|
+
// ============================================================================
|
|
37
|
+
class RevenueLoop {
|
|
38
|
+
config;
|
|
39
|
+
stripe;
|
|
40
|
+
state;
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.config = { ...exports.DEFAULT_REVENUE_CONFIG, ...config };
|
|
43
|
+
this.stripe = (0, stripe_client_js_1.getStripeClient)();
|
|
44
|
+
this.state = {
|
|
45
|
+
prices: {},
|
|
46
|
+
paymentLinks: {},
|
|
47
|
+
totalRevenue: 0,
|
|
48
|
+
activeSubscriptions: 0,
|
|
49
|
+
scansDelivered: 0,
|
|
50
|
+
digestsGenerated: 0,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// ==========================================================================
|
|
54
|
+
// Setup: Create Stripe Resources
|
|
55
|
+
// ==========================================================================
|
|
56
|
+
/**
|
|
57
|
+
* Initialize the revenue loop by creating Stripe product, prices, and payment links.
|
|
58
|
+
* Idempotent: checks for existing product before creating.
|
|
59
|
+
*/
|
|
60
|
+
async setup() {
|
|
61
|
+
if (!this.stripe.isConfigured()) {
|
|
62
|
+
throw new Error('Stripe not configured. Set STRIPE_API_KEY env var.');
|
|
63
|
+
}
|
|
64
|
+
console.log('[RevenueLoop] Setting up Stripe resources...');
|
|
65
|
+
// 1. Create or find product
|
|
66
|
+
const product = await this.ensureProduct();
|
|
67
|
+
this.state.product = product;
|
|
68
|
+
console.log(`[RevenueLoop] Product: ${product.id} (${product.name})`);
|
|
69
|
+
// 2. Create prices for each plan
|
|
70
|
+
const prices = {};
|
|
71
|
+
for (const [planName, planConfig] of Object.entries(this.config.plans)) {
|
|
72
|
+
const price = await this.ensurePrice(product.id, planName, planConfig);
|
|
73
|
+
prices[planName] = price;
|
|
74
|
+
console.log(`[RevenueLoop] Price ${planName}: ${price.id} ($${(planConfig.price / 100).toFixed(0)}/${planConfig.interval})`);
|
|
75
|
+
}
|
|
76
|
+
this.state.prices = prices;
|
|
77
|
+
// 3. Create payment links for each price
|
|
78
|
+
const paymentLinks = {};
|
|
79
|
+
for (const [planName, price] of Object.entries(prices)) {
|
|
80
|
+
const link = await this.stripe.createPaymentLink({
|
|
81
|
+
line_items: [{ price: price.id, quantity: 1 }],
|
|
82
|
+
metadata: { plan: planName, source: 'genesis-compintel' },
|
|
83
|
+
});
|
|
84
|
+
paymentLinks[planName] = link;
|
|
85
|
+
console.log(`[RevenueLoop] Payment Link ${planName}: ${link.url}`);
|
|
86
|
+
}
|
|
87
|
+
this.state.paymentLinks = paymentLinks;
|
|
88
|
+
return { product, prices, paymentLinks };
|
|
89
|
+
}
|
|
90
|
+
// ==========================================================================
|
|
91
|
+
// Revenue Operations
|
|
92
|
+
// ==========================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Check if a customer has an active CompIntel subscription.
|
|
95
|
+
*/
|
|
96
|
+
async checkSubscription(customerId) {
|
|
97
|
+
if (!this.stripe.isConfigured()) {
|
|
98
|
+
return { valid: false, reason: 'Stripe not configured' };
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const subs = await this.stripe.listSubscriptions({
|
|
102
|
+
customer: customerId,
|
|
103
|
+
status: 'active',
|
|
104
|
+
limit: 10,
|
|
105
|
+
});
|
|
106
|
+
// Find subscription for our product
|
|
107
|
+
const productId = this.state.product?.id;
|
|
108
|
+
const compIntelSub = subs.data.find(s => s.items.data.some(item => item.price.product === productId));
|
|
109
|
+
if (!compIntelSub) {
|
|
110
|
+
return { valid: false, reason: 'No active CompIntel subscription' };
|
|
111
|
+
}
|
|
112
|
+
// Determine plan from price
|
|
113
|
+
const priceId = compIntelSub.items.data[0]?.price.id;
|
|
114
|
+
const plan = Object.entries(this.state.prices).find(([, p]) => p.id === priceId)?.[0] || 'starter';
|
|
115
|
+
const planConfig = this.config.plans[plan];
|
|
116
|
+
return {
|
|
117
|
+
valid: true,
|
|
118
|
+
plan,
|
|
119
|
+
maxCompetitors: planConfig?.competitors || 3,
|
|
120
|
+
expiresAt: compIntelSub.current_period_end * 1000,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
return { valid: false, reason: `Stripe error: ${e.message}` };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Run a paid CompIntel scan for a customer.
|
|
129
|
+
* Checks subscription, runs scan, tracks revenue.
|
|
130
|
+
*/
|
|
131
|
+
async runPaidScan(customerId, competitors) {
|
|
132
|
+
// Check subscription
|
|
133
|
+
const sub = await this.checkSubscription(customerId);
|
|
134
|
+
if (!sub.valid) {
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
subscription: sub,
|
|
138
|
+
error: `No active subscription. Subscribe at: ${this.getPaymentUrl('starter')}`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// Enforce competitor limit
|
|
142
|
+
if (competitors.length > (sub.maxCompetitors || 3)) {
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
subscription: sub,
|
|
146
|
+
error: `Plan '${sub.plan}' allows ${sub.maxCompetitors} competitors. Upgrade at: ${this.getPaymentUrl('pro')}`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
// Run scan
|
|
150
|
+
const service = (0, competitive_intel_js_1.createCompetitiveIntelService)({ competitors });
|
|
151
|
+
const changes = await service.checkAll();
|
|
152
|
+
this.state.scansDelivered++;
|
|
153
|
+
let digest;
|
|
154
|
+
if (changes.length > 0) {
|
|
155
|
+
digest = await service.generateDigest(24);
|
|
156
|
+
this.state.digestsGenerated++;
|
|
157
|
+
}
|
|
158
|
+
// Track revenue (subscription already paid via Stripe)
|
|
159
|
+
const planConfig = this.config.plans[sub.plan];
|
|
160
|
+
if (planConfig) {
|
|
161
|
+
// Pro-rate per scan (assume ~30 scans/month at 4x daily check for each competitor)
|
|
162
|
+
const scanValue = Math.round(planConfig.price / 120); // Rough per-scan value
|
|
163
|
+
this.state.totalRevenue += scanValue;
|
|
164
|
+
}
|
|
165
|
+
return { success: true, changes, digest, subscription: sub };
|
|
166
|
+
}
|
|
167
|
+
// ==========================================================================
|
|
168
|
+
// Payment URLs
|
|
169
|
+
// ==========================================================================
|
|
170
|
+
/**
|
|
171
|
+
* Get the payment URL for a plan.
|
|
172
|
+
*/
|
|
173
|
+
getPaymentUrl(plan) {
|
|
174
|
+
return this.state.paymentLinks[plan]?.url || `[Setup required: run setup() first]`;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get all payment links.
|
|
178
|
+
*/
|
|
179
|
+
getPaymentLinks() {
|
|
180
|
+
const links = {};
|
|
181
|
+
for (const [plan, link] of Object.entries(this.state.paymentLinks)) {
|
|
182
|
+
links[plan] = link.url;
|
|
183
|
+
}
|
|
184
|
+
return links;
|
|
185
|
+
}
|
|
186
|
+
// ==========================================================================
|
|
187
|
+
// Stats
|
|
188
|
+
// ==========================================================================
|
|
189
|
+
getStats() {
|
|
190
|
+
return { ...this.state };
|
|
191
|
+
}
|
|
192
|
+
// ==========================================================================
|
|
193
|
+
// Private Helpers
|
|
194
|
+
// ==========================================================================
|
|
195
|
+
async ensureProduct() {
|
|
196
|
+
// Check if product already exists
|
|
197
|
+
try {
|
|
198
|
+
const existing = await this.stripe.listProducts({ active: true, limit: 100 });
|
|
199
|
+
const found = existing.data.find(p => p.name === this.config.productName ||
|
|
200
|
+
p.metadata?.genesis === 'compintel');
|
|
201
|
+
if (found)
|
|
202
|
+
return found;
|
|
203
|
+
}
|
|
204
|
+
catch { /* continue to create */ }
|
|
205
|
+
return this.stripe.createProduct({
|
|
206
|
+
name: this.config.productName || 'Genesis Competitive Intelligence',
|
|
207
|
+
description: this.config.productDescription,
|
|
208
|
+
metadata: { genesis: 'compintel', version: '11.0' },
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async ensurePrice(productId, planName, planConfig) {
|
|
212
|
+
// Check if price exists
|
|
213
|
+
try {
|
|
214
|
+
const existing = await this.stripe.listPrices({ product: productId, active: true });
|
|
215
|
+
const found = existing.data.find(p => p.unit_amount === planConfig.price &&
|
|
216
|
+
p.recurring?.interval === planConfig.interval);
|
|
217
|
+
if (found)
|
|
218
|
+
return found;
|
|
219
|
+
}
|
|
220
|
+
catch { /* continue to create */ }
|
|
221
|
+
return this.stripe.createPrice({
|
|
222
|
+
product: productId,
|
|
223
|
+
unit_amount: planConfig.price,
|
|
224
|
+
currency: 'usd',
|
|
225
|
+
recurring: { interval: planConfig.interval },
|
|
226
|
+
metadata: { plan: planName, genesis: 'compintel' },
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
exports.RevenueLoop = RevenueLoop;
|
|
231
|
+
// ============================================================================
|
|
232
|
+
// Factory
|
|
233
|
+
// ============================================================================
|
|
234
|
+
let revenueLoopInstance = null;
|
|
235
|
+
function getRevenueLoop(config) {
|
|
236
|
+
if (!revenueLoopInstance) {
|
|
237
|
+
revenueLoopInstance = new RevenueLoop(config);
|
|
238
|
+
}
|
|
239
|
+
return revenueLoopInstance;
|
|
240
|
+
}
|
|
241
|
+
function createRevenueLoop(config) {
|
|
242
|
+
return new RevenueLoop(config);
|
|
243
|
+
}
|
package/package.json
CHANGED