@codebakers/cli 2.9.0 → 3.0.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/commands/billing.d.ts +4 -0
- package/dist/commands/billing.js +91 -0
- package/dist/commands/extend.d.ts +4 -0
- package/dist/commands/extend.js +141 -0
- package/dist/commands/go.d.ts +4 -0
- package/dist/commands/go.js +173 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.js +83 -1
- package/dist/index.js +23 -5
- package/dist/lib/fingerprint.d.ts +23 -0
- package/dist/lib/fingerprint.js +136 -0
- package/dist/mcp/server.js +49 -16
- package/package.json +1 -1
- package/src/commands/billing.ts +99 -0
- package/src/commands/extend.ts +157 -0
- package/src/commands/go.ts +197 -0
- package/src/config.ts +101 -1
- package/src/index.ts +26 -5
- package/src/lib/fingerprint.ts +122 -0
- package/src/mcp/server.ts +60 -17
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.billing = billing;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const config_js_1 = require("../config.js");
|
|
10
|
+
/**
|
|
11
|
+
* Open a URL in the default browser
|
|
12
|
+
*/
|
|
13
|
+
function openBrowser(url) {
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
try {
|
|
16
|
+
if (platform === 'win32') {
|
|
17
|
+
(0, child_process_1.execSync)(`start "" "${url}"`, { stdio: 'ignore', shell: 'cmd.exe' });
|
|
18
|
+
}
|
|
19
|
+
else if (platform === 'darwin') {
|
|
20
|
+
(0, child_process_1.execSync)(`open "${url}"`, { stdio: 'ignore' });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
(0, child_process_1.execSync)(`xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}"`, {
|
|
24
|
+
stdio: 'ignore',
|
|
25
|
+
shell: '/bin/sh',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
console.log(chalk_1.default.yellow(`\n Could not open browser automatically.`));
|
|
31
|
+
console.log(chalk_1.default.gray(` Please open this URL manually:\n`));
|
|
32
|
+
console.log(chalk_1.default.cyan(` ${url}\n`));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Open billing page for subscription management
|
|
37
|
+
*/
|
|
38
|
+
async function billing() {
|
|
39
|
+
console.log(chalk_1.default.blue(`
|
|
40
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
41
|
+
║ ║
|
|
42
|
+
║ ${chalk_1.default.bold.white('CodeBakers Billing & Subscription')} ║
|
|
43
|
+
║ ║
|
|
44
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
45
|
+
`));
|
|
46
|
+
// Check current status
|
|
47
|
+
const apiKey = (0, config_js_1.getApiKey)();
|
|
48
|
+
const trial = (0, config_js_1.getTrialState)();
|
|
49
|
+
if (apiKey) {
|
|
50
|
+
console.log(chalk_1.default.green(' ✓ You have an active subscription\n'));
|
|
51
|
+
console.log(chalk_1.default.gray(' Opening settings page to manage your subscription...\n'));
|
|
52
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
53
|
+
const settingsUrl = `${apiUrl}/settings`;
|
|
54
|
+
openBrowser(settingsUrl);
|
|
55
|
+
console.log(chalk_1.default.gray(` ${settingsUrl}\n`));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (trial) {
|
|
59
|
+
if ((0, config_js_1.isTrialExpired)()) {
|
|
60
|
+
console.log(chalk_1.default.yellow(' ⚠️ Your trial has expired\n'));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const daysRemaining = (0, config_js_1.getTrialDaysRemaining)();
|
|
64
|
+
console.log(chalk_1.default.gray(` Trial: ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining\n`));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Show pricing
|
|
68
|
+
console.log(chalk_1.default.white(' Choose your plan:\n'));
|
|
69
|
+
console.log(chalk_1.default.cyan(' Pro ') + chalk_1.default.white('- $49/month ') + chalk_1.default.gray('(1 seat)'));
|
|
70
|
+
console.log(chalk_1.default.gray(' All 40 pattern modules'));
|
|
71
|
+
console.log(chalk_1.default.gray(' Unlimited projects'));
|
|
72
|
+
console.log(chalk_1.default.gray(' Priority support\n'));
|
|
73
|
+
console.log(chalk_1.default.cyan(' Team ') + chalk_1.default.white('- $149/month ') + chalk_1.default.gray('(5 seats)'));
|
|
74
|
+
console.log(chalk_1.default.gray(' Everything in Pro'));
|
|
75
|
+
console.log(chalk_1.default.gray(' Team collaboration'));
|
|
76
|
+
console.log(chalk_1.default.gray(' Shared API keys\n'));
|
|
77
|
+
console.log(chalk_1.default.cyan(' Agency ') + chalk_1.default.white('- $349/month ') + chalk_1.default.gray('(unlimited seats)'));
|
|
78
|
+
console.log(chalk_1.default.gray(' Everything in Team'));
|
|
79
|
+
console.log(chalk_1.default.gray(' White-label option'));
|
|
80
|
+
console.log(chalk_1.default.gray(' Dedicated support\n'));
|
|
81
|
+
console.log(chalk_1.default.gray(' Enterprise pricing available for large teams.\n'));
|
|
82
|
+
// Open billing page
|
|
83
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
84
|
+
const billingUrl = `${apiUrl}/billing`;
|
|
85
|
+
console.log(chalk_1.default.white(' Opening billing page...\n'));
|
|
86
|
+
openBrowser(billingUrl);
|
|
87
|
+
console.log(chalk_1.default.gray(` ${billingUrl}\n`));
|
|
88
|
+
console.log(chalk_1.default.gray(' After subscribing, run:'));
|
|
89
|
+
console.log(chalk_1.default.cyan(' codebakers setup\n'));
|
|
90
|
+
console.log(chalk_1.default.gray(' to configure your API key.\n'));
|
|
91
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.extend = extend;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const config_js_1 = require("../config.js");
|
|
11
|
+
/**
|
|
12
|
+
* Open a URL in the default browser
|
|
13
|
+
*/
|
|
14
|
+
function openBrowser(url) {
|
|
15
|
+
const platform = process.platform;
|
|
16
|
+
try {
|
|
17
|
+
if (platform === 'win32') {
|
|
18
|
+
(0, child_process_1.execSync)(`start "" "${url}"`, { stdio: 'ignore', shell: 'cmd.exe' });
|
|
19
|
+
}
|
|
20
|
+
else if (platform === 'darwin') {
|
|
21
|
+
(0, child_process_1.execSync)(`open "${url}"`, { stdio: 'ignore' });
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// Linux - try common browsers
|
|
25
|
+
(0, child_process_1.execSync)(`xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}"`, {
|
|
26
|
+
stdio: 'ignore',
|
|
27
|
+
shell: '/bin/sh',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
console.log(chalk_1.default.yellow(`\n Could not open browser automatically.`));
|
|
33
|
+
console.log(chalk_1.default.gray(` Please open this URL manually:\n`));
|
|
34
|
+
console.log(chalk_1.default.cyan(` ${url}\n`));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Sleep for a specified number of milliseconds
|
|
39
|
+
*/
|
|
40
|
+
function sleep(ms) {
|
|
41
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extend trial by connecting GitHub account
|
|
45
|
+
*/
|
|
46
|
+
async function extend() {
|
|
47
|
+
console.log(chalk_1.default.blue(`
|
|
48
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
49
|
+
║ ║
|
|
50
|
+
║ ${chalk_1.default.bold.white('Extend Your Trial with GitHub')} ║
|
|
51
|
+
║ ║
|
|
52
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
53
|
+
`));
|
|
54
|
+
// Check if user already has an API key (paid user)
|
|
55
|
+
const apiKey = (0, config_js_1.getApiKey)();
|
|
56
|
+
if (apiKey) {
|
|
57
|
+
console.log(chalk_1.default.green(' ✓ You\'re already logged in with an API key!\n'));
|
|
58
|
+
console.log(chalk_1.default.gray(' You have unlimited access. No extension needed.\n'));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Check for existing trial
|
|
62
|
+
const trial = (0, config_js_1.getTrialState)();
|
|
63
|
+
if (!trial) {
|
|
64
|
+
console.log(chalk_1.default.yellow(' No trial found.\n'));
|
|
65
|
+
console.log(chalk_1.default.white(' Start your free trial first:\n'));
|
|
66
|
+
console.log(chalk_1.default.cyan(' codebakers go\n'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Check if already extended
|
|
70
|
+
if (trial.stage === 'extended') {
|
|
71
|
+
console.log(chalk_1.default.yellow(' Your trial has already been extended.\n'));
|
|
72
|
+
if ((0, config_js_1.isTrialExpired)()) {
|
|
73
|
+
console.log(chalk_1.default.white(' Ready to upgrade? $49/month for unlimited access:\n'));
|
|
74
|
+
console.log(chalk_1.default.cyan(' codebakers upgrade\n'));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const expiresAt = new Date(trial.expiresAt);
|
|
78
|
+
const daysRemaining = Math.max(0, Math.ceil((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24)));
|
|
79
|
+
console.log(chalk_1.default.gray(` ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining.\n`));
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Check if converted
|
|
84
|
+
if (trial.stage === 'converted') {
|
|
85
|
+
console.log(chalk_1.default.green(' ✓ You\'ve upgraded to a paid plan!\n'));
|
|
86
|
+
console.log(chalk_1.default.gray(' Run ') + chalk_1.default.cyan('codebakers setup') + chalk_1.default.gray(' to configure your API key.\n'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Open browser for GitHub OAuth
|
|
90
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
91
|
+
const authUrl = `${apiUrl}/api/auth/github?trial_id=${trial.trialId}`;
|
|
92
|
+
console.log(chalk_1.default.white(' Opening browser for GitHub authorization...\n'));
|
|
93
|
+
openBrowser(authUrl);
|
|
94
|
+
console.log(chalk_1.default.gray(' Waiting for authorization...'));
|
|
95
|
+
console.log(chalk_1.default.gray(' (This may take a moment)\n'));
|
|
96
|
+
// Poll for completion
|
|
97
|
+
const spinner = (0, ora_1.default)('Checking authorization status...').start();
|
|
98
|
+
let extended = false;
|
|
99
|
+
let pollCount = 0;
|
|
100
|
+
const maxPolls = 60; // 2 minutes max
|
|
101
|
+
while (pollCount < maxPolls && !extended) {
|
|
102
|
+
await sleep(2000);
|
|
103
|
+
pollCount++;
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(`${apiUrl}/api/trial/status?trialId=${trial.trialId}`);
|
|
106
|
+
const data = await response.json();
|
|
107
|
+
if (data.stage === 'extended') {
|
|
108
|
+
extended = true;
|
|
109
|
+
// Update local trial state
|
|
110
|
+
const updatedTrial = {
|
|
111
|
+
...trial,
|
|
112
|
+
stage: 'extended',
|
|
113
|
+
expiresAt: data.expiresAt,
|
|
114
|
+
extendedAt: new Date().toISOString(),
|
|
115
|
+
...(data.githubUsername && { githubUsername: data.githubUsername }),
|
|
116
|
+
};
|
|
117
|
+
(0, config_js_1.setTrialState)(updatedTrial);
|
|
118
|
+
spinner.succeed('Trial extended!');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Ignore polling errors
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (extended) {
|
|
126
|
+
console.log(chalk_1.default.green(`
|
|
127
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
128
|
+
║ ✅ Trial Extended! ║
|
|
129
|
+
║ ║
|
|
130
|
+
║ ${chalk_1.default.white('You have 7 more days to build with CodeBakers.')} ║
|
|
131
|
+
║ ║
|
|
132
|
+
║ ${chalk_1.default.gray('Keep building - your project is waiting!')} ║
|
|
133
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
134
|
+
`));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
spinner.warn('Authorization timed out');
|
|
138
|
+
console.log(chalk_1.default.yellow('\n Please try again or authorize manually:\n'));
|
|
139
|
+
console.log(chalk_1.default.cyan(` ${authUrl}\n`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.go = go;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const config_js_1 = require("../config.js");
|
|
11
|
+
const fingerprint_js_1 = require("../lib/fingerprint.js");
|
|
12
|
+
/**
|
|
13
|
+
* Zero-friction entry point - start using CodeBakers instantly
|
|
14
|
+
*/
|
|
15
|
+
async function go() {
|
|
16
|
+
console.log(chalk_1.default.blue(`
|
|
17
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
18
|
+
║ ║
|
|
19
|
+
║ ${chalk_1.default.bold.white('CodeBakers - Zero Setup Required')} ║
|
|
20
|
+
║ ║
|
|
21
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
22
|
+
`));
|
|
23
|
+
// Check if user already has an API key (paid user)
|
|
24
|
+
const apiKey = (0, config_js_1.getApiKey)();
|
|
25
|
+
if (apiKey) {
|
|
26
|
+
console.log(chalk_1.default.green(' ✓ You\'re already logged in with an API key!\n'));
|
|
27
|
+
console.log(chalk_1.default.gray(' Run ') + chalk_1.default.cyan('codebakers status') + chalk_1.default.gray(' to check your setup.\n'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Check existing trial
|
|
31
|
+
const existingTrial = (0, config_js_1.getTrialState)();
|
|
32
|
+
if (existingTrial && !(0, config_js_1.isTrialExpired)()) {
|
|
33
|
+
const daysRemaining = (0, config_js_1.getTrialDaysRemaining)();
|
|
34
|
+
console.log(chalk_1.default.green(` ✓ Trial active (${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining)\n`));
|
|
35
|
+
if (existingTrial.stage === 'anonymous' && daysRemaining <= 2) {
|
|
36
|
+
console.log(chalk_1.default.yellow(' ⚠️ Trial expiring soon! Extend with GitHub:\n'));
|
|
37
|
+
console.log(chalk_1.default.cyan(' codebakers extend\n'));
|
|
38
|
+
}
|
|
39
|
+
await configureMCP();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Check if trial expired
|
|
43
|
+
if (existingTrial && (0, config_js_1.isTrialExpired)()) {
|
|
44
|
+
console.log(chalk_1.default.yellow(' ⚠️ Your trial has expired.\n'));
|
|
45
|
+
if (existingTrial.stage === 'anonymous') {
|
|
46
|
+
console.log(chalk_1.default.white(' Extend your trial for 7 more days with GitHub:\n'));
|
|
47
|
+
console.log(chalk_1.default.cyan(' codebakers extend\n'));
|
|
48
|
+
console.log(chalk_1.default.gray(' Or upgrade to Pro ($49/month):\n'));
|
|
49
|
+
console.log(chalk_1.default.cyan(' codebakers upgrade\n'));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log(chalk_1.default.white(' Ready to upgrade? $49/month for unlimited access:\n'));
|
|
53
|
+
console.log(chalk_1.default.cyan(' codebakers upgrade\n'));
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Start new trial
|
|
58
|
+
const spinner = (0, ora_1.default)('Starting your free trial...').start();
|
|
59
|
+
try {
|
|
60
|
+
const fingerprint = (0, fingerprint_js_1.getDeviceFingerprint)();
|
|
61
|
+
const apiUrl = (0, config_js_1.getApiUrl)();
|
|
62
|
+
const response = await fetch(`${apiUrl}/api/trial/start`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
deviceHash: fingerprint.deviceHash,
|
|
67
|
+
machineId: fingerprint.machineId,
|
|
68
|
+
platform: fingerprint.platform,
|
|
69
|
+
hostname: fingerprint.hostname,
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
if (data.error === 'trial_not_available') {
|
|
74
|
+
spinner.fail('Trial not available');
|
|
75
|
+
console.log(chalk_1.default.yellow(`
|
|
76
|
+
It looks like you've already used a CodeBakers trial.
|
|
77
|
+
|
|
78
|
+
Ready to upgrade? $49/month for unlimited access.
|
|
79
|
+
|
|
80
|
+
${chalk_1.default.cyan('codebakers upgrade')} or visit ${chalk_1.default.underline('https://codebakers.ai/pricing')}
|
|
81
|
+
`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
throw new Error(data.error || 'Failed to start trial');
|
|
86
|
+
}
|
|
87
|
+
// Check if returning existing trial
|
|
88
|
+
if (data.stage === 'expired') {
|
|
89
|
+
spinner.warn('Your previous trial has expired');
|
|
90
|
+
console.log('');
|
|
91
|
+
if (data.canExtend) {
|
|
92
|
+
console.log(chalk_1.default.white(' Extend your trial for 7 more days with GitHub:\n'));
|
|
93
|
+
console.log(chalk_1.default.cyan(' codebakers extend\n'));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.log(chalk_1.default.white(' Ready to upgrade? $49/month for unlimited access:\n'));
|
|
97
|
+
console.log(chalk_1.default.cyan(' codebakers upgrade\n'));
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Save trial state
|
|
102
|
+
const trialState = {
|
|
103
|
+
trialId: data.trialId,
|
|
104
|
+
stage: data.stage,
|
|
105
|
+
deviceHash: fingerprint.deviceHash,
|
|
106
|
+
expiresAt: data.expiresAt,
|
|
107
|
+
startedAt: data.startedAt,
|
|
108
|
+
...(data.githubUsername && { githubUsername: data.githubUsername }),
|
|
109
|
+
...(data.projectId && { projectId: data.projectId }),
|
|
110
|
+
...(data.projectName && { projectName: data.projectName }),
|
|
111
|
+
};
|
|
112
|
+
(0, config_js_1.setTrialState)(trialState);
|
|
113
|
+
spinner.succeed(`Trial started (${data.daysRemaining} days free)`);
|
|
114
|
+
console.log('');
|
|
115
|
+
// Configure MCP
|
|
116
|
+
await configureMCP();
|
|
117
|
+
// Show success message
|
|
118
|
+
console.log(chalk_1.default.green(`
|
|
119
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
120
|
+
║ ✅ CodeBakers is ready! ║
|
|
121
|
+
║ ║
|
|
122
|
+
║ ${chalk_1.default.white('Your 7-day free trial has started.')} ║
|
|
123
|
+
║ ║
|
|
124
|
+
║ ${chalk_1.default.gray('Try: "Build me a todo app with authentication"')} ║
|
|
125
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
126
|
+
`));
|
|
127
|
+
// Attempt auto-restart Claude Code
|
|
128
|
+
await attemptAutoRestart();
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
spinner.fail('Failed to start trial');
|
|
132
|
+
if (error instanceof Error) {
|
|
133
|
+
if (error.message.includes('fetch') || error.message.includes('network')) {
|
|
134
|
+
console.log(chalk_1.default.red('\n Could not connect to CodeBakers server.'));
|
|
135
|
+
console.log(chalk_1.default.gray(' Check your internet connection and try again.\n'));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
console.log(chalk_1.default.red(`\n ${error.message}\n`));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.log(chalk_1.default.red('\n An unexpected error occurred.\n'));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function configureMCP() {
|
|
147
|
+
const spinner = (0, ora_1.default)('Configuring Claude Code integration...').start();
|
|
148
|
+
const isWindows = process.platform === 'win32';
|
|
149
|
+
const mcpCmd = isWindows
|
|
150
|
+
? 'claude mcp add --transport stdio codebakers -- cmd /c npx -y @codebakers/cli serve'
|
|
151
|
+
: 'claude mcp add --transport stdio codebakers -- npx -y @codebakers/cli serve';
|
|
152
|
+
try {
|
|
153
|
+
(0, child_process_1.execSync)(mcpCmd, { stdio: 'pipe' });
|
|
154
|
+
spinner.succeed('CodeBakers connected to Claude Code');
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
158
|
+
if (errorMessage.includes('already exists') || errorMessage.includes('already registered')) {
|
|
159
|
+
spinner.succeed('CodeBakers already connected to Claude Code');
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
spinner.warn('Could not auto-configure Claude Code');
|
|
163
|
+
console.log(chalk_1.default.gray('\n Run this command manually:\n'));
|
|
164
|
+
console.log(chalk_1.default.cyan(` ${mcpCmd}\n`));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async function attemptAutoRestart() {
|
|
169
|
+
console.log(chalk_1.default.yellow('\n ⚠️ RESTART REQUIRED\n'));
|
|
170
|
+
console.log(chalk_1.default.gray(' Close this terminal and open a new one to activate CodeBakers.\n'));
|
|
171
|
+
// Note: Auto-restart is risky and could lose user work
|
|
172
|
+
// We show instructions instead of forcibly restarting
|
|
173
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -24,12 +24,25 @@ export declare const PROVISIONABLE_KEYS: ServiceName[];
|
|
|
24
24
|
type ServiceKeys = {
|
|
25
25
|
[K in ServiceName]: string | null;
|
|
26
26
|
};
|
|
27
|
+
export type TrialStage = 'anonymous' | 'extended' | 'expired' | 'converted';
|
|
28
|
+
export interface TrialState {
|
|
29
|
+
trialId: string;
|
|
30
|
+
stage: TrialStage;
|
|
31
|
+
deviceHash: string;
|
|
32
|
+
expiresAt: string;
|
|
33
|
+
startedAt: string;
|
|
34
|
+
extendedAt?: string;
|
|
35
|
+
githubUsername?: string;
|
|
36
|
+
projectId?: string;
|
|
37
|
+
projectName?: string;
|
|
38
|
+
}
|
|
27
39
|
interface ConfigSchema {
|
|
28
40
|
apiKey: string | null;
|
|
29
41
|
apiUrl: string;
|
|
30
42
|
experienceLevel: ExperienceLevel;
|
|
31
43
|
serviceKeys: ServiceKeys;
|
|
32
44
|
lastKeySync: string | null;
|
|
45
|
+
trial: TrialState | null;
|
|
33
46
|
}
|
|
34
47
|
export declare function getApiKey(): string | null;
|
|
35
48
|
export declare function setApiKey(key: string): void;
|
|
@@ -72,4 +85,24 @@ export declare function writeKeysToEnvFile(projectPath: string, options?: {
|
|
|
72
85
|
export declare function validateKeyFormat(name: ServiceName, value: string): boolean;
|
|
73
86
|
export declare function getConfigPath(): string;
|
|
74
87
|
export declare function getConfigStore(): ConfigSchema;
|
|
88
|
+
export declare function getTrialState(): TrialState | null;
|
|
89
|
+
export declare function setTrialState(trial: TrialState): void;
|
|
90
|
+
export declare function clearTrialState(): void;
|
|
91
|
+
export declare function updateTrialState(updates: Partial<TrialState>): void;
|
|
92
|
+
/**
|
|
93
|
+
* Check if the current trial has expired
|
|
94
|
+
*/
|
|
95
|
+
export declare function isTrialExpired(): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Get the number of days remaining in the trial
|
|
98
|
+
*/
|
|
99
|
+
export declare function getTrialDaysRemaining(): number;
|
|
100
|
+
/**
|
|
101
|
+
* Check if user has any valid access (API key OR active trial)
|
|
102
|
+
*/
|
|
103
|
+
export declare function hasValidAccess(): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Get authentication mode: 'apiKey', 'trial', or 'none'
|
|
106
|
+
*/
|
|
107
|
+
export declare function getAuthMode(): 'apiKey' | 'trial' | 'none';
|
|
75
108
|
export {};
|
package/dist/config.js
CHANGED
|
@@ -24,6 +24,14 @@ exports.writeKeysToEnvFile = writeKeysToEnvFile;
|
|
|
24
24
|
exports.validateKeyFormat = validateKeyFormat;
|
|
25
25
|
exports.getConfigPath = getConfigPath;
|
|
26
26
|
exports.getConfigStore = getConfigStore;
|
|
27
|
+
exports.getTrialState = getTrialState;
|
|
28
|
+
exports.setTrialState = setTrialState;
|
|
29
|
+
exports.clearTrialState = clearTrialState;
|
|
30
|
+
exports.updateTrialState = updateTrialState;
|
|
31
|
+
exports.isTrialExpired = isTrialExpired;
|
|
32
|
+
exports.getTrialDaysRemaining = getTrialDaysRemaining;
|
|
33
|
+
exports.hasValidAccess = hasValidAccess;
|
|
34
|
+
exports.getAuthMode = getAuthMode;
|
|
27
35
|
const conf_1 = __importDefault(require("conf"));
|
|
28
36
|
const fs_1 = require("fs");
|
|
29
37
|
const path_1 = require("path");
|
|
@@ -110,13 +118,14 @@ exports.PROVISIONABLE_KEYS = ['github', 'supabase', 'vercel'];
|
|
|
110
118
|
const defaultServiceKeys = Object.fromEntries(exports.SERVICE_KEYS.map(key => [key, null]));
|
|
111
119
|
const config = new conf_1.default({
|
|
112
120
|
projectName: 'codebakers',
|
|
113
|
-
projectVersion: '1.
|
|
121
|
+
projectVersion: '1.8.0',
|
|
114
122
|
defaults: {
|
|
115
123
|
apiKey: null,
|
|
116
124
|
apiUrl: 'https://codebakers.ai',
|
|
117
125
|
experienceLevel: 'intermediate',
|
|
118
126
|
serviceKeys: defaultServiceKeys,
|
|
119
127
|
lastKeySync: null,
|
|
128
|
+
trial: null,
|
|
120
129
|
},
|
|
121
130
|
// Migration to add new keys when upgrading from old version
|
|
122
131
|
migrations: {
|
|
@@ -131,6 +140,12 @@ const config = new conf_1.default({
|
|
|
131
140
|
}
|
|
132
141
|
store.set('serviceKeys', newKeys);
|
|
133
142
|
},
|
|
143
|
+
'1.8.0': (store) => {
|
|
144
|
+
// Add trial field if not present
|
|
145
|
+
if (!store.has('trial')) {
|
|
146
|
+
store.set('trial', null);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
134
149
|
},
|
|
135
150
|
});
|
|
136
151
|
// ============================================================
|
|
@@ -329,3 +344,70 @@ function getConfigPath() {
|
|
|
329
344
|
function getConfigStore() {
|
|
330
345
|
return config.store;
|
|
331
346
|
}
|
|
347
|
+
// ============================================================
|
|
348
|
+
// Trial State Management (Zero-Friction Onboarding)
|
|
349
|
+
// ============================================================
|
|
350
|
+
function getTrialState() {
|
|
351
|
+
return config.get('trial');
|
|
352
|
+
}
|
|
353
|
+
function setTrialState(trial) {
|
|
354
|
+
config.set('trial', trial);
|
|
355
|
+
}
|
|
356
|
+
function clearTrialState() {
|
|
357
|
+
config.set('trial', null);
|
|
358
|
+
}
|
|
359
|
+
function updateTrialState(updates) {
|
|
360
|
+
const current = config.get('trial');
|
|
361
|
+
if (current) {
|
|
362
|
+
config.set('trial', { ...current, ...updates });
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Check if the current trial has expired
|
|
367
|
+
*/
|
|
368
|
+
function isTrialExpired() {
|
|
369
|
+
const trial = config.get('trial');
|
|
370
|
+
if (!trial)
|
|
371
|
+
return true;
|
|
372
|
+
const expiresAt = new Date(trial.expiresAt);
|
|
373
|
+
return new Date() > expiresAt;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Get the number of days remaining in the trial
|
|
377
|
+
*/
|
|
378
|
+
function getTrialDaysRemaining() {
|
|
379
|
+
const trial = config.get('trial');
|
|
380
|
+
if (!trial)
|
|
381
|
+
return 0;
|
|
382
|
+
const expiresAt = new Date(trial.expiresAt);
|
|
383
|
+
const now = new Date();
|
|
384
|
+
const diffMs = expiresAt.getTime() - now.getTime();
|
|
385
|
+
const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
|
|
386
|
+
return Math.max(0, diffDays);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Check if user has any valid access (API key OR active trial)
|
|
390
|
+
*/
|
|
391
|
+
function hasValidAccess() {
|
|
392
|
+
// Paid users always have access
|
|
393
|
+
const apiKey = config.get('apiKey');
|
|
394
|
+
if (apiKey)
|
|
395
|
+
return true;
|
|
396
|
+
// Check trial
|
|
397
|
+
const trial = config.get('trial');
|
|
398
|
+
if (!trial)
|
|
399
|
+
return false;
|
|
400
|
+
return !isTrialExpired();
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get authentication mode: 'apiKey', 'trial', or 'none'
|
|
404
|
+
*/
|
|
405
|
+
function getAuthMode() {
|
|
406
|
+
const apiKey = config.get('apiKey');
|
|
407
|
+
if (apiKey)
|
|
408
|
+
return 'apiKey';
|
|
409
|
+
const trial = config.get('trial');
|
|
410
|
+
if (trial && !isTrialExpired())
|
|
411
|
+
return 'trial';
|
|
412
|
+
return 'none';
|
|
413
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,9 @@ const config_js_1 = require("./commands/config.js");
|
|
|
23
23
|
const audit_js_1 = require("./commands/audit.js");
|
|
24
24
|
const heal_js_1 = require("./commands/heal.js");
|
|
25
25
|
const push_patterns_js_1 = require("./commands/push-patterns.js");
|
|
26
|
+
const go_js_1 = require("./commands/go.js");
|
|
27
|
+
const extend_js_1 = require("./commands/extend.js");
|
|
28
|
+
const billing_js_1 = require("./commands/billing.js");
|
|
26
29
|
// Show welcome message when no command is provided
|
|
27
30
|
function showWelcome() {
|
|
28
31
|
console.log(chalk_1.default.blue(`
|
|
@@ -35,7 +38,7 @@ function showWelcome() {
|
|
|
35
38
|
╚═══════════════════════════════════════════════════════════╝
|
|
36
39
|
`));
|
|
37
40
|
console.log(chalk_1.default.white(' Getting Started:\n'));
|
|
38
|
-
console.log(chalk_1.default.cyan(' codebakers
|
|
41
|
+
console.log(chalk_1.default.cyan(' codebakers go') + chalk_1.default.gray(' Start free trial instantly (no signup!)'));
|
|
39
42
|
console.log(chalk_1.default.cyan(' codebakers scaffold') + chalk_1.default.gray(' Create a new project from scratch'));
|
|
40
43
|
console.log(chalk_1.default.cyan(' codebakers init') + chalk_1.default.gray(' Add patterns to existing project\n'));
|
|
41
44
|
console.log(chalk_1.default.white(' Development:\n'));
|
|
@@ -55,9 +58,9 @@ function showWelcome() {
|
|
|
55
58
|
console.log(chalk_1.default.cyan(' codebakers heal') + chalk_1.default.gray(' Auto-detect and fix common issues'));
|
|
56
59
|
console.log(chalk_1.default.cyan(' codebakers doctor') + chalk_1.default.gray(' Check CodeBakers setup\n'));
|
|
57
60
|
console.log(chalk_1.default.white(' All Commands:\n'));
|
|
58
|
-
console.log(chalk_1.default.gray(' setup, scaffold, init, generate, upgrade, status
|
|
59
|
-
console.log(chalk_1.default.gray('
|
|
60
|
-
console.log(chalk_1.default.gray(' serve, mcp-config, mcp-uninstall\n'));
|
|
61
|
+
console.log(chalk_1.default.gray(' go, extend, billing, setup, scaffold, init, generate, upgrade, status'));
|
|
62
|
+
console.log(chalk_1.default.gray(' audit, heal, doctor, config, login, install, uninstall'));
|
|
63
|
+
console.log(chalk_1.default.gray(' install-hook, uninstall-hook, serve, mcp-config, mcp-uninstall\n'));
|
|
61
64
|
console.log(chalk_1.default.gray(' Run ') + chalk_1.default.cyan('codebakers <command> --help') + chalk_1.default.gray(' for more info\n'));
|
|
62
65
|
}
|
|
63
66
|
const program = new commander_1.Command();
|
|
@@ -65,7 +68,22 @@ program
|
|
|
65
68
|
.name('codebakers')
|
|
66
69
|
.description('CodeBakers CLI - Production patterns for AI-assisted development')
|
|
67
70
|
.version('2.9.0');
|
|
68
|
-
//
|
|
71
|
+
// Zero-friction trial entry (no signup required)
|
|
72
|
+
program
|
|
73
|
+
.command('go')
|
|
74
|
+
.alias('start')
|
|
75
|
+
.description('Start using CodeBakers instantly (no signup required)')
|
|
76
|
+
.action(go_js_1.go);
|
|
77
|
+
program
|
|
78
|
+
.command('extend')
|
|
79
|
+
.description('Extend your free trial with GitHub')
|
|
80
|
+
.action(extend_js_1.extend);
|
|
81
|
+
program
|
|
82
|
+
.command('billing')
|
|
83
|
+
.alias('subscribe')
|
|
84
|
+
.description('Manage subscription or upgrade to paid plan')
|
|
85
|
+
.action(billing_js_1.billing);
|
|
86
|
+
// Primary command - one-time setup (for paid users)
|
|
69
87
|
program
|
|
70
88
|
.command('setup')
|
|
71
89
|
.description('One-time setup: login + configure Claude Code (recommended)')
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device fingerprinting for zero-friction trial system
|
|
3
|
+
* Creates a stable, unique identifier for each device to prevent trial abuse
|
|
4
|
+
*/
|
|
5
|
+
export interface DeviceFingerprint {
|
|
6
|
+
machineId: string;
|
|
7
|
+
deviceHash: string;
|
|
8
|
+
platform: string;
|
|
9
|
+
hostname: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get a complete device fingerprint
|
|
13
|
+
* The deviceHash is the primary identifier used for trial tracking
|
|
14
|
+
*/
|
|
15
|
+
export declare function getDeviceFingerprint(): DeviceFingerprint;
|
|
16
|
+
/**
|
|
17
|
+
* Validate that we can create a fingerprint
|
|
18
|
+
* Used for diagnostics
|
|
19
|
+
*/
|
|
20
|
+
export declare function canCreateFingerprint(): {
|
|
21
|
+
success: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
};
|