genesis-ai-cli 11.0.0 → 11.1.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/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/competitive-intel.d.ts +4 -0
- package/dist/src/services/competitive-intel.js +57 -32
- package/dist/src/services/revenue-loop.d.ts +103 -0
- package/dist/src/services/revenue-loop.js +243 -0
- package/package.json +1 -1
|
@@ -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
|
+
}
|
|
@@ -104,6 +104,10 @@ export declare class CompetitiveIntelService {
|
|
|
104
104
|
* Check a single competitor for changes.
|
|
105
105
|
*/
|
|
106
106
|
private checkCompetitor;
|
|
107
|
+
/**
|
|
108
|
+
* Call LLM with MCP fallback to direct HTTP.
|
|
109
|
+
*/
|
|
110
|
+
private callLLM;
|
|
107
111
|
/**
|
|
108
112
|
* Analyze a detected change using LLM.
|
|
109
113
|
*/
|
|
@@ -202,28 +202,61 @@ class CompetitiveIntelService {
|
|
|
202
202
|
return changes;
|
|
203
203
|
}
|
|
204
204
|
/**
|
|
205
|
-
*
|
|
205
|
+
* Call LLM with MCP fallback to direct HTTP.
|
|
206
206
|
*/
|
|
207
|
-
async
|
|
207
|
+
async callLLM(messages, maxTokens = 200) {
|
|
208
|
+
// Try MCP first
|
|
208
209
|
try {
|
|
209
210
|
const mcp = (0, index_js_1.getMCPClient)();
|
|
210
211
|
const result = await mcp.call('openai', 'openai_chat', {
|
|
211
212
|
model: this.config.llmModel,
|
|
212
|
-
messages
|
|
213
|
-
{
|
|
214
|
-
role: 'system',
|
|
215
|
-
content: `You are a competitive intelligence analyst. Analyze changes on competitor websites and determine their business significance. Respond with JSON: {"significance": "low|medium|high|critical", "analysis": "brief analysis"}`
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
role: 'user',
|
|
219
|
-
content: `Competitor: ${competitorName}\nPage type: ${page.type}\nURL: ${page.url}\n\nChange detected: ${diffSummary}\n\nOld content (first 1000 chars): ${oldContent.slice(0, 1000)}\n\nNew content (first 1000 chars): ${newContent.slice(0, 1000)}\n\nWhat is the business significance of this change?`
|
|
220
|
-
}
|
|
221
|
-
],
|
|
213
|
+
messages,
|
|
222
214
|
temperature: 0.3,
|
|
223
|
-
max_tokens:
|
|
215
|
+
max_tokens: maxTokens,
|
|
224
216
|
});
|
|
225
217
|
const r = result;
|
|
226
|
-
const content = r?.data?.choices?.[0]?.message?.content || r?.choices?.[0]?.message?.content
|
|
218
|
+
const content = r?.data?.choices?.[0]?.message?.content || r?.choices?.[0]?.message?.content;
|
|
219
|
+
if (content)
|
|
220
|
+
return content;
|
|
221
|
+
}
|
|
222
|
+
catch { /* fall through to direct */ }
|
|
223
|
+
// Fallback: direct OpenAI HTTP call
|
|
224
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
225
|
+
if (!apiKey)
|
|
226
|
+
return '{}';
|
|
227
|
+
const resp = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
headers: {
|
|
230
|
+
'Content-Type': 'application/json',
|
|
231
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
232
|
+
},
|
|
233
|
+
body: JSON.stringify({
|
|
234
|
+
model: this.config.llmModel,
|
|
235
|
+
messages,
|
|
236
|
+
temperature: 0.3,
|
|
237
|
+
max_tokens: maxTokens,
|
|
238
|
+
}),
|
|
239
|
+
});
|
|
240
|
+
if (!resp.ok)
|
|
241
|
+
return '{}';
|
|
242
|
+
const data = await resp.json();
|
|
243
|
+
return data?.choices?.[0]?.message?.content || '{}';
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Analyze a detected change using LLM.
|
|
247
|
+
*/
|
|
248
|
+
async analyzeChange(competitorName, page, diffSummary, oldContent, newContent) {
|
|
249
|
+
try {
|
|
250
|
+
const content = await this.callLLM([
|
|
251
|
+
{
|
|
252
|
+
role: 'system',
|
|
253
|
+
content: `You are a competitive intelligence analyst. Analyze changes on competitor websites and determine their business significance. Respond with JSON: {"significance": "low|medium|high|critical", "analysis": "brief analysis"}`
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
role: 'user',
|
|
257
|
+
content: `Competitor: ${competitorName}\nPage type: ${page.type}\nURL: ${page.url}\n\nChange detected: ${diffSummary}\n\nOld content (first 1000 chars): ${oldContent.slice(0, 1000)}\n\nNew content (first 1000 chars): ${newContent.slice(0, 1000)}\n\nWhat is the business significance of this change?`
|
|
258
|
+
}
|
|
259
|
+
]);
|
|
227
260
|
const parsed = JSON.parse(content.match(/\{[\s\S]*\}/)?.[0] || '{}');
|
|
228
261
|
return {
|
|
229
262
|
significance: parsed.significance || 'medium',
|
|
@@ -242,25 +275,17 @@ class CompetitiveIntelService {
|
|
|
242
275
|
return { insights: ['No significant changes detected.'], recommendations: [] };
|
|
243
276
|
}
|
|
244
277
|
try {
|
|
245
|
-
const mcp = (0, index_js_1.getMCPClient)();
|
|
246
278
|
const changesSummary = competitorData.map(c => `${c.name}: ${c.changes.map(ch => `[${ch.significance}] ${ch.summary}`).join('; ')}`).join('\n');
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
{
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
],
|
|
259
|
-
temperature: 0.5,
|
|
260
|
-
max_tokens: 500,
|
|
261
|
-
});
|
|
262
|
-
const r = result;
|
|
263
|
-
const content = r?.data?.choices?.[0]?.message?.content || r?.choices?.[0]?.message?.content || '{}';
|
|
279
|
+
const content = await this.callLLM([
|
|
280
|
+
{
|
|
281
|
+
role: 'system',
|
|
282
|
+
content: 'You are a strategic analyst. Given competitor changes, produce key insights and recommendations. Respond with JSON: {"insights": ["..."], "recommendations": ["..."]}'
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
role: 'user',
|
|
286
|
+
content: `Recent competitor changes:\n${changesSummary}\n\nProvide 2-4 key insights and 1-3 actionable recommendations.`
|
|
287
|
+
}
|
|
288
|
+
], 500);
|
|
264
289
|
const parsed = JSON.parse(content.match(/\{[\s\S]*\}/)?.[0] || '{}');
|
|
265
290
|
return {
|
|
266
291
|
insights: parsed.insights || ['Analysis unavailable.'],
|
|
@@ -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