@codebakers/cli 2.9.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +328 -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 +1319 -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 +386 -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 +1524 -17
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
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getDeviceFingerprint = getDeviceFingerprint;
|
|
37
|
+
exports.canCreateFingerprint = canCreateFingerprint;
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const crypto = __importStar(require("crypto"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
/**
|
|
42
|
+
* Get a stable machine identifier based on OS
|
|
43
|
+
* - Windows: MachineGuid from registry
|
|
44
|
+
* - macOS: IOPlatformUUID from system
|
|
45
|
+
* - Linux: /etc/machine-id
|
|
46
|
+
*/
|
|
47
|
+
function getMachineId() {
|
|
48
|
+
try {
|
|
49
|
+
const platform = os.platform();
|
|
50
|
+
if (platform === 'win32') {
|
|
51
|
+
// Windows: Use MachineGuid from registry
|
|
52
|
+
const output = (0, child_process_1.execSync)('reg query "HKLM\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
53
|
+
const match = output.match(/MachineGuid\s+REG_SZ\s+(.+)/);
|
|
54
|
+
if (match && match[1]) {
|
|
55
|
+
return match[1].trim();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (platform === 'darwin') {
|
|
59
|
+
// macOS: Use hardware UUID
|
|
60
|
+
const output = (0, child_process_1.execSync)('ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
61
|
+
const match = output.match(/"IOPlatformUUID"\s*=\s*"(.+)"/);
|
|
62
|
+
if (match && match[1]) {
|
|
63
|
+
return match[1];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Linux: Use machine-id
|
|
68
|
+
const output = (0, child_process_1.execSync)('cat /etc/machine-id', {
|
|
69
|
+
encoding: 'utf-8',
|
|
70
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
71
|
+
});
|
|
72
|
+
return output.trim();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Fallback handled below
|
|
77
|
+
}
|
|
78
|
+
// Fallback: Create a stable hash from hostname + username + home directory
|
|
79
|
+
// This is less reliable but works when we can't access system IDs
|
|
80
|
+
const fallbackData = [
|
|
81
|
+
os.hostname(),
|
|
82
|
+
os.userInfo().username,
|
|
83
|
+
os.homedir(),
|
|
84
|
+
os.platform(),
|
|
85
|
+
os.arch(),
|
|
86
|
+
].join('|');
|
|
87
|
+
return crypto.createHash('sha256').update(fallbackData).digest('hex').slice(0, 36);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get a complete device fingerprint
|
|
91
|
+
* The deviceHash is the primary identifier used for trial tracking
|
|
92
|
+
*/
|
|
93
|
+
function getDeviceFingerprint() {
|
|
94
|
+
const machineId = getMachineId();
|
|
95
|
+
// Collect stable machine characteristics
|
|
96
|
+
const fingerprintData = {
|
|
97
|
+
machineId,
|
|
98
|
+
hostname: os.hostname(),
|
|
99
|
+
username: os.userInfo().username,
|
|
100
|
+
platform: os.platform(),
|
|
101
|
+
arch: os.arch(),
|
|
102
|
+
cpuModel: os.cpus()[0]?.model || 'unknown',
|
|
103
|
+
totalMemory: Math.floor(os.totalmem() / (1024 * 1024 * 1024)), // GB rounded
|
|
104
|
+
homeDir: os.homedir(),
|
|
105
|
+
};
|
|
106
|
+
// Create a stable hash from all characteristics
|
|
107
|
+
const deviceHash = crypto
|
|
108
|
+
.createHash('sha256')
|
|
109
|
+
.update(JSON.stringify(fingerprintData))
|
|
110
|
+
.digest('hex');
|
|
111
|
+
return {
|
|
112
|
+
machineId,
|
|
113
|
+
deviceHash,
|
|
114
|
+
platform: os.platform(),
|
|
115
|
+
hostname: os.hostname(),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate that we can create a fingerprint
|
|
120
|
+
* Used for diagnostics
|
|
121
|
+
*/
|
|
122
|
+
function canCreateFingerprint() {
|
|
123
|
+
try {
|
|
124
|
+
const fp = getDeviceFingerprint();
|
|
125
|
+
if (fp.deviceHash && fp.deviceHash.length === 64) {
|
|
126
|
+
return { success: true };
|
|
127
|
+
}
|
|
128
|
+
return { success: false, error: 'Invalid fingerprint generated' };
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|